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。$IFSzsh
例如,如果find输出./foo bar.pdf\n./*foo*\tbar.pdf\n(\t表示 TAB 和\nNL),默认值为$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 -finzsh因为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 年、fish2005 年、bash2009 年和tcsh2010 年tcsh被复制(尽管也复制了该***/部分)。并且所有这些默认情况下都不启用它。不幸的是,请注意,bash和fish **都遵循指向目录的符号链接(例如-L/ -followin 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)