我想从bash shell脚本中运行一个命令,该脚本在单引号和变量中包含单引号和一些其他命令。
如repo forall -c '....$variable'。
在这种格式中,对$进行转义,不展开变量。
我尝试了以下变体,但被拒绝:
1 2 3 4 5 6 7
| repo forall -c '...."$variable" '
repo forall -c" '....$variable'"
" repo forall -c '....$variable'"
repo forall -c"'" ....$variable"'" |
如果我用这个值代替变量,命令就可以执行了。
请告诉我哪里出错了。
- repo forall -c ' ...before... '"$variable"' ...after...'
- @N.M.回答
- @注意,单引号是repo命令的一部分。我想这个不行
- bash只接受单引号。要么您不在bash中,要么单引号不是repo命令的一部分。
- 我不确定能不能找到你,但是如果需要单引号的话,我就不会对所有的-c"……在……之前"进行回复了。$variable"…在……"工作之后?
- 它是一个在bash shell中运行的bash脚本,repo forall命令在单引号内接受参数。如果我替换实际值,命令运行正常。
- @凯文。不,它给出错误:"before value after":"before value after"没有此类文件或目录。
- 从那以后我就一直在研究这个问题。我很幸运有回购协议。不过,我不清楚您是否需要在单引号之间强制使用命令。我研究了repo语法,我认为您不需要这样做。您可以在命令周围使用双引号,然后使用您需要的任何单引号和双引号,前提是您要转义双引号。如果你能为我提供真实的案例,我想我能提供更多的帮助。
- 顺便说一句,您可能从bash shell/脚本运行它,但是repo命令在sh shell中运行,根据文档:"-c:要执行的命令和参数。该命令通过/bin/sh和作为外壳位置参数传递后的任何参数进行计算。"<-link
- 另一种可行的方法是将所需变量传递给repo执行,它似乎成功地将其传播到生成的sh上。例如:variable=$variable./repo forall-c'blahblah$variable blahblah'。包含完整命令的单引号由repo处理,实际计算$variable。
- @凯文:是的,你是对的。所有回购都采用双引号,而不是单引号。我真蠢,一次也不检查。谢谢你的帮助。如果你把你的答案贴出来,我会接受它为正确答案。
- @凯文:我也读过执行文件,我认为你是对的。但没时间检查。我要用双引号。非常感谢你!!!!
- 谢谢你,rachit:)
- @rachit:是时候接受我的答案了;-)
- 参见:bash中单引号和双引号的区别。
- 更广泛地说,什么时候用引号括住shell变量?
在单引号内,所有的东西都是字面上保存的,毫无例外。
这意味着您必须关闭引号,插入一些内容,然后重新输入。
1 2 3
| 'before'"$variable"'after'
'before'"'"'after'
'before'\''after' |
正如您可以验证的那样,以上每一行对于shell都是一个单独的单词。字符串连接只是通过并列完成的。引号(单引号或双引号,视情况而定)用于禁止解释各种特殊字符,如空格、$、;…有关引用的好教程,请参阅马克·里德的答案。同样相关:哪些字符需要在bash中转义?
不要连接由shell解释的字符串
您应该绝对避免通过连接变量来构建shell命令。这是一个坏主意,类似于SQL片段的串联(SQL注入!).
通常可以在命令中包含占位符,并将命令与变量一起提供,以便被调用方可以从调用参数列表中接收它们。
例如,以下内容非常不安全。不要这样做
1 2
| script="echo "Argument 1 is: $myvar""
/bin/sh -c"$script" |
如果EDOCX1[2]的内容不可信,则存在以下漏洞:
1
| myvar='foo"; echo"you were hacked' |
不要使用上面的调用,而是使用位置参数。下面的调用更好——它是不可利用的:
1 2
| script='echo"arg 1 is: $1"'
/bin/sh -c"$script" --"$myvar" |
请注意,在分配给script时使用了单勾号,这意味着它是按字面意思进行的,没有变量扩展或任何其他形式的解释。
- @乔,那就不行了。因为repo命令只接受参数,直到第一个引号结束。之后的任何操作都将被repo忽略并生成另一个错误。
- @rachit-外壳不是这样工作的。"some"thing' like'this对shell来说都是一个单词。就解析而言,引号不会终止任何内容,而且shell调用的命令无法告诉引用内容的方式。
- 是的,乔。所以我现在明白了。谢谢你的帮助!
- 第二句话("你连单引号都逃不掉")使得单引号成为了外壳的黑洞。
- @埃弗特:不知道怎么说得更好。我把句子删掉了。
- @乔索,真丢脸:没有,笑话一落千丈。
repo命令不关心它得到什么样的报价。如果需要参数扩展,请使用双引号。如果这意味着你不得不反斜杠很多东西,使用单引号的大部分,然后打破它们,进入双打的部分,你需要扩展发生。
1
| repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff' |
如果你感兴趣的话,解释如下。
从shell运行命令时,该命令作为参数接收的是以空结尾的字符串数组。这些字符串可以绝对包含任何非空字符。
但是,当shell从命令行构建字符串数组时,它会专门解释一些字符;这是为了使命令更容易(实际上是可能的)键入。例如,空格通常表示数组中字符串之间的边界;因此,单个参数有时称为"words"。但是一个论点可能仍然有空间;你只需要某种方式来告诉外壳你想要什么。
您可以在任何字符(包括空格或其他反斜杠)前面使用反斜杠来告诉shell从字面上处理该字符。但是当你可以这样做的时候:
echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier
…会让人厌烦的。因此,壳牌公司提供了另一种选择:引号。这些有两个主要品种。
双引号称为"分组引号"。它们防止通配符和别名被扩展,但主要是用于在一个单词中包含空格。其他事情,如参数和命令扩展(由$发出信号的那种事情)仍然会发生。当然,如果您希望在双引号内使用文字双引号,则必须将其反斜杠:
echo"\"Thank you. That'll be \$4.96, please,\" said the cashier"
单引号更严厉。他们之间的一切都是完全按照字面意思,包括反斜杠。绝对没有办法在单引号中得到一个字面单引号。
幸运的是,外壳中的引号不是单词分隔符;它们本身不会终止单词。您可以在同一单词内输入和输出引号,包括不同类型的引号,以获得所需的结果:
echo '"Thank you. That'\''ll be $4.96, please," said the cashier'
这样做更容易——反斜杠更少,尽管右单引号、反斜杠文字单引号、左单引号序列需要一些适应。
现代shell添加了另一种POSIX标准中未指定的引用样式,其中前导单引号的前缀是美元符号。如此引用的字符串遵循类似于ANSI C编程语言中字符串文字的约定,因此有时称为"ansi字符串"和$'…'对"ansi引号"。在这样的字符串中,上面关于反斜杠的建议不再适用。相反,它们又变得特别了——不仅可以通过在其前面加上反斜杠来包含一个单引号或反斜杠,而且shell还扩展了ansi c字符转义(例如,换行符为
,制表符为\t,十六进制代码为HH的字符为\xHH)。但是,否则,它们将表现为单引号字符串:不会发生参数或命令替换:
1
| echo $'"Thank you. That\'ll be $4.96, please," said the cashier' |
需要注意的是,作为echo命令的参数接收的单个字符串在所有这些示例中都是完全相同的。在shell完成对命令行的分析之后,无法让正在运行的命令知道引用了什么。即使它想。
- 对。我现在明白了。它工作得很好。非常感谢你的帮助!
- 这个回答太棒了。
- $'string'格式的posix是否兼容?还有,它有名字吗?
- @通配符$'string'是一个非posix扩展,我将其称为"ansi字符串";我将这些事实合并到了答案中。这样的字符串可以在大多数与Bourne兼容的现代shell中使用:bash、dash、ksh(at&t和pd)和zsh都支持它们。
- 链接返回-在命令行参数中需要转义哪些字符?在Unix和Linux堆栈交换上。
编辑:(根据相关评论:)
从那以后我就一直在研究这个问题。我很幸运有回购协议。不过,我不清楚您是否需要在单引号之间强制使用命令。我研究了repo语法,我认为您不需要这样做。您可以在命令周围使用双引号,然后使用您需要的任何单引号和双引号,前提是您要转义双引号。
下面是对我有用的-
1 2
| QUOTE="'"
hive -e"alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE" |
- 我希望tbl_hdfs_dir_path不是用户提供的输入btw,这将允许一个很好的SQL注入…
- 把QUOTE放在单独的变量中是完全多余的;双引号中的单引号只是一个普通字符。(另外,不要对私有变量使用大写。)
只使用PrimTf
而不是
1
| repo forall -c '....$variable' |
使用printf将变量标记替换为扩展变量。
例如:
1 2 3
| template='.... %s'
repo forall -c $(printf"${template}""${variable}") |
- 这是坏的;printf在这里并没有真正给你买任何东西,并且没有引用命令子脚本会导致新的引用问题。
变量可以包含单引号。
1 2 3
| myvar=\'....$variable\'
repo forall -c $myvar |
- 他们可以,但这不是正确的方法-你还是应该把"$myvar"放在双引号中。
这对你有用吗?
1
| eval repo forall -c '....$variable' |
- 是给你的吗?这个问题已经五岁了,已经有了一些答案,甚至一个公认的答案…