Buscar todos os nomes de arquivos com um número específico de caracteres

3

Eu quero mudar todos os nomes de arquivos que eles têm o comprimento exato de 16 caracteres (dígitos e letras minúsculas). Eu tentei [0-9a-z]{16} e [0-9a-z]\{16\} para o espaço reservado regX no snippet a seguir, mas não funciona.

for file in <regX>
do
  mv "$file" "${file}.txt"
done
    
por Hölderlin 09.04.2017 / 04:50

4 respostas

3

Com extglob

shopt -s extglob
for file in +([0-9a-z])
do
    [[ ${#file} == 16 ]] && echo mv "$file" "${file}.txt"
done
  • +([0-9a-z]) significa um ou mais de [0-9a-z] caracteres
  • ${#file} fornece o tamanho do nome do arquivo
  • echo é para execução a seco, remova assim que as coisas estiverem bem
por 09.04.2017 / 15:29
4

Padrões de globulação de concha não são expressões regulares . Eles podem ser parecidos, mas funcionam de maneira diferente em alguns aspectos.

A expressão regular [0-9a-z]{16} corresponde a uma sequência que contém uma execução de 16 caracteres do intervalo de caracteres mencionado (a sequência que contém a correspondência pode ser maior).

O padrão de globulação de concha equivalente seria algo como

*[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]*

Isso ocorre porque

  1. Padrões globosos não suportam modificadores que dizem "repita os N tempos anteriores".
  2. Os padrões de globbing são, por padrão, ancorados no início e no final. É por isso que insiro * no início e no final do padrão. Um * corresponde a qualquer número de qualquer caractere. Remova-os se quiser corresponder exatamente 16 caracteres.

Você pode criar uma string de 16 [0-9a-z] usando Perl:

$ pattern="$( perl -e 'print "[0-9a-z]" x 16' )"

Com bash , você pode optar por iterar todos os nomes no diretório atual e testar se os nomes correspondem à sua expressão regular:

for name in *; do
    if [[ -f "$name" ]] && [[ "$name" =~ ^[0-9a-z]{16}$ ]]; then
        echo mv "$name" "$name.txt"
    fi
done

Remova o echo quando souber que está fazendo a coisa certa.

Observe que ancorei o padrão para que os nomes mais longos não correspondam. Também estou certificando-me, com o -f test, que os nomes que recebemos são realmente apenas de arquivos regulares.

    
por 09.04.2017 / 10:17
3

Se você tem perl-rename (chamado rename em sistemas baseados em Debian, perl-rename em outros), você pode fazer:

perl-rename -n 's/^[0-9a-z]{16}$/$&.txt/' *

Se isso acontecer, remova o -n para realmente renomear os arquivos. A sintaxe de perl-rename é um comando perl. Aqui, estamos usando o operador de substituição ( s/from/to/ ), que substituirá from por to . O from neste caso é o seu regex e o to é a variável especial $& que significa "o que foi correspondido", mais a extensão .txt .

Para fazer isso com um shell glob (use @ Kusalananda's ou @Sundeep , isto é apenas por uma questão de conclusão):

for f in [0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]; do
    mv -- "$f" "$f".txt
done

Com o GNU find :

find . -regextype posix-extended -regex '.*/[0-9a-z]{16}' -exec mv {} {}".txt" \;
    
por 09.04.2017 / 15:04
1
L16=$(csh -c 'repeat 16 echo -n "?"')
find . -name "$L16" ! -name '*[!0-9a-z]*' -exec sh -c '
   mv "$1" "$1.txt"
' {} {} \;
    
por 09.04.2017 / 09:44