简体   繁体   English

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

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

I've got a Bash script (Cygwin) that uses some Windows paths with spaces in them. 我有一个Bash脚本(Cygwin),该脚本使用一些Windows路径,其中包含空格。 Consequently, I have escaped the space with a \\ in my variable definition. 因此,我在变量定义中使用\\转义了空格。

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="asdf@asdf.com"


# 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 nosender@mydomain.com --from-name "Automated CSS Downloader" --subject "Downloaded new file(s) \
  from CSS" $args eric@mydomain.com

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'
+ recipient=asdf@asdf.com
+ 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 nosender@mydomain.com --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 eric@mydomain.com
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. 因此,如您所见,路径中的转义空间未在$ args变量中导出。 I am assuming the error comes on the line "args=...". 我假设错误来自“ args = ...”行。 But I am not sure how to escape $destinationPath to ensure that the escaped characters are retained. 但是我不确定如何对$ destinationPath进行转义以确保保留转义的字符。

I've tried using double quotes (with no escaped space) in destinationPath, but to no avail. 我试过在destinationPath中使用双引号(无转义空间),但无济于事。 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. 如果我尝试在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变量,但是我真的不知道我在用它做什么,而且似乎也无法使其工作,尽管我怀疑解决方案与$有关。 IFS。

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. 问题是您希望每个文件最终都作为email一个参数,但是您想要将所有这些参数累加到Bash变量中。 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. 完全没有办法:可以将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="asdf@asdf.com"


# 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 nosender@mydomain.com \
        --from-name "Automated CSS Downloader" \
        --subject "Downloaded new file(s) from CSS" \
        "${args[@]}" eric@mydomain.com

You might also want to make the attachments variable into an array; 您可能还需要将attachments变量设置为数组; 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 for form, unless you've altered the value of $IFS , so file=${file//[ \\\\n]/} shouldn't be necessary.) 但是,如果附件名称中有空格,您当前的代码将无法工作(而且我很确定, for表单中的参数扩展将消除换行符,除非您更改了$IFS的值,因此file=${file//[ \\\\n]/}不必。)

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="asdf@asdf.com"


        # 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 nosender@mydomain.com --from-name "Automated CSS Downloader" --subject "Downloaded new file(s) \
from CSS" $args eric@mydomain.com

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM