
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 echo
antes mv
para 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 env
parasuas 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 $f
conterá 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 basename
acontece man basename
e 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 printf
para preenchê-lo $int
e 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 %s
especificadores 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 mv
para ferramentas externas do bash
shell.
#!/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