【问题标题】:How to scape shell variable with spaces within AWK script如何在 AWK 脚本中使用空格转义 shell 变量
【发布时间】:2018-11-29 22:06:23
【问题描述】:

我将“file1 Nov 2018.txt”的路径存储在变量“var”中。然后我在 awk 脚本中使用这个 shell 变量 生成另一个脚本(这是一个小例子)。问题是路径和文件名有空格,甚至我把变量放在双引号“”之间 在 awk 中,我放在单引号 '' 之间也不起作用。我收到错误“没有这样的文件或目录”

如何处理这个有空格的路径?

脚本是这样的:

var="/mydrive/d/data 2018/Documents Nov/file1 Nov 2018.txt"
z=$(awk -v a="$var" 'BEGIN{str = "cat " 'a' ; print str}')
eval "$z"

我收到以下错误:

$ eval "$z"
cat: /mydrive/d/data: No such file or directory
cat: 2018/Documents: No such file or directory
cat: Nov/file1: No such file or directory
cat: Nov: No such file or directory
cat: 2018.txt: No such file or directory

感谢您的帮助。

【问题讨论】:

  • 你真正想要什么?你的逻辑看起来很奇怪。你的 awk 构建了一个 cat file 字符串,然后你 eval theStr... 为什么?!你知道 awk 可以加载文件,你也可以 cat 没有 awk...
  • 尝试转义文件名中的空格。
  • @Kent 就像我说的,这是一个小例子来说明。在我的实际脚本中,awk 获取另一个脚本的输出并生成一个 imagemagick 脚本,但问题是一样的,该脚本不起作用,因为路径包含空格。所以在我的原始脚本中,我有一个很长的 awk 逻辑,它将所有内容存储在变量“str”中,并且这个变量包含我使用 eval 运行的最终脚本。我希望有意义。
  • 强制邪恶eval评论!

标签: bash awk escaping


【解决方案1】:

单引号转义序列在这里派上用场。请注意,047 是 ASCII ' 字符的八进制值,awk 允许您在字符串中使用 \nnn 以包含使用其八进制值的任何字符。

$ cat 'foo bar.txt'
a  b c
1  2 3

$ var="foo bar.txt"

$ echo "$var"
foo bar.txt

$ z=$(awk -v a="$var" 'BEGIN{print "cat \047" a "\047"}')

$ eval "$z"
a  b c
1  2 3

也许printf 会更好一点:

$ awk -v a="$var" 'BEGIN{ printf "cat \047%s\047\n", a }'
cat 'foo bar.txt'

问题在于单引号对 shell 具有特殊含义,因此当 awk 程序中也使用单引号时,当该程序位于命令行时,发生冲突也就不足为奇了。

这可以通过将 awk 程序放在它自己的文件中来避免:

$ cat a.awk
BEGIN { printf "cat '%s'\n", a }

$ awk -v a="$var" -f a.awk
cat 'foo bar.txt'

【讨论】:

  • 嗨 jas,太好了!!!,它似乎是这样工作的。 “\047”是什么意思?
  • 047 是 ASCII ' 字符的八进制值,awk 允许您在字符串中使用 \nnn 以包含使用其八进制值的任何字符。
  • 非常感谢您对我的疑问的解释。
  • 正确使用单引号 (\047s) 但您应该使用 ENVIRON 或在脚本后将 $var 作为 arg 传递,而不是使用 -v 这将扩展转义序列(例如转换 @ 987654330@ 到文字制表符)。
【解决方案2】:

删除 a 周围的单引号并添加转义的双引号。

$ echo success > "a b"
$ var="a b"; z=$(awk -v a="$var" 'BEGIN{print "cat \"" a "\""}');
$ eval "${z}"

success

但是,您很可能正在执行一些不必要的复杂任务。

【讨论】:

  • 感谢您的帮助。效果很好!由于看起来比 jas 的解决方案略短,因此被选为更好的答案。我这样做是因为真正的 awk 脚本会生成一个长的 ImageMagick 脚本,该脚本使用文件名和带空格的路径。我知道看起来我比需要的复杂。呵呵
  • 也许您没有充分利用管道的力量?与其用字符串构建复杂的命令,不如通过管道组合功能。
  • 感谢您的建议。
  • GerCas - @jas 的答案更接近正确。简洁不应该成为您选择软件的标准,您需要思考软件的功能,如果您不了解两个程序之间的差异,请提出问题。尝试给定var='$(echo "Boo!")' 的两种解决方案,现在考虑如果将echo "Boo!" 替换为rm -r '/' 会发生什么(不要这样做!)。
【解决方案3】:
$ cat > path\ to/test
foo
$ z=$(awk -v a="$var" 'BEGIN{gsub(/ /,"\\ ",a); str = "cat " a ; print str}')   
$ echo "$z"
cat path\ to/test
$ eval "$z"
foo

关键(在此解决方案中)是:gsub(/ /,"\\ ",a) 即。使用 \ 转义空格(\\ 由于 awk)。

【讨论】:

    【解决方案4】:

    使用 bash 的 printf %q "$var",您可以正确地转义任何字符串以供以后在 eval 中使用 - 即使换行符也将得到正确处理。但是,结果字符串可能包含特殊符号,例如 \,当使用 awk -v var="$var" 分配变量时,awk 可以解释这些符号。因此,最好通过 stdin 传递变量:

    path='/path/with spaces/and/special/symbols/like/*/?/\/...'
    cmd=$(printf %q "$path" | awk '{print "cat "$0}')
    eval "$cmd"
    

    在这个例子中生成的命令$cmd

    cat /path/with\ spaces/and/special/symbols/like/\*/\?/\\/...
    

    【讨论】:

    • 感谢您的解决方案。我不知道 %q 要逃跑。感谢大家为同一个问题提供每个解决方案。
    猜你喜欢
    • 2022-01-18
    相关资源
    最近更新 更多