Multiple spaces in a file name passed to a bash script - bash

Suppose I have a script named script1, and I want to pass to it one file named file in 1 (with 4 spaces between "file and "in")
So, I want to be able to pass this file name to the script without using "":
./script1 file in 1
instead of:
./script1 "file in 1"
I tried to use "$*" in the script to receive the file name correctly, but what the script receives is: file in 1 (the spaces are omitted)
Is there a way to do that in bash?

There is nothing you can or should do. Suppose your script is (correctly) written as
#!/bin/bash
cat "$1"
If your instructor expects your script to handle
./script file in 1
and
./script "file in 1"
identically, your instructor is profoundly mistaken about how the shell works.

The command:
./script file in 1
will execute ./scrip with three distinct input files: file, in, and 1. If you want ./scrip to read file in 1 as a single file but don't want to use quotes in the call, then you have some options:
#!/usr/bin/env bash
# Not ideal, as it also matches any number of characters
# between "file", "in" and "1": file--in--1
cat "$(ls $1*$2*$3)"
# Better but also matches single (or any number) of spaces: file in 1
cat "$(ls $1*$2*$3 | grep "$1 *$2 *$3")"
# Will only match: file in 1
cat "$(ls $1*$2*$3 | grep -E "$1 {4}$2 {1}$3")"

Related

A Bash script that output lines in the files including a word

If I write a sh file, the script input 3 arguments of a folder, type of file (for example .txt) and a word. I need to check if there are 3 arguments at least and if not printing a message and then read all the files in the folder name and print all the lines that contains the word of the type file.
For example I have folder_name-->myScript.sh example.txt and in the example.txt we have the text:
hello word
hello everybody
good bye
And when I'll run "./example.sh folder_name hello txt" will output:
hello world
hello everybody
I try to write this:
#!/bin/bash
# Checking number of arguments.
if test "$#" -lt 3
then
echo "no enough arguments"
else
folder_name=${1}
type_file=${2}
word=${3}
# Show file contents with the word
echo "Lines that contains the ${word}:"
# cat "${}
I don't know how to use the cat to read all the files and check and then print.
If you only want to print matching lines, use grep, not cat.
The wildcard *."$type_file" will match all the files with the given suffix.
So the command should be:
cd "$folder_name"
grep -F -w "$word" *."$type_file"
The -F option matches $word as a fixed string, not a regular expression. -w makes it match whole words, not part of a word.
If you don't want to see the filenames before all the matching lines, add the -h option.
#!/bin/bash
# Checking number of arguments.
if test "$#" -lt 3
then
echo "no enough arguments"
else
folder_name=${1}
type_file=${2}
word=${3}
# Show file contents with the word
echo "Lines that contains the ${word}:"
cd "$folder_name"
grep -F -w "$word" *."$type_file"
fi

Rename files reading old file name and new file name from a txt file (bash)

I need to rename a bunch of files in a directory:
2016001.fas
2016002.fas
2016003.fas
...
Reading through a .txt that has the actual and the new filename tab separated:
2016001 L.innocua001
2016002 L.innocua002
2016003 L.monocytogenes001
...
Maybe using a one line for loop or a pipe in bash.
As a note, I can also have the list with actual and new filename in .csv or comma separated .txt format if needed. I appreciate the help given.
xargs -a renames.txt -n 2 sh -c 'echo mv -- "$1.fas" "$2.fas"' _
xargs -a renames.txt: process content of the renames.txt as arguments to a command.
-n 2: pick 2 arguments at a time.
sh -c: command is to run an inline shell
The inline shell 'echo mv -- "$1.fas" "$2.fas"': Performs the actual rename using arguments 1 and 2 provided by xargs.
# remove echo when output matches the intent
echo mv -- "$1.fas" "$2.fas"
Method using a shell only to read renames.txt and execute the renames:
while read -r a b; do
# Remove echo if satisfied by the output
echo mv -- "$a.fas" "$b.fas"
done <renames.txt
Alternate method with awk to transform the renames.txt file into a shell script with the rename commands:
awk '{print "mv -- "$1".fas "$2".fas"}' renames.txt
Once satisfied by the output of awk above; save to a shell script file, or pipe directly to sh.

grep output different in bash script

I am creating a bash script that will simply use grep to look through a bunch of logs for a certain string.
Something interesting happens though.
For the purpose of testing all of the log files the files are named test1.log, test2.log, test3.log, etc.
When using the grep command:
grep -oHnR TEST Logs/test*
The output contains all instances from all files in the folder as expected.
But when using a command but contained in the bash script below:
#!/bin/bash
#start
grep -oHnR $1 $2
#end
The output displays the instances from only 1 file.
When running the script I am using the following command:
bash test.bash TEST Logs/test*
Here is an example of the expected output (what occurs when simply using grep):
Logs/test2.log:8:TEST
Logs/test2.log:20:TEST
Logs/test2.log:41:TEST
Logs/test.log:2:TEST
Logs/test.log:18:TEST
and here is an example of the output received when using the bash script:
Logs/test2.log:8:TEST
Logs/test2.log:20:TEST
Logs/test2.log:41:TEST
Can someone explain to me why this happens?
When you call the line
bash test.bash TEST Logs/test*
this will be translated by the shell to
bash test.bash TEST Logs/test1.log Logs/test2.log Logs/test3.log Logs/test4.log
(if you have four log files).
The command line parameters TEST, Logs/test1.log, Logs/test2.log, etc. will be given the names $1, $2, $3, etc.; $1 will be TEST, $2 will be Logs/test1.log.
You just ignore the remaining parameters and use just one log file when you use $2 only.
A correct version would be this:
#!/bin/bash
#start
grep -oHnR "$#"
#end
This will pass all the parameters properly and also take care of nastinesses like spaces in file names (your version would have had trouble with these).
To understand what's happening, you can use a simpler script:
#!/bin/bash
echo $1
echo $2
That outputs the first two arguments, as you asked for.
You want to use the first argument, and then use all the rest as input files. So use shift like this:
#!/bin/bash
search=$1
shift
echo "$1"
echo "$#"
Notice also the use of double quotes.
In your case, because you want the search string and the filenames to be passed to grep in the same order, you don't even need to shift:
#!/bin/bash
grep -oHnR -e "$#"
(I added the -e in case the search string begins with -)
The unquoted * is being affected by globbing when you are calling the script.
Using set -x to output what is running from the script makes this more clear.
$ ./greptest.sh TEST test*
++ grep -oHnR TEST test1.log
$ ./greptest.sh TEST "test*"
++ grep -oHnR TEST test1.log test2.log test3.log
In the first case, bash is expanding the * into the list of file names versus the second case it is being passed to grep. In the first case you actually have >2 args (as each filename expanded would become an arg) - adding echo $# to the script shows this too:
$ ./greptest.sh TEST test*
++ grep -oHnR TEST test1.log
++ echo 4
4
$ ./greptest.sh TEST "test*"
++ grep -oHnR TEST test1.log test2.log test3.log
++ echo 2
2
You probably want to escape the wildcard on your bash invocation:
bash test.bash TEST Logs/test\*
That way it'll get passed through to grep as an *, otherwise the shell will have expanded it to every file in the Logs dir whose name starts with test.
Alternatively, change your script to allow more than one file on the command line:
#!/bin/bash
hold=$1
shift
grep -oHnR $hold $#

How to pass a shell script argument as a variable to be used when executing grep command

I have a file called fruit.txt which contains a list of fruit names (apple, banana.orange,kiwi etc). I want to create a script that allows me to pass an argument when calling the script i.e. script.sh orange which will then search the file fruit.txt for the variable (orange) using grep. I have the following script...
script name and argument as follows:
script.sh orange
script snippet as follows:
#!/bin/bash
nameFind=$1
echo `cat` "fruit.txt"|`grep` | $nameFind
But I get the grep info usage command and it seems that the script is awaiting some additional command etc. Advice greatly appreciated.
The piping syntax is incorrect there. You are piping the output of grep as input to the variable named nameFind. So when the grep command tries to execute it is only getting the contents of fruit.txt. Do this instead:
#!/bin/bash
nameFind=$1
grep "$nameFind" fruit.txt
Something like this should work:
#!/bin/bash
name="$1"
grep "$name" fruit.txt
There's no need to use cat and grep together; you can simply pass the name of the file as the third argument, after the pattern to be matched. If you want to match fixed strings (i.e. no regular expressions), you can also use the -F modifier:
grep -F "$name" fruit.txt

how to pass file as an argument to the script file

I have a shell script written in bash and this script should take file as an argument,can any one tell me how to write script for this any ideas on this are apprecited
Thanks,
You can access the command line arguments passed to your script using positional parameters.
Also to check if the right number of arguments have been passed to the script, you can make use of the variable $# which holds the number of arguments passed.
if [ $# -eq 1 ]; then
# exactly 1 argument was passed..use it..its available in $1
echo "Argument $1"
else
# either 0 or >1 arguments were passed...error out.
echo "Incorrect number of arguments passed"
exit 1
fi
Sample run:
$ bash a.sh
Incorrect number of arguments passed
$ bash a.sh foo
Argument foo
$ bash a.sh foo bar
Incorrect number of arguments passed
$
If you need to operate on the file, you can take the name of the file as an argument and just use the file with the specified name.
If you just need to read the contents of the file, you can use redirection to have the script read the contents of the file on standard in. You can do this using ./script < inputfile

Resources