使用模式和替换列表递归地重命名文件

使用模式和替换列表递归地重命名文件

我有以下文件结构:

  • 一些目录
    • 一些文件.txt
    • 这里还有另一个文件.log
    • 还有一个文件.mp3
  • 另一个目录
    • 与其他一些 file.txt
  • 根级别文件.txt
  • 根级别的另一个文件.ext

我现在想做的是运行一个小脚本,该脚本将另一个文件作为输入,其中包含某种类型的模式/替换对,以根据它们递归地重命名这些文件。这样每个“another”(不区分大小写)都会被“foo”替换,或者每个“some”被“bar”替换。

我已经尝试了很多迭代文件和读取所述输入文件的方法,但是没有任何效果,我最终成功地意外覆盖了我的测试脚本。但有很多lswhilesedmv正在使用中。

我自己无法解决的两件事是如何处理文件名中的空格以及如何不处理在先前的模式匹配中已重命名的文件。

也许你可以给我指出正确的方向?

答案1

TOP="`pwd -P`" \
find . -type d -exec sh -c '
   for d
   do
      cd "$d" && \
         find . ! -name . -prune -type f -exec sh -c '\''
            while IFS=\; read -r pat repl
            do
               rename "s/$pat/$repl/g" "$@"
               N=$#
               for unmoved
               do
                  if [ -f "$unmoved" ]
                  then
                     set X ${1+"$@"} "$unmoved"
                     shift
                  fi
               done
               shift "$N"
               case $# in 0 ) break ;; esac
            done < patterns.csv
         '\'' x \{\} +
      cd "$TOP"
   done
' x {} +
  • 仅设置find网络目录并吞下sh它们。这最大限度地减少了 的调用次数sh
  • find在每个目录中设置网络regular文件,深度级别仅为 1,然后将它们放入shgulp 中。这最大限度地减少了rename实用程序被调用的次数。
  • 设置一个while循环来读入各个pattern <-> replacement对并将它们应用于所有regular文件。
  • 在过程中rename-我们会记录rename处理后文件是否仍然存在。如果我们发现一个文件仍然存在,那么这意味着,由于某种原因,它无法重命名,因此将在下一次迭代中尝试pat/repl。 OTOH,如果文件已成功重命名,那么我们不会pat/repl通过将其从命令行参数列表中删除来对该文件应用下一次迭代。

答案2

rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
   while read -r old new; do
      rename "s/$old/$new/i" "$@"
   done < "$rPairs"
' x {} +

假设您的重命名对文件中没有非 ASCII 字符,并且该文件远离搜索路径。

答案3

在拉克什·夏尔马(Rakesh Sharma)的回答之后,我在进行了更多尝试并睡了一会儿之后找到了正确的方向。

最后我想出了以下脚本:

#!/bin/bash


while IFS=";" read pattern replacement
do
  if [[ ! -z $pattern ]]
  then
    echo "Checking files for pattern '$pattern'."

    find ./files -name "*$pattern*" -type f | while read fpath
    do
      fname=$(basename "$fpath")
      dname=$(dirname "$fpath")

      echo "  Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
      mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
    done
  fi
done < patterns.csv

它读取文件并循环遍历其填充和变量pattern.csv的行。在第二步中,找到目录中与当前模式匹配的所有文件。必须这样做以避免在第二个模式匹配时再次尝试重命名文件,因为这会失败。最后,它仅重命名文件本身,而不是使用 shell 参数替换来重命名包含该文件的目录。$pattern$replacement./files

不起作用的是替换不区分大小写的匹配项,但我可以忍受。

答案4

要记住的重要一点是,遍历目录树是一个缓慢的过程,因此只执行一次。我们首先要做的是find仅查看树中的目录。对于每个目录,我们查找regular files它们下面的所有目录(这里没有递归)。然后,我们对这些文件名应用重命名转换,同时记录它是否成功。如果成功,那么我们将跳出 while 循环,从而阻止下一个 patt/repl 应用于该文件。

tempd="`mktemp -d`" \
find . -type d -exec sh -c '
   cd "$1" && \
   for f in ./*
   do
      [ -f "$f" ] || continue
      while IFS=\; read -r patt repl
      do
         case $f in
            ./*"$patt"* )
               rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
               case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
         esac
      done < /tmp/patterns.csv
   done
' {} {} \;

相关内容