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

Foi útil?

Solução

Você pode usar o recurso de expansão de parâmetros da festa

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top