Bash переместить и переименовать, анализируя вывод find
У меня есть вложенные каталоги файлов PDF, и я хотел бы извлечь их в каталог более высокого уровня, переименовав их следующим образом:
Мои файлы что-то вроде:
./path1/pathA/fileI.pdf
./path1/pathB/fileII.pdf
Я хочу добиться:
./path1_pathA_fileI.pdf
./path1_pathB_fileII.pdf
Я знаю, что могу сделать список файлов, выполнив
find . -type f -name "*.pdf"
И я могу представить решение, используя
find . -type f -name "*.pdf" | mv -t ...
Но я не знаю, как заполнить... потому что я не понимаю парсинга и назначения переменных в bash. Как разделить путь на "/" и сформировать новый путь и имя файла, как указано выше?
Спасибо заранее!
1 ответ
Пытаться:
find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'f=${1#./}; mv "$1" "./${f//\//_}"' None {} \;
Это безопасно для всех имен файлов, даже если в их именах есть символы новой строки.
Как это устроено
-mindepth 2
Это говорит find не обрабатывать файлы, которые уже находятся в текущем каталоге.
-type f -name '*.pdf'
Это ограничивает поиск обычными файлами с
pdf
расширение.-exec bash -c '...' None {} \;
Это запускает команду в строке в кавычках, предоставляя имя файла в качестве первого аргумента,
$1
,Для наших целей строка
None
это просто заполнитель. Он назначен$0
в соглашениях bash - это имя команды, которую мы запускаем.f=${1#./}; mv "$1" "./${f//\//_}"
Это (а) удаляет префикс
./
из имени файла, и (б) перемещает файл в нужное место с новым именем.${1#./}
пример удаления префикса bash Возвращает стриг$1
с./
снято с начала.${f//\//_}
пример замены шаблона в bash Возвращает строку$f
со всем/
заменено на_
, Чтобы узнать больше об этих функциях, см. Разделman bash
entitled Parameter Expansion.
More efficient version
The above version invoke bash for every file found. Alternatively, we can invoke bash just once for several files found. To do this, we wrap our command in a for
цикл:
find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'for f in "$@"; do f=${f#./}; echo mv "$f" "./${f//\//_}"; done' None {} +
Alternate problem
Suppose that all the files we want are in a second level directory and we want the moved files to have the order of the directory names reversed so that instead of ./path1_pathA_fileI.pdf
, we end up with ./pathA_path1_fileI.pdf
, В этом случае:
for d1 in */; do d1=${d1%/}; for d2 in "$d1"/*/; do d2=${d2%/}; p="${d2#$d1/}_$d1"; for f in "./$d2"/*.pdf; do echo mv "$f" "./${p}_${f#./$d2/}"; done; done; done
Or, for those who prefer their commands spread out over multiple lines:
for d1 in */
do
d1=${d1%/}
for d2 in "$d1"/*/
do
d2=${d2%/}
p="${d2#$d1/}_$d1"
for f in "./$d2"/*.pdf
do
echo mv "$f" "./${p}_${f#./$d2/}"
done
done
done