
-i
플래그(→ ) 를 취하는 스크립트가 있는데 $interactive
, 설정된 경우 각 대상과 다른 대상에 대해 예/아니요-기본값-아니오 질문을 합니다 rm -i
.
Zsh read -q
는 이러한 상황을 위해 설계되었습니다. 단일 키를 허용하고 y
키가 y또는 이면 변수를 으로 설정하고 Y, 그렇지 않으면 로 설정합니다 n
.
내 문제는 프롬프트를 반복하고 있다는 것입니다. 그래서 그 자체로 동일한 줄에 내 프롬프트를 반복적으로 인쇄합니다. 즉, 이 코드는 다음과 같습니다.
# moshPids is an array of pids
for p in $moshPids; do
if [[ -n $interactive ]]; then
read -q "answer?Kill $p? (N/y)"
[[ "${answer}" == 'n' ]] && continue
fi
# do_kill set to print “killing $1” in debug
do_kill $p
done
결과 :
$ myCmd
Kill 123? (N/y)nKill 456? (N/y)nKill 789? (N/y)yKilling 789
$
다음과 같이 프롬프트에 줄 바꿈을 포함하는 등 이에 대한 많은 솔루션이 있습니다(그 중 일부는 이 사이트의 답변에 있음).
for p in $moshPids; do
if [[ -n $interactive ]]; then
read -q "answer?Kill $p? (N/y)
"
[[ "${answer}" == 'n' ]] && continue
fi
do_kill $p
done
결과 :
$ myCmd
Kill 123? (N/y)
nKill 456? (N/y)
nKill 789? (N/y)
yKilling 789
$
이것은 나에게 추악한 것 같습니다. 또 다른 해결책은 명령 echo >&2
뒤에 추가하는 것 입니다 read
. 이는 적어도 언뜻 보기에는 완벽해 보입니다.
$ myCmd
Kill 123? (N/y)n
Kill 456? (N/y)y
Killing 456
Kill 789? (N/y)n
$
그러나 기본값을 수락하면 ⏎빈 줄이 표시됩니다( ⏎
실제 출력에는 표시되지 않지만 입력을 표시하기 위해 추가됨).
$ myCmd
Kill 123? (N/y)⏎
Kill 456? (N/y)y
Killing 456
Kill 789? (N/y)⏎
$
기준
따라서 한 번의 클릭 응답을 원합니다.
- 여러 힌트/답변이 한 줄에 인쇄되지 않습니다.
- ⏎기본값 수락을 누르면 빈 줄이 생성되지 않습니다.
- 키가 또는 인 경우 로
read -q
설정하는 동작을 계속 유지합니다 . 그렇지 않으면 로 설정합니다.answer
y
yYn
솔루션 1
이것이 나의 첫 번째 해결책입니다.
for p in $moshPids; do
if [[ -n $interactive ]]; then
read -k1 "answer?Kill $p? (N/y)"
[[ $answer != $'\n' ]] && echo >&2
[[ "${answer}" =~ '[yY]' ]] || continue
fi
do_kill $p
done
여기에서는 -k1
대신 "한 문자, 모든 문자 읽기" 동작을 얻도록 -q
전달됩니다 . read
이렇게 하면 문자가 개행 문자와 일치하는지 테스트할 수 있습니다. ( $answer != $'\n'
)가 없으면 누락된 개행 문자를 인쇄할 수 있습니다.
그러나 이제는 또는 $answer
키뿐만 아니라 누른 키로 설정됩니다 . 그래서 우리는 ( ) 를 확인해야 합니다 .y
n
y
Y
"${answer}" =~ '[yY]'
이 문제를 처리하는 더 좋은 방법이 있습니까?
솔루션 2
stty
또한 키보드 에코를 일시적으로 비활성화하는 호출을 사용하여 이것을 고려했습니다 .
for p in $moshPids; do
if [[ -n $interactive ]]; then
stty -echo
read -q "answer?Kill $p? (N/y)"
stty echo
echo >&2
[[ "${answer}" == 'n' ]] && continue
fi
do_kill $p
done
예쁜 출력의 우선순위를 정하는지 또는 롤백 시 입력을 볼 수 있는지에 따라 키 응답의 가시성을 비활성화하면 더 나을 수도 있고 그렇지 않을 수도 있습니다. (이 경우 예/아니요 질문에서 yes 옵션은 항상 무언가를 인쇄하고 noecho는 괜찮아 보입니다. 필요한 경우 항상 if echo -n "${answer}"
뒤에 추가 read
하고 일종의 확장 주문을 사용하여 개행을 공백으로 바꿀 수 있습니다.)
첫 번째 솔루션보다 한 줄 더 길지만 read -q
정규식에 대해 테스트할 필요가 없고 무조건 개행을 에코하기 때문에 이해하기가 더 쉬울 것입니다.
하지만 다른 한편으로는,하다원숭이 대 터미널; 두 명령을 충분히 가깝게 배치했지만 그렇게 해서는 안 됩니다.또한명령 중단에 대한 시간 창은 터미널이 미쳐버릴 만큼 충분히 크지만 여전히 문제가 됩니다. (또한 완전성을 위해 -i
비대화형으로 호출되지 않는 검사를 추가해야 합니다. 그렇지 않으면 stty
AND read
연산이 실패합니다.)
제가 제안한 두 가지보다 더 좋고 더 관용적인 솔루션이 있습니까? 이는 -i
많은 "대화형" 명령(물론 대부분은 쉘이 아닌 다른 것으로 작성됨)이 뒤따르는 명백한 동작 인 것 같습니다 .
답변1
고쳐 쓰다:
매뉴얼에서:
read
...
-s Don't echo back characters if reading from the terminal.
효과가있다:
for p in $pids; do
read -qs "?Kill $p? (N/y)"
>&2 echo $REPLY
case $REPLY in
(y) do_kill $p ;;
esac
done
read -q
반품 상태도 제공됩니다.
for p in $pids; do
if read -qs "?Kill $p? (N/y)"; then
>&2 echo $REPLY; do_kill $p # answer was y or Y
else
>&2 echo $REPLY # answer was not y or Y
fi
done
당신이 원한다면.
-i
설정을 처리하는 옵션에 대해서는 for를 루프 외부에 $interactive
두십시오 . 예를 들면 다음과 같습니다.test
$interactive
for
interactive_kill() {
for p in $@; do
read -qs "?Kill $p? (N/y)"
>&2 echo $REPLY
case $REPLY in
(y) do_kill $p ;;
esac
done
}
if [[ -n $interactive ]]; then
interactive_kill $pids
else
do_kill $pids # if do_kill() needs exactly one arg wrap in a for loop
fi
반품:
- 쉘 모드를 테스트
y
하거나Y
필요하지 않으므로=~
연산자를 사용하거나 명령문에서 사용할 수 있습니다.[yY]
[[
=
[yY]
case
- 첫 번째 예에서는 논리가 약간 왜곡된 것 같습니다.
[[ "${answer}" == 'n' ]] && continue
do_kill $p
언제
if [[ $answer = y ]]; then
do_kill $p
fi
읽기가 더 쉽습니다.
답변2
나는 또한 당신이 설명하는 동작을 원하지만 간단하지 않습니다. 나는 도우미 함수를 작성했습니다.
ask_yes_no() {
read -qs "?$1 (y/N) "
out=$?
print $REPLY >&2
return $out
}
# example usage
for thing in one two three; do
if ask_yes_no "Do thing $thing?"; then
echo "Do thing $thing"
else
echo "Nope"
fi
done
출력 예:
Do thing one? (y/N) n
Nope
Do thing two? (y/N) y
Do thing two
Do thing three? (y/N) y
Do thing three