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; }