У меня следующая структура файла:
- Некоторый каталог
- Какой-то файл.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
' {} {} \;