Пытаться:
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