2

为什么这不起作用?

$ 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、默认 sed 和 gsed 4.2.2,来自 Mac 端口

4

2 回答 2

2

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

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

sort -t :
sort -t:

这两者都赋予了:期权价值-tsed与和-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其他相关 shell中模拟它:printf

printf "%s\n" "$@"

将其包装为一个函数:

al()
{
    printf "%s\n" "$@"
}
于 2013-04-10T22:51:36.230 回答
0

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

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

于 2013-04-10T22:17:52.997 回答