How to save quotes in Bash arguments?

I have a Bash script where I want to keep quotes in the passed arguments.

Example:

./test.sh this is "some test" 

then I want to use these arguments and reuse them, including quotation marks and quotation marks in the entire argument list.

I tried using \"$@\" , but that removes the quotes inside the list.

How to do it?

+69
bash quotes
Nov 03 '09 at 16:53
source share
14 answers

Just use single quotes around a double-quoted string:

 ./test.sh this is '"some test"' 

Thus, double quotes inside single quotes are also interpreted as a string.

But I would recommend putting the whole line between single quotes:

  ./test.sh 'this is "some test" ' 

To understand what the shell does, or rather interpret the arguments in the scripts, you can write a small script similar to the following:

 #!/bin/bash echo $@ echo "$@" 

Then you will see and test what happens when the script is called with different lines.

-10
Nov 03. '09 at 16:57
source share
β€” -

using "$@" will replace the arguments as a list without re-escaping them with spaces (they were separated once when the shell script was called), which is usually what you want if you just want to repeat the -pass arguments to another program .

What are you trying to do and how does it not work?

+81
Nov 03 '09 at 19:17
source share

Yuku's answer only works if you are the only user of your script, while Dennis Williamson is great if you are mostly interested in printing lines and expecting that they don't have quotation marks.

Here's a version that can be used if you want to pass all arguments as a single large quote argument to the -c bash or su parameter:

 #!/bin/bash C='' for i in "$@"; do i="${i//\\/\\\\}" C="$C \"${i//\"/\\\"}\"" done bash -c "$C" 

So, all arguments get a quote around them (harmless if not before, for this purpose), but we also avoid any screens, and then avoid quotes that were already in the argument (syntax ${var//from/to} performs a global substring of a substring).

You could, of course, only quote material that already had spaces in it, but here it does not matter. One of the possibilities of a script like this is to have a specific predefined set of environment variables (or, with su, to run the material as a specific user without this mess for double quoting).




Update. I recently had a reason to do this with POSIX with minimal markup, which will lead to this script (the last printf prints the command line used to invoke the script, which you must have copy-paste to invoke with equivalent arguments):

 #!/bin/sh C='' for i in "$@"; do case "$i" in *\'*) i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"` ;; *) : ;; esac C="$C '$i'" done printf "$0%s\n" "$C" 

I switched to '' , since shells also interpret things like $ and !! at "" -quotes.

+39
Jan 04 '12 at 7:15
source share

If it were safe to assume that an argument containing a space should (and should) be specified, you can add them as follows:

 #!/bin/bash whitespace="[[:space:]]" for i in "$@" do if [[ $i =~ $whitespace ]] then i=\"$i\" fi echo "$i" done 

Here is an example run:

 $ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx' abc def "ghi jkl" "mno pqr" "stu vwx" 

You can also insert literal tabs and new lines using Ctrl - V Tab and Ctrl - V Ctrl - J in double or single quotes instead of using screens in $'...' .

A note on inserting characters in Bash: if you use the Vi ( set -o vi ) key bindings in Bash (Emacs defaults to set -o emacs ), you need to be in insert mode to insert characters. In Emacs mode, you are always in insert mode.

+32
Nov 03 '09 at 19:08
source share

There are two safe ways to do this:

1. Extension of shell parameters : ${variable@Q}:

When expanding a variable using ${variable@Q} :

An extension is a string that represents the value of a parameter in quotation marks in a format that can be reused as input.

Example:

 $ expand-q() { for i; do echo ${i@Q}; done; } # Same as for 'i in "$@"'... $ expand-q word "two words" 'new > line' "single'quote" 'double"quote' word 'two words' $'new\nline' 'single'\''quote' 'double"quote' 

2. printf %q "$quote-me"

printf supports internal quoting. A manual entry for printf reads:

%q Causes printf to output the corresponding argument in a format that can be used as shell input.

Example:

 $ cat test.sh #!/bin/bash printf "%q\n" "$@" $ $ ./test.sh this is "some test" 'new >line' "single'quote" 'double"quote' this is some\ test $'new\nline' single\'quote double\"quote $ 



Please note that the 2nd method is a little cleaner if you display the quoted text to a person.

Related: For bash, POSIX sh, and zsh: a single quote string of quotes, not a backslash

+24
Sep 13 '16 at 6:19 06:19
source share

As Tom Hale said, one way to do this is with printf using %q for quot-escape.

For example:

send_all_args.sh

 #!/bin/bash if [ "$#" -lt 1 ]; then quoted_args="" else quoted_args="$(printf " %q" "${@}")" fi bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_args}" 

send_fewer_args.sh

 #!/bin/bash if [ "$#" -lt 2 ]; then quoted_last_args="" else quoted_last_args="$(printf " %q" "${@:2}")" fi bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_last_args}" 

receiver.sh

 #!/bin/bash for arg in "$@"; do echo "$arg" done 

Usage example:

 $ ./send_all_args.sh $ ./send_all_args.sh ab a b $ ./send_all_args.sh "a' b" 'c "e ' a' b c "e $ ./send_fewer_args.sh $ ./send_fewer_args.sh a $ ./send_fewer_args.sh ab b $ ./send_fewer_args.sh "a' b" 'c "e ' c "e $ ./send_fewer_args.sh "a' b" 'c "e ' 'f " g' c "ef " g 
+8
Mar 03 '17 at 21:19
source share

I need this to forward all the arguments to another translator. What turned out to be right for me is:

 bash -c "$(printf ' %q' "$@")" 

Example (when it is called forward.sh):

 $ ./forward.sh echo "3 4" 3 4 $ ./forward.sh bash -c "bash -c 'echo 3'" 3 

(Of course, the real scenario I am using is more complex, including in my case nohup, redirects, etc., but this is the key part.)

+8
May 16 '18 at 9:53
source share

My problem was similar, and I used the mixed ideas posted here.

We have a server with a PHP script that sends emails. And then we have a second server that connects to the 1st server through SSH and runs it.

The script name is the same on both servers, and both are actually executed through a bash script.

On server 1 (local) bash script we have only:

 /usr/bin/php /usr/local/myscript/myscript.php "$@" 

This is located at /usr/local/bin/myscript and is called by the remote server. It works fine even for arguments with spaces.

But then on the remote server we cannot use the same logic, because the 1st server will not receive quotes from "$@" . I used ideas from John Mudd and Dennis Williamson to recreate an array of parameters and parameters with quotes. I like the idea of ​​adding escaped quotes only when there are spaces in the element.

Thus, the remote script works with:

 CSMOPTS=() whitespace="[[:space:]]" for i in "$@" do if [[ $i =~ $whitespace ]] then CSMOPTS+=(\"$i\") else CSMOPTS+=($i) fi done /usr/bin/ssh "$USER@$SERVER" "/usr/local/bin/myscript ${CSMOPTS[@]}" 

Note that I am using "${CSMOPTS[@]}" to send an array of parameters to the remote server.

Thanks for the eveyone that posted here! It really helped me! :)

+5
Jan 29 '17 at 20:59 on
source share

Changed unhammer array usage example.

 printargs() { printf "'%s' " "$@"; echo; }; # http://superuser.com/a/361133/126847 C=() for i in "$@"; do C+=("$i") # Need quotes here to append as a single array element. done printargs "${C[@]}" # Pass array to a program as a list of arguments. 
+4
Apr 01 '15 at 18:46
source share

Quotes are interpreted by bash and are not stored in command line arguments or variable values.

If you want to use quoted arguments, you must specify them every time you use them:

 val="$3" echo "Hello World" > "$val" 
+3
Nov 03 '09 at 17:03
source share

Just use:

 "${@}" 

For example:

 # cat t2.sh for I in "${@}" do echo "Param: $I" done # cat t1.sh ./t2.sh "${@}" # ./t1.sh "This is a test" "This is another line" ab "and also c" Param: This is a test Param: This is another line Param: a Param: b Param: and also c 
+1
Nov 08 '18 at 19:24
source share

Yes, it seems that it is impossible to keep quotes, but for the question I was dealing with, this was not necessary.

I have a bash function that will search the folder recursively and grep to search for a string, the problem is passing a string with spaces, such as "find this string". Passing this to the bash script will then take the base argument $ n and pass it to grep, in which case grep assumes these are different arguments. I solved this using the fact that when you quote bash to call a function, it groups the elements in quotation marks into one argument. I just needed to decorate this argument with quotation marks and pass it to the grep command.

If you know what argument you get in bash, which requires quotes for the next step, you can simply decorate it with quotes.

0
Apr 20 '18 at 16:28
source share

As Gary S. Weaver showed in his source code tips, the trick is to invoke bash with the '-c' parameter and then quote the following.

eg

 bash -c "<your program> <parameters>" 

or

 docker exec -it <my docker> bash -c "$SCRIPT $quoted_args" 
0
Jan 22 '19 at 12:22
source share

If you need to pass all the bash arguments from another programming language (for example, if you want to run bash -c or emit_bash_code | bash ), use this:

  • escape all single quote characters that you have with '\'' .
  • then put the result in quotation marks

Thus, the argument abc'def will be converted to 'abc'\''def' . The characters '\'' interpreted as follows: an existing quotation ends with the first first quote, followed by the escaped single single quote \' , then a new quote begins.

0
Aug 20 '19 at 16:04
source share



All Articles