在 shell 脚本中,如果我们想循环遍历某些文件名,我们可以通过 shell 通配符获得这些文件名,例如所有子目录中以字符串“Example”开头的所有 MKV 文件 - 我们如何在不使用 的情况下做到这一点eval
?
例如,当以下脚本执行循环时
#!/usr/local/bin/zsh
for i in /media/mybook/Example*/; do;
t="ls \"$i\"*.mkv"
s=$(eval $t)
echo $s
done
有没有办法摆脱eval
?
答案1
我很困惑你为什么要eval
在这里使用。你可以直接写s=$(ls "$i"*.mkv)
。但调用ls
毫无意义并且会破坏文件名。只需正常迭代文件即可。
for dir in /media/mybook/Example*/; do;
if ! [ -d "$dir" ]; then continue; fi
for file in "$dir"/*.mkv; do
if ! [ -e "$file" ]; then continue; fi
echo "$file"
done
done
请注意如何"$dir"
在双引号内(以便目录名称中的任何特殊字符(例如空格)保持原样),但在*
引号之外,因此将其视为通配符。
的行continue
是为了跳过通配符不匹配的特殊情况。在 sh 中,当通配符不匹配时,它会保持原样,因此for
会看到一个名称列表,其中一个元素是字面上的/media/mybook/Example*/
(对于外循环)。一些 shell(ksh、bash、zsh)有办法避免这种情况,例如在 bash 中:
shopt -s nullglob
for dir in /media/mybook/Example*/; do;
for file in "$dir"/*.mkv; do
echo "$file"
done
done
如果您只是处理文件并且不需要对目录执行任何操作,则没有必要使用两个嵌套循环。
for dir in /media/mybook/Example*/*.mkv; do
if ! [ -e "$file" ]; then continue; fi
echo "$file"
done
所有这些片段都作用于Example*
目录本身内的文件,而不是其子目录中的文件。如果你想递归地遍历目录,请参见拘萨罗南达的回答。
答案2
使用find
:
find /media/mybook/Example*/ -type f -name '*.mkv' -print
如果您不想递归到子目录,请-maxdepth 1
在之前添加。-type f
或者只是一个直接的 shell 循环(如果有数千个文件将不起作用),并假设您只想查看第一个子目录级别:
for pathname in /media/mybook/Example*/*.mkv; do
printf '%s\n' "$pathname"
done
如果您想递归到子目录,请使用zsh
或bash
及其globstar
shell 选项集(同样,不适用于数千个文件):
for pathname in /media/mybook/Example*/**/*.mkv; do
printf '%s\n' "$pathname"
done
有关的:
到做带有路径名的东西:
和find
:
find /media/mybook/Example*/ -type f -name '*.mkv' -exec sh -c '
for pathname do
# Use "$pathname" here
done' sh {} +
...sh -c
显然可以更改为bash -c
或者zsh -c
如果您需要使用这些 shell 的任何特殊功能。
有关的:
使用 shell 循环:
for pathname in /media/mybook/Example*/*.mkv; do
# Use "$pathname" here
done
还相关: