如何在 Bash 参数中保留引号?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1668649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:39:14  来源:igfitidea点击:

How to keep quotes in Bash arguments?

bashquotes

提问by zlack

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

我有一个 Bash 脚本,我想在传递的参数中保留引号。

Example:

例子:

./test.sh this is "some test"

then I want to use those arguments, and re-use them, including quotes and quotes around the whole argument list.

然后我想使用这些参数,并重新使用它们,包括整个参数列表周围的引号和引号。

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

我尝试使用\"$@\",但这会删除列表中的引号。

How do I accomplish this?

我该如何实现?

采纳答案by Randy Sugianto 'Yuku'

Just use single quotes around the string with the double quotes:

只需在带有双引号的字符串周围使用单引号:

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

So the double quotes of inside the single quotes were also interpreted as string.

所以单引号里面的双引号也被解释为字符串。

But I would recommend to put the whole string between single quotes:

但我建议将整个字符串放在单引号之间:

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

In order to understand what the shell is doing or rather interpreting arguments in scripts, you can write a little script like this:

为了了解 shell 正在做什么或者解释脚本中的参数,您可以编写一个像这样的小脚本:

#!/bin/bash

echo $@
echo "$@"

Then you'll see and test, what's going on when calling a script with different strings

然后你会看到并测试,用不同的字符串调用脚本时会发生什么

回答by Chris Dodd

using "$@"will substitute the arguments as a list, without re-splitting them on whitespace (they were split once when the shell script was invoked), which is generally exactly what you want if you just want to re-pass the arguments to another program.

using"$@"会将参数替换为一个列表,而不会在空格上重新拆分它们(在调用 shell 脚本时将它们拆分一次),如果您只想将参数重新传递给另一个程序,这通常正是您想要的。

What are you trying to do and in what way is it not working?

你想做什么,它以什么方式不起作用?

回答by unhammer

Yuku's answer only works if you're the only user of your script, while Dennis Williamson's is great if you're mainly interested in printing the strings, and expect them to have no quotes-in-quotes.

Yuku 的答案仅在您是脚本的唯一用户时才有效,而如果您主要对打印字符串感兴趣,并且希望它们没有引号,则 Dennis Williamson 的答案很棒。

Here's a version that can be used if you want to pass all arguments as one big quoted-string argument to the -cparameter of bashor su:

如果要将所有参数作为一个大的带引号的字符串参数传递给or的-c参数,则可以使用以下版本:bashsu

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

So, allthe arguments get a quote around them (harmless if it wasn't there before, for this purpose), but we also escape any escapes and then escape any quotes that were already in an argument (the syntax ${var//from/to}does global substring substitution).

因此,所有参数都会在它们周围得到一个引号(为此目的,如果它之前不存在则无害),但我们也会转义任何转义,然后转义已经在参数中的任何引号(语法进行${var//from/to}全局子字符串替换) .

You could of course only quote stuff which already had whitespace in it, but it won't matter here. One utility of a script like this is to be able to have a certain predefined set of environment variables (or, with su, to run stuff as a certain user, without that mess of double-quoting everything).

你当然可以只引用已经有空格的东西,但在这里无关紧要。像这样的脚本的一个实用程序是能够拥有一组特定的预定义环境变量(或者,使用 su,以特定用户身份运行内容,而不会出现双引号的混乱情况)。



Update: I recently had reason to do this in a POSIX way with minimal forking, which lead to this script (the last printf there outputs the command line used to invoke the script, which you should be able to copy-paste in order to invoke it with equivalent arguments):

更新:我最近有理由以最小分叉的 POSIX 方式执行此操作,这导致此脚本(那里的最后一个 printf 输出用于调用脚本的命令行,您应该能够复制粘贴以调用它具有等效参数):

#!/bin/sh
C=''
for i in "$@"; do
    case "$i" in
        *\'*)
            i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"`
            ;;
        *) : ;;
    esac
    C="$C '$i'"
done
printf "
#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
    if [[ $i =~ $whitespace ]]
    then
        i=\"$i\"
    fi
    echo "$i"
done
%s\n" "$C"

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

我改用了,''因为 shell 也解释了像$!!""引号中的东西。

回答by Paused until further notice.

If it's safe to make the assumption that an argument that contains white space must have been (and should be) quoted, then you can add them like this:

如果可以安全地假设包含空格的参数必须(并且应该)被引用,那么您可以像这样添加它们:

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

Here is a sample run:

这是一个示例运行:

$ 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'

You can also insertliteral tabs and newlines using Ctrl-VTaband Ctrl-VCtrl-Jwithin double or single quotes instead of using escapes within $'...'.

您也可以插入使用文字标签和换行符Ctrl-VTabCtrl- VCtrl-J双或单引号,而不是使用中逃脱内$'...'

A note on insertingcharacters in Bash: If you're using Vi key bindings (set -o vi) in Bash (Emacs is the default - set -o emacs), you'll need to be in insertmode in order to insert characters. In Emacs mode, you're always in insert mode.

在 Bash 中插入字符的注意事项:如果您在 Bash 中使用 Vi 键绑定 ( set -o vi)(Emacs 是默认的 - set -o emacs),您需要处于插入模式才能插入字符。在 Emacs 模式下,您始终处于插入模式。

回答by Tom Hale

There are two safe ways to do this:

有两种安全的方法可以做到这一点:

1. Shell parameter expansion: ${variable@Q}:

1.外壳参数扩展${variable@Q}:

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

通过${variable@Q}以下方式扩展变量时:

The expansion is a string that is the value of parameter quoted in a format that can be reused as 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
$

2. printf %q "$quote-me"

2. printf %q "$quote-me"

printfsupports quoting internally. The manual's entry for printfsays:

printf支持内部引用。该手册的入门printf说:

%qCauses printf to output the corresponding argument in a format that can be reused as shell input.

%q使 printf 以可重复用作 shell 输入的格式输出相应的参数。

Example:

例子:

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


Note the 2nd way is a bit cleaner if displaying the quoted text to a human.

请注意,如果将引用的文本显示给人类,则第二种方式会更简洁一些。

Related: For bash, POSIX sh and zsh: Quote string with single quotes rather than backslashes

相关:对于 bash、POSIX sh 和 zsh:使用单引号而不是反斜杠的引号字符串

回答by isarandi

I needed this for forwarding all arguments to another interpreter. What ended up right for me is:

我需要这个来将所有参数转发给另一个解释器。最终对我来说是:

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

Example (when named as forward.sh):

示例(当命名为 forward.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}"

(Of course the actual script I use is more complex, involving in my case nohup and redirections etc., but this is the key part.)

(当然我使用的实际脚本更复杂,在我的情况下涉及 nohup 和重定向等,但这是关键部分。)

回答by Gary S. Weaver

Like Tom Hale said, one way to do this is with printfusing %qto quote-escape.

就像 Tom Hale 所说的那样,一种方法是printf使用%q引用转义。

For example:

例如:

send_all_args.sh

send_all_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}"

send_fewer_args.sh

send_fewer_args.sh

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

receiver.sh

接收器.sh

$ ./send_all_args.sh
$ ./send_all_args.sh a b
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 a b
b
$ ./send_fewer_args.sh "a' b" 'c "e '
c "e 
$ ./send_fewer_args.sh "a' b" 'c "e ' 'f " g'
c "e 
f " g

Example usage:

用法示例:

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

回答by Gus Neves

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

我的问题很相似,我使用了此处发布的混合想法。

We have a server with a PHP script that sends e-mails. And then we have a second server that connects to the 1st server via SSH and executes it.

我们有一个带有发送电子邮件的 PHP 脚本的服务器。然后我们有第二台服务器,它通过 SSH 连接到第一台服务器并执行它。

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

两台服务器上的脚本名称相同,并且实际上都是通过 bash 脚本执行的。

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

在服务器 1(本地)bash 脚本上,我们只有:

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[@]}"

This resides on /usr/local/bin/myscriptand is called by the remote server. It works fine even for arguments with spaces.

这驻留在/usr/local/bin/myscript远程服务器上并由远程服务器调用。即使对于带有空格的参数,它也能正常工作。

But then at the remote server we can't use the same logic because the 1st server will not receive the quotes from "$@". I used the ideas from JohnMudd and Dennis Williamson to recreate the options and parameters array with the quotations. I like the idea of adding escaped quotations only when the item has spaces in it.

但是在远程服务器上我们不能使用相同的逻辑,因为第一台服务器不会收到来自"$@". 我使用了 JohnMudd 和 Dennis Williamson 的想法来重新创建带有引号的选项和参数数组。我喜欢仅当项目中有空格时才添加转义引号的想法。

So the remote script runs with:

所以远程脚本运行:

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.                               

Note that I use "${CSMOPTS[@]}"to pass the options array to the remote server.

请注意,我"${CSMOPTS[@]}"用来将选项数组传递给远程服务器。

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

感谢在这里发帖的每一个人!它真的帮助了我!:)

回答by JohnMudd

Changed unhammer's example to use array.

unhammer的示例更改为使用数组。

"${@}"

回答by FrameGrace

Just use:

只需使用:

# 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" a b "and also c"
Param: This is a test
Param: This is another line
Param: a
Param: b
Param: and also c

For example:

例如:

val=""
echo "Hello World" > "$val"

回答by mouviciel

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

引号由 bash 解释,不存储在命令行参数或变量值中。

If you want to use quoted arguments, you have to quote them each time you use them:

如果要使用带引号的参数,则每次使用时都必须引用它们:

##代码##