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

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

logXXXX는 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그러나 or sh등 의 버전에 따라 확장 zsh방법에 대해 서로 다른 생각이 있을 수 있다는 생각이 들었습니다 *.

페이지 man bash에서는 확장이 *일치하는 파일 이름의 알파벳순 오름차순 목록인지 여부를 밝히지 않습니다. 내가 사용할 수 있는 모든 시스템에서 시도할 때마다 상승하는 것 같습니다. 그러나 이것이 정의된 동작입니까, 아니면 단지 구현에 특정한 것입니까?

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

답변1

모든 쉘에서 glob은 기본적으로 정렬됩니다.그들은 이미 도우미 옆에 /etc/glob있었습니다70년대 초 Unix의 첫 번째 버전에서 글로브를 확장하기 위해 Ken Thompson의 쉘에 의해 호출되었습니다(그리고 글로브에 이름을 부여했습니다).

의 경우 shPOSIX에서는 를 통해 정렬해야 합니다. 즉 , 일부는 여전히 를 통해 수행하지만 바이트 값만을 기반으로 하는 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

위에서 로케일을 기준으로 정렬하는 쉘의 경우, 여기 로케일이 있는 GNU 시스템에서는 en_GB.UTF-8정렬 -시 파일 이름의 가 무시된다는 점을 알 수 있습니다(대부분의 구두점 문자는 그렇습니다). The 는 ó좀 더 예상되는 방식으로(적어도 영국인에게는) 정렬되고, 대소문자는 무시됩니다(동점을 결정하는 경우를 제외하고).

그러나 log① log②에 대해 일부 불일치가 있음을 알 수 있습니다. 그 이유는 ①과 ②의 정렬 순서가 GNU 로케일에 정의되어 있지 않기 때문입니다(현재는 언젠가 수정될 예정입니다). 동일하게 정렬되므로 임의의 결과를 얻을 수 있습니다.

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

$ 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 매뉴얼 페이지에서는 다음을 지정합니다.

경로명 확장

단어 분할 후 -f옵션이 설정되지 않은 경우 bash는 각 단어에서 *, ?및 문자를 검색합니다 [. 이러한 문자 중 하나가 나타나면 해당 단어는 패턴으로 간주되고 […] 패턴과 일치하는 알파벳순으로 정렬된 파일 이름 목록으로 대체됩니다.

답변3

일부 셸에서 매우 구체적인 셸 옵션을 실행하지 않는 한 출력은 동일하게 보장됩니다.

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

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

또한보십시오POSIX 로케일의 LC_COLLATE 카테고리, 간단히 말해서 이면 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

관련 정보