Перемещение файлов из подкаталогов в один каталог и префикс исходного имени каталога

1291
Brad Dwyer

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

./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параметром, который будет конденсироваться мои файлы в один каталог, но будет в конечном итоге потери контекста каталога из файла.

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

3
Почему бы не сделать сценарий? var firstName 6 лет назад 1
Я закончил тем, что написал (супер неэффективный) скрипт (в nodejs .. это то, что я знаю лучше всего), но надеялся узнать что-то новое :) Brad Dwyer 6 лет назад 0

4 ответа на вопрос

8
Cyrus

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

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

Выход:

a / 1.png переименован в dest / a_1.png a / 2.png переименован в dest / a_2.png a / 3.png переименован в dest / a_3.png b / 1.png переименован в dest / b_1.png b / 2.png переименован в dest / b_2.png b / 3.png переименован в dest / b_3.png c / 1.png переименован в dest / c_1.png 

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

Хех, очень умно, просто замена `/` на `_` делает работу в этом конкретном случае, конечно. slhck 6 лет назад 1
Здорово, я не знал про Perl `переименовать` - выглядит полезно! Brad Dwyer 6 лет назад 0
Возможен и другой подход Йохена Латса с `mmv`:` rename -n 's | (. *) / (. *) | Dest / $ 1_ $ 2 |' * / *. Png` Cyrus 6 лет назад 0
6
slhck

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

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

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

3
AFH

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

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

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

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

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

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

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

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

line() { read -r; r=$?; echo "$REPLY"; return $r; } 
Ре "что все это может быть обработано в расширении параметров bash", это скорее предполагает, что вы используете bash, не так ли? В то время как Perl & c не зависят от оболочки, AFAIK. jamesqf 6 лет назад 0
@jamesqf - спрашивающий включил в качестве тега `bash`, отсюда и предположение. AFH 6 лет назад 1
2
Jochen Lutz

В качестве альтернативы переименованию, существует 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 также поддерживает копирование, связывание или добавление файлов.