Bash 별표* 와일드카드는 항상 (오름차순) 정렬된 목록을 생성합니까?

Bash 별표* 와일드카드는 항상 (오름차순) 정렬된 목록을 생성합니까?

비슷한 이름을 가진 파일들로 가득 찬 디렉토리가 있습니다. logXX여기서 XX는 0으로 채워진 대문자 16진수 숫자입니다. 예를 들면 다음과 같습니다.

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

일반적으로 총 파일 수는 20~30개 미만입니다. 내 특정 시스템(신뢰할 수 있는 NTP 또는 GPS 시간 소스가 없는 내장 시스템)에서 날짜와 시간을 신뢰할 수 없습니다. 그러나 파일 이름은 위에 표시된 대로 안정적으로 증가합니다.

특정 유형의 최신 단일 로그 항목에 대해 모든 파일을 반복 하고 싶습니다 . 예를 들어 이러한 파일을 함께 배치하고 grep싶습니다 .cat

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

bash그러나 다른 버전 이나 sh다른 버전은 확장 zsh방법에 대해 *다른 아이디어를 가질 수 있다는 생각이 듭니다 .

man bash페이지에서는 확장명이 *일치하는 파일 이름의 명시적인 오름차순 알파벳 목록인지 여부를 밝히지 않습니다. 사용 가능한 모든 시스템에서 시도할 때마다 증가하는 것 같습니다. 하지만 정의된 동작인가요, 아니면 구현에 따라 달라지나요?

즉, cat /tmp/logs/log*모든 로그 파일을 알파벳 순서로 연결하는 데 절대적으로 의존할 수 있습니까?

답변1

모든 쉘에서 glob은 기본적으로 정렬됩니다.그들은 이미 /etc/glob어시스턴트 와 함께 있습니다1970년대 초 Unix의 첫 번째 버전에서 glob(따라서 glob이라는 이름)을 확장하기 위해 Ken Thompson의 쉘에 의해 호출되었습니다.

POSIX 에서는 for 와 같이 사용자 로케일의 정렬 순서를 사용하여 sh정렬해야 하지만 일부는 여전히 바이트 값만을 기준으로 정렬 됩니다.strcoll()lsstrcmp()

$ 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

로케일을 기준으로 정렬되는 쉘의 경우 en_GB.UTF-8로케일이 있는 GNU 시스템 에서는 -. 좀 더 기대되는 방식으로 정렬하고 ó(적어도 영국인의 경우) 대소문자를 무시합니다(관계 결정이 관련되지 않는 한).

그러나 로그 ①과 로그 ② 사이에 약간의 불일치가 있음을 알 수 있습니다. 이는 ①과 ②의 정렬 순서가 GNU 로케일에 정의되어 있지 않기 때문입니다(아직, 언젠가는 수정될 예정입니다). 순서가 동일하므로 무작위 결과가 나옵니다.

로케일을 변경하면 정렬 순서에 영향을 미칩니다. strcmp()유사한 정렬을 얻으려면 로케일을 C로 설정할 수 있습니다 .

$ 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

일부 로케일은 전체 ASCII 전체 문자열을 사용하더라도 약간의 혼동을 일으킬 수 있습니다. 체코 ch어와 마찬가지로(적어도 GNU 시스템에서는)요소 구성정렬 후 h:

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

또는 @ninjalj가 지적했듯이 헝가리 로케일의 낯선 사람도 있습니다.

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

에서 zsh정렬을 선택할 수 있습니다글로벌 예선. 예를 들어:

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.

echo *(n)다음 옵션을 사용하여 숫자 정렬을 전역적으로 활성화할 수도 있습니다 numericglobsort.

$ 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

당신(나처럼)이 특정 상황에서 주문에 대해 혼란스럽다면(여기서 내 영국 로케일을 사용하여) 다음을 참조하십시오.여기더 알아보기.

답변2

Bash 매뉴얼 페이지에서는 다음을 지정합니다.

경로명 확장

단어 분할 후 -fbash는 이 옵션이 설정되지 않은 한 문자를 스캔 *하고 각 단어를 ?스캔 합니다 [. 이러한 문자 중 하나가 발생하면 해당 단어는 패턴으로 처리되고 [...] 패턴과 일치하는 파일 이름의 알파벳순 목록으로 대체됩니다.

답변3

특정 셸에서 매우 특정한 셸 옵션을 실행하지 않는 한 출력은 동일하다는 것이 보장됩니다.

순서는 다음과 같이 지정됩니다.POSIX 표준:

패턴이 기존 파일 이름 또는 경로 이름과 일치하는 경우 패턴은 해당 파일 이름 및 경로 이름으로 대체되어야 합니다.현재 로케일에서 유효한 조합 순서에 따라 정렬. 이 조합 순서에 모든 문자의 전체 조합이 없는 경우(XBD LC_COLLATE 참조) 동등하게 조합된 모든 파일 이름 또는 경로 이름은 POSIX 로케일의 조합 순서를 사용하여 바이트별로 추가로 비교되어야 합니다.

당신은 또한 볼 수 있습니다POSIX 로케일의 LC_COLLATE 카테고리, 간단히 말해서 if LC_COLLATE=C는 ASCII 순서로 정렬됩니다.


설명서 bash에 언급된

LC_COLLATE

이 변수는 경로 이름 확장 결과를 정렬할 때 사용되는 데이터 정렬을 결정하고 범위 표현식, 동등 클래스, 경로 이름 확장 및 패턴 일치의 정렬 순서의 동작을 결정합니다.

ksh93그리고 zsh비슷한 표현이 있는데, 이는 그들이 이 점에 있어서 POSIX 표준을 따르고 있다고 믿게 만듭니다.

pdksh및 등의 다른 셸은 dash파일 이름 글로빙으로 인한 파일 이름 순서를 고려하지 않습니다. 나는 이것이 적어도 POSIX 로케일을 사용할 때 여전히 동일한 표준을 따른다는 것을 의미한다고 믿고 싶습니다. 내 경험상 아직까지 ASCII 파일 이름의 "이상한" 순서를 지정하는 쉘을 본 적이 없습니다.

답변4

주요 목표가 입력 파일을 연령별로 정렬하는 것이라면 가장 오래된 것부터 다음과 같이 작성할 수 있습니다.

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

로그 회전 및 압축도 관련된 경우:

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

관련 정보