【问题标题】:invoking sed with a shell variable使用 shell 变量调用 sed
【发布时间】:2013-04-02 22:51:48
【问题描述】:

为什么这不起作用?

$ s="-e 's/^ *//' -e 's/ *$//'"

$ ls | sed $s
sed: 1: "'s/^
": invalid command code '

$ ls | gsed $s
gsed: -e expression #1, char 1: unknown command: `''

但这确实:

$ ls | eval sed $s
... prints staff ...

$ ls | eval gsed $s
... prints staff ...

尝试从 $s 中删除单引号,但它仅适用于没有空格的模式:

$ s="-e s/a/b/"
$ ls |  sed $s
... prints staff ...

$ s="-e s/^ *//"
$ ls |  sed $s
sed: 1: "s/^
": unterminated substitute pattern

$ s="-e s/^\ *//"
$ ls |  sed $s
sed: 1: "s/^\
": unterminated substitute pattern

Mac OS 10.8、bash 4.2、Mac 端口的默认 sed 和 gsed 4.2.2

【问题讨论】:

  • 第一种情况,变量展开后,其值中的单引号没有特殊意义——即不再被视为引号。当您评估它时,您正在对扩展字符串进行第二次传递,然后单引号被识别为重要。
  • 希望这只是一个出于好奇的问题,您实际上并没有考虑这样做。
  • 其实我是。我正在动态计算模式,然后在流中使用它们。

标签: bash shell variables sed expansion


【解决方案1】:

看似简单的问题,却有着复杂的答案。大多数问题出在外壳上。 sed 只是部分问题。 (换句话说,您可以使用许多不同的命令来代替 sed 并且会遇到类似的问题。)

请注意,当参数字符串附加到选项时,大多数带有选项字母和单独参数字符串的命令也将起作用。例如:

sort -t :
sort -t:

这两个都将值: 赋予-t 选项。与sed-e 选项类似。也就是说,您可以编写以下任何一种:

sed -n -e /match/p
sed -n -e/match/p

让我们看一下您编写的sed 命令之一:

$ s="-e s/a/b/"
$ ls |  sed $s

sed 命令在这里传递的是两个参数(在它的命令名称之后):

-e
s/a/b/

这是sed 的一组完美的参数。那么第一个有什么问题呢?

$ s="-e 's/^ *//' -e 's/ *$//'"
$ ls | sed $s

嗯,这一次,sed 命令传递了 6 个参数:

-e
's/^
*//'
-e
's/
*$//'

您可以使用al 命令(参数列表 - 在单独的行上打印每个参数;在此答案的底部进行了描述和实现)来查看参数如何呈现给sed。只需在示例中输入al 代替sed

现在,-e 选项后面应该跟一个有效的sed 命令,但's/^ 不是一个有效的命令;引用 ' 不是有效的 sed 命令。当您在 shell 提示符下键入命令时,shell 会处理单引号并将其删除,因此 sed 通常不会看到它,但这会在 shell 变量展开之前发生。

那么,为什么eval 有效:

$ s="-e 's/^ *//' -e 's/ *$//'"
$ ls | eval sed $s

eval 重新评估命令行。它看到:

eval sed -e 's/$ *//' -e 's/ *$//'

并完成完整的评估过程。它在对字符进行分组后删除单引号,因此sed 看到:

-e
s/$ *//
-e
s/ *$//

这都是完全有效的sed 脚本。

您的一项测试是:

$ s="-e s/^ *//"
$ ls |  sed $s

这失败了,因为sed 被赋予了参数:

-e
s/^
*//

第一个不是有效的替代命令,第二个不太可能是有效的文件名。有趣的是,您可以通过在$s 周围加上双引号来解决这个问题,例如:

$ s="-e s/^ *//"
$ ls |  sed "$s"

现在sed 得到一个参数:

-e s/^ *//

但是-e 可以附加命令,并且命令前面的空格被忽略,所以这都是有效的。但是,您不能在第一次尝试时做到这一点:

$ s="-e 's/^ *//' -e 's/ *$//'"
$ ls | sed "$s"

现在您得知' 未被识别。但是,您可以使用:

$ s="-e s/^ *//; s/ *$//"
$ ls | sed "$s"

同样,sed 看到一个参数,-e 选项的参数中有两个以分号分隔的 sed 命令。

您可以从这里敲响变奏曲。我发现al 命令非常有用;它经常帮助我了解哪里出了问题。


al 的来源——参数列表

#include <stdio.h>

int main(int argc, char **argv)
{
    while (*++argv)
        puts(*argv);
    return 0;
}

这是您可以编写的最小的有用 C 程序之一(“hello world”短了一行,但除了演示如何编译和运行程序之外,它并没有什么用处)。它在一行上单独列出了它的每个参数。您也可以在bash 和其他相关的shell 中使用printf 命令模拟它:

printf "%s\n" "$@"

把它包装成一个函数:

al()
{
    printf "%s\n" "$@"
}

【讨论】:

    【解决方案2】:

    sed 适用于您的正常替换模式,因为它没有任何元字符。你只有a和b。当涉及元字符时,您需要单引号。

    我认为 sed 对您的变量赋值情况正常工作的唯一方法是使用 eval。

    【讨论】:

      猜你喜欢
      • 2016-10-06
      • 2011-07-28
      • 2017-03-26
      • 2021-09-28
      • 2014-01-09
      • 2010-10-20
      • 2021-09-24
      • 2018-09-16
      • 2011-10-23
      相关资源
      最近更新 更多