Рекурсивное переименование файлов с использованием списка шаблонов и замен

Рекурсивное переименование файлов с использованием списка шаблонов и замен

У меня следующая структура файла:

  • Некоторый каталог
    • Какой-то файл.txt
    • Еще один файл здесь.log
    • Еще один файл.mp3
  • Другой каталог
    • С каким-то другим файлом.txt
  • Файл на корневом уровне.txt
  • Другой файл на корневом уровне.ext

Что я хочу сделать сейчас, так это запустить небольшой скрипт, который берет другой файл в качестве входных данных, содержащий некоторый тип пар шаблон/замена, чтобы рекурсивно переименовать эти файлы в соответствии с ними. Так, чтобы каждый "another" (без учета регистра) заменялся на "foo" или каждый "some" на "bar".

Я уже пробовал много вещей с итерацией по файлам и чтением указанного входного файла, но ничего не работало так, как должно было, и мне в конце концов удалось случайно перезаписать мой тестовый скрипт. Но было много ls, while, sedили mvв использовании.

Две вещи, которые я не смог решить самостоятельно: как обрабатывать пробелы в именах файлов и как не обрабатывать файлы, которые уже были переименованы в предыдущем сопоставлении с шаблоном.

Может быть, вы сможете указать мне правильное направление?

решение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их в gulp. Это минимизирует количество вызовов sh.
  • Настройте findв каждом из этих каталогов сетевые regularфайлы, только на уровне глубины 1, и скармливайте их shв gulp. Это минимизирует количество вызовов renameутилиты.
  • Настройте whileцикл для считывания различных pattern <-> replacementпар и применения их ко всем regularфайлам.
  • В процессе rename-ingмы сохраняем запись о том, был ли файл все еще в состоянии после процесса rename. Если мы обнаруживаем, что файл все еще существует, то это означает, что по какой-то причине его не удалось переименовать, и, следовательно, его попытаются переименовать в следующей pat/replитерации. С другой стороны, если файл был успешно переименован, то мы НЕ применяем следующую 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

После ответа Ракеша Шармы я двинулся в правильном направлении, немного поэкспериментировав и немного поспав.

В конце концов я придумал следующий сценарий:

#!/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и проходит по его строкам, заполняя переменные $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
' {} {} \;

Связанный контент