Спробуйте:
find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'f=${1#./}; mv "$1" "./${f//\//_}"' None {} \;
Це безпечно для всіх імен файлів, навіть тих, що мають нові рядки у своїх іменах.
Як це працює
-mindepth 2
Це говорить про пошук не обробляти файли, які вже є в поточному каталозі.
-type f -name '*.pdf'
Це обмежує пошук звичайними файлами з pdf
розширенням.
-exec bash -c '...' None {} \;
Це запускає команду в рядку в лапках яке постачає ім'я файлу в якості першого аргументу, $1
.
Для наших цілей рядок None
- це просто заповнювач. Згідно з $0
умовами bash, це призначено назвою команди, яку ми виконуємо.
f=${1#./}; mv "$1" "./${f//\//_}"
Це (а) видаляє префікс ./
з імені файлу, і (b) переміщує файл у потрібне місце з новим іменем.
${1#./}
є прикладом видалення префікса bash . Він повертає стрімкі $1
з ./
вилученими з початку. ${f//\//_}
є прикладом заміни шаблону Баша . Він повертає рядок $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