传递给命令行参数时如何在 Bash 中转义变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17257388/
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-18 05:45:19  来源:igfitidea点击:

How to escape a variable in Bash when passing to a command-line argument

bashvariablesescapingifs

提问by Eric B.

I've got a Bash script (Cygwin) that uses some Windows paths with spaces in them. Consequently, I have escaped the space with a \in my variable definition.

我有一个 Bash 脚本(Cygwin),它使用一些带有空格的 Windows 路径。因此,我\在变量定义中使用 a 转义了空间。

Everything within the script works fine. However, I need to pass this variable as an argument to a command-line executable. When I do that, my escaping gets messed up.

脚本中的所有内容都可以正常工作。但是,我需要将此变量作为参数传递给命令行可执行文件。当我这样做时,我的逃跑变得一团糟。

Sample non-functional script:

示例非功能性脚本:

#!/bin/sh

# File paths
destinationPath="/cygdrive/c/Documents and Settings/Eric/My Documents/"
attachments="\n2013-12-12.pdf"
body="Test Body"
recipient="[email protected]"


# Prepare attachments
args=""
for file in $attachments ; do
    file=${file//[ \n]/}
    touch $file
    mv $file "$destinationPath/$file"
    args="$args -a $destinationPath/$file"
done


# Send email
echo -e $body | email --from-addr [email protected] --from-name "Automated CSS Downloader" --subject "Downloaded new file(s) \
  from CSS" $args [email protected]

Output from the script:

脚本的输出:

$ bash -x test.sh
+ destinationPath='/cygdrive/c/Documents and Settings/Eric/My Documents/'
+ attachments='\n2013-12-12.pdf'
+ body='Test Body'
+ [email protected]
+ args=
+ for file in '$attachments'
+ file=2013-12-12.pdf
+ touch 2013-12-12.pdf
+ mv 2013-12-12.pdf '/cygdrive/c/Documents and Settings/Eric/My Documents//2013-12-12.pdf'
mv: listing attributes of `2013-12-12.pdf': Invalid argument
+ args=' -a /cygdrive/c/Documents and Settings/Eric/My Documents//2013-12-12.pdf'
+ echo -e Test Body
+ email --from-addr [email protected] --from-name 'Automated CSS Downloader' --subject 'Downloaded new file(s) from CSS' -a /cygdrive/c/Documents and Settings/Eric/My Documents//2013-12-12.pdf [email protected]
email: WARNING: Email address 'and' is invalid. Skipping...
email: FATAL: Could not open attachment: /cygdrive/c/Documents: No such file or directory

So, as you can see, the escaped space in the path is not being exported in the $args variable. I am assuming the error comes on the line "args=...". But I am not sure how to escape $destinationPath to ensure that the escaped characters are retained.

因此,如您所见,路径中的转义空间并未在 $args 变量中导出。我假设错误出现在“args=...”行上。但我不确定如何转义 $destinationPath 以确保保留转义字符。

I've tried using double quotes (with no escaped space) in destinationPath, but to no avail. If I try to double quote $destinationPath in the args= line, then the output also gets all screwed up with a bunch of extra quoting.

我尝试在 destinationPath 中使用双引号(没有转义空格),但无济于事。如果我尝试在 args= 行中双引号 $destinationPath,那么输出也会被一堆额外的引用搞砸。

How can I get this to work? I've tried playing around with the $IFS variable, but I don't really know what I'm doing with it and can't seem to get it working with that either, although I suspect the solution has something to do with $IFS.

我怎样才能让它工作?我试过使用 $IFS 变量,但我真的不知道我在用它做什么,似乎也无法让它工作,尽管我怀疑该解决方案与 $ 有关国际金融服务公司。

回答by rici

There's no good way of doing this without an array. (There's a not good way of doing it, using eval, but the array is simpler.)

没有数组就没有好的方法可以做到这一点。(有一个不好的方法,使用eval,但数组更简单。)

The problem is that you want each file to end up as one argument to email, but you want to accumulate all those arguments into a Bash variable. There's just no way to do that: you can cause the Bash variable to be inserted as a single argument ("$arg") or you can cause it to be inserted as however many words it gets split into ($arg), but you can't get it to be split according to whether or not spaces were escaped when the variable was created, because Bash only remembers the string assigned to a variable, not the escape marks.

问题是您希望每个文件最终都作为 的一个参数email,但您希望将所有这些参数累积到一个 Bash 变量中。只是没有办法做到这一点:您可以将 Bash 变量作为单个参数 ( "$arg")插入,也可以将其插入为将其拆分为多个单词 ( $arg),但您无法将其插入根据创建变量时是否对空格进行转义来拆分,因为 Bash 只记住分配给变量的字符串,而不是转义标记。

However, you can do it with an array, because you can make every filename exactly one array element, and you can get Bash to insert an array as one argument per element.

但是,您可以使用数组来实现,因为您可以使每个文件名恰好是一个数组元素,并且您可以让 Bash 插入一个数组作为每个元素的一个参数。

Here's how:

就是这样:

# File paths                                                                                                                              
destinationPath="/cygdrive/c/Documents and Settings/Eric/My Documents/"
attachments="\n2013-12-12.pdf"
body="Test Body"
recipient="[email protected]"


# Prepare attachments                                                                                                             
args=()
for file in $attachments ; do
    file=${file//[ \n]/}
    touch $file
    mv $file "$destinationPath/$file"
    args+=(-a "$destinationPath/$file")
done

# Send email                                                                                                                      
echo -e $body |
  email --from-addr [email protected] \
        --from-name "Automated CSS Downloader" \
        --subject "Downloaded new file(s) from CSS" \
        "${args[@]}" [email protected]

You might also want to make the attachmentsvariable into an array; from the presence of the newline character, I'm assuming that you're actually setting it in some more complicated way, so I didn't change the code. But your current code won't work if the attachment name has a space in it (and I'm pretty sure that the newline will be eliminated by the parameter expansion in the forform, unless you've altered the value of $IFS, so file=${file//[ \\n]/}shouldn't be necessary.)

您可能还想将attachments变量变成一个数组;从换行符的存在来看,我假设您实际上是以更复杂的方式设置它,所以我没有更改代码。但是,如果附件名称中有空格,您当前的代码将不起作用(我很确定换行符将被for表单中的参数扩展消除,除非您更改了 的值$IFS,所以不file=${file//[ \\n]/}应该)没必要。)

回答by robson

You need escaped double quoted marks when providing destinationPath in a string.

在字符串中提供 destinationPath 时,您需要转义双引号。

This should work:

这应该有效:

#!/bin/sh                                                                                                                                 

# file paths                                                                                                                              
destinationPath="/cygdrive/c/Documents and Settings/Eric/My Documents/"
attachments="\n2013-12-12.pdf"
body="Test Body"
recipient="[email protected]"


        # prepare attachments                                                                                                             
        args=""
        for file in $attachments ; do
            file=${file//[ \n]/}
            touch $file
            mv $file "$destinationPath/$file"
            #HERE YOU NEED ESCAPED DOUBLEQUOTED
            args="$args -a \"$destinationPath/$file\""
        done

        # send email                                                                                                                      
        echo -e $body | email --from-addr [email protected] --from-name "Automated CSS Downloader" --subject "Downloaded new file(s) \
from CSS" $args [email protected]