具有数字顺序的球


27

我在目录中有以下pdf文件列表:

c0.pdf   c12.pdf  c15.pdf  c18.pdf  c20.pdf  c4.pdf  c7.pdf
c10.pdf  c13.pdf  c16.pdf  c19.pdf  c2.pdf   c5.pdf  c8.pdf
c11.pdf  c14.pdf  c17.pdf  c1.pdf   c3.pdf   c6.pdf  c9.pdf

我想使用ghostscript以数字顺序将它们连接起来(类似于此):

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf *.pdf

但是,shell扩展顺序不重现数字的自然顺序,而是按字母顺序:

$ for f in *.pdf; do echo $f; done
c0.pdf
c10.pdf
c11.pdf
c12.pdf
c13.pdf
c14.pdf
c15.pdf
c16.pdf
c17.pdf
c18.pdf
c19.pdf
c1.pdf
c20.pdf
c2.pdf
c3.pdf
c4.pdf
c5.pdf
c6.pdf
c7.pdf
c8.pdf
c9.pdf

如何在扩展中实现所需的顺序(如果可能的话,无需0在文件名中的数字上手动添加-padding)?

我找到了使用的建议ls | sort -V,但无法使它适用于我的特定用例。


在所有情况下,您只能使用两位数字,因此字母顺序将与数字顺序匹配。除非您想用困难的方式做事。
通配符

1
至少3位数字!记住Y2K。
waltinator

Answers:


12

根据您的环境,您可以使用ls -vGNU coreutils,例如:

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
   -sOutputFile=out.pdf $(ls -v)

或者,如果您使用的是FreeBSD或OpenBSD的最新版本:

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
   -sOutputFile=out.pdf $(ls | sort -V)

ls -vnatural sort of (version) numbers within text因此可以使用,以及...
森迪普•

@Sundeep:的确如此,但这似乎是GNU coreutils唯一的解决方案。
雷神

是的,似乎是特定
Sundeep

1
@Sundeep:POSIX也未指定的-V功能sort。但是,它似乎已经传播得更远了,例如FreeBSD和OpenBSD都sort支持它。
雷神

哦,可以,您还可以添加这些详细信息来回答吗?我在寻找类似问题时遇到了这个问题(按数字顺序排列),看到ls用过的我检查了它是否具有选项本身,而不是通过管道进行排序:)
Sundeep


12

如果所有有问题的文件都具有相同的前缀(即数字前的文本;c在这种情况下),则可以使用

gs   …args…   c?.pdf c ??。pdf

c?.pdf扩展到c0.pdf c1.pdfc9.pdf。  c??.pdf扩展到c10.pdf c11.pdfc20.pdf (并根据需要扩展到c99.pdf)。当每个包含路径名扩展字符的命令行单词扩展为根据LC_COLLATE变量排序(整理)的文件名列表时,由于相邻通配符(glob)的扩展而产生的列表不会合并;它们只是连接在一起。(我似乎记得,shell手册页曾经明确指出过这一点,但现在找不到。)

当然,如果文件可以升级c999.pdf,则应该使用c?.pdf c??.pdf c???.pdf。诚然,如果您有很多数字,这可能会很乏味。您可以略微缩写;例如,对于(最多)五个数字,您可以使用c?{,?{,?{,?{,?}}}}.pdf。如果文件名列表稀疏(例如,有一个c0.pdf和一个c12345.pdf,但不一定是介于两者之间的每个数字),则可能应该设置该nullglob选项。否则,如果(例如)您没有带两位数的文件,则将把文字c??.pdf参数传递给程序。

如果你有多个前缀(如, 和,与一个或两个数字编号),可以使用明显的,蛮力的方法:a<number>.pdfb<number>.pdf c<number>.pdf

a?.pdf a??.pdf b?.pdf b??.pdf c?.pdf c??.pdf

或将其折叠为{a,b,c}?{,?}.pdf


1
这是最好的答案,因为它超出了粗略使用的任何索赔lsstat或其他任何东西; 并按要求在bash中工作。
凯尔

5

如果没有差距,则以下内容可能会有所帮助(尽管是粗略的,但对于边缘情况和一般性而言并不稳健)-只是想出一个主意:

FILES="c0.pdf"
for i in $(seq 1 20); do FILES="${FILES} c${i}.pdf"; done
gs [...args...] $FILES

如果可能存在差距,则[ -f c${i}.pdf ]可以添加一些检查。

编辑还可以看到此答案,根据该答案,您可以(使用Bash)使用

gs [..args..] c{1..20}.pdf

除非您有充分的理由不这么做,否则通常引用外壳变量引用(例如"$FILES""$i")是一个好主意,并且您确定自己知道自己在做什么。(通过对比,虽然花括号可能很重要,但它们不如引号重要,因此例如"c$i.pdf"就足够了。)像这样的命令(其中包含用空格分隔的文件列表)似乎是一个很好的理由使用而不引用它(因为在这种情况下将无法使用)。…(续)gs  [ …args… ]  $FILES$FILES$FILES"$FILES"
G-Man说'Resstate Monica''Oct4

(续)…但是请参阅忘记在bash / POSIX shell中引用一个变量的安全性,尤其是我对它的回答,以获取有关如何将多单词变量作为bash中的数组(例如FILES=("c0.pdf")FILES+=("c$i.pdf"))进行处理的注释;还有这个答案,它使用了我建议的技术。
G-Man说'恢复莫妮卡'

1

只是引用并修正托尔的答案...永远不要解析ls!

您可以使用sort -V(非POSIX扩展名进行排序):

printf '%s\0' ./* | sort -zV \
    | xargs -0 gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH \
        -sDEVICE=pdfwrite -sOutputFile=out.pdf

(对于某些命令,显然对于gs来说就是这样的命令,您需要使用“ ./ ”而不是 “ ...”。如果一个不起作用,请尝试另一个)


1
不解析LS输出是因为ls显示文件名换行分隔,而换行符是有效的任何一个文件名,但在这里你正在做同样的事情stat,但加入其他几个问题(如与文件名启动的问题使用-,如果文件过多(stat是不可移植的命令),则会出现问题)。而且,由于使用了split + glob运算符而没有调整IFS或禁用glob,因此文件名中的空格,制表符或通配符仍然会出现问题。
斯特凡Chazelas

为了sort -V可靠地使用GNU ,您需要${(z)"$(printf '%s\0' * | sort -zV)"}in zsh(尽管zsh已经有(n)数字排序)或readarray -td '' files < <(printf '%s\0' * | sort -zV)in bash4.4+
斯特凡Chazelas

@StéphaneChazelas谢谢,您是对的,换行符可能是一个问题,但这不是不解析ls的唯一原因。是的,我很懒,也没有添加-要么。但是我应该使用printf ...我将改变它。
彼得

对于ls单独(即无-l),那些是什么其他问题?请注意,--这对于名为的文件没有帮助-
斯特凡Chazelas

@StéphaneChazelas版本之间还有其他区别……例如,那里有一些打印“ total 0”,而最新的ls版本甚至在不需要的地方加上了引号... touch \"test\"; ls -1例如'"test"'在我的ls上显示。它根本不是要被解析的……它是一个用户界面,而不是脚本命令。
彼得
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.