我有以下腳本,應該可以解決標題中的問題,但顯然它無法將值分配給鍵。原因是意外錯誤還是腳本有重大錯誤?
資料夾名稱是 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 在子 shell 中運行管道的所有部分,因此循環內的分配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
如果您在互動式 shell 中嘗試它可能不起作用,因為它與作業控制相關不是正在啟用。
看:為什麼我的變數在一個「while read」迴圈中是本地變量,但在另一個看似相似的循環中卻不是?
無論如何,這看起來有點奇怪:
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
。冒號保留,但尾隨空格消失。