awk 与 xargs sh –c 中的其他命令结合使用的正确语法

awk 与 xargs sh –c 中的其他命令结合使用的正确语法

如何使该命令起作用:

ls * | xargs -I {} sh -c 'echo {}; awk '{print $1}' {} | uniq'

它应该做简单的事情:uniq在第一列中打印文件夹中每个文件的名称和值

它不起作用,因为该$符号被识别为字符串符号的结尾,并且我猜应该与引号有关。

错误信息:

awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string

答案1

第二个单引号终止第一个单引号字符串'echo {}; awk '。然后{print $1}是不带引号的,然后是另一个单引号字符串' {} | uniq'。这在任何具有语法突出显示的编辑器中都应该很清楚;如果您查看问题中的语法突出显示,也很清楚。

这里最简单的方法是完全避免嵌套引用。将 awk 脚本作为参数传递给sh.

xargs -I {} sh -c 'echo "$1"; awk "$0"' '{print $1}' {} | uniq'

(我还{}用 的相应参数替换了脚本内部sh。永远不要{}在脚本内部使用:它将被解析为 shell 语法,而不是文件名,因此在任何包含 shell 特殊字符的文件名上都会灾难性地失败。)

要在单引号文字中有效地包含单引号,请使用'\''(正式地,这结束了单引号文字,然后添加一个由于前面的反闪而按字面解释的单引号,然后开始另一个单引号文字;但是效果是一样的)。

xargs -I {} sh -c 'echo {}; awk '\''{print $1}'\'' {} | uniq'

或者,在一个级别使用单引号,在另一级别使用双引号,但这会变得更加棘手。

(我认为您的无意义命令只是ls *一个极其简化的示例。)

答案2

xargs你根本不需要。

正如我在本网站其他地方(抱歉,不记得在哪里)从一位顶级用户那里读到的:

是的,xargs是一个很酷的玩具。不,您不需要使用它。

这:

ls * | xargs -I {} sh -c 'echo {}; awk '{print $1}' {} | uniq'

完全更换有了这个:

for f in *; do echo "$f"; awk '{print $1}' "$f" | uniq; done

这给你一个重要的安全比以前的版本有所改进,更不用说可读性和实际功能了。 (当然,由于尝试嵌套单引号,第一个版本根本不起作用**.)

然而,即使你修复了版本的引用,你也将自己置于敞开的境地。通过将任意文件名的名称填充到 内的 shell 命令中-c,您可以有效地在该文件名上运行eval,等等有许多可以利用的漏洞只需制作特定的文件名即可。例如,touch ';rm -rf "$HOME" #'将导致您的主目录被删除。


为了完全保证处理奇数文件名,包括可以解释为awk选项标志的文件名,请使用以下命令:

for f in *; do printf '%s\n' "$f"; awk '{print $1}' < "$f" | uniq; done

答案3

你有两个主要问题:

  1. 管道进入ls *xargs完全错误的。它将要如果任何文件名包含空格、换行符、shell 通配符,或者(取决于您运行的内容xargs)以-.

    代替使用find ... -print0 | xargs -0

  2. 嵌套引号。正如@Gilles 在他的回答中提到的,有一些方法可以正确地做到这一点,但它是非常如果您有多层嵌套引号,很容易迷失和困惑 - 即使您成功了,明天您也可能无法(轻松)阅读或理解代码,更不用说六个月后了。

这是很多更容易编写一个执行您想要的操作的脚本并使用 xargs 运行它。

如果脚本与多个文件名参数独立工作,那么它将与xargs- 一起工作,而不必使用-I {}(这意味着-L 1.FreeBSD 的版本xargs也有一个-J避免该问题的选项)。

例如,myscript.sh

#! /bin/sh

for f in "$@" ; do
    echo "$f"
    awk '{ print $1 }' -- "$f" | uniq
done

awk我尝试理解的大多数版本的--意思是停止处理选项args. ,这与freebsd的没有original-awk相同。如果你没有,只需从命令行中将其删除)awkawkawk

并将其运行为:

./myscript.sh *

请注意,它将*匹配子目录和文件。

或者像这样:

find . -maxdepth 1 -type f -print0 | xargs -0r /path/to/myscript.sh

或者

find . -maxdepth 1 -type f -exec /path/to/myscript.sh {} +

这两个将仅处理当前目录中的常规文件。

如果输入文件未预先排序,请使用sort -u代替uniq.

相关内容