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

发布于 2024-08-10 01:23:05 字数 246 浏览 4 评论 0原文

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

示例:

./test.sh this is "some test"

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

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

我该如何实现这个目标?

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

Example:

./test.sh this is "some test"

Then I want to use those arguments, and reuse 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

活雷疯 2024-08-17 01:23:05

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

请注意,这是一种特殊形式,只有当它完全以这种方式出现时才会被识别。如果您在引号中添加任何其他内容,结果将合并为一个参数。

Using "$@" will substitute the arguments as a list, without resplitting 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 repass the arguments to another program.

Note that this is a special form and is only recognized as such if it appears exactly this way. If you add anything else in the quotes the result will get combined into a single argument.

撞了怀 2024-08-17 01:23:05

有两种安全的方法可以执行此操作:

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

通过${variable@Q}扩展变量时:

扩展是一个字符串,它是以可重复用作输入的格式引用的参数值。

示例:

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

2. printf %q "$quote-me"

printf 支持内部引用。 printf 的手册条目< /a> 说:

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

示例:

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


请注意,如果向人类显示引用的文本,第二种方法会更清晰一些。

相关:对于 Bash,POSIX sh 和 Z shell(可执行 zsh):用单引号而不是反斜杠引用字符串

new\nline' 'single'\''quote' 'double"quote'

2. printf %q "$quote-me"

printf 支持内部引用。 printf 的手册条目< /a> 说:

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

示例:



请注意,如果向人类显示引用的文本,第二种方法会更清晰一些。

相关:对于 Bash,POSIX sh 和 Z shell(可执行 zsh):用单引号而不是反斜杠引用字符串

new\nline' single\'quote double\"quote $

请注意,如果向人类显示引用的文本,第二种方法会更清晰一些。

相关:对于 Bash,POSIX sh 和 Z shell(可执行 zsh):用单引号而不是反斜杠引用字符串

new\nline' 'single'\''quote' 'double"quote'

2. printf %q "$quote-me"

printf 支持内部引用。 printf 的手册条目< /a> 说:

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

示例:


请注意,如果向人类显示引用的文本,第二种方法会更清晰一些。

相关:对于 Bash,POSIX sh 和 Z shell(可执行 zsh):用单引号而不是反斜杠引用字符串

There are two safe ways to do this:

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

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

The expansion is a string that is the value of parameter quoted 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'

2. printf %q "$quote-me"

printf supports quoting internally. The manual's entry for printf says:

%q Causes printf to output the corresponding argument in a format that can be reused 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


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

Related: For Bash, POSIX sh and Z shell (executable zsh): Quote string with single quotes rather than backslashes

new\nline' 'single'\''quote' 'double"quote'

2. printf %q "$quote-me"

printf supports quoting internally. The manual's entry for printf says:

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

Example:



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

Related: For Bash, POSIX sh and Z shell (executable zsh): Quote string with single quotes rather than backslashes

new\nline' single\'quote double\"quote $

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

Related: For Bash, POSIX sh and Z shell (executable zsh): Quote string with single quotes rather than backslashes

new\nline' 'single'\''quote' 'double"quote'

2. printf %q "$quote-me"

printf supports quoting internally. The manual's entry for printf says:

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

Example:


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

Related: For Bash, POSIX sh and Z shell (executable zsh): Quote string with single quotes rather than backslashes

狼亦尘 2024-08-17 01:23:05

Yuku 的答案仅在您满足以下条件时才有效脚本的唯一用户,而丹尼斯·威廉姆森的答案<如果您主要对打印字符串感兴趣,并且希望它们没有引号中的引号,那么 /a> 非常有用。

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

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

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

您当然可以只引用其中已经有空格的内容,但这在这里并不重要。像这样的脚本的一个实用程序是能够拥有一组特定的预定义环境变量(或者,使用 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"

我切换到 '' 因为 shell 也解释 $!! 中的内容""-引号。

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

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

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

So, all the 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).

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).


I 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):

#!/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 !! in ""-quotes.

吾性傲以野 2024-08-17 01:23:05

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

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

这是一个示例运行:

$ ./argtest abc def "ghi jkl" 

您还可以插入文字使用 Ctrl-V TabCtrl-V 制表符和换行符在双引号或单引号内按 Ctrl-J,而不是在 $'...' 中使用转义符。

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

mno\tpqr'

您还可以插入文字使用 Ctrl-V TabCtrl-V 制表符和换行符在双引号或单引号内按 Ctrl-J,而不是在 $'...' 中使用转义符。

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

stu\nvwx' abc def "ghi jkl" "mno pqr" "stu vwx"

您还可以插入文字使用 Ctrl-V TabCtrl-V 制表符和换行符在双引号或单引号内按 Ctrl-J,而不是在 $'...' 中使用转义符。

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

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:

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

Here is a sample run:

$ ./argtest abc def "ghi jkl" 

You can also insert literal tabs and newlines using Ctrl-V Tab and Ctrl-V Ctrl-J within double or single quotes instead of using escapes within $'...'.

A note on inserting characters 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 insert mode in order to insert characters. In Emacs mode, you're always in insert mode.

mno\tpqr'

You can also insert literal tabs and newlines using Ctrl-V Tab and Ctrl-V Ctrl-J within double or single quotes instead of using escapes within $'...'.

A note on inserting characters 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 insert mode in order to insert characters. In Emacs mode, you're always in insert mode.

stu\nvwx' abc def "ghi jkl" "mno pqr" "stu vwx"

You can also insert literal tabs and newlines using Ctrl-V Tab and Ctrl-V Ctrl-J within double or single quotes instead of using escapes within $'...'.

A note on inserting characters 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 insert mode in order to insert characters. In Emacs mode, you're always in insert mode.

垂暮老矣 2024-08-17 01:23:05

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

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

示例(当命名为forward.sh时):(

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

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

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

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

Example (when named as forward.sh):

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

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

ヤ经典坏疍 2024-08-17 01:23:05

就像汤姆·黑尔所说的,一种方法这样做是通过 printf 使用 %q 进行引号转义。

例如:

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

Like Tom Hale said, one way to do this is with printf using %q to quote-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

Example usage:

$ ./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
微凉徒眸意 2024-08-17 01:23:05

只需使用:

"${@}"

例如:

# 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

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" a b "and also c"
Param: This is a test
Param: This is another line
Param: a
Param: b
Param: and also c
烟酒忠诚 2024-08-17 01:23:05

我将 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.

I changed unhammer's example to use array.

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.
铁轨上的流浪者 2024-08-17 01:23:05

我的问题是类似的,我使用了这里发布的混合想法。

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

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

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

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

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

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

因此,远程脚本运行时为:

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[@]}" 将选项数组传递到远程服务器。

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.

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

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

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

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

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.

So the remote script runs 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 use "${CSMOPTS[@]}" to pass the options array to the remote server.

栖迟 2024-08-17 01:23:05

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

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

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

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

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

val="$3"
echo "Hello, World!" > "$val"
爱的那么颓废 2024-08-17 01:23:05

如果您需要从另一种编程语言将所有参数传递给 Bash(例如,如果您想要执行 bash -cemit_bash_code | bash),使用这个:

  • 使用 '\'' 转义所有单引号字符。
  • 然后,将结果用单引号括起来。

abc'def 的参数将被转换为 'abc'\''def'。字符 '\'' 解释如下:已经存在的引用以第一个第一个引号终止,然后转义的单引号 \' 出现,然后是新的引用开始。

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

  • escape all single quote characters you have with '\''.
  • then, surround the result with singular quotes

The argument of abc'def will thus be converted to 'abc'\''def'. The characters '\'' are interpreted as following: the already existing quoting is terminated with the first first quote, then the escaped singular single quote \' comes, then the new quoting starts.

深者入戏 2024-08-17 01:23:05

正如 Gary S. Weaver 所示他的源代码提示,技巧是使用参数“-c”调用 Bash,然后引用下一个。

例如

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

docker exec -it <my docker> bash -c "$SCRIPT $quoted_args"

As Gary S. Weaver has shown in his source code tips, the trick is to call Bash with parameter '-c' and then quote the next.

E.g.

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

or

docker exec -it <my docker> bash -c "$SCRIPT $quoted_args"
腻橙味 2024-08-17 01:23:05

是的,似乎不可能保留引号,但对于我正在处理的问题来说,这是没有必要的。

我有一个 Bash 函数,它将递归地向下搜索文件夹并且 grep 对于字符串,问题是传递一个带有空格的字符串,例如“find this string”。将其传递给 Bash 脚本将获取基本参数 $n 并将其传递给 grep,这让 grep 相信这些是不同的参数。我解决这个问题的方法是利用以下事实:当您引用 Bash 来调用函数时,它将引号中的项目分组为单个参数。我只需要用引号修饰该参数并将其传递给 grep 命令。

如果您知道在 bash 中收到的参数需要在下一步中使用引号,则可以用引号进行装饰。

Yes, seems that it is not possible to ever preserve the quotes, but for the issue I was dealing with it wasn't necessary.

I have a Bash function that will search down folder recursively and grep for a string, the problem is passing a string that has spaces, such as "find this string". Passing this to the Bash script will then take the base argument $n and pass it to grep, this has grep believing these are different arguments. The way I solved this by using the fact that when you quote Bash to call the function it groups the items in the quotes into a single argument. I just needed to decorate that argument with quotes and pass it to the grep command.

If you know what argument you are receiving in bash that needs quotes for its next step you can just decorate with with quotes.

樱娆 2024-08-17 01:23:05

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

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

因此单引号内的双引号也被解释为字符串。

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

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

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

#!/bin/bash

echo $@
echo "$@"

然后您将看到并测试,当使用不同的字符串调用脚本

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:

#!/bin/bash

echo $@
echo "$@"

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文