Có phải ngôi sao Bash * ký tự đại diện luôn tạo ra một danh sách được sắp xếp (tăng dần) không?


53

Tôi có một thư mục chứa đầy các tệp có tên như logXXXX là số thập lục phân hai ký tự, không đệm, chữ hoa như:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

Nói chung, sẽ có ít hơn tổng số 20 hoặc 30 tệp. Ngày và giờ trên hệ thống cụ thể của tôi không phải là thứ có thể dựa vào (một hệ thống nhúng không có nguồn thời gian NTP hoặc GPS đáng tin cậy). Tuy nhiên, tên tệp sẽ tăng đáng tin cậy như được hiển thị ở trên.

Tôi muốn grepthông qua tất cả các tệp cho mục nhập nhật ký gần đây nhất của một loại nhất định, tôi đã hy vọng catcác tệp cùng nhau như ...

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

Tuy nhiên, điều đó xảy ra với tôi rằng các phiên bản khác nhau của bashhoặc shhoặc zshvv có thể có những ý tưởng khác nhau về cách *mở rộng.

Các man bashtrang không nói có hay không việc mở rộng *sẽ là một danh sách chữ cái chắc chắn tăng dần của tên tập tin phù hợp. Nó dường như tăng dần mỗi lần tôi thử nó trên tất cả các hệ thống tôi có sẵn cho tôi - nhưng đó có phải là hành vi XÁC ĐỊNH hay chỉ là triển khai cụ thể?

Nói cách khác, tôi hoàn toàn có thể dựa vào cat /tmp/logs/log*để ghép tất cả các tệp nhật ký của mình lại với nhau theo thứ tự bảng chữ cái không?


1
@ADDB Thứ tự sắp xếp mặc định sortgiống như đối với trình bao khi nó mở rộng một mẫu hình cầu tên.
Kusalananda

9
Đó là thực hành đặt tên tập tin khủng khiếp. Tại sao bạn bắt đầu chạy với log (0) = - infty?
EP

14
@EP Hệ thống tập tin của chúng tôi là một siêu hình xuyến 7 chiều phức tạp với việc đánh số siêu thực của các nút. Nó đã được mở rộng với một số chi nhánh tối nghĩa của busybox và chúng tôi bị mắc kẹt ngay bây giờ :)
Wossname

1
Bạn có thể tránh catbằng grep -h pattern /tmp/logs/log*để ngăn chặn prepending tên tập tin đến các trận đấu. (Ít nhất là với GNU grep, tôi đã không kiểm tra POSIX hoặc busybox.)
Peter Cordes

1
@Kusalananda Bạn đã nghe nói về việc sử dụng vô dụng cat, đây là việc sử dụng vô íchsort
con mèo

Câu trả lời:


52

Trong tất cả các shell, các khối được sắp xếp theo mặc định. Họ đã được người /etc/globtrợ giúp được gọi bởi vỏ của Ken Thompson để mở rộng các khối trong phiên bản đầu tiên của Unix vào đầu những năm 70 (và đã mang lại cho họ những cái tên ảm đạm).

Đối với sh, POSIX không yêu cầu chúng được sắp xếp theo cách strcoll(), đó là sử dụng thứ tự sắp xếp trong ngôn ngữ của người dùng, giống như lsmặc dù một số người vẫn thực hiện thông qua strcmp(), chỉ dựa trên các giá trị byte.

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01
$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log  log  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log
log
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

Bạn có thể nhận thấy ở trên rằng đối với các shell đó sắp xếp dựa trên miền địa phương, ở đây trên hệ thống GNU có en_GB.UTF-8miền địa phương, -tên tệp trong tệp sẽ bị bỏ qua để sắp xếp (hầu hết các ký tự dấu chấm câu). Việc ónày được sắp xếp theo cách được mong đợi hơn (ít nhất là đối với người Anh) và trường hợp bị bỏ qua (trừ khi quyết định quan hệ).

Tuy nhiên, bạn sẽ nhận thấy một số điểm không nhất quán cho log log②. Đó là bởi vì thứ tự sắp xếp của và không được xác định trong các ngôn ngữ GNU (hiện tại; hy vọng nó sẽ được sửa vào một ngày nào đó). Họ sắp xếp giống nhau, vì vậy bạn nhận được kết quả ngẫu nhiên.

Thay đổi ngôn ngữ sẽ ảnh hưởng đến thứ tự sắp xếp. Bạn có thể đặt miền địa phương thành C để có được một strcmp()loại giống như:

$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01

Lưu ý rằng một số địa phương có thể gây ra một số nhầm lẫn ngay cả đối với các chuỗi all-ASCII all-alnum. Giống như những người Séc (ít nhất là trên các hệ thống GNU), nơi chlà một yếu tố đối chiếu sắp xếp sau h:

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

Hoặc, như được chỉ ra bởi @ninjalj, thậm chí những người lạ hơn ở các địa phương Hungary:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

Trong zsh, bạn có thể chọn sắp xếp với vòng loại toàn cầu . Ví dụ:

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

Loại số echo *(n)cũng có thể được kích hoạt trên toàn cầu với numericglobsorttùy chọn:

$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log log log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

Nếu bạn (như tôi) bị nhầm lẫn bởi thứ tự đó trong trường hợp cụ thể đó (ở đây sử dụng ngôn ngữ Anh của tôi), xem tại đây để biết chi tiết.


1
Trường hợp 'ch' có thể còn lạ hơn: một số địa phương có thể quyết định rằng 'ch', 'Ch' và 'CH' là 1 phần tử đối chiếu nhau, trong khi 'cH' là hai phần tử đối chiếu. Xem: unicode.org/cldr/trac/ticket/889 CLDR hiện tại dường như không hoàn toàn nhất quán: tiếng Hungary hiện tại ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) có các quy tắc như &C<cs<<<Cs<<<CS, trong khi &C<cs<<<cS<<<Cs<<<CSđược đánh dấu là một dự thảo thử nghiệm đề xuất. Đánh giá từ một số dữ liệu cũ được nhập vào CLDR, AIX và MS cũ hơn có vẻ thích "chữ thường thì chữ hoa là 2 yếu tố đối chiếu khác nhau".
ninjalj

Và tôi đã thấy các hệ thống mà nó không hoạt động. :(
Joshua

38

Trang man cho bash không chỉ định:

Mở rộng tên đường dẫn

Sau khi chia tách từ, trừ khi các -ftùy chọn đã được thiết lập, bash quét mỗi từ cho các nhân vật *, ?[. Nếu một trong những ký tự này xuất hiện, thì từ đó được coi là một mẫu và được thay thế bằng một danh sách tên tệp được sắp xếp theo thứ tự bảng chữ cái phù hợp với mẫu [Hồi].


1
Chỉ cần tìm thấy một lỗi thú vị trong mankết xuất văn bản của putty hoặc văn bản ... nếu văn bản tôi đang tìm kiếm bị "bao bọc từ" thì lệnh / search sẽ không tìm thấy. Chỉ cần tối đa hóa thiết bị đầu cuối của tôi và nó ở đó :)
Wossname

2
Bạn bảo hiểm bash. Tho OP cũng quan tâm đến "zsh, v.v."
Kusalananda

29

Trừ khi bạn kích hoạt một số tùy chọn shell rất cụ thể trong một số shell, đầu ra được đảm bảo giống nhau.

Thứ tự được chỉ định trong tiêu chuẩn POSIX :

Nếu mẫu phù hợp với bất kỳ tên tệp hoặc tên đường dẫn hiện có, mẫu sẽ được thay thế bằng tên tệp và tên đường dẫn đó, được sắp xếp theo trình tự đối chiếu có hiệu lực trong miền địa phương hiện tại . Nếu chuỗi đối chiếu này không có tổng thứ tự của tất cả các ký tự (xem XBD LC_COLLATE), bất kỳ tên tệp hoặc tên đường dẫn nào đối chiếu bằng nhau nên được so sánh từng byte bằng cách sử dụng chuỗi đối chiếu cho miền địa phương POSIX.

Xem thêm Danh mục LC_COLLATE trong Địa điểm POSIX , nói ngắn gọn là nếu LC_COLLATE=C, thì mọi thứ được sắp xếp theo thứ tự ASCII.


Các bashđề cập đến nhãn hiệu

LC_COLLATE

Biến này xác định thứ tự đối chiếu được sử dụng khi sắp xếp kết quả của việc mở rộng tên đường dẫn và xác định hành vi của các biểu thức phạm vi, các lớp tương đương và đối chiếu các chuỗi trong mở rộng tên đường dẫn và khớp mẫu.

ksh93zshcó một từ ngữ tương tự, khiến tôi tin rằng họ tuân theo tiêu chuẩn POSIX về vấn đề này.

Các shell khác, thích pdkshdashkhông nói bất cứ điều gì về việc sắp xếp các tên tệp do tên tập tin Globing. Tôi muốn tin rằng điều này có nghĩa là họ vẫn tuân thủ cùng một tiêu chuẩn, ít nhất là khi sử dụng ngôn ngữ POSIX. Theo kinh nghiệm của tôi, tôi đã không bắt gặp một cái vỏ nào thực hiện bất kỳ cách sắp xếp tên tập tin ASCII "lạ" nào.


2
Xem numericglobsorttùy chọn trong zshđó sẽ ảnh hưởng đến việc sắp xếp. Mặc dù tôi muốn kích hoạt nó trên cơ sở echo *(n)toàn cầu hơn là bật tùy chọn trên toàn cầu.
Stéphane Chazelas

Một nitlog. Bash, ở chế độ mặc định, KHÔNG tuân thủ Posix.
fpmurphy

@ fpmurphy1 Nói thêm.
Kusalananda

@Kusalananda. Bash chưa bao giờ được chứng nhận là khiếu nại POSIX. Để có được "tuân thủ POSIX" trong Bash, bạn phải gọi Bash bằng --posixtùy chọn dòng lệnh hoặc thực thiset -o posix
fpmurphy

@ fpmurphy1 Có, nhưng việc sắp xếp mở rộng các ký tự toàn cầu tên tệp không bị ảnh hưởng bởi posixchế độ của Bash . Xem gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Điều này khiến tôi tin rằng (hy vọng, thay vào đó) rằng việc sắp xếp tuân thủ POSIX.
Kusalananda

1

Nếu mục tiêu chính là sắp xếp các tệp đầu vào theo độ tuổi của chúng, trước tiên, bạn có thể viết

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

Và nếu các bản ghi được xoay và nén cũng có liên quan:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever

4
Nó đã được đề cập rằng dấu thời gian trên các tập tin không đáng tin cậy.
Kusalananda

3
@Kusalananda, đúng vậy, thời gian hệ thống của chúng tôi thường được coi là một trình tạo số ngẫu nhiên :)
Wossname
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.