看似简单的问题,却有着复杂的答案。大多数问题出在外壳上。这只是部分问题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
其他相关 shell中模拟它:printf
printf "%s\n" "$@"
将其包装为一个函数:
al()
{
printf "%s\n" "$@"
}