I have this list of pdf files in a directory:
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
I want to concatenate these using ghostscript in numerical order (similar to this):
gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf *.pdf
But the shell expansion order does not reproduce the natural order of the numbers but the alphabetical order:
$ 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
How can I achieve the desired order in the expansion (if possible without manually adding 0-padding to the numbers in the file names)?
I’ve found suggestions to use ls | sort -V, but I couldn’t get it to work for my specific use case.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Once more, zsh’s glob qualifiers come to the rescue.
echo *.pdf(n)
Method 2
Depending on your environment you can use ls -v with GNU coreutils, e.g.:
gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf $(ls -v)
Or if you are on recent versions of FreeBSD or OpenBSD:
gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf $(ls | sort -V)
Method 3
If all the files in question have the same prefix
(i.e., the text before the number; c in this case), you can use
gs …args… c?.pdf c??.pdf
c?.pdf expands to c0.pdf c1.pdf … c9.pdf.
c??.pdf expands to c10.pdf c11.pdf … c20.pdf
(and up to c99.pdf, as applicable).
While each command-line word containing pathname expansion character(s)
is expanded to a list of filenames sorted (collated) in accordance
with the LC_COLLATE variable,
the lists resulting from the expansion of adjacent wildcards (globs)
are not merged; they are simply concatenated.
(I seem to recall that the shell man page once stated this explicitly,
but I can’t find it now.)
Of course if the files can go up to c999.pdf,
you should use c?.pdf c??.pdf c???.pdf.
Admittedly, this can get tedious if you have a lot of digits.
You can abbreviate it a little; for example, for (up to) five digits,
you can use c?{,?{,?{,?{,?}}}}.pdf.
If your list of filenames is sparse
(e.g., there’s a c0.pdf and a c12345.pdf,
but not necessarily every number in between),
you should probably set the nullglob option.
Otherwise, if (for example) you have no files with two-digit numbers,
you would get a literal c??.pdf argument passed to your program.
If you have multiple prefixes
(e.g., a<number>.pdf,
b<number>.pdf ,
and c<number>.pdf ,
with numbers of one or two digits),
you can use the obvious, brute force approach:
a?.pdf a??.pdf b?.pdf b??.pdf c?.pdf c??.pdf
or collapse it to {a,b,c}?{,?}.pdf.
Method 4
If there are no gaps, the following could prove helpful (albeit sketchy and not robust concerning edge-cases and generality) — just to get an idea:
FILES="c0.pdf"
for i in $(seq 1 20); do FILES="${FILES} c${i}.pdf"; done
gs [...args...] $FILES
If there may be gaps, some [ -f c${i}.pdf ] check could be added.
Edit also see this answer, according to which you could (using Bash) use
gs [..args..] c{1..20}.pdf
Method 5
Just quoting and fixing Thor’s answer… NEVER parse ls!
You can use sort -V (a non-POSIX extension to sort):
printf '%s' ./* | sort -zV
| xargs -0 gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH
-sDEVICE=pdfwrite -sOutputFile=out.pdf
(for some commands, apparently for gs is such a command, you need “./” instead of ““… if one doesn’t work, try the other)
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0