I have a bash file which is passed arguments which contain spaces. The bash file looks like:
#!/bin/bash
another_app "$1"
However, instead of processing the argument as a single argument, as I believe it should, it processes it as a number of arguments depending on how many spaces. For example, if I call my bash file such:
my_app "A Number Of Words"
Then the "another_app" application gets passed 4 different arguments, instead of one. How can I just pass the single argument through to the second application?
The others are correct it will depend somewhat on how the 2nd app handles the args. You can also have a little control as to how the args are passed. You can do this with some quoting or using the "$#" var as mentioned by #steve
For example app1.sh
#!/bin/bash
echo "Argument with no quotes"
./another_app.sh $1
echo "Argument with quotes"
./another_app.sh "$1"
echo "Argument with \$#"
./another_app.sh "$#"
and another_app.sh
#!/bin/bash
echo "Inside $0"
echo "Number of args passed to me: $#"
for X in "${#}"
do
echo $X
done
echo "Exiting $0"
Call the second application using "$#":
#!/bin/bash
another_app "$#"
Related
I want to create many new bash scripts through this bash. Here is my code.
#!/bin/bash
# create bash shell automatically
for file in "$*"
do
if [ ! -e "$file" ]
then
touch $file
chmod u+x $file
echo "#!/bin/bash" >> $file
echo "# " >> $file
echo "create success"
fi
done
if [ $# \> 1 ]
then
echo "$# shell files are created!"
else
echo "$# shell is created!"
fi
When I run this script like this:
./create_shell test1 test2 test3
the terminal said:
"line9:ambiguous redirect"
"line10:ambiguous redirect"
What does that mean?
The problem came from the use of $*, whether you quote or unquote it. The correct way to iterate through positional parameter is using "$#", notice the double quote.
for file in "$#"; do
: ...
done
or even POSIXly:
for file do
: ...
done
You got the error message from bash, because bash see the content of $file was not expanded to one word, it was expanded to three separated words test1, test2, test3. Try:
a='1 2 3'
echo 1 >> $a
to see what will happen.
Not to mention that you spoil the sole reason for using "$#". In order for this construct to work, you must also put double quotes around the variables that derive from it, if you don't want the split+glob operators to be invoked. Leaving variables unquote will lead to many security implications.
The '$*' is a string not the parameter array. You can do it like this
fileArr=($*)
for file in ${fileArr[#]}
do you things
The quotes around "$*" will prevent bash from parsing the passed parameter into individual tokens. The loop will be executed once with all of the parameters passed (i.e. if you ran test.sh one two three then the echo command will try to redirect to "one two three"). Just remove the quotes.
Say I let a user input as many arguments as he wants, how do read all the inputs?
So if a user typed in asdf asfd asdf, it should say 3 arguments
Right now I have
#!/bin/bash
read $#
echo "There are $# arguments"
but whenever I type in anything it always equals 0
If you want to read all the parameters from a variable using the same style as if you would have done it from the command line. You could do it in a function
#!/bin/bash
function param_count() {
for arg in $#
do
echo " arg: $arg"
done
echo "arg count= $#"
}
read foo
param_count $foo
If you really want to read a line as a sequence of words, your best bet in bash is to read it into an array:
$ read -a words
And now a few words from our sponsor
$ echo ${#words[#]}
8
$ echo "${words[3]}"
few
But that's not the way to pass arguments to a shell script. read just splits lines into words using whitespace (or whatever IFS is set to.) It does not:
Handle quotes
Allow the insertion of shell variables
Expand pathname patterns
Passing arguments on the command line lets you do all of the above, making the utility more convenient to use, and does not require any extra work to extract the arguments, making the utility easier to write.
This is the way to get it:
read ARGUMENTS
set -- $ARGUMENTS
echo "There are $# arguments"
Explanation:
read ARGUMENTS
Read the input and save it into ARGUMENTS
set -- $ARGUMENTS
Set the positional parameters to the given input
At this point, you can use that input as if it was given in the command-line.
e.g.:
echo "$1"
echo "$2"
...
Delete read $# line.
Then launch your script with arguments. Just like this:
/path/to/script arg1 arg2 arg3 4 5 6
The output should be:
There are 6 arguments
Here is an example script that shows you how to get the argument count, access the individual arguments and read in a new variable:
#!/usr/bin/env bash
echo "There are $# arguments, they are:"
for arg in $#
do
echo " - $arg"
done
# Access the aguments
echo "The first argument is $1"
echo "The second argument is $2"
echo "The third argument is $3"
echo "All arguments: $#"
# Read a variable
read var
echo "Some var: $var"
I'm trying to write a "phone home" script, which will log the exact command line (including any single or double quotes used) into a MySQL database. As a backend, I have a cgi script which wraps the database. The scripts themselves call curl on the cgi script and include as parameters various arguments, including the verbatim command line.
Obviously I have quite a variety of quote escaping to do here and I'm already stuck at the bash stage. At the moment, I can't even get bash to print verbatim the arguments provided:
Desired output:
$ ./caller.sh -f -hello -q "blah"
-f hello -q "blah"
Using echo:
caller.sh:
echo "$#"
gives:
$ ./caller.sh -f -hello -q "blah"
-f hello -q blah
(I also tried echo $# and echo $*)
Using printf %q:
caller.sh:
printf %q $#
printf "\n"
gives:
$ ./caller.sh -f hello -q "blah"
-fhello-qblah
(I also tried print %q "$#")
I would welcome not only help to fix my bash problem, but any more general advice on implementing this "phone home" in a tidier way!
There is no possible way you can write caller.sh to distinguish between these two commands invoked on the shell:
./caller.sh -f -hello -q "blah"
./caller.sh -f -hello -q blah
There are exactly equivalent.
If you want to make sure the command receives special characters, surround the argument with single quotes:
./caller.sh -f -hello -q '"blah"'
Or if you want to pass just one argument to caller.sh:
./caller.sh '-f -hello -q "blah"'
You can get this info from the shell history:
function myhack {
line=$(history 1)
line=${line#* }
echo "You wrote: $line"
}
alias myhack='myhack #'
Which works as you describe:
$ myhack --args="stuff" * {1..10} $PATH
You wrote: myhack --args="stuff" * {1..10} $PATH
However, quoting is just the user's way of telling the shell how to construct the program's argument array. Asking to log how the user quotes their arguments is like asking to log how hard the user punched the keys and what they were wearing at the time.
To log a shell command line which unambiguously captures all of the arguments provided, you don't need any interactive shell hacks:
#!/bin/bash
line=$(printf "%q " "$#")
echo "What you wrote would have been indistinguishable from: $line"
I understand you want to capture the arguments given by the caller.
Firstly, quotes used by the caller are used to protect during the interpretation of the call. But they do not exist as argument.
An example: If someone call your script with one argument "Hello World!" with two spaces between Hello and World. Then you have to protect ALWAYS $1 in your script to not loose this information.
If you want to log all arguments correctly escaped (in the case where they contains, for example, consecutive spaces...) you HAVE to use "$#" with double quotes. "$#" is equivalent to "$1" "$2" "$3" "$4" etc.
So, to log arguments, I suggest the following at the start of the caller:
i=0
for arg in "$#"; do
echo "arg$i=$arg"
let ++i
done
## Example of calls to the previous script
#caller.sh '1' "2" 3 "4 4" "5 5"
#arg1=1
#arg2=2
#arg3=3
#arg4=4 4
#arg5=5 5
#Flimm is correct, there is no way to distinguish between arguments "foo" and foo, simply because the quotes are removed by the shell before the program receives them. What you need is "$#" (with the quotes).
I'm writing a bash script which has to pass a variable to another program:
./program $variable
The problem is, it is absolutely necessary for $variable to be passed as a single parameter, which isn't the case if it contains whitespace.
variable=Hello World
./program $variable
-> program receives two arguments: 'Hello' 'World'
Quoting it doesn't do anything at all (well done, bash devs):
variable=Hello World
./program "$variable"
-> program receives: 'Hello' 'World'
Double quoting it does crazy stuff (well done, bash devs):
variable=Hello World
./program "'$variable'"
-> program receives: "'Hello" "World'"
Is there an easy way to do this? Heck, is there a way to do this at all?
Update: Okay, since the problem doesn't seem to be bash, here's some additional info.
The program I'm passing arguments to is a python script. Without modifying the arguments in any way, I get
print sys.argv
-> ['/usr/bin/program', "'Hello", "World'"]
How can I fix that?
Edit: No, I haven't tried
variable="Hello World"
because I never declare $variable. It's not being declared inside my bash function and I'm not allowed to modify it.
Edit: Okay, I got it to work that way.
local temp="$variable"
./program "$temp"
I'd like to know why it works that way and not any other way, though.
did you try with var="hello world"?
i tried this in my solaris box.
> setenv var "hello world"
> cat temp.sh
#!/bin/sh
echo $1
echo $2
> ./temp.sh "$var"
hello world
>
as you can see the $2 is not printed.$var is considered as only one argument.
When you call your script pass the arguments within quotes.
Example script:
#!/bin/bash
for arg in "$#"; do
echo "arg: $1";
shift;
done
When you call it with:
./program "parameter with multiple words" parameter2 parameter3 "another parameter"
The output should be:
arg: parameter with multiple words
arg: parameter2
arg: parameter3
arg: another parameter
Have a look on http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html .
The problem is that the expansion of variables is done before of the command line parameters hence your behavior.
You might work it arround with setting IFS to something weird as
IFS='###' V='foo bar baz'; ./program $V
The problem seems to be inside the "program"
variable="Hello World" # quotes are needed because of the space
./program "$variable" # here quotes again
and inside the program
echo "program received $# arguments:"
i=1
for arg in "$#" # again quotes needed
do echo "arg $((i=i+1)): '$arg'" # and again
done
This is almost certainly a problem in the way you are reading the variable in your program.
For instance suppose this is your script (just one line for testing):
echo "$1"
Let's call it echo.sh. If you run echo.sh "test best", you will get test best.
But if your program says
echo $1
you might get behaviour like what you're seeing.
I have two shell scripts. One script dynamically creates the call to the second script, based on the parameter it received, and then executes the call.
My problem is that the parameters the first script gets may contain spaces, so I must quote the parameter in the call to script2.
This is an example to the problem:
script1.sh:
#!/bin/sh
param=$1
command="./script2.sh \"$param\""
echo $command
$command
script2.sh:
#!/bin/sh
param=$1
echo "the value of param is $param"
When I run:
./script1.sh "value with spaces"
I get:
./script2.sh "value with spaces"
the value of param is "value
Which is of course not what I need.
What is wrong here??
TIA.
EDIT :
I found the solution thanks to the useful link in tripleee's comment. Here it is in case it helps anybody.
In short, in order to solve this, one should use an array for the arguments.
script1.sh:
#!/bin/sh
param=$1
args=("$param")
script_name="./script2.sh"
echo $script_name "${args[#]}"
$script_name "${args[#]}"
Use "$#" to refer to all command-line parameters with quoting intact.