循环遍历文件名而不使用 eval

循环遍历文件名而不使用 eval

在 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

如果您想递归到子目录,请使用zshbash及其globstarshell 选项集(同样,不适用于数千个文件):

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

还相关:

相关内容