Bash는 'select' 루프가 실행될 때 SIGINT 트랩을 무시합니다.

Bash는 'select' 루프가 실행될 때 SIGINT 트랩을 무시합니다.

선택 루프와 함께 "트랩"을 사용하면, 즉 옵션이 표시되는 동안 CTRL+C를 눌러 중단하려고 하면 터미널에 ^C만 인쇄됩니다. 스크립트에서 "트랩"을 제거하면 정상적으로 종료됩니다. 즉, Ctrl+C를 사용할 수 있습니다.

저는 이것을 두 가지 다른 버전의 bash(CentOS와 함께 제공되는 버전과 Fedora와 함께 제공되는 버전)에서 테스트했는데 Fedora 버전(4.4.23(1)-release)에 문제가 있습니다. CentOS와 함께 제공되는 Bash 버전 4.2.46(2) 릴리스는 제대로 작동하는 것 같습니다. 또한 터미널에서 로컬로 테스트하고 SSH를 통해 원격으로 테스트했습니다. 문제는 항상 Fedora에 있습니다.

내가 말하는 내용을 확인하기 위해 코드를 게시하겠습니다.

이것은 작동하지 않습니다.

#!/bin/bash

trap exit SIGINT

select opt in One Two Three; do
        break
done

전체 "트랩 종료 SIGINT" 줄을 제거하면 문제 없이 CTRL+C를 사용할 수 있으며 제대로 작동합니다.

이 문제를 해결하거나 우회하는 방법에 대한 아이디어가 있습니까?

답변1

이 문제를 해결하거나 우회하는 방법에 대한 아이디어가 있습니까?

--posixoptions 을 사용 하거나 일시적으로 posix 모드를 켜면 이를 우회할 수 있습니다 set -o posix.

set -o posix
select opt in foo bar baz; do
    echo "opt=$opt"
done
set +o posix

이 동작에 대한 설명을 보려면 확인할 수 있습니다.zread()내장 기능에서 사용되는 함수 read( bash 에서 내부적으로 호출되기도 함 select):

  while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
    /* XXX - bash-5.0 */
    /* We check executing_builtin and run traps here for backwards compatibility */
    if (executing_builtin)
      check_signals_and_traps ();   /* XXX - should it be check_signals()? */
    else
      check_signals ();

어떤 특별한 이유로 인해 내장 함수가 명시적으로 호출될 때만 설정되고 에 의해 호출될 때는 설정 되지 executing_builtin않습니다 . 이는 버그처럼 보이며 의도적인 것은 아닙니다.readselect

posix 모드에서 실행하면 신호가 비활성화됩니다 read. 이 경우 zreadintr()호출되는 것과 달리 zread()중단된 시스템 호출은 트랩 실행 후 다시 호출되지 않습니다. read(2)바라보다builtins/read.def:

      if (unbuffered_read == 2)
        retval = posixly_correct ? zreadintr (fd, &c, 1) : zreadn (fd, &c, nchars - nr);
      else if (unbuffered_read)
        retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1);
      else
        retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);

Bash의 "restart" 내장 명령에 대한 read자세한 내용여기.

답변2

매뉴얼의 관련 부분은 다음 bash과 같습니다(적어도 그렇게 작동한다고 생각합니다).

명령이 완료되기를 기다리고 bash트랩이 설정되었다는 신호를 받으면 명령이 완료될 때까지 트랩이 실행되지 않습니다.

따라서 트랩 핸들러는 명령이 완료되기를 기다리고 있기 select때문에 루프 본문이 실행될 때까지 호출되지 않습니다 . bash입력이 수신되면 select트랩 핸들러가 실행됩니다.

다음 수정된 스크립트는 이를 더 잘 보여줍니다.

#!/bin/bash

trap 'echo INT;exit' SIGINT

select opt in One Two Three; do
    printf 'Got %s (%s)\n' "$REPLY" "$opt"
done

bash5.0.3을 사용하여 실행하고 을 선택한 1다음 Ctrl+C를 누르고 를 Enter선택합니다 3.

$ bash script.sh
1) One
2) Two
3) Three
#? 1
Got 1 (One)
#? ^C
1) One
2) Two
3) Three
#? 3
INT

트랩 핸들러는 현재 입력( 3)이 승인되고 이전 루프 본문이 실행될 때 실행됩니다.select

트랩 핸들러는아니요Enter프롬프트 에서 버튼을 누르면 메뉴가 다시 표시 Ctrl+C되므로 버튼을 누르면 실행됩니다 .Enterselect

관련 정보