bash 脚本中单引号的问题

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

Problems with single quotes in a bash script

bash

提问by Jeff Erickson

I am trying to run a command from a bash script. The command itself has single quotes in it, but I can't get them to work as there are variables inside the single quotes that need to be expanded. Here is the bash script:

我正在尝试从 bash 脚本运行命令。命令本身有单引号,但我无法让它们工作,因为单引号内有需要扩展的变量。这是 bash 脚本:

#!/bin/bash

if [ "" == "PracSci" ];
then
    echo "Running tabulate.science.r"
    R CMD BATCH --no-save --no-restore --slave '--args   ' tabulate.science.r /dev/tty
else
    echo "Running tabulate.r"
    R CMD BATCH --no-save --no-restore --slave '--args   ' tabulate.r /dev/tty
fi

When run as ./script.sh 2 Vocab May12for example, the command itself is run without the variables values.

./script.sh 2 Vocab May12例如,当运行时,命令本身在没有变量值的情况下运行。

How can I expand the variables while still getting the command to run?

如何在仍然运行命令的同时扩展变量?

Just to be clear, the command I am trying to run is: R CMD BATCH --no-save --no-restore --slave '--args $1 $2 $3' tabulate.r /dev/tty(including the single quotes in the command).

为了清楚起见,我尝试运行的命令是:( R CMD BATCH --no-save --no-restore --slave '--args $1 $2 $3' tabulate.r /dev/tty包括命令中的单引号)。

Thank you!

谢谢!

回答by Gordon Davisson

Short answer: use double-quotes instead of single-quotes (as @Pavel suggested). And when in doubt, use set -xto find out how the shell is parsing your commands.

简短回答:使用双引号而不是单引号(如@Pavel 建议的那样)。如有疑问,请使用set -x来了解 shell 如何解析您的命令。

Long answer: quotes aren't passed to the command; instead, they are parsed (and removed) by the shell, before the arguments are passed to the command. What they do is to change the way the shell parses what's inside the quotes (generally by suppressing the special meanings of some characters), which means that you need to choose the appropriate quotes to allow the special characters you want parsed, and suppress the ones you want treated as just plain characters. For instance, consider the following command:

长答案:引号不会传递给命令;相反,在将参数传递给命令之前,它们会被 shell 解析(并删除)。他们所做的就是改变shell解析引号里面内容的方式(一般是通过抑制某些字符的特殊含义),这意味着你需要选择合适的引号来允许你想要解析的特殊字符,并抑制那些您希望仅将其视为普通字符。例如,考虑以下命令:

R CMD BATCH --no-save --no-restore --slave '--args 2 Vocab May12' tabulate.r /dev/tty

The shell will ignore the special meanings of all characters in the single-quoted section of the command. The only characters with any special meaning there are the spaces, which normally act as separators between arguments; in this case, the single-quotes make the shell treat them as part of a single argument. These other commands:

shell 将忽略命令的单引号部分中所有字符的特殊含义。唯一具有特殊含义的字符是空格,通常用作参数之间的分隔符;在这种情况下,单引号使 shell 将它们视为单个参数的一部分。这些其他命令:

R CMD BATCH --no-save --no-restore --slave "--args 2 Vocab May12" tabulate.r /dev/tty
R CMD BATCH --no-save --no-restore --slave --args' '2' 'Vocab' 'May12 tabulate.r /dev/tty
R CMD BATCH --no-save --no-restore --slave --args\ 2\ Vocab\ May12 tabulate.r /dev/tty

...all do exactly the same thing, because they all (one way or another) get the shell to treat the spaces as part of an argument, rather than separators between arguments.

...都做完全相同的事情,因为它们都(以一种或另一种方式)让 shell 将空格视为参数的一部分,而不是参数之间的分隔符。

Now, let's look at the command that isn't working for you:

现在,让我们看看对您不起作用的命令:

R CMD BATCH --no-save --no-restore --slave '--args   ' tabulate.r /dev/tty

The problem is that while the single-quotes are suppressing the special meaning of the spaces (as you want), they're also suppressing the special meaning of the $s (which you don't want). So you need something more selective. One option would be to quote/escape the spaces but leave the $1etc unquoted:

问题是,虽然单引号抑制了空格的特殊含义(如您所愿),但它们也抑制了$s的特殊含义(您不想要的)。所以你需要一些更有选择性的东西。一种选择是引用/转义空格但不引用$1等:

R CMD BATCH --no-save --no-restore --slave --args' '' '' ' tabulate.r /dev/tty
R CMD BATCH --no-save --no-restore --slave --args\ \ \  tabulate.r /dev/tty

(note that both of these commands do exactly the same thing.) These would mostly work, but have a bit of a potential for trouble: the shell will replace $1etc with the arguments to the script, but then it does some additional parsing on them: looking for spaces to use as argument separators, wildcards to do filename matching on, etc. All things I'm pretty sure you don't want -- although since the arguments probably won't contain any special characters this probably won't be an issue. Probably.

(请注意,这两个命令执行的操作完全相同。)这些命令大部分都可以工作,但可能会遇到一些麻烦:shell 会将$1etc替换为脚本的参数,但随后会对它们进行一些额外的解析: 寻找用作参数分隔符的空格、进行文件名匹配的通配符等。我很确定你不想要的所有东西——尽管因为参数可能不包含任何特殊字符,这可能不会成为问题。大概。

The best option I see is to simply use double-quotes:

我看到的最佳选择是简单地使用双引号:

R CMD BATCH --no-save --no-restore --slave "--args   " tabulate.r /dev/tty

Double-quotes suppress the special meaning of spaces(as you want), allow $to trigger variable expansion (also as you want), BUT prevent any further parsing once the variables have been expanded (also probably what you want). Spaces or other funny characters in arguments might still cause trouble within the R script, but at least this prevents them from causing trouble before the R script is even started.

双引号抑制空格的特殊含义(如您所愿),允许$触发变量扩展(也如您所愿),但一旦变量被扩展(也可能是您想要的),则阻止任何进一步的解析。参数中的空格或其他有趣的字符可能仍然会在 R 脚本中引起问题,但至少这可以防止它们在 R 脚本启动之前引起问题。

回答by Pavel

use double quotes - or am I missing the point?

使用双引号 - 或者我错过了重点?

回答by Yasser Zamani

you can use "(double-quote) instead or closing ' with ' i.e.

您可以使用“(双引号)代替或用 ' 关闭 ' 即

#!/bin/bash

if [ "" == "PracSci" ];
then
    echo "Running tabulate.science.r"
    R CMD BATCH --no-save --no-restore --slave '--args '' '' ' tabulate.science.r /dev/tty
else
    echo "Running tabulate.r"
    R CMD BATCH --no-save --no-restore --slave '--args '' '' ' tabulate.r /dev/tty
fi

Or if you need args ...in a single quote again then simply write

或者,如果您需要args ...再次单引号,那么只需写

#!/bin/bash

if [ "" == "PracSci" ];
then
    echo "Running tabulate.science.r"
    R CMD BATCH --no-save --no-restore --slave "'--args   '" tabulate.science.r /dev/tty
else
    echo "Running tabulate.r"
    R CMD BATCH --no-save --no-restore --slave "'--args   '" tabulate.r /dev/tty
fi

回答by Kilian Foth

If I understand you correctly, you want " '--args $1 $2 $3' "instead of plain '--args $1 $2 $3'. Since the top-level token is a double-quoted string, the variables will be expanded, and the 'will stay in the token as well to that R can use them.

如果我理解正确,你想要 " '--args $1 $2 $3' "而不是普通的'--args $1 $2 $3'. 由于顶级标记是双引号字符串,变量将被扩展,并且'将保留在标记中,以便 R 可以使用它们。