Bash:如何在引用中保留引号?

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

例:

./test.sh this is "some test" 

那么我想使用这些参数,并重新使用它们,包括整个参数列表中的引号和引号。

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

我如何做到这一点?

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

使用"$@"会将参数replace为一个列表,而不用将它们重新分割成空格(当shell脚本被调用时它们被分割一次),这通常正是你想要的,如果你只是想重新传递参数到另一个程序。

你想做什么,以什么方式不工作?

Yuku的答案只适用于如果你是剧本的唯一使用者,而Dennis Williamson则是非常棒的,如果你主要想打印string,并且期望他们没有引号。

如果你想把所有的参数作为一个大引用stringparameter passing给bash或者su-c参数,那么这个版本是可以使用的:

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

所以, 所有的参数都会得到一个引号(为了这个目的,前面没有这个参数是无害的),但是我们也逃脱了任何转义,然后转义已经在参数中的引号(语法${var//from/to}做全局子串replace)。

你当然可以只引用已经有空白的东西,但在这里没关系。 像这样的脚本的一个实用工具是能够有一个预定义的一组环境variables(或者,用su作为特定的用户运行的东西,而不是双重引用所有的东西)。


更新:我最近有理由以最小的分叉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 "$0%s\n" "$C" 

我切换到''因为壳也解释的东西像$!!""引用。

如果假设一个包含空格的参数一定是(而且应该)被引用是安全的,那么你可以像这样来添加它们:

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

这是一个示例运行:

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

您还可以在双引号或单引号内使用CtrlV TabCtrlV CtrlJ 插入文字标签和换行符,而不是使用$'...'内的转义符。

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

bash支持在内部引用。 该手册的printf条目说:

%q导致printf以可以重新用作shellinput的格式输出相应的参数。

例:

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

瞧。

改变unhammer的例子使用数组。

 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. 

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

如果您想使用引用的参数,则必须在每次使用引用时引用它们:

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

我的问题是相似的,我用这里张贴的混合想法。

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

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

在服务器1(本地)bash脚本我们只是:

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

这位于/usr/local/bin/myscript ,由远程服务器调用。 即使有空格的参数也可以正常工作。

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

所以远程脚本运行:

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

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

感谢eveyone发布在这里! 这真的帮助我! 🙂

就像Tom Hale所说的那样,使用%q来引用escape的方法就是printf

例如:

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 

用法示例:

 $ ./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