폴더를 추출하고 파일 이름을 키와 값으로 분할하고 연관 배열에 저장하는 방법은 무엇입니까?

폴더를 추출하고 파일 이름을 키와 값으로 분할하고 연관 배열에 저장하는 방법은 무엇입니까?

제목의 문제를 해결해야 하는 다음 스크립트가 있지만 분명히 키에 값을 할당하는 것은 작동하지 않습니다. 그 이유는 우연한 오류인가요, 아니면 스크립트에 상당한 실수가 있는 것인가요?

폴더 이름은 다음과 같은 보석 파일의 이름입니다.gem-file-foo-1.2.3

이 예에서는 키가 가정되고 gem-file-foo값은 버전 번호 1.2.3또는 동일한 gem의 여러 버전이 있는 경우 여러 버전 번호의 문자열입니다.

다음과 같은 키는 출력되지 않습니다 echo "${!my_gems[@]}".왜 안 돼?

#!/bin/bash

directory=$GEM_HOME/gems
declare -A my_gems

get_gemset_versions () {

last_key=""
values=""

FIND_RESULTS=$(find $directory -maxdepth 1 -type d -regextype posix-extended -regex "^${directory}\/[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$")

 printf "%s\n" $FIND_RESULTS | sort | 
  while read -r line; do
    line=${line##*/}
            
    KEY="${line%-*}"
            
    VALUE="${line##*-}"
  
        if [[ $last_key -eq "" ]]; then
            last_key=$KEY
        fi
        
        if [[ $last_key -eq $KEY ]]; then
            values="$values ${VALUE}"
        else
            values="${VALUE}"
            last_key=$KEY
        fi
        
        my_gems[$KEY]=$values
    done
 
    echo "${!my_gems[@]}"

 }


get_gemset_versions

또한 동일한 보석 패키지를 여름화하는 논리 $last_key에는 결함이 있는 것 같습니다. $key이것이 반드시 질문의 일부는 아니지만 여기에 잘못된 논리를 적용하고 있는지 지적해 주시면 좋을 것입니다.

감사해요

답변1

당신은:

printf "%s\n" $FIND_RESULTS | sort | 
  while read -r line; do
    ...
    done
 
    echo "${!my_gems[@]}"

여기서 들여쓰기에 관계없이 는 echo파이프라인 외부에 있습니다. Bash는 기본적으로 서브셸에서 파이프라인의 모든 부분을 실행하므로 파이프라인이 끝난 후에는 루프 내의 할당이 while표시되지 않습니다. Shellcheck.net은 이에 대해 다음과 같이 경고합니다.

Line 32:
        my_gems[$KEY]=$values
        ^-- SC2030: Modification of my_gems is local (to subshell caused by pipeline).

안타깝게도 해결 방법을 제공하지 않습니다.

Bash에서는 lastpipe옵션을 활성화하거나 파이프를 프로세스 대체로 바꿀 수 있습니다.

shopt -s lastpipe
echo test | while read line; do
    out=$line
  done
echo "out=$out"

또는

while read line; do
  out=$line
done < <(echo test)
echo "out=$out"

( lastpipe작업 제어와 연결되어 있으므로 대화형 셸에서 시도하면 작동하지 않을 수 있습니다.~ 아니다활성화 중입니다.)

보다:내 변수가 하나의 '읽는 동안' 루프에서는 로컬이지만 겉보기에 유사한 다른 루프에서는 로컬이 아닌 이유는 무엇입니까?


어쨌든 이것은 약간 이상해 보입니다.

FIND_RESULTS=$(find ...)

 printf "%s\n" $FIND_RESULTS 

find줄 바꿈으로 구분된 파일 이름을 출력합니다. 파일 이름에 포함된 내용이 없는 한 괜찮습니다. 그러나 여기서는 변수를 통한 왕복과 인용되지 않은 확장에서 분리되는 단어도 모든 파일 이름을 공백으로 분할합니다.

바로 달려가시면 됩니다 find ... | while .... 또는 while ...; done < <(find...).

또한 선행 및 후행 공백이 깨지는 것을 while IFS= read -r line; do방지하기 위해 거의 항상 를 사용하고 싶어한다는 점에 유의하십시오 . read글쎄, 나는 당신의 파일 이름이 그것들을 포함하지 않기를 바랍니다. 그러나 어쨌든.

지금은 좋은 참조 질문을 찾을 수 없지만 이는 IFS공백 포함과 관련된 질문입니다. 다른 선행 및 후행 구분 기호는 하나의 필드에만 제거되지 않습니다 read. 예 를 들어 문자 IFS=": " read -r foo <<< "::foobar "그대로 . 콜론은 유지되지만 후행 공백은 사라졌습니다.foo::foobar

관련 정보