用shell变量调用sed

时间:2013-04-10 21:44:09

标签: bash shell variables sed expansion

为什么这不起作用?

$ 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

2 个答案:

答案 0 :(得分:2)

简单的问题和复杂的答案。大部分问题都与shell有关; 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看到一个参数,并且sed选项的参数中有两个以分号分隔的-e命令。

你可以从这里响起变化。我发现al命令非常有用;它经常帮助我理解哪里出了问题。


al的来源 - 参数列表

#include <stdio.h>

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

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

printf "%s\n" "$@"

将其包裹为一个功能:

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

答案 1 :(得分:0)

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

我认为sed只能通过使用eval来正常工作。