Exclui espaços, hifens e sublinhados em nomes de arquivos?

9

O que é um bom comando para excluir espaços, hifens e sublinhados de todos os arquivos em um diretório ou arquivos selecionados?

Eu uso o seguinte comando com as ações personalizadas do Thunar para slugify nomes de arquivos:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

Mas esse comando apenas substitui espaços por traços / hífens e minúsculas com caracteres limitados.

Eu usei o seguinte comando no terminal para excluir espaços de milhares de nomes de arquivos em uma pasta e funcionou bem rápido:

 rename "s/ //g" *

Mais uma vez, apenas elimina espaços e não hífens / traços e sublinhados também.

Idealmente, não quero espaços, hifens / traços e sublinhados em meus nomes de arquivos. E seria ótimo se o comando pudesse ser usado com o Thunar Custom Actions nos arquivos selecionados.

    
por user8547 04.08.2014 / 22:01

5 respostas

10

A versão de rename que vem com o pacote perl suporta expressões regulares:

rename "s/[-_ ]//g" *

Alternativamente,

rename -i "s/[-_ ]//g" *

O -i sinalizador fará rename usar o modo interativo, avisando se o destino já existe, em vez de sobrescrever silenciosamente.

A renomeação de Perl é às vezes chamada de prename .

Renomear Perl versus renomear o util-linux

Em sistemas semelhantes ao Debian, a renomeação do perl parece ser o padrão e os comandos acima devem funcionar.

Em algumas distribuições, o utilitário rename do util-linux é o padrão. Este utilitário é completamente incompatível com o rename do Perl.

  • Todos: Primeiro, verifique se o rename do Perl está disponível com o nome prename .

  • Debian: a renomeação de Perl deve ser o padrão. Também está disponível como prename . O executável rename , no entanto, está sob o controle de /etc/alternatives e, portanto, poderia ter sido alterado para algo diferente.

  • archlinux: Execute pacman -S perl-rename e o comando está disponível como perl-rename . Para um nome mais conveniente, crie um alias. (Dica do chapéu: CinzeladoAbs)

  • Mac OSX De acordo com esta resposta , rename pode ser instalada no OSX usando homebrew via:

    brew install rename 
    
  • Download direto: rename também está disponível em Perl Monks:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename
    
por 04.08.2014 / 22:07
5

Eu substituiria todos os comandos tr , com um comando sed de substituição, por exemplo:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done
    
por 04.08.2014 / 22:12
4

Sem contar mv , você não precisa realmente de um processo externo para isso - você pode simplesmente poof .

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

Ainda assim, isso significa uma invocação de mv por arquivo e, assim, provavelmente, rename é melhor. Embora isso deva funcionar, dados apenas um POSIX mv em $PATH e um shell POSIX.

Então, eu desenvolvi uma espécie de demonstração louca para isso. O conjunto de testes é gerado como:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\%04o\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

Em primeiro lugar, serei o primeiro a reconhecer que o comando acima produz resultados que podem ser obtidos com mais facilidade por outros meios. Mas outros meios provavelmente não demonstrariam bem o que poderia ser feito com $IFS e uma pequena imaginação (doente?) .

Portanto, o primeiro bit é bastante direto:

  • tee canaliza 5 cópias de sua entrada - o documento chamado CGEN

  • dd bloqueia sua entrada por novas linhas em 90 bytes por bloco e canaliza isso para ...

  • sed associa 2 desses blocos a dois caracteres \n ewline, ' escolhe os resultados e adiciona a string touch -- a cada ciclo de linha antes de enviar para ... / p>

  • sh que, então, executa todas as entradas como comandos do shell

O #CGEN embora ... Bem, brevemente ...

  • a parte inferior printf imprime 252 0s

  • o penúltimo recebe 252% dos argumentos'' null-string e para cada um imprime o conteúdo de $n seguido da string " $i "

  • eval interpreta os argumentos do próximo up printf antes de imprimir os resultados dessa interpretação como números octal precedidos por 2 barras invertidas por peça

  • o último printf imprime os valores de bytes para esses octals 2 de cada vez, seguidos da string -_ ---___ para cada par

  • $n é inicializado para uma equação que irá incrementar $i em um para cada avaliação, exceto se ignorar os valores 10, 39 ou 47 - (que são \n ewline, ' aspas simples e / barra em decimal ASCII respectivamente)

O resultado final é um diretório contendo muitos nomes de arquivos realmente feios contendo cada byte no meu charset de 1 a 255, exceto o aspas simples (apenas ignorado para evitar mais uma declaração sed s/// ) e a barra / . Esses nomes de arquivos são assim:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-'M-' -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

Agora, receberei alguns dados sobre esses arquivos:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

OUTPUT

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

Ok. Agora, finalmente, para a ação:

ifsqz '_ -'
chksqz '_ -'

OUTPUT

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

Sucesso! Você pode ver por si mesmo:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\]]^^''aa??bbccddeeffgghh
]]^^''aabbc??cddeeffgghhii
^^''aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\
RRSSTTUUVVWW??XXYYZZ[[\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\]]^^??''aabbccddee
ZZ[[\]]^^''??aabbccddeeff
    
por 05.08.2014 / 04:27
2

se você tem perl, você geralmente tem renomear. você pode fazer:

> type rename
rename is /usr/bin/rename

e mostre como esse script está escrito:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker ([email protected]),
#  from Larry Wall's original script eg/rename from the perl source.
#

Este script não suporta -i flag (esta é a versão no meu sistema), mas talvez o seu suporte. E quanto aos argumentos? A primeira é a expressão regular com formato PCRE, funciona como filtro, modifica o nome da entrada para o nome da saída. Lista de nomes de entrada fornecidos por asterisco '*'. por exemplo, você faz:

> cd /tmp
> rename 's/ //g' *

em real '*' pode ser expandido para:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

Quando você tem arquivos realmente grandes, você está em armadilha. O shell expandirá sua linha por mais tempo do que o sistema aceita. então você pode fazer uma solução usando find ou xargs. Usar 'find' é um problema, porque renomear será chamado muitas vezes igual a contagem de arquivos no diretório. Use melhor xargs com opção -r. uma chamada de renomeação modifica muitos arquivos. por exemplo:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

último problema, o que significa:

's/ //g'

esta é uma expressão regular para nomes de modificação. depois de primeiro '/' é o espaço. isso é detectado e substituído por string após o segundo '/'. Mas há seqüência vazia terminou com o terceiro '/', então o espaço é substituído por nada. A opção 'g' torna essa expressão repetitiva. A expressão vai andar por todo o nome do começo ao fim e detecta todos os espaços.

Mas e se você tiver um caractere de tabulação ou outro caractere 'branco'? existe substituto para este '\ s'. que outros personagens desnecessários? basta adicioná-lo à expressão. Tudo perto com colchetes, por exemplo:

's/[\s_-]//g'

isso é tudo. você vê semelhança? Eu acho que você deveria ler man perlrequick e man perlretut, isso explica você (eu espero) como a expressão regular funciona. você pode usar o comando rename em seu próprio script, se precisar.

    
por 05.08.2014 / 16:15
1

O seguinte laço shell sh removerá todos os espaços, sublinhados e traços dos nomes dos arquivos no diretório atual, tomando cuidado para não sobrescrever nenhum arquivo existente:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

Para bash e ksh , e sendo um pouco mais detalhado com a lógica:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

Remova o echo quando tiver certeza de que faz o que deseja.

O comando tr excluirá ( -d ) qualquer caractere no conjunto de caracteres fornecido ( ' _-' ). É importante ter o traço no início ou no final do conjunto ou será interpretado como um intervalo de caracteres.

    
por 24.07.2016 / 10:11