3

У меня есть структура каталогов, как это:

./a/1.png
./a/2.png
./a/3.png
./b/1.png
./b/2.png
./b/3.png
./c/1.png
...

И я хочу взять все файлы в подкаталогах и переместить их в новый каталог, чтобы их имена были чем-то вроде

../dest/a_1.png
../dest/a_2.png
../dest/a_3.png
../dest/b_1.png
../dest/b_2.png
../dest/b_3.png
../dest/c_1.png
...

Самое близкое, что я смог найти, не написав сценарий, чтобы сделать это файл за файлом, - это использовать команду find с параметром --backup=numbered который сконденсировал бы мои файлы в один каталог, но в итоге потерял бы контекст каталога из имя файла.

Есть ли краткий способ сделать это?

4 ответа4

8

С помощью отдельной команды rename Perl:

rename -n 's|/|_|; s|^|dest/|' */*.png

Выход:

a/1.png renamed as dest/a_1.png
a/2.png renamed as dest/a_2.png
a/3.png renamed as dest/a_3.png
b/1.png renamed as dest/b_1.png
b/2.png renamed as dest/b_2.png
b/3.png renamed as dest/b_3.png
c/1.png renamed as dest/c_1.png

Если все выглядит хорошо, удалите опцию -n .

6

С Bash 4 и рекурсивным сглаживанием (shopt -s globstar):

for f in **/*.png; do
  dn=$(basename "$(dirname "$f")")
  bn=$(basename "$f")
  mv -- "$f" "../dest/${dn}_${bn}"
done

Обратите внимание, что dirname /foo/bar/baz возвращает /foo/bar а не просто bar , поэтому вам нужно вызывать basename для результата, чтобы просто получить bar в случае, если вы работаете из другой родительской папки.

3

В качестве альтернативного подхода вам не нужны внешние программы, такие как rename , basename и т.д. - все это может быть обработано в расширении параметров bash :-

find SourceDir ... | while read -r f; do mv "$f" "TargetDir/${f//\//_}"; done

Расширение немного сложнее, поэтому вот что происходит:

  • Файлы для перемещения находятся с помощью команды find .
  • Каждое имя файла читается по очереди в $f .
  • Параметр read -r и двойные кавычки вокруг расширений обрабатывают странные имена, включая пробелы в именах файлов.
  • Команда mv перемещает имена, такие a/b/c в TargetDir/a_b_c .
  • Целевое расширение заменяет каждый / на _ , но выглядит устрашающе, потому что / является частью синтаксиса замещения.
  • Общая форма - ${param/old/new} , которая заменяет первый экземпляр old new в расширении.
  • Здесь нужна форма ${param//old/new} , которая заменяет каждый экземпляр old new в расширении.
  • Чтобы / было частью старой строки, ее необходимо экранировать как \/ , следовательно, довольно неясный ${f//\//_}: первый / вводит синтаксис подстановки, второй / определяет замену каждого , третий (экранированный) / является старой строкой, а последний / вводит новую строку (_).

Я не часто вижу эту форму расширения в скриптах, но об этом стоит знать, так как иногда она может быть очень полезна.

Есть некоторые имена файлов, которые будут нарушать это (встроенные символы новой строки и пробелы в начале или в конце, хотя есть два пути обхода последнего:-

  • Используйте read -r вместо read -r f и используйте REPLY вместо f .
  • Используйте while f="$(line)" вместо read -r f .

Последний более аккуратный, но использует внешнюю line программы, которая может быть недоступна во всех системах, хотя ее можно кодировать как функцию:

line() { read -r; r=$?; echo "$REPLY"; return $r; }
2

В качестве альтернативы переименованию, существует MMV, который использует стандартные шаблоны оболочки.

Со страницы руководства:

Mmv перемещает (или копирует, добавляет или связывает, как указано) каждый исходный файл, соответствующий шаблону от , до целевого имени, указанного в шаблоне. Это многократное действие выполняется безопасно, то есть без какого-либо неожиданного удаления файлов из-за столкновений целевых имен с существующими именами файлов или с другими целевыми именами. Более того, прежде чем что-либо предпринимать, mmv пытается обнаружить любые ошибки, которые могут возникнуть в результате всего указанного набора действий, и дает пользователю выбор: либо продолжить работу, избегая неисправных частей, либо прерывая работу.

Для вашего случая использования:

mmv -n '*/*' 'dest/#1_#2'
a/1.jpg -> dest/a_1.jpg
a/2.jpg -> dest/a_2.jpg
a/3.jpg -> dest/a_3.jpg
b/1.jpg -> dest/b_1.jpg
b/2.jpg -> dest/b_2.jpg
b/3.jpg -> dest/b_3.jpg
c/1.jpg -> dest/c_1.jpg
c/2.jpg -> dest/c_2.jpg
c/3.jpg -> dest/c_3.jpg

Как и переименование, '-n' - это не выполнять. Удалите его для фактического переименования.

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .