Дополните имена файлов, чтобы Unix перечислил их в числовом порядке

Дополните имена файлов, чтобы Unix перечислил их в числовом порядке

У меня есть папка с несколькими файлами, отформатированными следующим образом:

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

У меня возникла проблема: когда я запускаю ls, Unix перечисляет их как

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

Я хотел бы дополнить числа так, чтобы Unix (надеюсь) перечислил их в таком порядке:

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
.
.
.

Я нашел пост с похожей проблемой,Дополнение числа в имени файла до фиксированной длины, но я попробовал применить их решение к своему случаю и не смог заставить его работать. Вот что я попробовал:

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

Признаю, что это слепая адаптация их решения к моей ситуации, что мне не нравится, поскольку я не совсем понимаю базовый синтаксис, и у меня пока недостаточно высокий рейтинг на StackExchange, чтобы прокомментировать этот пост.

Я новичок в написании сценариев оболочки, поэтому объяснение синтаксиса было бы полезно и желательно.

Если это поможет, я использую macOS Mojave 10.14.6 и у меня установлен Brew.

Заранее спасибо.

решение1

С помощью zsh(теперь это оболочка пользователя по умолчанию в macOS):

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

решение2

Эта команда найдет и переименует все файлы «*.pdf» в текущем каталоге, имена которых соответствуют шаблону, описанному в вопросе, на те же имена, но с номером, дополненным двумя цифрами:

#!/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

Удалите echoперед mvтем, как запустить, mvно сначала запустите его как есть, чтобы убедиться, что он делает то, что вам нужно. Пример использования с сохраненным выше скриптом в 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

Первая строка:

#!/usr/bin/env bash

этопритон. Кроме того, я использую envдляего особенности.

Эта линия

shopt -s nullglob

наборынульглоб вариант оболочки что также объясняется на связанном сайте:

nullglob

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

Это необходимо для предотвращения запуска цикла for, если нет файлов, соответствующих критериям шаблона:

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

*[[:space:]][0-9].pdfэто шаблон оболочки, который означает поиск файлов, имена которых состоят из любого количества символов, за которыми следует пробел, за которым следует одна цифра и заканчиваются на.pdf. Внутри цикла $fбудет храниться имя обработанного .pdf-файла.

Здесь

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

мы используемзамена команды для присвоения целой части указанного имени файла переменной. Вы можете увидеть, что basenameделает in man basenameи воспроизвести конвейер в терминале:

$ 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

(Обратите внимание, что $здесь естьприглашение командной строки используется для обозначения начала новой строки, а не как часть команды).

В следующей строке

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

мы извлекаем часть имени из .pdf-файла, то есть все, что находится перед номером.

В следующей строке

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

мы используем встроенную printfкоманду Bash для заполнения $intи сохранения ее в переменной с именем padded_int. В последней строке цикла

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

мы запускаем ( echoконечно, без) фактическое переименование, используя printf и снова подстановку команд. printfиспользуется с 2 %sспецификаторами формата и 2 соответствующими аргументами, аналогично языку C.

Последняя строка

done

замыкает цикл.

решение3

Использование расширения параметров PE. Только mvдля внешних инструментов из bashоболочки.

#!/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

Связанный контент