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”。
问题很少。
>>在您的第一个命令中,您当前的 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是必须的。
一般来说也有这个问题:为什么printf比echo?但是,在这种情况下,您使用的echo不是选项,并且它获得的字符串是静态的,因此应该没问题。
| 归档时间: |
|
| 查看次数: |
1110 次 |
| 最近记录: |