转义变量以用作另一个脚本的内容


20

这个问题与如何编写正确转义​​的字符串文字无关。我找不到任何有关如何转义变量以直接在脚本或其他程序中使用的相关问题。

我的目标是使一个脚本能够生成其他脚本。这是因为生成的脚本中的任务将在另一台计算机上运行0到n次,并且生成它们的数据可能会在运行之前(再次)更改,因此直接通过网络进行操作将不行。

给定一个可能包含特殊字符(例如单引号)的已知变量,我需要将其写为完全转义的字符串文字,例如,一个foo包含的变量bar'baz应在生成的脚本中显示为:

qux='bar'\''baz'

通过附加"qux=$foo_esc"到脚本的其他行来编写。我是这样用Perl做到的:

foo_esc="'`perl -pe 's/('\'')/\\1\\\\\\1\\1/g' <<<"$foo"`'"

但这似乎太过分了。

我仅靠bash并没有取得成功。我尝试了这些的许多变体:

foo_esc="'${file//\'/\'\\\'\'}'"
foo_esc="'${file//\'/'\\''}'"

但是要么多余的斜杠出现在输出中(当我这样做时echo "$foo"),否则它们会导致语法错误(如果从shell进行操作,则需要进一步的输入)。


alias和/或set相当普遍
mikeserv

@mikeserv对不起,我不确定您的意思。
沃尔夫

alias "varname=$varname" varnamevar=value set
mikeserv

2
@mikeserv对于我来说,这还不足以理解您的建议应该做的事情,也不足以将我用作转义任何变量的通用方法。伴侣,这是一个已解决的问题。
沃尔夫

Answers:


26

Bash在这种情况下有一个参数扩展选项

${parameter@Q}扩展名是一个字符串,它是以可重用作输入的格式引用的参数值。

因此,在这种情况下:

foo_esc="${foo@Q}"

Bash 4.4及更高版本支持此功能。对于其他形式的扩展,以及专门生成完整的赋值语句(@A),也有几种选择。


7
整洁,但只有4.2给出bad substitution
Walf

3
Z壳等效为"${foo:q}"
JdeBP '17

真的救了我的命!"${foo@Q}"作品!

@JdeBP表示Z shell等效项不起作用。关于zsh还有其他想法吗?
史蒂文·肖

1
我找到了答案:“ $ {(@ qq)foo}”
Steven Shaw

10

Bash提供了printf带有%q格式说明符的内置函数,即使在较旧的(<4.0)版本的Bash中,它也可以为您执行外壳转义:

printf '[%q]\n' "Ne'er do well"
# Prints [Ne\'er\ do\ well]

printf '[%q]\n' 'Sneaky injection $( whoami ) `ls /root`'
# Prints [Sneaky\ injection\ \$\(\ whoami\ \)\ \`ls\ /root\`]

此技巧还可以用于从函数返回数据数组:

function getData()
{
  printf '%q ' "He'll say hi" 'or `whoami`' 'and then $( byebye )'
}

declare -a DATA="( $( getData ) )"
printf 'DATA: [%q]\n' "${DATA[@]}"
# Prints:
# DATA: [He\'ll\ say\ hi]
# DATA: [or\ \`whoami\`]
# DATA: [and\ then\ \$\(\ byebye\ \)]

请注意,printf内置的Bash printf与大多数类似Unix的操作系统捆绑在一起的实用程序不同。如果由于某种原因该printf命令调用了实用程序而不是内置函数,则可以始终执行builtin printf


我不确定如果我需要打印的内容'Ne'\''er do well'等等(即输出中包含引号)有什么帮助。
沃尔夫

1
@Walf我认为您不了解这两种形式是等效的,并且两者彼此之间完全一样安全。例如,[[ 'Ne'\''er do well' == Ne\'er\ do\ well ]] && echo 'equivalent!'回声equivalent!
Dejay Clayton '18

我确实很想念:P,但是我更喜欢引用形式,因为在语法高亮的查看器/编辑器中更容易阅读。
沃尔夫

@Walf看来您的方法很危险,考虑到在示例Perl中,传递类似的值会'hello'导致返回不正确的value ''\''hello'',该值具有不必要的前导空字符串(前两个单引号)和不适当的尾随单引号。
Dejay Clayton '18年

1
为了澄清起见,@ Walf $'escape-these-chars'是Bash 的ANSI-C引用功能,该功能使指定字符串中的所有字符都转义。因此,要轻松创建在文件名中包含换行符的字符串文字(例如$'first-line\nsecond-line')\n在此构造中使用。)
Dejay Clayton

8

我想我不是RTFM。可以这样完成:

q_mid=\'\\\'\'
foo_esc="'${foo//\'/$q_mid}'"

然后 echo "$foo_esc"给出预期'bar'\''baz'


我实际上是如何使用它的:

function esc_var {
    local mid_q=\'\\\'\'
    printf '%s' "'${1//\'/$mid_q}'"
}

...

foo_esc="`esc_var "$foo"`"

修改它以使用printfDejay解决方案中的内置方法:

function esc_vars {
    printf '%q' "$@"
}

3
如果我的版本支持,我将使用@ michael-homer的解决方案。
Walf

4

有几种解决方案可以引用var值:

  1. 别名
    在大多数shell中(可以使用别名)(csh,tcsh以及其他可能的csh除外):

    $ alias qux=bar\'baz
    $ alias qux
    qux='bar'\''baz'

    是的,这在很多情况下都有效 sh类似破折号或灰烬的外壳。

  2. 设置
    也是大多数炮弹(再次,没有CSH):

    $ qux=bar\'baz
    $ set | grep '^qux='
    qux='bar'\''baz'
  3. 排版
    在某些shell中(至少是ksh,bash和zsh):

    $ qux=bar\'baz
    $ typeset -p qux
    typeset qux='bar'\''baz'             # this is zsh, quoting style may
                                         # be different for other shells.
  4. 出口
    首先:

    export qux=bar\'baz

    然后使用:
    export -p | grep 'qux=' export -p | grep 'qux='
    export -p qux

  5. 报价
    echo "${qux@Q}"
    echo "${(qq)qux}" 可以使用#一至四个Q的。


别名方法很聪明,似乎是由POSIX指定的。为了获得最大的便携性,我认为这是要走的路。我相信grepexportset可能会破坏包含嵌入式换行符的变量的建议。
jw013
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.