find . -name '*.txt' -print0 | xargs -0 -n1 bash -c 'mv "$0" "${0/oldname/newname}"'
Obviamente, esse padrão de renomeação é apenas um exemplo simples, mas cuidado, pois ele edita o caminho inteiro, não apenas o nome do arquivo.
Eu encontrei esta resposta como faço para ... mas simplesmente não funciona - ele não renomeou nenhum arquivo por razões desconhecidas para mim
Antes de começar a pesquisar, achei que deveria ser uma tarefa fácil, mesmo para o pinguim novato, mas não parece ser assim para mim.
Por exemplo, eu simplesmente não posso dizer a ls
para listar todos os *.txt
em todas as subpastas, o que foi uma surpresa para mim (sem grep ou similar).
Então eu achei find
e find . -name name_1.txt
listas arquivos bem, mas
for f in $(find . -name name_1.txt) ; do echo "$f" ; done
divide caminhos de arquivos inteiros com espaço como separador, portanto, é inutilizável passar essa saída para algum comando como mv
ou rename
Eu quero perguntar o que há de errado com o comando acima e, se possível, algum oneliner bacana, para que eu possa renomear recursivamente name_1.txt
to name_2.txt
find . -name '*.txt' -print0 | xargs -0 -n1 bash -c 'mv "$0" "${0/oldname/newname}"'
Obviamente, esse padrão de renomeação é apenas um exemplo simples, mas cuidado, pois ele edita o caminho inteiro, não apenas o nome do arquivo.
o comando mmv fará isso em uma chamada bem simples:
mmv "; name_1.txt" "# 1name_2.txt"
O ";" O caractere curinga, que é reutilizado no nome do arquivo de substituição como "# 1", também corresponde a subdiretórios.
Se precisar de exemplos mais complicados, você deve procurar na página do manual.
Como apontam @ams, muitas soluções recursivas de substituição exigem que você trabalhe com todo o caminho, não apenas com o nome do arquivo.
Eu uso a ação find
-execdir
para resolver esse problema. Se você usar -execdir
em vez de -exec
, o comando especificado será executado a partir do subdiretório que contém o arquivo correspondente. Portanto, em vez de passar todo o caminho para rename
, ele passa apenas ./filename
. Isso torna muito mais fácil escrever o comando replace.
find /the/path -type f \
-name '*_one.txt' \
-execdir rename 's/\.\/(.+)_one\.txt$/$1_two.txt/' {} \;
Em detalhes:
-type f
significa procurar apenas arquivos, não diretórios -name '*_one.txt'
significa significa apenas corresponder nomes de arquivos que terminam em _one.txt rename
em vez de mv. renomear usa uma expressão regular Perl para renomear cada arquivo. -type
e -name
são o caractere de continuação da linha de bash. Eu os uso para tornar este exemplo mais legível, mas eles não são necessários na prática. -execdir
é necessária. Ele está lá para expor o ponto-e-vírgula, que finaliza o comando executado por -execdir
. Diversão! Explicação do regex:
s/
início da regex \.\/
corresponde ao líder ./que o -execdir passa. Use \ para escapar do. e / metacaracteres (.+)
corresponde ao início do nome do arquivo. Os parênteses capturam a correspondência para uso posterior _one\.txt
match "_one.txt", escape do metacaractere de ponto $
ancora a correspondência no final da string /
marca o final da parte "correspondência" da expressão regular e o início da parte "substituir" $1
faz referência ao nome do arquivo existente, porque capturamos com parênteses. Se você usar vários conjuntos de parênteses na parte "match", você pode consultá-los aqui usando $ 2, $ 3, etc. _two.txt
o novo nome do arquivo terminará em "_two.txt". Não há necessidade de escapar do metacaractere ponto aqui na seção "substituir" /
end do regex Antes
tree --charset=ascii
|-- a_one.txt
|-- b_one.txt
|-- Not_this.txt
'-- dir1
'-- c_one.txt
Depois
tree --charset=ascii
|-- a_two.txt
|-- b_two.txt
|-- Not_this.txt
'-- dir1
'-- c_two.txt
Dica: a opção rename
's -n é útil. Ele faz uma corrida a seco e mostra a você quais nomes ele mudará, mas não faz alterações.
Você pode explorar a infinidade de opções disponíveis:
'$ apt-cache search rename | grep -i rename'
Tags bash