제목의 문제를 해결해야 하는 다음 스크립트가 있지만 분명히 키에 값을 할당하는 것은 작동하지 않습니다. 그 이유는 우연한 오류인가요, 아니면 스크립트에 상당한 실수가 있는 것인가요?
폴더 이름은 다음과 같은 보석 파일의 이름입니다.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