단일 키 입력을 읽는 zsh 스크립트 프롬프트: 적절한 경우 개행을 추가하시겠습니까?

단일 키 입력을 읽는 zsh 스크립트 프롬프트: 적절한 경우 개행을 추가하시겠습니까?

-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설정하는 동작을 계속 유지합니다 . 그렇지 않으면 로 설정합니다.answeryyYn

솔루션 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키뿐만 아니라 누른 키로 설정됩니다 . 그래서 우리는 ( ) 를 확인해야 합니다 .ynyY"${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비대화형으로 호출되지 않는 검사를 추가해야 합니다. 그렇지 않으면 sttyAND 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$interactivefor

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

관련 정보