Duy*_*Duy 6 shell find shell-script recursive command-substitution
我正在编写一些脚本:
for x in `find ./ -name *.pdf`
do
echo pathname $x
done
Run Code Online (Sandbox Code Playgroud)
我的文件名是Test1 ( Volume II)
, Test2 ( Volume II)
. 我得到了回报
pathname Test1
pathname (
pathname Volume
pathname II
…
Run Code Online (Sandbox Code Playgroud)
我如何让它保持作为一个变量?
正如本网站多次所说,在 Bourne/POSIX shell 中不加引号的变量扩展(如 in $var
)或命令替换(如 in`cmd`
或$(cmd)
)(或算术扩展(如 in $((11 * 11))
)在大多数 shell 中)是 split+glob 运算符。
根据特殊变量的当前值(默认情况下包含 SPC、TAB 和 NL 字符(以及 中的 NUL ))以及由此产生的每个单词,对内容$var
或输出cmd
(不带尾随换行符)进行拆分拆分受文件名生成的影响,也称为globbing。$IFS
zsh
例如,如果find
输出./foo bar.pdf\n./*foo*\tbar.pdf\n
(\t
表示 TAB 和\n
NL),默认值为$IFS
,则命令替换将扩展为./foo bar.pdf\n./*foo*\tbar.pdf
(删除尾随换行符),然后拆分为./foo
,bar.pdf
, ./*foo*
, 和foo.pdf
和./*foo*
通配符模式将扩展为许多参数,因为当前目录中存在名称包含foo
.
如果只想拆分换行符,则需要设置 $IFS
为仅换行符:
IFS='
'
Run Code Online (Sandbox Code Playgroud)
如果您不想扩展通配符模式,则需要使用以下命令禁用它
set -f
Run Code Online (Sandbox Code Playgroud)
但是请注意,换行符与文件名中的任何字符一样有效,因此更一般地说,find -print
输出不能可靠地进行后处理。
输出如下:
./a.pdf
./b.pdf
Run Code Online (Sandbox Code Playgroud)
要么表示当前目录中的a.pdf
和b.pdf
文件,要么表示目录中调用b.pdf
的a.pdf\n.
文件。
一些find
实现,如 GNU find
(它的起源)有一个-print0
谓词来输出文件名,后跟 NUL 字符而不是 NL 字符。使用标准find
,您可以使用-exec printf '%s\0' {} +
相同的结果。NUL 是唯一不能出现在文件名中的字符。
但是,zsh
它是唯一可以在其变量(如$IFS
字符)中存储 NUL 字符的 shell ,因此:
IFS=$'\0'
for i in `find ... -print0`; do
...
done
Run Code Online (Sandbox Code Playgroud)
(不需要set -f
inzsh
因为zsh
在命令替换时不进行 globbing)将zsh
在其他 shell 中工作,但不能在其他 shell 中工作。
最好且可移植的是find
调用您要在这些文件上运行的命令。正如@Gnouc 所建议的:
find . -name '*.pdf' -exec the command {} \;
Run Code Online (Sandbox Code Playgroud)
如果您需要更复杂的 shell 语句,您仍然可以执行以下操作:
find . -name '*.pdf' -exec sh -c '
for i do
something complex with "$i"
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
使用zsh
或bash
,您还可以执行以下操作:
find . -name '*.pdf' -print0 |
while IFS= read -r -d '' file; do
whatever with "$file"
done
Run Code Online (Sandbox Code Playgroud)
但是请注意,循环中的 stdin 会受到影响。
zsh
(自 1990 年以来)find
通过一种语法将大部分功能包含在其通配功能中,您可以在其中指定任何级别的子目录((*/)#
语法或其更简单的形式**/
)和通配限定符(它们是-type f
, -mtime
, -perm
... infind
) .
其中的**/
一部分ksh93
在 2003 年、fish
2005 年、bash
2009 年和tcsh
2010 年tcsh
被复制(尽管也复制了该***/
部分)。并且所有这些默认情况下都不启用它。不幸的是,请注意,bash
和fish
**
都遵循指向目录的符号链接(例如-L
/ -follow
in find
,或***
inzsh
或tcsh
)。
在这些 shell 中,您可以pdf
在任何级别的子目录中查找文件而不必依赖find
,但请注意上面关于fish
和的警告bash
,并且仅zsh
通配符限定符。
因此,例如,zsh
相当于:
find . -name '*.pdf' -type f -exec ls -ld {} +
Run Code Online (Sandbox Code Playgroud)
将是:
ls -ld ./**/*.pdf(D.)
Run Code Online (Sandbox Code Playgroud)
使用 时bash
,您必须执行以下操作:
shopt -s failglob
shopt -s globstar
files=(./**/*.pdf) &&
for i do
[ -f "$i" ] && ! [ -L "$i" ] && set -- "$i" "$@"
shift
done && ls -ld "$@"
Run Code Online (Sandbox Code Playgroud)
最安全的方法是使用 globbing:
for file in *pdf; do echo pathname "$file"; done
Run Code Online (Sandbox Code Playgroud)
如果您需要递归查找所有 pdf,请执行以下操作:
shopt -s globstar
for file in **/*pdf; do echo pathname "$file"; done
Run Code Online (Sandbox Code Playgroud)