Пытаться:
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
является просто заполнителем. В соответствии с соглашениями bash он присваивается $0
- это имя команды, которую мы запускаем.
f=${1#./}; mv "$1" "./${f//\//_}"
Это (а) удаляет префикс ./
из имени файла, и (б) перемещает файл в нужное место с новым именем.
${1#./}
Является примером удаления префикса bash. Возвращает стринг $1
с удалением ./
с начала. ${f//\//_}
является примером замены шаблона в bash. Возвращает строку $f
со всеми /
заменёнными на _
. Чтобы узнать больше об этих функциях, смотрите раздел man bash
озаглавленный Расширение параметров .
Более эффективная версия
Приведенная выше версия вызывает bash для каждого найденного файла. В качестве альтернативы, мы можем вызвать bash только один раз для нескольких найденных файлов. Для этого мы заключаем нашу команду в цикл for
:
find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'for f in "$@"; do f=${f#./}; echo mv "$f" "./${f//\//_}"; done' None {} +
Альтернативная проблема
Предположим, что все нужные нам файлы находятся в каталоге второго уровня, и мы хотим, чтобы перемещенные файлы имели обратный порядок имен каталогов, чтобы вместо ./path1_pathA_fileI.pdf
мы ./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
Или для тех, кто предпочитает, чтобы их команды распределялись на несколько строк:
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