내 연관 배열에는 백틱, 괄호 등이 포함된 키를 포함하여 임의의 키가 있습니다.
$ typeset -A arr
$ key='`'
$ arr[$key]=backquote
$ echo $arr[$key]
backquote
지금 설정을 해제해야 합니다 arr[$key]
. 내가 시도한 웹 검색 :
$ unset "arr[$key]"
unset: arr[`]: invalid parameter name
$ unset "arr[${(b)key}]"
unset: arr[`]: invalid parameter name
...불운. 이제 이것이 오류 메시지를 제공한다는 점에서 운이 좋습니다. 다음 경우의 결과를 제외하고는 아무것도 실패하지 않는 것 같습니다.
$ typeset -A arr
$ key='?~>#'
$ arr[$key]=symbols
$ echo "$arr[$key]"
symbols
$ unset "arr[${(b)key}]"
$ echo "$arr[$key]"
symbols
실제로 의 모든 기호는 ?~>#
동일한 동작을 트리거합니다.
무슨 일이 일어나고 있고 예상되는 동작을 얻는 방법에 대한 설명이 있습니까?
참고: 이 질문은 이 질문과 비슷한 제목을 가진 ZSH 메일링 리스트의 일부 스레드와 관련이 있습니다(여기그리고거기).
답변1
와, 정말 엉망이네요.
~부터Bart Schaefer의 2016년 패치, 로 병합됨커밋 95663e936596933d529a648ed3d6c707d1a1dffe의 패치 37914실험적으로 zsh 5.4에서 처음 릴리스된 것은 다음 문자를 포함하지 않는 unset "arr[$key]"
한 key
작동합니다 \`()[]
. 이 6개 문자 앞에는 백슬래시가 붙어야 하며, 다른 문자 앞에는 백슬래시가 붙어서는 안 됩니다(예: unset 'arr[\*] arr[\;]'
설정되지 않은 키 \*
및 \;
, 아닌 *
키를 사용해 보세요 ;
). 이것은 어떤 것에 대한 언급이 아닙니다.매개변수 확장 플래그( ${(b)key}
및 그 ${(q)key}
변형) 예.
또한 추가적인 문제가 있습니다. Null 키를 설정 해제하는 방법을 찾을 수 없습니다. unset 'arr[]'
버그이므로 다른 작업을 수행하면 null이 아닌 키가 설정 해제됩니다. null 키를 설정 해제하기 위해 찾은 유일한 해결 방법은 다음을 사용하는 것입니다.아래 첨자 기호원치 않는 키를 필터링합니다(Stéphane Chazelas가 설명했듯이).2018 zsh-작업자 스레드).
다음 함수는 zsh ≥5.4(zsh 5.8에서 테스트됨)에서 작동하며 빈 키를 제거해야 하는 경우에만 배열을 복사합니다.
# Usage: unset_keys ARRAY [KEY]...
# ARRAY must be the name of an associative array parameter.
# Equivalent to unset 'ARRAY[KEY1]' 'ARRAY[KEY2]' ...
# except that this function works correctly even with keys containing
# special characters or is empty. See
# https://unix.stackexchange.com/questions/626393/in-zsh-how-do-i-unset-an-arbitrary-associative-array-element
function unset_keys {
emulate -LR zsh
if [[ -${(Pt)1}- != *-association-* ]]; then
return 120 # Fail early if $1 is not the name of an associative array
fi
if ((${#@[2,$#]:#?*})); then
if [[ -n ${${(P)1}[${:-}]+y} ]]; then
# Copy all entries with non-empty keys
: "${(AAP)1::=${(@kv)${(P)1}[(I)?*]}}"
fi
set -- $@ # Remove empty keys from the to-do list
fi
if (($# < 2)); then
return 0
fi
set -- "$1" "${@[2,$#]//\\/\\\\}"
set -- "$1" "${@[2,$#]//\`/\\\`}"
set -- "$1" "${@[2,$#]//\(/\\(}"
set -- "$1" "${@[2,$#]//\)/\\)}"
set -- "$1" "${@[2,$#]//\[/\\[}"
set -- "$1" "${@[2,$#]//\]/\\]}"
noglob unset $1[${^@[2,$#]}]
}
이는 어쨌든 하나의 복사본만 실행하는 더 간단한 함수입니다.
# Usage: unset_keys ARRAY [KEY]...
# ARRAY must be the name of an associative array parameter.
# Equivalent to unset 'ARRAY[KEY1]' 'ARRAY[KEY2]' ...
# except that this function works correctly even with keys containing
# special characters or is empty. See
# https://unix.stackexchange.com/questions/626393/in-zsh-how-do-i-unset-an-arbitrary-associative-array-element
function unset_keys {
emulate -LR zsh
setopt extended_glob
if [[ -${(Pt)1}- != *-association-* ]]; then
return 120 # Fail early if $1 is not the name of an associative array
fi
set -- "$1" "${(j:|:)${(@b)@[2,$#]}}"
# Copy all entries except the specified ones
: "${(AAP)1::=${(@kv)${(P)1}[(I)^($~2)]}}"
}
zsh 5.4 이전에는 이것이 제가 탐구하지 않은 다른 혼란이었습니다.
이것이 제가 사용하는 테스트 도구입니다. 나는 그것이 합리적인 범위를 제공한다고 생각하지만 그것을 완벽하게 만드는 데 시간을 소비하지 않았습니다.
set -e
test_keys=(
'()safe' '(r)set' '(R)set' '(k)safe' # look like valid subscript flags
'(a' '(a)' '(n:foo:)a' '(n:1)a' # look like invalid subscript flags
'set' '"set"' \'set\' '\s\e\t'
'safe' '"safe"' \'safe\' '\s\a\f\e'
'\\' '\\\' '\\\\' '""' \'\'
'two words' 'two spaces' ' initial space' 'trailing space '
$'\x80\\' $'\x80\`' $'\x80\~' # broken UTF-8
''
'?~>#'
)
for ((i=0; i<255; i++)); do
printf -v n '\\x%02x' $i
eval "test_keys+=(\$'$n')"
done
function populate_test_array {
for k in "${(@)test_keys}"; do
arr[$k]=set
done
}
function check_expected_keys {
local -a actual_keys
actual_keys=("${(@k)arr}")
actual_keys=("${(@o)actual_keys}") # Sorting in one step seems to misplace the empty string at the end (zsh 5.8 on Ubuntu 20.04), so sort in two steps.
local actual_list="${(j: :)${(@qqqq)actual_keys}}"
local expected_list="${(j: :)${(@qqqq)expected_keys}}"
if [[ "$actual_list" != "$expected_list" ]]; then
<<EOF
Failure: unexpected list of keys after $1
expected: $expected_list
actual : $actual_list
EOF
((++errors))
fi
}
typeset -A arr
errors=0
populate_test_array
expected_keys=("${(@o)test_keys}")
test_keys=("${(@)test_keys:#safe}") # [safe] must stay until the end
for k in "${(@)test_keys}"; do
unset_keys arr "$k"
if (($+arr[$k])); then
printf 'Failure: unset %s did not unset it\n' "${(qq)k}"
((++errors))
else
expected_keys=("${(@)expected_keys:#"$k"}")
fi
check_expected_keys "unset ${(qq)k}"
done
populate_test_array
unset_keys arr "${(@)test_keys}"
expected_keys=(safe)
check_expected_keys "unsetting all"
exit $((!!errors))