Batch renomear arquivos com Bash
-
03-07-2019 - |
Pergunta
Como pode Bash renomear uma série de pacotes para remover seus números de versão? Eu tenho sido brincando ao redor com tanto expr
e %%
, sem sucesso.
Exemplos:
Xft2-2.1.13.pkg
se torna Xft2.pkg
jasper-1.900.1.pkg
se torna jasper.pkg
xorg-libXrandr-1.2.3.pkg
se torna xorg-libXrandr.pkg
Solução
Você pode usar o recurso de expansão de parâmetros da festa ??p>
for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done
Cotações são necessários para nomes de arquivos com espaços.
Outras dicas
Se todos os arquivos estão no mesmo diretório a seqüência
ls |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
vai fazer o seu trabalho. O sed comando irá criar uma seqüência de mv comandos, que você pode então tubo no shell. É melhor primeiro executar o gasoduto sem a | sh
à direita, de modo a verificar que o comando faz o que quiser.
Para recurse através de vários diretórios usar algo como
find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
Note que no sed a expressão regular seqüência de agrupamento é suportes precedidos por uma barra invertida, \(
e \)
, ao invés de colchetes simples (
e )
.
Eu vou fazer algo como isto:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
suposta todo o seu arquivo está no diretório atual. Se não, tente usar encontrar como aconselhado acima de Javier.
Editar :. Além disso, esta versão não utilizar qualquer das funções específicas do bash, como os outros acima, que leva a mais portabilidade
Aqui está uma POSIX quase equivalente ao atualmente aceito resposta . Este comercializa o único Bash-expansão de parâmetros ${variable/substring/replacement}
para um que está disponível em qualquer shell Bourne-compatível.
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
O ${variable%pattern}
expansão parâmetro produz o valor de variable
com qualquer sufixo que partidas pattern
removido. (Também é ${variable#pattern}
para remover um prefixo.)
Eu mantive a -[0-9.]*
subpadrão da resposta aceita, embora seja talvez enganosa. Não é uma expressão regular, mas um padrão glob; por isso não significa "um traço seguido de zero ou mais números ou pontos". Em vez disso, significa "um traço, seguido de um número ou um ponto, seguido por qualquer coisa". O "nada" será o menor jogo possível, não o mais longo. (Bash ofertas ##
e %%
para aparar o mais longo prefixo ou sufixo possível, em vez do mais curto.)
melhor sed
uso para isso, algo como:
find . -type f -name "*.pkg" |
sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
while read nameA nameB; do
mv $nameA $nameB;
done
figurando-se a expressão regular é deixada como um exercício (como está lidando com nomes que incluem espaços)
Podemos assumir sed
está disponível em qualquer * nix, mas não podemos ter a certeza
ele vai apoiar sed -n
para gerar mv comandos. ( NOTA:. Apenas GNU sed
faz isso)
Mesmo assim, builtins bash e sed, podemos rapidamente chicotear acima de uma função shell para fazer isso.
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
mv -v "$file" "$(sed $sed_pattern <<< $file)"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Uso
sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg
before:
./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg
after:
./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg
Criando pastas de destino:
Desde mv
não cria automaticamente pastas de destino não podemos usar
nossa versão inicial de sedrename
.
É um relativamente pequena mudança, por isso seria bom para incluir esse recurso:
Vamos precisar de uma função de utilidade, abspath
(ou caminho absoluto) desde o bash
não tem essa configuração no.
abspath () { case "$1" in
/*)printf "%s\n" "$1";;
*)printf "%s\n" "$PWD/$1";;
esac; }
Uma vez que temos de que podemos gerar a pasta de destino (s) por um padrão sed / renomeação que inclui nova estrutura da pasta.
Isso irá garantir que sabemos os nomes dos nossos pastas de destino. Quando nós renomear vamos precisar usá-lo no nome do arquivo de destino.
# generate the rename target
target="$(sed $sed_pattern <<< $file)"
# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"
# finally move the file to the target name/folders
mv -v "$file" "$target"
Aqui está a pasta cheia roteiro ciente ...
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
target="$(sed $sed_pattern <<< $file)"
mkdir -p "$(dirname $(abspath $target))"
mv -v "$file" "$target"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Claro, ele ainda funciona quando não temos pastas alvo específicos também.
Se quisermos colocar todas as músicas em uma pasta, ./Beethoven/
podemos fazer isso:
Uso
sedrename 's|Beethoven - |Beethoven/|g' *.mp3
before:
./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3
after:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
Bonus rodada ...
Usando esse script para mover arquivos de pastas em uma única pasta:
Assumindo que quis reunir-se todos os arquivos combinados, e colocá-los na pasta atual, podemos fazê-lo:
sedrename 's|.*/||' **/*.mp3
before:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
after:
./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3
Nota sobre os padrões de regex sed
regras padrão sed regulares aplicar neste script, esses padrões não são
PCRE (Perl Compatible Regular Expressions). Você poderia ter sed
estendida sintaxe de expressão regular, usando sed -r
ou sed -E
dependendo da sua plataforma.
Veja a man re_format
compatível com POSIX para uma descrição completa de
sed padrões básicas e estendidas de expressões regulares.
Eu acho que renomeação é uma ferramenta muito mais simples de usar para este tipo de coisa. Eu encontrei-o no Homebrew para OSX
Para o seu exemplo, eu faria:
rename 's/\d*?\.\d*?\.\d*?//' *.pkg
'S' significa substituto. O formulário é s / searchPattern / substituição / files_to_apply. Você precisa usar regex para isso que leva um pouco de estudo, mas vale bem a pena o esforço.
Isso parece funcionar assumindo que
- tudo termina com $ pkg
- sua versão # 's sempre começam com um "-"
retirar a .pkg, em seguida, retirar - ..
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
Eu tive vários arquivos *.txt
a ser renomeado como .sql
na mesma pasta.
abaixo funcionou para mim:
for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done
Obrigado por esta resposta. Eu também tinha algum tipo de problema. Movendo arquivos .nzb.queued para .nzb arquivos. Tinha espaços e outra cruft nos nomes de arquivo e isso resolveu o meu problema:
find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh
É com base na resposta de Diomidis Spinellis.
O regex cria um grupo para toda a nome de arquivo e um grupo para a parte antes .nzb.queued e cria um comando shell movimento. Com as cordas citado. Isso também evita a criação de um loop em shell script, porque isso já é feito por sed.