如何使该命令起作用:
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
你有两个主要问题:
管道进入
ls *
是xargs
完全错误的。它将要如果任何文件名包含空格、换行符、shell 通配符,或者(取决于您运行的内容xargs
)以-
.代替使用
find ... -print0 | xargs -0
。嵌套引号。正如@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
相同。如果你没有,只需从命令行中将其删除)awk
awk
awk
并将其运行为:
./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
.