按名称递归比较目录内容,忽略文件扩展名

Rob*_*cio 8 directory diff shell-script filenames

我有一个包含大约 7,000 个音乐文件的目录。我使用 lame 递归地将其中的所有文件重新编码到一个单独的目录中,输出具有相同相对路径和文件名的所有文件。输出文件具有 .mp3 扩展名,但某些输入文件具有不同的扩展名(.wma、.aac 等)。

我可以看到输出目录中缺少 ~100 个文件的文件计数差异。我想要做的是运行两个目录的比较并获取源中存在但不在目标中的文件列表。这很简单,除非我需要忽略文件扩展名的差异。

我试过在干运行打开的情况下使用 rsync,但我想不出一种方法来忽略文件扩展名。我也尝试过 diff,但无法找到仅按名称检查但忽略文件扩展名的选项。我开始想我可以在两个目录上执行递归 ls,删除文件扩展名,然后比较输出,但我真的不知道从哪里开始使用 sed 或 awk 修改 ls 输出。

Gil*_*il' 8

要查看列表,这里有两种变体,一种递归到子目录中,另一种不递归。都使用特定于 bash、ksh 和 zsh 的语法。

comm -3 <(cd source && find -type f | sed 's/\.[^.]*$//' | sort) \
        <(cd dest && find -type f | sed 's/\.[^.]*$//' | sort)
comm -3 <(cd source && for x in *; do printf '%s\n' "${x%.*}"; done | sort) \
        <(cd dest && for x in *; do printf '%s\n' "${x%.*}"; done | sort)
Run Code Online (Sandbox Code Playgroud)

更短,在 zsh 中:

comm -3 <(cd source && print -lr **/*(:r)) <(cd dest && print -lr **/*(:r))
comm -3 <(print -lr source/*(:t:r)) <(print -lr dest/*(:t:r))
Run Code Online (Sandbox Code Playgroud)

comm命令列出了两个文件 ( comm -12)共有的行,这些行仅在第一个文件 ( comm -23) 中或仅在第二个文件 ( comm -13) 中。数字表示从输出中减去的内容¹。必须对两个输入文件进行排序。

在这里,文件实际上是命令的输出。Shell<(…)通过提供“假”文件(FIFO 或/dev/fd/命名文件描述符)作为命令的参数来评估构造。

¹ 所以这里的否定说者是完全有道理的。


如果您想对文件执行操作,您可能需要遍历源文件。

cd source
for x in *; do
  set -- "…/dest/${x%.*}".*
  if [ $# -eq 1 ] && ! [ -e "$1" ]; then
    echo "$x has not been converted"
  elif [ $# -gt 1 ]; then
    echo "$x has been converted to more than one output file: " "$@"
  else
    echo "$x has been converted to $1"
  fi
done
Run Code Online (Sandbox Code Playgroud)