将新行附加到多个文件

The*_*ler 1 unix command-line sh find

我正在尝试使用以下命令将新行附加到多个文件:

find -name *.ovpn -exec sh echo "line to append" >> {} \;
Run Code Online (Sandbox Code Playgroud)

在执行此操作之前,我运行了一个不同的命令以确保它按我预期的方式工作:

find -name *.ovpn -exec sh echo "hello" \;
Run Code Online (Sandbox Code Playgroud)

但这只是为找到的每个文件打印出“sh: 0: Can't open echo”。

Kam*_*ski 8

问题很少。

>>在您的第一个命令中,您当前的 shell 将被解释为重定向到一个字面上名为 的文件{},除非它被引用。

*.ovpn可以在find运行之前通过 shell globbing 进行扩展。如果当前目录中至少有一个与模式匹配的对象,就会发生这种情况。你确实想引用这个。比较这个问题

你得到,Can't open echo因为你确实在告诉shopen echo。要执行命令,您需要sh -c.

find不指定路径是不可移植的(比较这个问题)。虽然您可能会侥幸逃脱,但我提到了这个问题,以使答案对其他用户更有用。

这是你的第一个命令的改进版本,有点工作(不要运行它,继续阅读):

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "{}"' \;
Run Code Online (Sandbox Code Playgroud)

请注意,我必须{}在单引号内加双引号。这些双引号被“看到” sh,并使带有空格等的文件名用作重定向目标。如果没有引号,您最终可能会echo "line to append" >> foo bar.ovpn得到相当于echo "line to append" bar.ovpn >> foo. 引用使它echo "line to append" >> "foo bar.ovpn"代替。

不幸的是,包含文件名"会破坏这种语法。

传递{}给的正确方法sh不是将其包含在命令字符串中,而是将其内容作为单独的参数传递:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$0"' {} \;
Run Code Online (Sandbox Code Playgroud)

$0在命令字符串内扩展为我们sh在 之后得到的第一个参数-c '…'。现在即使"在文件名中也不会破坏语法。

通常(如在脚本中)引用您使用的第一个参数$1。这就是一些用户宁愿使用虚拟参数 be 的原因$0,如下所示:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$1"' dummy {} \;
Run Code Online (Sandbox Code Playgroud)

如果它是一个脚本,$0将扩展到它的名字。这就是为什么看到这dummy实际上是sh(或者bash,如果有人打电话bash -c …等;检查这个链接)并不少见的原因。像这样:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$1"' sh {} \;
Run Code Online (Sandbox Code Playgroud)

可是等等!为每个文件find单独调用一个sh。我不希望您拥有数千个.ovpn文件,但通常您可能希望处理许多文件而不产生不必要的进程。我们可以优化tee -a可以将多个文件作为单个进程写入的方法:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" | tee -a "$@" >/dev/null' sh {} +
Run Code Online (Sandbox Code Playgroud)

请注意{} +,这会一次传递多个路径。在由sh -c我们执行的命令中,我们用 检索它们"$@",它扩展为"$1" "$2" "$3" …. 在这种情况下,填充(未使用)的虚拟参数$0是必须的。

一般来说也有这个问题:为什么printfecho但是,在这种情况下,您使用的echo不是选项,并且它获得的字符串是静态的,因此应该没问题。


归档时间:

查看次数:

1110 次

最近记录:

5 年,3 月 前