Como uma abordagem alternativa, você não precisa de programas externos como rename , basename , etc - todos podem ser manipulados dentro da expansão do parâmetro bash : -
find SourceDir ... | while read -r f; do mv "$f" "TargetDir/${f//\//_}"; done
A expansão é um pouco difícil de seguir, então aqui está o que acontece: -
- Os arquivos a serem movidos são encontrados com
find .
- Cada nome de arquivo é lido, por sua vez, em
$f .
- O parâmetro
read -r e as aspas duplas em torno das expansões manipulam nomes estranhos, incluindo espaços nos nomes dos arquivos.
- O comando
mv move nomes como a/b/c para TargetDir/a_b_c .
- A expansão de destino substitui todos os
/ por _ , mas parece assustadora porque / faz parte da sintaxe de substituição.
- A forma geral é
${param/old/new} , que substitui a primeira instância de old por new na expansão.
- O formulário necessário aqui é
${param//old/new} , que substitui todas as instâncias de old por new na expansão.
- Para que
/ faça parte da sequência antiga, ela deve ter escape como \/ , portanto, o obscuro ${f//\//_} : o primeiro / apresenta a sintaxe de substituição, a segunda / especifica substitua todos os , o terceiro (com escape) / é a sequência antiga e o% final / apresenta a nova sequência ( _ ).
Eu não vejo frequentemente esta forma de expansão em scripts, mas vale a pena conhecer, pois pode ser muito útil às vezes.
Existem alguns nomes de arquivos que quebram isso (caracteres de nova linha incorporados e espaços iniciais ou finais, embora haja duas maneiras em torno do último: -
- Use
read -r em vez de read -r f e use REPLY em vez de f .
- Use
while f="$(line)" em vez de read -r f .
O último é mais puro, mas usa o programa externo line , que pode não estar disponível em todos os sistemas, embora possa ser codificado como uma função:
line() { read -r; r=$?; echo "$REPLY"; return $r; }