기본 이름을 기준으로 파일 경로 이름 배열을 정렬합니다.

기본 이름을 기준으로 파일 경로 이름 배열을 정렬합니다.

배열에 저장된 파일 경로 이름 목록이 있다고 가정해 보겠습니다.

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에 대해서만 다른 배열을 만들고 sortbasenames 배열에 적용합니다.

감사해요.

답변1

sortGNU 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

" dir1dir2는 임의의 경로 이름"이므로 단일 디렉토리(또는 동일한 수의 디렉토리)로 구성될 것으로 기대할 수 없습니다. 그래서 우리는마지막경로 이름의 슬래시는 경로 이름의 다른 곳에 나타나지 않는 콘텐츠를 가리킵니다. 해당 문자가 @데이터에 나타나지 않는다고 가정하면 다음과 같이 기본 이름을 기준으로 정렬할 수 있습니다.

cat pathnames | sed 's|\(.*\)/|\1@|' | sort -t@ -k+2 | sed 's|@|/|'

첫 번째 sed명령은 다음을 대체합니다.마지막각 경로 이름에 선택된 구분 기호가 있는 슬래시, 두 번째 구분 기호는 변경 사항을 되돌립니다. (단순화를 위해 경로 이름은 한 줄에 하나씩 전달될 수 있다고 가정합니다. 쉘 변수에 있는 경우 먼저 한 줄에 하나씩 형식으로 변환합니다.)

답변4

ksh 또는 zsh와 달리 bash에는 배열 또는 임의의 문자열 목록 정렬을 기본적으로 지원하지 않습니다. glob 또는 glob의 출력을 정렬 할 수 있지만 alias( 마지막 3개는 사용자의 로케일 정렬 순서가 아니지만) 여기서는 실제로 작동하지 않습니다.settypeset

POSIX 도구 상자에는 임의의 문자열 목록을 쉽게 정렬할 수 있는 기능이 없습니다. ( sortNUL 및 개행 문자 이외의 짧은 문자 시퀀스만 있고(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와 유사)의 경우 대신 사용합니다 (그리고 다시 명령처럼 사용자의 소수점을 존중합니다 ).+31.2e-5<=>cmp-Mlocalesort

명령 매개변수의 최대 크기로 제한됩니다. 이를 방지하려면 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 ''perleval "array=($(perl...))"

를 사용하면 zsh정렬 순서를 정의할 수 있는 전역 확장을 가짜로 만들 수 있습니다.

sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))

우리는 reply=($filearray)실제로 glob을 강제로 /배열의 요소로 확장합니다(처음에는 그냥). 그런 다음 파일 이름 끝을 기준으로 정렬 순서를 정의합니다.

strcmp()- 와 유사한 순서 의 경우 로캘을 C로 수정합니다. 숫자 순서 지정(합계를 비교할 때 큰 차이를 만들지 않는 GNU 와 유사 sort -V(예: 소수점이 있는 로캘에서) )의 경우 glob 한정자를 추가합니다.sort -n1.41.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진수 인코딩으로 변환하고 정렬 후에 다시 변환할 수 있습니다.awksort

관련 정보