Glob với thứ tự số


27

Tôi có danh sách các tập tin pdf trong một thư mục:

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

Tôi muốn ghép nối chúng bằng ghostscript theo thứ tự số (tương tự như thế này):

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

Nhưng thứ tự mở rộng vỏ không tái tạo thứ tự tự nhiên của các số mà theo thứ tự chữ cái:

$ 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

Làm cách nào tôi có thể đạt được thứ tự mong muốn trong việc mở rộng (nếu có thể mà không cần thêm thủ công 0-padding vào các số trong tên tệp)?

Tôi đã tìm thấy các đề xuất để sử dụng ls | sort -V, nhưng tôi không thể làm cho nó hoạt động cho trường hợp sử dụng cụ thể của mình.


Bạn chỉ có thể sử dụng hai chữ số trong mọi trường hợp, vì vậy thứ tự chữ cái sẽ khớp với thứ tự số. Trừ khi bạn muốn làm mọi thứ một cách khó khăn.
tự đại diện

1
3 chữ số, ít nhất! Nhớ Y2K.
ví von

Câu trả lời:


12

Tùy thuộc vào môi trường của bạn, bạn có thể sử dụng ls -vvới lõi GNU, ví dụ:

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

Hoặc nếu bạn đang sử dụng các phiên bản FreeBSD hoặc OpenBSD gần đây:

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

ls -vsẽ natural sort of (version) numbers within textrất có thể được sử dụng như là tốt ...
Sundeep

@Sundeep: Thật vậy, nhưng đây dường như là một giải pháp cốt lõi của GNU.
Thor

vâng, có vẻ như GNU cụ thể - pubs.opengroup.org/onlinepub/9699919799
Sundeep

1
@Sundeep: -VTính năng của sortPOSIX cũng không được chỉ định. Tuy nhiên, nó dường như đã lan rộng hơn, ví dụ cả FreeBSD và OpenBSD đều sorthỗ trợ nó.
Thor

oh ok, bạn có thể thêm những chi tiết này để trả lời không? Tôi đã bắt gặp câu trả lời này trong khi tìm kiếm vấn đề tương tự (toàn cầu theo thứ tự số) và thấy lsđược sử dụng, tôi đã kiểm tra xem nó có tùy chọn thay vì đường ống để sắp xếp không :)
Sundeep


12

Nếu tất cả các tệp trong câu hỏi có cùng một tiền tố (nghĩa là văn bản trước số; ctrong trường hợp này), bạn có thể sử dụng

gs   sườn tranh luận về   c? .pdf c ??. pdf

c?.pdfmở rộng để c0.pdf c1.pdf... c9.pdfc??.pdfmở rộng để c10.pdf c11.pdf... c20.pdf (và lên đến c99.pdf, như được áp dụng). Mặc dù mỗi từ dòng lệnh chứa (các) ký tự mở rộng tên đường dẫn được mở rộng thành danh sách tên tệp được sắp xếp (đối chiếu) theo LC_COLLATEbiến, các danh sách dẫn đến việc mở rộng các ký tự đại diện liền kề (globs) không được hợp nhất; chúng chỉ đơn giản là nối liền. (Tôi dường như nhớ lại rằng trang shell man đã từng nói rõ điều này, nhưng tôi không thể tìm thấy nó ngay bây giờ.)

Tất nhiên nếu các tập tin có thể đi lên c999.pdf, bạn nên sử dụng c?.pdf c??.pdf c???.pdf. Phải thừa nhận rằng điều này có thể trở nên tẻ nhạt nếu bạn có nhiều chữ số. Bạn có thể viết tắt nó một chút; ví dụ: cho (tối đa) năm chữ số, bạn có thể sử dụng c?{,?{,?{,?{,?}}}}.pdf. Nếu danh sách tên tệp của bạn thưa thớt (ví dụ: có một c0.pdfvà một c12345.pdf, nhưng không nhất thiết là mỗi số ở giữa), có lẽ bạn nên đặt nullglobtùy chọn. Mặt khác, nếu (ví dụ) bạn không có tệp có số có hai chữ số, bạn sẽ nhận được một c??.pdfđối số bằng chữ được chuyển đến chương trình của bạn.

Nếu bạn có nhiều tiền tố (ví dụ , và , với những con số của một hoặc hai chữ số), bạn có thể sử dụng, phương pháp brute force rõ ràng:a<number>.pdfb<number>.pdf c<number>.pdf

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

hoặc sụp đổ nó để {a,b,c}?{,?}.pdf.


1
Đây là câu trả lời tốt nhất bởi vì nó vượt quá bất kỳ khiếu nại sử dụng sơ sài của ls, stathoặc bất cứ điều gì khác; và cũng hoạt động trong bash theo yêu cầu.
Kyle

5

Nếu không có khoảng trống , những điều sau đây có thể chứng minh sự hữu ích (mặc dù sơ sài và không mạnh mẽ liên quan đến các trường hợp cạnh và tính tổng quát) - chỉ để có được một ý tưởng:

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

Nếu có thể có những khoảng trống, một số [ -f c${i}.pdf ]kiểm tra có thể được thêm vào.

Chỉnh sửa cũng thấy câu trả lời này , theo đó bạn có thể (sử dụng Bash) sử dụng

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

Nói chung, nên trích dẫn các tham chiếu biến shell của bạn (ví dụ: "$FILES""$i") trừ khi bạn có lý do chính đáng để không và bạn chắc chắn rằng bạn biết bạn đang làm gì. (Ngược lại, trong khi niềng răng có thể quan trọng, chúng không quan trọng bằng dấu ngoặc kép, vì vậy, ví dụ, "c$i.pdf"là đủ tốt.) Một lệnh như , trong đó chứa một danh sách các tệp được phân tách bằng dấu cách, có vẻ như là một lý do tốt để sử dụng mà không trích dẫn nó (vì sẽ không hoạt động trong bối cảnh đó). Tiết (Cont'd)gs  [ …args… ]  $FILES$FILES$FILES"$FILES"
G-Man nói 'Phục hồi Monica'

(Tiếp theo) Nhìn thấy ý nghĩa bảo mật của việc quên trích dẫn một biến trong shell bash / POSIX , đặc biệt là câu trả lời của tôi cho nó , để biết các cách xử lý các biến nhiều từ dưới dạng mảng trong bash (ví dụ, FILES=("c0.pdf")FILES+=("c$i.pdf")); cũng câu trả lời này , trong đó sử dụng các kỹ thuật tôi đề nghị.
G-Man nói 'Phục hồi Monica'

1

Chỉ cần trích dẫn và sửa câu trả lời của Thor ... KHÔNG BAO GIỜ phân tích ls!

Bạn có thể sử dụng sort -V(tiện ích mở rộng không phải POSIX để sắp xếp):

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

(đối với một số lệnh, rõ ràng đối với gs là một lệnh như vậy, bạn cần "./ " thay vì " " ... nếu một lệnh không hoạt động, hãy thử lệnh khác)


1
Đầu ra không phân tích ls là vì ls hiển thị tên tệp được phân tách dòng mới trong khi dòng mới có giá trị như bất kỳ tên tệp nào, nhưng ở đây bạn đang làm điều tương tự với statviệc thêm một số vấn đề khác (như vấn đề với tên tệp bắt đầu với -, vấn đề nếu có quá nhiều tệp, statlà một lệnh không di động). Và bởi vì bạn đã sử dụng toán tử split + global mà không điều chỉnh IFS hoặc vô hiệu hóa các khối, bạn vẫn sẽ gặp vấn đề với tên tệp có dấu cách hoặc tab hoặc ký tự đại diện.
Stéphane Chazelas

Để sử dụng GNU sort -Vđáng tin cậy, bạn sẽ cần ${(z)"$(printf '%s\0' * | sort -zV)"}trong zsh(mặc dù zsh(n)cho loại số đã được) hoặc readarray -td '' files < <(printf '%s\0' * | sort -zV)trong bash4.4+.
Stéphane Chazelas

@ StéphaneChazelas cảm ơn, và bạn nói đúng rằng dòng mới có thể là một mối quan tâm, nhưng đó không phải là lý do duy nhất để không phân tích ls. Và vâng, tôi đã lười biếng và không thêm - một trong hai. Nhưng tôi nên sử dụng printf ... Tôi sẽ thay đổi điều đó.
Peter

đối với lsmột mình (đó là không có -l), những mối quan tâm khác là gì? Lưu ý rằng --sẽ không giúp cho một tập tin được gọi -.
Stéphane Chazelas

@ StéphaneChazelas có những khác biệt khác giữa các phiên bản ... như một số bản in "tổng 0" trên đó, và các phiên bản ls mới nhất thậm chí còn trích dẫn xung quanh những thứ mà bạn không muốn chúng ... touch \"test\"; ls -1ví dụ như hiển thị '"test"'trên ls của tôi. Nó đơn giản không có nghĩa là được phân tích cú pháp ... đó là giao diện người dùng, không phải là lệnh script.
Peter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.