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


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, але не зміг змусити його працювати для мого конкретного випадку використання.


Ви можете просто використовувати двоцифрові числа у всіх випадках, так що алфавітний порядок буде відповідати числовому порядку. Якщо ви не хочете робити справи важким способом.
Wildcard

1
3 цифри цифри, як мінімум! Запам’ятайте Y2K.
waltinator

Відповіді:


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: POSIX також не визначає -Vфункцію sort. Однак, схоже, вона поширилася далі, наприклад, FreeBSD і OpenBSD sortпідтримують її.
Тор

о добре, чи можете ви додати ці деталі, щоб відповісти? Я натрапив на цю відповідь, шукаючи подібну проблему (глобус в числовому порядку) і побачивши, що lsя використав, я перевірив, чи є вона сама по собі замість трубопроводів для сортування :)
Sundeep


12

Якщо всі файли, про які йдеться, мають однаковий префікс (тобто текст перед номером; cв цьому випадку), ви можете використовувати

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

c?.pdfрозширюється до c0.pdf c1.pdfc9.pdfc??.pdfрозширюється до c10.pdf c11.pdfc20.pdf (і до c99.pdf, за наявності). Хоча кожне слово командного рядка, що містить символи (и) розширення імені траєкторії, розширюється до списку імен файлів, відсортованих (зіставлених) відповідно до LC_COLLATEзмінної, списки, отримані в результаті розширення суміжних макіяжів (глобусів), не об'єднуються; вони просто з'єднані. (Я, мабуть, згадую, що колись сторінка "shell shell" явно заявила це явно, але зараз я не можу її знайти.)

Звичайно, якщо файли можуть працювати вгорі 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або що - небудь ще; а також працює в базі за потребою.
Кайл

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

Просто цитуючи та фіксуючи відповідь Тор ... НІКОЛИ не розбирайте 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 відображає імена файлів, розділених рядком, а новий рядок є таким же дійсним, як і будь-яке ім'я файлу, але тут ви робите те саме, 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одиноких (тобто без -л), які ще інші проблеми ? Зауважте, що --не допоможе файл, який називається -.
Стефан Шазелас

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