Respostas:
Uma maneira portátil (que funcionará em qualquer sistema compatível com POSIX):
find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;
No bash4, você pode usar a globstar para obter globs recursivos (**):
shopt -s globstar
for file in /the/path/**/*.abc; do
mv "$file" "${file%.abc}.edefg"
done
O rename
comando (perl) no Ubuntu pode renomear arquivos usando a sintaxe de expressão regular perl, que você pode combinar com a globstar ou find
:
# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)
# Best to process the files in chunks to avoid exceeding the maximum argument
# length. 100 at a time is probably good enough.
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done
# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +
Veja também http://mywiki.wooledge.org/BashFAQ/030
${1%.abc}...
sh -c
o primeiro argumento é atribuído a $ 0 e os argumentos restantes são atribuídos aos parâmetros posicionais . Portanto, _
é um argumento fictício e '{}' é o primeiro argumento posicional a sh -c
.
Isso fará a tarefa necessária se todos os arquivos estiverem na mesma pasta
rename 's/.abc$/.edefg/' *.abc
Para renomear os arquivos recursivamente, use isto:
find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc
em uma versão moderna do Bash.
s
no começo, e que outras opções posso usar lá. Obrigado !
Um problema com as renomeações recursivas é que, independentemente do método usado para localizar os arquivos, ele passa o caminho inteiro para rename
, não apenas o nome do arquivo. Isso dificulta a renomeação complexa em pastas aninhadas.
Eu uso find
a -execdir
ação de para resolver este problema. Se você usar em -execdir
vez de -exec
, o comando especificado será executado no subdiretório que contém o arquivo correspondente. Então, ao invés de passar o caminho inteiro rename
, ele apenas passa ./filename
. Isso facilita muito escrever o regex.
find /the/path -type f \
-name '*.abc' \
-execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;
Em detalhe:
-type f
significa apenas procurar arquivos, não diretórios-name '*.abc'
significa apenas nomes de arquivos de correspondência que terminam em .abc'{}'
é o espaço reservado que marca o local em -execdir
que o caminho encontrado será inserido. As aspas simples são necessárias, para permitir que ele lide com nomes de arquivos com espaços e caracteres de shell.-type
e -name
são o caractere de continuação de linha bash. Eu os uso para tornar este exemplo mais legível, mas eles não são necessários se você colocar seu comando em uma única linha.-execdir
linha é necessária. Ele existe para escapar do ponto-e-vírgula, que encerra o comando executado por -execdir
. Diversão!Explicação do regex:
s/
início da regex\.\/
coincidir com o ./ principal que o -execdir passa. Use \ para escapar do. e / metacaracteres (nota: esta parte varia dependendo da sua versão do find
. Veja o comentário do usuário @apollo)(.+)
corresponde ao nome do arquivo. Os parênteses capturam a correspondência para uso posterior\.abc
escape do ponto, combine o abc$
ancorar a partida no final da string
/
marca o final da parte "correspondente" da regex e o início da parte "substituir"
version1_
adicione este texto a cada nome de arquivo
$1
referencia o nome do arquivo existente, porque o capturamos entre parênteses. Se você usar vários conjuntos de parênteses na parte "correspondente", poderá consultá-los aqui usando $ 2, $ 3 etc..abc
o novo nome do arquivo terminará em .abc. Não há necessidade de escapar do metacaractere de ponto aqui na seção "substituir"/
fim da regexAntes
tree --charset=ascii
|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
`-- nested_file.abc
Depois de
tree --charset=ascii
|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
`-- version1_nested_file.abc
Dica: rename
a opção -n é útil. Ele executa uma execução a seco e mostra quais nomes serão alterados, mas não faz nenhuma alteração.
--execdir
não passou na liderança, ./
portanto a \.\/
parte no regex pode ser omitida. Pode ser que eu esteja no OSX e não no ubuntu
Outra maneira portátil:
find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
# Rename all *.txt to *.text
for f in *.txt; do
mv -- "$f" "${f%.txt}.text"
done
Veja também a entrada sobre por que você não deve analisar ls
.
Editar: se você precisar usar o nome da base, sua sintaxe será:
for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done
https://unix.stackexchange.com/questions/19654/changing-extension-to-multiple-files
Isto é o que eu fiz e funcionou da maneira que eu queria. Eu usei o mv
comando. Eu tinha vários .3gp
arquivos e queria renomeá-los todos para.mp4
Aqui está um breve guia para ele:
for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done
Que simplesmente varre o diretório atual, pega todos os arquivos .3gp e renomeia (usando o mv) para ren-name_of_file.mp4
file.3gp
para file.3gp.mp4
, o que pode não ser o que a maioria das pessoas deseja.
.3gp
ou .mp4
aqui foram apenas para fins ilustrativos.
basename "$i" .mp4
para remover a extensão anterior em vez de "ren- $ i.mp4".
Eu usaria o comando mmv do pacote com o mesmo nome :
mmv ';*.abc' '#1#2.edefg'
Os ;
corresponde a zero ou mais */
e corresponde #1
na substituição. O *
corresponde a #2
. A versão não recursiva seria
mmv '*.abc' '#1.edefg'
Renomeie arquivos e diretórios com find -execdir | rename
Se você vai renomear arquivos e diretórios, não apenas com um sufixo, esse é um bom padrão:
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find . -depth -execdir rename 's/findme/replaceme/' '{}' \;
A -execdir
opção impressionante faz um cd
no diretório antes de executar o rename
comando, ao contrário -exec
.
-depth
verifique se a renomeação ocorre primeiro nos filhos e, depois, nos pais, para evitar possíveis problemas com os diretórios-pai ausentes.
-execdir
é necessário porque a renomeação não funciona bem com caminhos de entrada sem nome de base, por exemplo, o seguinte falha:
rename 's/findme/replaceme/g' acc/acc
A PATH
invasão é necessária porque -execdir
tem uma desvantagem muito irritante: find
é extremamente opinativa e se recusa a fazer qualquer coisa -execdir
se você tiver algum caminho relativo em sua PATH
variável de ambiente, por exemplo ./node_modules/.bin
, falhando com:
find: O caminho relativo './node_modules/.bin' está incluído na variável de ambiente PATH, que é insegura em combinação com a ação -execdir de find. Remova essa entrada de $ PATH
Consulte também: Por que o uso da ação '-execdir' é inseguro para o diretório que está no PATH?
-execdir
é uma extensão de localização GNU para POSIX . rename
é baseado em Perl e vem do rename
pacote. Testado no Ubuntu 18.10.
Mudar o nome da solução alternativa à procura
Se seus caminhos de entrada não vierem find
, ou se você já teve o suficiente do aborrecimento do caminho relativo, podemos usar alguns Perl lookahead para renomear com segurança diretórios como em:
git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'
Eu não encontrei um análogo conveniente para -execdir
com xargs
: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686
O sort -r
é necessário para garantir que os arquivos vir após seus respectivos diretórios, desde caminhos mais longos vir após mais curtos com o mesmo prefixo.