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