Bash переместить и переименовать, анализируя вывод find

767
Canaryyellow

У меня есть вложенные каталоги файлов 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. Как разделить путь на «/» и сформировать новый путь и имя файла, как указано выше?

Спасибо заранее!

4

1 ответ на вопрос

6
John1024

Пытаться:

find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'f=$; mv "$1" "./$"' None {} \; 

Это безопасно для всех имен файлов, даже если в их именах есть символы новой строки.

Как это устроено

  • -mindepth 2

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

  • -type f -name '*.pdf'

    Это ограничивает поиск обычными файлами с pdfрасширением.

  • -exec bash -c '...' None {} \;

    Это запускает команду в строке в кавычках поставляющего имя файла в качестве первого аргумента, $1.

    Для наших целей строка Noneпросто заполнитель. В $0соответствии с соглашениями bash он назначается имени команды, которую мы запускаем.

  • f=$; mv "$1" "./$"

    Это (а) удаляет префикс ./из имени файла, и (б) перемещает файл в нужное место с новым именем.

    $является примером в Bash удаления префикса . Возвращает стриг $1с ./удаленным с самого начала. $является примером BASH по замещению шаблона . Возвращает строку $fс /заменой всех на _. Чтобы узнать больше об этих функциях, см. Раздел man bash« Расширение параметров» .

Более эффективная версия

Приведенная выше версия вызывает bash для каждого найденного файла. В качестве альтернативы, мы можем вызвать bash только один раз для нескольких найденных файлов. Для этого мы заключаем нашу команду в forцикл:

find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'for f in "$@"; do f=$; echo mv "$f" "./$"; done' None {} + 

Альтернативная проблема

Предположим, что все файлы, которые мы хотим, находятся в каталоге второго уровня, и мы хотим, чтобы перемещенные файлы имели обратный порядок имен каталогов, чтобы вместо этого ./path1_pathA_fileI.pdfмы получили ./pathA_path1_fileI.pdf. В этом случае:

for d1 in */; do d1=$; for d2 in "$d1"/*/; do d2=$; p="$_$d1"; for f in "./$d2"/*.pdf; do echo mv "$f" "./$_$"; done; done; done 

Или для тех, кто предпочитает, чтобы их команды распределялись по нескольким строкам:

for d1 in */ do d1=$ for d2 in "$d1"/*/ do d2=$ p="$_$d1" for f in "./$d2"/*.pdf do echo mv "$f" "./$_$" done done done 
Спасибо за пошаговую разбивку! Что, если бы я хотел также захотеть изменить порядок двух строк dir, чтобы окончательный путь был ./pathA_path1_fileI.pdf В этом случае подстановка шаблона $ не работает. Как это можно сделать? Canaryyellow 8 лет назад 0
@Canaryyellow ОК. Я добавил код для этого. John1024 8 лет назад 0

Похожие вопросы