제한된 사전 정의 목록의 단어를 매개변수로 받아들여야 하는 스크립트를 작성 중입니다. 또한 꼭 이루어지길 바랍니다. complete
와 사이의 중복을 피하기 위해 목록을 변수에 저장합니다 case
. 그래서 저는 이것을 썼고 완성은 작동하지만 case
문은 작동하지 않습니다. 왜? 사례문 매개변수를 만들기 위해 변수를 사용할 수는 없나요?
declare -ar choices=('foo' 'bar' 'baz')
function do_work {
case "$1" in
"${choices[*]}")
echo 'yes!'
;;
*)
echo 'no!'
esac
}
complete -W "${choices[*]}" do_work
답변1
의 목록은 구분된 목록 complete -W list
으로 해석되며 완료형이 고려됩니다.$IFS
$IFS
따라서 다음과 같은 경우:
complete -W 'a b,c d' do_work
do_work
a
공백이 포함된 경우 와 b,c
공백이 포함된 경우와 포함되지 않은 경우에 완성 기능이 제공됩니다 .d
$IFS
a b
c d
$IFS
,
그래서 대부분 설계상 깨졌습니다. 또한 임의의 문자열을 완성으로 제공하는 것도 허용하지 않습니다.
이러한 제한 사항을 적용할 때 할 수 있는 최선의 방법은 $IFS
수정되지 않을 것이라고 가정하고(따라서 공백, 탭 및 줄 바꿈이 항상 포함되므로 완성 단어에 문자를 사용할 수 없음) 다음을 수행하는 것입니다.
choices='foo bar baz'
do_work() {
case "$1" in
(*' '*) echo 'no!';;
(*)
case " $choices " in
(*" $1 "*) echo 'yes!';;
(*) echo 'no!';;
esac;;
esac
}
complete -W "$choices" do_work
readonly IFS
수정 되지 않도록 를 추가할 수 있지만 , 특히 부모 범위에서 읽기 전용으로 선언된 지역 변수를 선언하는 것을 허용하지 않는다는 점을 $IFS
고려하면 문제가 발생할 수 있습니다 . 따라서 이를 수행하는 함수라도 파괴하다.bash
local IFS=,
배열 요소에 문자열이 있는지 확인하는 방법에 대한 보다 일반적인 질문에 대해서는 bash
(zsh와 달리) 연산자가 없지만 루프를 사용하여 쉽게 구현할 수 있습니다.
amongst() {
local string needle="$1"
shift
for string do
[[ $needle = "$string" ]] && return
done
false
}
그런 다음:
do_work {
if amongst "$1" "${choices[@]}"; then
echo 'yes!'
else
echo 'no!'
fi
}
문자열을 찾는 데 더 적합한 구조는 해시 테이블이나 연관 배열을 사용하는 것입니다.
typeset -A choices=( [foo]=1 [bar]=1 [baz]=1 )
do_work() {
if [[ -n ${choices[+$1]} ]]; then
echo 'yes!'
else
echo 'no!'
fi
}
complete -W "${!choices[*]}" do_work
이는 "${!choices[*]}"
연관 배열의 키를 $IFS
해당 지점의 첫 번째 문자와 연결합니다(설정되었지만 비어 있는 경우 구분 기호가 사용되지 않음).
Bash 연관 배열은 빈 키를 가질 수 없으므로 빈 문자열은 선택 사항 중 하나가 될 수 없지만 complete -W
어쨌든 지원되지 않으며 사용자가 목록 완성을 제외하고는 빈 문자열을 완성하는 것은 별로 유용하지 않습니다. 허용 가능한 값 중
답변2
문의 값은 구문의 일부이므로 명시 case
적으로 구분해야 합니다 . 다만, Case문의 값은 반드시 패턴이어야 하므로 확장 패턴을 사용할 수도 있다.|
|
string="*@($(IFS="|";echo "${choices[*]}"))*"
다음과 같이 확장 스키마로 생성 하여 사용합니다.
#!/bin/bash
declare -ar choices=('foo' 'bar' 'baz')
string="*@($(IFS="|";echo "${choices[*]}"))*" # value contains one option
#string="@($(IFS="|";echo "${choices[*]}"))" # value is exactly one option
shopt -s extglob
function do_work {
case "$1" in
$string) echo 'yes!' ;;
*) echo 'no!' ;;
esac
}
complete -W "${choices[*]}" do_work
그러나 이것은 extglob의 옵션을 변경하고 있으며, 종료하기 전에 스크립트를 시작할 때의 값으로 반환하여 피할 수 있습니다. $string
함수가 실행될 때가 아니라 함수가 정의될 때 값이 확장된다는 점에 유의하세요 . 그러나 더 간단한 해결책은 =
테스트의 오른쪽이 내부에서 작동한다는 [[...]]
사실을 사용하여 해당 셸 옵션을 설정하지 않고 설정을 해제하는 것입니다.extglob 쉘 옵션이 활성화된 것과 마찬가지로(아니요, 죄송합니다. 에서는 작동하지 않습니다 [...]
.) 따라서 케이스 표현을 [[ $1 = $string ]]
and(echo 명령의 경우)로 단순화할 수 있습니다. 이는 다음과 같이 작동합니다.
[[ $1 = $string ]] && echo 'yes!' || echo 'no!'
그러나 일반적으로 함수를 사용하는 것이 더 좋으며 함수가 종료 코드를 반환한다고 보장하지 않으므로 0
실제로 다음을 사용해야 합니다.
if [[ $1 = $string ]]; then yesaction; else noaction; fi
이 답변의 어려운 부분을 이미 해결했으므로 공백과 함께 옵션을 사용하도록 완성 기능을 활성화할 수도 있습니다. IFS가 옵션을 분할할 때 필요한 것은 ${complete_var}
각 옵션을 따옴표로 묶고 공백으로 구분하는 것뿐입니다.
#!/bin/bash
declare -a choices=('foo' 'foo bar' 'baz')
option_string="@($(printf -v var "%s|" "${choices[@]}"; printf '%s' "${var%?}" ))"
complete_var="$( printf -v var "'\"%s\"' " "${choices[@]}"; printf '%s' "${var%?}" )"
#printf '%s\n' "${option_string}" "${complete_var}"
yesaction() { echo 'yes!'; }
noaction () { echo 'no!' ; }
do_work () {
if [[ $1 == $option_string ]]
then yesaction
else noaction
fi
}
complete -W "${complete_var}" do_work
return 34;
exit
그러면 이러한 값이 함수 내에서 고정된 값으로 설정됩니다. choices
읽기 전용으로 설정할 필요가 없습니다 . 그리고 옵션 내에 공백을 허용하세요.
.
이는 쉘에서 스크립트를 get() 한다고 가정합니다 . 그것은 다음과 같습니다:
$ . ./script.sh
$ do_work <kbd>TAB</kbd>-<kbd>TAB</kbd> # will show "foo" "bar" and "foo bar" as options.
내가 볼 수 있는 유일한 문제는 완료된 값이 큰따옴표 안에 표시된다는 것입니다 "foo"
. 하지만 제 생각에는 이것은 해결해야 할 문제가 아닙니다.
답변3
=~
연산자를 사용하여 값이 배열에 있는지 확인할 수도 있습니다 .
if [[ " ${choices[*]} " =~ " ${1} " ]]; then
echo "yes!";
else
echo "no!";
fi