Reputation: 374
This is an example I created to demonstrate the quirky behavior. I expected bash to pass the quoted command line argument as is.
john@doe:~/tmp$ cat script.sh
#! /bin/bash
set -o xtrace
$1 sleep 3
john@doe:~/tmp$ ./script.sh "echo"
+ echo sleep 3
sleep 3
john@doe:~/tmp$ ./script.sh "echo -n"
+ echo -n sleep 3
sleep 3john@doe:~/tmp$
But sometimes bash edits my string argument, and add ticks around symbols like ; or &&
john@doe:~/tmp$ ./script.sh "echo hello ;"
+ echo hello ';' sleep 3
hello ; sleep 3
john@doe:~/tmp$ ./script.sh "echo hello && "
+ echo hello '&&' sleep 3
hello && sleep 3
What is this bash rule and how to circumvent it? thanks a lot :)
Upvotes: 4
Views: 395
Reputation: 46903
The rule is outlined in the manual: Section Shell Operation.
You'll read:
The following is a brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:
- Reads its input from a file (see Shell Scripts), from a string supplied as an argument to the
-c
invocation option (see Invoking Bash), or from the user’s terminal.- Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by metacharacters. Alias expansion is performed by this step (see Aliases).
- Parses the tokens into simple and compound commands (see Shell Commands).
- Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments.
- Performs any necessary redirections (see Redirections) and removes the redirection operators and their operands from the argument list.
- Executes the command (see Executing Commands).
- Optionally waits for the command to complete and collects its exit status (see Exit Status).
So, with you script:
#! /bin/bash
$1 sleep 3
What happens when you execute ./script "echo hello ;"
?
;
that is in your argument echo hello ;
.
At this point, it only sees the three tokens $1
, sleep
and 3
.$1
is expanded into echo
, hello
and ;
(not that the semicolon here just becomes an argument, it's not understood as a control operator, as the scanning of control operators was already done in step 2 and will not be performed anymore).echo
with arguments hello
, ;
, sleep
and 3
. So it happily echoes hello ; sleep 3
.echo
.There are 2 ways to circumvent this: the first one is to use eval
, but this is dangerous and has many caveats: replace your script with
#!/bin/bash
eval "$1 sleep 3"
With this script:
$ ./script "echo hello;"
hello
$
(and the final prompt is displayed after a delay of 3 seconds). Same for ./script "echo hello &&"
. But this is not a good idea. Read on.
As a general rule, you should avoid (at least in Bash) mixing data and code: a variable is here to hold data (strings), not code. So this technique isn't a good one. I'm saying it's not a good one not only because someone once said that you shouldn't mix data with code, but because it will become unusable if you're starting to use special characters in your “code”. For example, if you want to echo the *
symbol, you'll have to use the clunky call ./script "echo \"*\";"
. And there are many other caveats.
Code is held in functions or scripts. Using a function:
say_hello_and_execute() {
echo hello; "$@"
}
Then export this function: export -f say_hello_and_execute
and use it as an argument to your script: ./script say_hello_and_execute
.
The same can be achieved with a script: call the following script say_hello_and_execute
:
#!/bin/bash
echo hello; "$@"
then chmod +x say_hello_and_execute
and run your script as ./script ./say_hello_and_execute
.
This answer tries to answer your question. Though, it would be better if you could tell us exactly what you're trying to achieve, as there might be a better design to achieve that.
Upvotes: 1
Reputation: 72746
The rule is to quote metacharacters that would otherwise have a different interpretation. A semicolon is a statement separator, as is &&
. The quoting makes it easy to cut and paste the particular line and have it reproduce the original effect.
Think about the difference of
echo hello '&&'
versus
echo hello &&
The first echos two words, the second waits for more input. The quoting you call quirky works as designed, is what you actually want, and no, there's no way around it short of hacking bash source code.
Upvotes: 1