В качестве альтернативного подхода вам не нужны внешние программы, такие как 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; }