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

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

제목의 문제를 해결해야 하는 다음 스크립트가 있지만 키에 값을 할당할 수 없는 것 같습니다. 원인이 예상치 못한 오류였나요? 아니면 스크립트의 주요 버그였나요?

폴더 이름은 gem 파일의 이름입니다. 예를 들어gem-file-foo-1.2.3

이 예에서 키는 버전 번호여야 하며, 동일한 gem의 여러 버전이 있는 경우 gem-file-foo값은 버전 번호 또는 여러 버전 번호의 문자열이어야 합니다.1.2.3

어떤 키도 출력되지 않습니다 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 "휴가(leave)는 foo문자 그대로 를 의미합니다 ::foobar. 콜론은 유지되지만 후행 공백은 사라집니다.

관련 정보