배열에 저장된 파일 경로 이름 목록이 있다고 가정해 보겠습니다.
filearray=("dir1/0010.pdf" "dir2/0003.pdf" "dir3/0040.pdf" )
파일 이름의 기본 이름을 기준으로 배열의 요소를 숫자 순서로 정렬하고 싶습니다.
sortedfilearray=("dir2/0003.pdf" "dir1/0010.pdf" "dir3/0040.pdf")
어떻게 해야 하나요?
기본 이름 부분으로만 정렬할 수 있습니다.
basenames=()
for file in "${filearray[@]}"
do
filename=${file##*/}
basenames+=(${filename%.*})
done
sortedbasenamearr=($(printf '%s\n' "${basenames[@]}" | sort -n))
생각 중이야
- 키가 기본 이름이고 값이 경로 이름인 연관 배열을 생성하므로 경로 이름에 대한 액세스는 항상 기본 이름을 통해 이루어집니다.
- basenames에 대해서만 다른 배열을 만들고
sort
basenames 배열에 적용합니다.
감사해요.
답변1
sort
GNU coreutils는 사용자 정의 필드 구분 기호와 키를 허용합니다. /
전체 경로 대신 기본 이름을 기준으로 정렬하려면 필드 구분 기호로 설정 하고 두 번째 필드를 기준으로 정렬합니다.
printf "%s\n" "${filearray[@]}" | sort -t/ -k2
생산할 것이다
dir2/0003.pdf
dir1/0010.pdf
dir3/0040.pdf
답변2
oldIFS="$IFS"; IFS=$'\n'
if [[ -o noglob ]]; then
setglob=1; set -o noglob
else
setglob=0
fi
sorted=( $(printf '%s\n' "${filearray[@]}" |
awk '{ print $NF, $0 }' FS='/' OFS='/' |
sort | cut -d'/' -f2- ) )
IFS="$oldIFS"; unset oldIFS
(( setglob == 1 )) && set +o noglob
unset setglob
파일 이름 정렬개행 문자이름을 따서 이름을 지정하면 sort
이 단계에서 문제가 발생합니다.
첫 번째 열의 기본 이름과 나머지 열의 전체 경로를 포함하는 /
구분된 목록을 생성합니다 .awk
0003.pdf/dir2/0003.pdf
0010.pdf/dir1/0010.pdf
0040.pdf/dir3/0040.pdf
이것이 바로 정렬이 수행되는 작업이며, cut
첫 번째 /
로 구분된 열을 제거하는 것입니다. 결과는 새로운 bash
배열로 변환됩니다.
답변3
" dir1
및 dir2
는 임의의 경로 이름"이므로 단일 디렉토리(또는 동일한 수의 디렉토리)로 구성될 것으로 기대할 수 없습니다. 그래서 우리는마지막경로 이름의 슬래시는 경로 이름의 다른 곳에 나타나지 않는 콘텐츠를 가리킵니다. 해당 문자가 @
데이터에 나타나지 않는다고 가정하면 다음과 같이 기본 이름을 기준으로 정렬할 수 있습니다.
cat pathnames | sed 's|\(.*\)/|\1@|' | sort -t@ -k+2 | sed 's|@|/|'
첫 번째 sed
명령은 다음을 대체합니다.마지막각 경로 이름에 선택된 구분 기호가 있는 슬래시, 두 번째 구분 기호는 변경 사항을 되돌립니다. (단순화를 위해 경로 이름은 한 줄에 하나씩 전달될 수 있다고 가정합니다. 쉘 변수에 있는 경우 먼저 한 줄에 하나씩 형식으로 변환합니다.)
답변4
ksh 또는 zsh와 달리 bash에는 배열 또는 임의의 문자열 목록 정렬을 기본적으로 지원하지 않습니다. glob 또는 glob의 출력을 정렬 할 수 있지만 alias
( 마지막 3개는 사용자의 로케일 정렬 순서가 아니지만) 여기서는 실제로 작동하지 않습니다.set
typeset
POSIX 도구 상자에는 임의의 문자열 목록을 쉽게 정렬할 수 있는 기능이 없습니다. ( sort
NUL 및 개행 문자 이외의 짧은 문자 시퀀스만 있고(LINE_MAX는 일반적으로 PATH_MAX보다 짧음) 파일 경로가 Null 바이트 시퀀스가 아닌 다른 문자열이 있도록 행을 정렬합니다. 0보다 크다).
따라서 문자열 비교 연산자를 awk
사용하여 자체 정렬 알고리즘을 구현하거나<
심지어bash
(사용 [[ < ]]
), bash
이식 가능한 임의 경로의 경우 가장 쉬운 방법은 아마도 다음을 사용하는 것입니다 perl
.
이를 통해 bash4.4+
다음을 수행할 수 있습니다.
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
이것은 strcmp()
비슷한 명령을 제공합니다. 로캘 조합(예 ls
: glob 또는 의 출력)을 기준으로 정렬하려면 -Mlocale
에 인수를 추가하세요 perl
. 숫자 정렬(16진수 는 아니지만 천 단위 구분 기호 대신 sort -g
동일한 숫자를 지원한다는 점에서 GNU와 유사)의 경우 대신 사용합니다 (그리고 다시 명령처럼 사용자의 소수점을 존중합니다 ).+3
1.2e-5
<=>
cmp
-Mlocale
sort
명령 매개변수의 최대 크기로 제한됩니다. 이를 방지하려면 perl
인수를 통하는 대신 파일 목록을 표준 입력에 전달할 수 있습니다 .
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
이전 버전의 경우 루프를 대신 bash
사용 하거나 올바르게 인용된 경로 목록을 출력하여 .while IFS= read -rd ''
readarray -d ''
perl
eval "array=($(perl...))"
를 사용하면 zsh
정렬 순서를 정의할 수 있는 전역 확장을 가짜로 만들 수 있습니다.
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
우리는 reply=($filearray)
실제로 glob을 강제로 /
배열의 요소로 확장합니다(처음에는 그냥). 그런 다음 파일 이름 끝을 기준으로 정렬 순서를 정의합니다.
strcmp()
- 와 유사한 순서 의 경우 로캘을 C로 수정합니다. 숫자 순서 지정(합계를 비교할 때 큰 차이를 만들지 않는 GNU 와 유사 sort -V
(예: 소수점이 있는 로캘에서) )의 경우 glob 한정자를 추가합니다.sort -n
1.4
1.23
.
n
이 외에도 oe{expression}
함수를 사용하여 정렬 순서를 정의할 수도 있습니다. 예를 들면 다음과 같습니다.
by_tail() REPLY=$REPLY:t
또는 다음과 같은 고급 버전:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(그래서 a/foo2bar3.pdf
(2,3 숫자)는 b/bar1foo3.pdf
(1,3) 뒤에 정렬되고 c/baz2zzz10.pdf
(2,10) 앞에 정렬됩니다.) 다음과 같이 사용됩니다.
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
물론, 이것이 주요 용도이기 때문에 실제 구체에 적용될 수 있습니다. 예를 들어, pdf
기본 이름/꼬리별로 정렬된 임의의 디렉터리에 있는 파일 목록의 경우:
pdfs=(**/*.pdf(N.oe+by_tail))
1 - 기반 정렬이 허용되고 짧은 문자열의 경우 strcmp()
문자열을 전달하기 전에 16진수 인코딩으로 변환하고 정렬 후에 다시 변환할 수 있습니다.awk
sort