Bash перемістіть і перейменуйте, розбираючи вихід знахідки


3

Я вклав каталоги файлів 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 ...

Але я не знаю, як заповнити ... тому що я не розумію розбору та призначення змінних у баші. Як можна розділити шлях на "/" та сформувати новий шлях та ім'я файлу, як зазначено вище?

Заздалегідь дякую!

Відповіді:


6

Спробуйте:

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

Дякуємо за покрокову розбивку! Що робити, якщо я хотів би також захотіти змінити порядок двох рядків dir, щоб кінцевим шляхом був ./pathA_path1_fileI.pdf У цьому випадку заміна шаблону $ {f // \ // _} не працює. Як це можна було зробити?
Canaryyellow

@Canaryyellow ОК. Я додав код для цього.
John1024
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.