How can I preserve quotes in printing a bash script's arguments
The quotes are removed before the arguments are passed to your script, so it's too late to preserve them. What you can do is preserve their effect when passing the arguments to the inner command, and reconstruct an equivalent quoted/escaped version of the arguments for printing.
For passing the arguments to the inner command "$@"
-- with the double-quotes, $@ preserves the original word breaks, meaning that the inner command receives exactly the same argument list that your script did.
For printing, you can use the %q format in bash's printf command to reconstruct the quoting. Note that this won't always reconstruct the original quoting, but will construct an equivalent quoted/escaped string. For example, if you passed the argument 'uptime ; uname -a'
it might print uptime\ \;\ uname\ -a
or "uptime ; uname -a"
or any other equivalent (see @William Pursell's answer for similar examples).
Here's an example of using these:
printf "Running command:"
printf " %q" innercmd "$@" # note the space before %q -- this inserts spaces between arguments
printf "\n"
innercmd "$@"
If you have bash version 4.4 or later, you can use the @Q
modifier on parameter expansions to add quoting. This tends to prefer using single-quotes (as opposed to printf %q
's preference for escapes). You can combine this with $*
to get a reasonable result:
echo "Running command: innercmd ${*@Q}"
innercmd "$@"
Note that $*
mashes all arguments together into a single string with whitespace between them, which is normally not useful, but in this case each argument is individually quoted so the result is actually what you (probably) want. (Well, unless you changed IFS
, in which case the "whitespace" between arguments will be the first character of $IFS
, which may not be what you want.)
Bash stripping quotes - how to preserve quotes
I typically use single quotes to avoid shell interpolation when you want to quote strings with quotes in them.
./test.sh -t some.comman -extraswitch '"some addtional info"'
Printing command line arguments in quoted form
1. Using parameter expansion:
You could try inline:
$ set -- 'foo bar' baz
$ echo ${@@Q}
'Foo bar' 'baz'
$ echo ${@@A}
set -- 'Foo bar' 'baz'
$ DOLLAR_AT="'one two' three"
$ echo ${DOLLAR_AT@Q}
''\''one two'\'' three'
$ echo ${DOLLAR_AT@A}
DOLLAR_AT=''\''one two'\'' three'
More info in bash's manpage, under Parameter Expansion subsection.
${parameter@operator}
Parameter transformation. The expansion is either a transforma‐
tion of the value of parameter or information about parameter
itself, depending on the value of operator. Each operator is a
single letter:
...
Q The expansion is a string that is the value of parameter
quoted in a format that can be reused as input.
...
A The expansion is a string in the form of an assignment
statement or declare command that, if evaluated, will
recreate parameter with its attributes and value.
For re-using this:
$ myString1=${@@Q}
$ set -- something\ else
$ . <(echo "set -- $myString1")
$ echo ${@@A}
set -- 'Foo bar' 'baz'
1.1. Difference between $*
and $@
.
Simply $*
is used to generate strings where $@
could generate both arrays or stringS, depending on context.
From bash's manpage:
* Expands to the positional parameters, starting from one...
... That is, "$*" is equivalent to "$1c$2c...", where c is
the first character of the value of the IFS variable...
@ Expands to the positional parameters, starting from one...
... That is, "$@" is equivalent to "$1" "$2" ...
Small demo:
$ paramExpDemo() {
local IFS=+ _arry
_arry="${@@Q}" # var="..." -> context string
declare -p _arry
_arry="${*@Q}"
declare -p _arry
_arry=("${@@Q}") # var=(...) -> context array
declare -p _arry
_arry=("${*@Q}")
declare -p _arry
}
then
$ paramExpDemo 'foo bar' baz
declare -- _arry="'foo bar'+'baz'"
declare -- _arry="'foo bar'+'baz'"
declare -a _arry=([0]="'foo bar'" [1]="'baz'")
declare -a _arry=([0]="'foo bar'+'baz'")
In context string
, $@
and $*
will give same result, but in context array
, $*
will return a string when $@
return an array.
$ set -- 'foo bar' baz
$ Array=("${@@Q}")
$ declare -p Array
declare -a Array=([0]="'foo bar'" [1]="'baz'")
$ echo "${Array[@]@Q}"
''\''foo bar'\''' ''\''baz'\'''
2. Using printf %q
As Léa Gris rightly commented out, you could use:
$ printf '%q\n' "$@"
Foo\ bar
baz
$ printf -v myString2 "%q " "$@"
$ echo "${myString2% }"
Foo\ bar baz
For re-using this:
$ set -- something\ else
$ . <(echo "set -- $myString2")
$ echo ${@@A}
set -- 'Foo bar' 'baz'
Note: As Charles Duffy commented out,this is not perfect! In fact, there is no way to retrieve which syntax was used in command line.
$ printf "<%q>\n" "$myString1" "${myString2% }"
<\'Foo\ bar\'\ \'baz\'>
<Foo\\\ bar\ baz>
As both string work for sourcing variable, they are not same, so further string manipulation could become complex...
Can I get the original quote characters surrounding arguments in my Bash script?
No, there is no way to obtain the command line from before the shell performed whitespace tokenization, wildcard expansion, and quote removal on it.
If you want to pass in literal quotes, try
./script.sh '"-a"' '"this is a sentence"' '"-b"' '"1"'
Notice also how your original command line could have been written
'./script.sh' '-a' 'this is a sentence' '-b' '1'
How to quotes in bash function parameters?
The reason this happens is because bash interprets the arguments, as you thought. The quotes simply aren't there any more when it calls the function, so this isn't possible. It worked in DOS because programs could interpret the command line themselves, not that it helps you!
Preserving escapes in bash arguments $@
You need to use an array.
cmd=( "$@" )
printf '%q\n' "${cmd[@]}"
nohup "${cmd[@]}"
Scalar variables (strings) are NUL-delimited, so they can't contain an argument list (which is, by its nature, NUL-separated).
Related Topics
How to Run a Shell Script At Startup
How to Get the Total Cpu Usage of an Application from /Proc/Pid/Stat
How to Generate New Variable Names on the Fly in a Shell Script
How to Setup Public-Key Authentication
Get Most Recent File in a Directory on Linux
How to Get Unique Values from an Array in Bash
Getting a Unique Id from a Unix-Like System
How to Include All Objects of an Archive in a Shared Object
Fast Concatenation of Multiple Gzip Files
How to Generate a Core Dump in Linux on a Segmentation Fault
What Does "&" At the End of a Linux Command Mean
How Many Socket Connections Possible
Calling Printf in X86_64 Using Gnu Assembler
Maximum Number of Processes in Linux
How to View the List of Functions a Linux Shared Library Is Exporting