为了更好地学习 shell,并且不必总是求助于 xargs,我一直在尝试发现任何其他方法:
find . -name *.tcl | xargs -I{} cat {}
xargs 让它感觉很混乱,我想知道是否有多种方法可以实现这一点。
编辑:我确实发现另一种解决方案是使用:
find . -name "*.tcl" | cat `cat /dev/stdin`
我不明白为什么我必须先 cat 一个文件名,然后 cat 将其视为文件而不是字符串......
答案1
您还可以在子进程中使用 find 并将输出提供给 cat :)
cat $(find . -name "*.tcl")
答案2
您可以在查找中使用 exec:
find . -name "*.tcl" -exec cat {} \;
-exec
和 尾随之间的所有内容\;
都是要运行的命令。与 xargs 一样,您可以将查找结果替换为 {}。对于找到的每个文件,都会运行 cat (很像 xargs 如何迭代通过 STDIN 给出的文件列表)。
有些人可能会认为这更有效,因为您没有使用管道或启动另一个应用程序。
答案3
如果您使用bash
,只需:
shopt -s globstar # to match files recursively
cat -- **/*.tcl
答案4
为了简单起见,我更喜欢这个已经提出的干净且可读的解决方案马克·科马林斯基。然而,在 Bash 中有很多方法可以做到这一点。另一种有趣的方法是完全避免cat
并利用重定向:
find . -name '*.tcl' -exec sh -c 'printf "%s\n" "$(< {})' \;;
事实上,如果您想纯粹使用 Bash 内置函数来完成整个任务,那么您可以将重定向与混沌带来的全局解:
shopt_globstar_temp="$(shopt -p globstar)";
shopt -s globstar;
for filename in **/*.bat; do
printf "%s" "$(< "${filename}")";
done;
${shopt_globstar_temp};
这些有点令人费解,但我在这里的目的是为了说明 Bash 可以做到文件描述符和重定向的一些强大功能。给定问题通常有多种解决方案。
我不明白为什么我必须先 cat 一个文件名,然后 cat 将其视为文件而不是字符串......
的输出cat /dev/fd/stdin
将与上一个示例中的输出相同find
,因此它将有效地被该文件列表替换<filename1>.tcl <filename2>.tcl ...
并cat
使用该文件列表作为其参数列表。
如果您想知道为什么必须stdin
在同一示例中使用 cat,原因是并非所有程序都stdin
以与处理参数相同的方式处理。如果数据传输到cat
through stdin
,则将cat
简单地输出相同的数据,而不是将其解释为要读取的文件名。