填充文件名,以便 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 上的排名还没有足够高,无法评论该帖子。

我是 shell 脚本编写的新手,因此对语法含义的解释会很有帮助并且值得赞赏。

如果有帮助的话,我使用的是 macOS Mojave 10.14.6,并且安装了 Brew。

提前致谢。

答案1

使用zsh(现在是 macOS 上的默认用户 shell):

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

答案2

这将查找当前目录中名称遵循问题中描述的模式的所有“*.pdf”文件并将其重命名为相同名称,但数字填充为 2 位数字:

#!/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之前删除它,但首先按原样运行它,以确保它执行您希望它执行的操作。将上述脚本保存到的示例用法:mvmvpdf.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是一种 shell 模式,意味着查找名称由任意数量的字符组成的文件,后跟一个空格,后跟一个数字,并以.pdf。循环内部$f将保存已处理的 .pdf 文件的名称。

这里

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

我们用命令替换 将给定文件名的整数部分分配给变量。您可以在终端basename中查看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")"

我们使用 Bash 内置printf命令来填充$int并将其保存到名为 的变量中padded_int。在循环的最后一行

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

我们再次运行(echo当然没有)使用命令替换的实际重命名printfprintf与 2 个格式说明符和 2 个相应的参数一起使用%s,与 C 中类似。

最后一行

done

关闭循环。

答案3

使用PE参数扩展。仅mv适用于 shell 中的外部工具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

相关内容