Preencha os nomes dos arquivos para que o Unix os liste em ordem numérica

Preencha os nomes dos arquivos para que o Unix os liste em ordem numérica

Tenho uma pasta com vários arquivos formatados da seguinte forma:

C Block Scan copy 1.pdf
C Block Scan copy 2.pdf
C Block Scan copy 3.pdf
C Block Scan copy 4.pdf
.
.
.

Um problema que estou tendo é que quando executo ls, o Unix os lista como

C Block Scan copy 1.pdf
C Block Scan copy 10.pdf
C Block Scan copy 11.pdf
C Block Scan copy 12.pdf
.
.
.

Eu gostaria de preencher os números para que o Unix (espero) os liste em ordem como

C Block Scan copy 01.pdf
C Block Scan copy 02.pdf
C Block Scan copy 03.pdf
.
.
.
C Block Scan copy 09.pdf
C Block Scan copy 10.pdf
C Block Scan copy 11.pdf
C Block Scan copy 12.pdf
.
.
.

Encontrei uma postagem com um problema semelhante,Preenchendo um número em um nome de arquivo até um comprimento fixo, mas tentei aplicar a solução deles ao meu caso e não consegui fazê-la funcionar. Aqui está o que eu tentei:

for f in *.pdf; do
    int=`basename $f .pdf | cut -d '.' -f 2`
    new_name=`printf "file.%0.2i.pdf\n” $int`
    [ ! -f $new_name ] && mv $f $new_name
done

Admito que esta é uma adaptação cega da solução deles à minha situação, da qual não gosto porque realmente não entendo a sintaxe subjacente e não tenho uma classificação alta o suficiente no StackExchange para comentar essa postagem ainda .

Sou novo em scripts de shell, portanto, uma explicação do significado da sintaxe seria útil e apreciada.

Se ajudar, estou usando o macOS Mojave 10.14.6 e tenho o Brew instalado.

Desde já, obrigado.

Responder1

Com zsh(agora o shell de usuário padrão no macOS):

autoload zmv # best in ~/.zshrc
zmv '(* )([0-9].pdf)' '${1}0$2'

Responder2

Isso encontrará e renomeará todos os arquivos "*.pdf" no diretório atual cujos nomes seguem o padrão descrito na pergunta com o mesmo nome, mas com números preenchidos com 2 dígitos:

#!/usr/bin/env bash

shopt -s nullglob

for f in *[[:space:]][0-9].pdf; do
    int="$(basename "$f" | rev | cut -d ' ' -f1 | rev | cut -d . -f1)"
    name="$(basename "$f" | rev | cut -d ' ' -f2- | rev)"
    padded_int="$(printf "%02d\n" "$int")"

    echo mv "$f" "$(printf "%s %s.pdf" "$name" "$padded_int")"

done

Remova echoantes mvpara realmente executar mv, mas primeiro execute-o como está para ter certeza de que ele faz o que você deseja. Exemplo de uso com o script acima salvo em pdf.sh:

$ for i in {1..10}; do touch "C Block Scan copy $i.pdf"; done
$ ./pdf.sh
mv C Block Scan copy 1.pdf C Block Scan copy 01.pdf
mv C Block Scan copy 2.pdf C Block Scan copy 02.pdf
mv C Block Scan copy 3.pdf C Block Scan copy 03.pdf
mv C Block Scan copy 4.pdf C Block Scan copy 04.pdf
mv C Block Scan copy 5.pdf C Block Scan copy 05.pdf
mv C Block Scan copy 6.pdf C Block Scan copy 06.pdf
mv C Block Scan copy 7.pdf C Block Scan copy 07.pdf
mv C Block Scan copy 8.pdf C Block Scan copy 08.pdf
mv C Block Scan copy 9.pdf C Block Scan copy 09.pdf

A primeira linha:

#!/usr/bin/env bash

é umShebang. Além disso, eu uso envparasuas características.

Está linha

shopt -s nullglob

conjuntosnullglob opção de shell que também é explicado no site vinculado:

nullglob

If set, Bash allows filename patterns which match no files to
expand to a null string, rather than themselves.

É necessário evitar que o loop for seja iniciado se não houver arquivos que correspondam aos critérios do padrão:

for f in *[[:space:]][0-9].pdf; do

*[[:space:]][0-9].pdfé um padrão de shell que significa procurar arquivos que tenham nomes compostos por qualquer número de caracteres seguidos por um espaço em branco seguido por um único dígito e terminem com.pdf. Dentro do loop $fconterá o nome do arquivo .pdf processado.

Aqui

int="$(basename "$f" | rev | cut -d ' ' -f1 | rev | cut -d . -f1)"

nós usamossubstituição de comando para atribuir parte inteira do nome de arquivo fornecido a uma variável. Você pode ver o que basenameacontece man basenamee reproduzir o pipeline no terminal:

$ f='C Block Scan copy 1.pdf'
$ echo basename "$f" | rev | cut -d ' ' -f1 | rev | cut -d . -f1
1
$ f='C Block Scan copy 5.pdf'
$ echo basename "$f" | rev | cut -d ' ' -f1 | rev | cut -d . -f1
5

(Observe que $aqui está umprompt de linha de comando usado para indicar o início de uma nova linha, não faz parte do comando).

Na próxima linha

name="$(basename "$f" | rev | cut -d ' ' -f2- | rev)"

extraímos a parte do nome do .pdf, ou seja, tudo antes do número.

Na próxima linha

padded_int="$(printf "%02d\n" "$int")"

usamos o comando interno do Bash printfpara preenchê-lo $inte salvá-lo em uma variável chamada padded_int. Na última linha do loop

echo mv "$f" "$(printf "%s %s.pdf" "$name" "$padded_int")"

executamos (sem, echoé claro) a renomeação real usando printf e substituição de comando novamente. printfé usado com 2 %sespecificadores de formato e 2 argumentos correspondentes, da mesma forma que em C.

A última linha

done

fecha o ciclo.

Responder3

Usando expansão de parâmetros PE. Somente mvpara ferramentas externas do bashshell.

#!/usr/bin/env bash

shopt -s nullglob

for f in *[[:space:]][0-9].pdf; do
  n=${f##* }  ##: Remain only 1.pdf, 2.pdf etc.
  n=0${n%.*}  ##: Remain only 1 , 2 etc. and pad 0 so it will be 01, 02 ..
  echo mv -v "$f" "${f/[0-9]/"$n"}"  ##: Replace all [0-9] with the value of "$n"
done

informação relacionada