Глоб с числовым порядком


28

У меня есть этот список файлов 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

Но порядок расширения оболочки воспроизводит не естественный порядок чисел, а алфавитный порядок:

$ 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.
Вальтинатор

Ответы:


12

В зависимости от вашей среды вы можете использовать ls -vс GNU 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 -vбудет natural sort of (version) numbers within textтак, что может быть использовано также ...
Sundeep

@Sundeep: Действительно, но, похоже, это решение GNU coreutils только.
Тор

да, похоже на GNU - pubs.opengroup.org/onlinepubs/9699919799
Sundeep

1
@Sundeep: -Vособенность sortне определен POSIX либо. Однако, похоже, что он распространился дальше, например, как FreeBSD, так и OpenBSD sortего поддерживают.
Тор

о хорошо, вы можете добавить эти детали, чтобы ответить? Я наткнулся на этот ответ, когда искал похожую проблему (глобус в числовом порядке) и, увидев lsиспользованный, я проверил, есть ли у него опция вместо сортировки по
трубам


12

Если все рассматриваемые файлы имеют одинаковый префикс (т. Е. Текст перед номером; cв этом случае), вы можете использовать

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

c?.pdfрасширяется до c0.pdf c1.pdf... c9.pdfc??.pdfрасширяется до c10.pdf c11.pdfc20.pdf (и до c99.pdf, если применимо). Хотя каждое слово командной строки, содержащее символы расширения пути, расширяется до списка имен файлов, отсортированных (сопоставленных) в соответствии с LC_COLLATEпеременной, списки, являющиеся результатом расширения смежных подстановочных знаков (глобусов), не объединяются; они просто соединены. (Кажется, я вспомнил, что на странице руководства по оболочке это прямо указывалось, но сейчас я не могу ее найти.)

Конечно, если файлы могут идти до c999.pdf, вы должны использовать c?.pdf c??.pdf c???.pdf. Правда, это может быть утомительно, если у вас много цифр. Вы можете немного сократить его; например, для (до) пяти цифр вы можете использовать c?{,?{,?{,?{,?}}}}.pdf. Если ваш список имен файлов редкий (например, есть a c0.pdfи a 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
Это лучший ответ , потому что это вне всяких претензий эскизного использования ls, statили что - нибудь еще; а также работает в 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 говорит: «Восстановите Монику»

(Продолжение)… Но посмотрите на последствия для безопасности того, что вы забыли заключить переменную в оболочку bash / POSIX , в частности, мой ответ на нее , для заметок о том, как обрабатывать переменные из нескольких слов как массивы в bash (например, FILES=("c0.pdf")и FILES+=("c$i.pdf")); также этот ответ , который использует технику, которую я предлагаю.
G-Man говорит: «Восстановите Монику»

1

Просто цитирую и исправляю ответ Тора ... НИКОГДА не разбирайте!

Вы можете использовать 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 или отключения глобусов, у вас все равно будут проблемы с именами файлов с пробелом или символом табуляции или подстановочными символами.
Стефан

Для того, чтобы использовать GNU sort -Vнадежно, вы должны были бы ${(z)"$(printf '%s\0' * | sort -zV)"}в zsh(хотя zshесть (n)для численного рода уже есть ) или readarray -td '' files < <(printf '%s\0' * | sort -zV)в bash4.4+.
Стефан

@ StéphaneChazelas спасибо, и вы правы, что новая строка может быть проблемой, но это не единственная причина не анализировать ls. И да, я был ленив и не добавил - либо. Но я должен был использовать printf ... Я изменю это.
Питер

для lsодного (то есть без -l), каковы эти другие проблемы ? Обратите внимание, что --это не поможет для файла с именем -.
Стефан

@ StéphaneChazelas есть другие различия между версиями ... например, там напечатано "total 0", а новейшие версии ls даже заключают в кавычки то, что вам не нужно ... touch \"test\"; ls -1например, показы '"test"'на моем ls. Он просто не предназначен для анализа ... это пользовательский интерфейс, а не команда сценариев.
Питер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.