Bash는 어느 스트림에 프롬프트를 작성합니까?

Bash는 어느 스트림에 프롬프트를 작성합니까?

bash(프롬프트, 사용자 입력, 결과)의 모든 출력을 파일로 리디렉션하려고 합니다.

예:

/bin/bash > file.txt 2>&1

나는 이것이 효과가 있을 것이라고 생각했지만 프롬프트를 얻지 못했습니다. 누구든지 내가 뭘 잘못하고 있는지 말해 줄 수 있나요?

답변1

Bash는 대화형 모드에서만 프롬프트를 출력합니다. 즉, 일반적으로 터미널(Linux의 경우/dev/tty)로 출력됩니다. /dev/stdout도 아니고 /dev/stdin도 아닙니다 :)

지금은 확실하지 않지만 완전한 기능을 갖춘 tty를 사용할 수 없을 때 bash가 제한된 상호 작용 모드를 허용할 것이라고 상상할 수 있습니다. 그렇다면 나는예상되는표준 출력에 쓰라는 메시지를 표시합니다. 아직 테스트하지 않았습니다.

훌륭한 개념 증명:

(for a in some set of words; do echo $a > /dev/tty; done) 2>&1 > /dev/null

리디렉션이 없는 것처럼 1..10만 출력됩니다. 프롬프트와 마찬가지로 출력을 터미널로 직접 보냅니다(터미널이 없으면 실패함).

Tip: 모두 모아보고 싶다면 확인해 보세요.

답변2

가장 간단한 방법은

bash -i >/tmp/logfile 2>&1

명령을 입력하면 Bash는 모든 것을 쓰고 /tmp/logfile명령을 계속 실행하지만 터미널에는 아무것도 표시되지 않습니다. 종료할 때 Ctrl+를 누르 D거나 를 입력 하여 터미널 세션을 종료할 수 있습니다 exit.

리디렉션 없이 동일한 작업을 실행하면 stderrhello 메시지만 파일에 기록되고 다른 모든 내용은 현재 터미널에서 실행됩니다. 따라서 bash가 프롬프트 스트림(및 다음 명령 모두)을 출력하는 것에 대한 질문에 대한 대답은 다음과 같습니다.표준 에러.

아, 그렇습니다. 이 -i매개변수는 bash가 대화형 모드에서 실행되도록 강제합니다. 그 사람들의 말을 듣지 마세요. 이 작업을 수행하는 데 마법이 필요하지 않습니다.

답변3

프롬프트는 truss(Solaris의 경우)에 표시된 대로 stderr에 기록됩니다.

$ truss -ft write -p 10501
10501:  write(2, " d", 1)               = 1
10501:  write(2, " a", 1)               = 1
10501:  write(2, " t", 1)               = 1
10501:  write(2, " e", 1)               = 1
10501:  write(2, "\n", 1)               = 1
10521:  write(1, " S a t u r d a y ,   S e".., 46)  = 46
10501:      Received signal #18, SIGCLD [caught]
10501:        siginfo: SIGCLD CLD_EXITED pid=10521 status=0x0000
10501:  write(2, " $  ", 2)             = 2

답변4

대화형 모드에서 bash는 프롬프트를 stderr로 출력합니다. 다음 strace 명령은 이를 보여줍니다( --norc다른 프롬프트를 설정하는 ~/.bashrc의 옵션이 내 사용자 정의 프롬프트를 재정의하는 것으로 해석되지 않도록 확인).

$ export PS1="MYPROMPT> "
MYPROMPT> strace -f -- bash --norc 
execve("/usr/bin/bash", ["bash"], 0x7ffcd83805b0 /* 59 vars */) = 0
[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
[...]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
[...]
write(2, "MYPROMPT> ", 10MYPROMPT> )              = 10 <----- prompt written into stderr (fd = 2)
pselect6(1, [0], NULL, NULL, NULL, {[], 8}

stdout 및 stderr을 파일로 리디렉션하는 동일한 명령은 프롬프트가 인쇄되지 않지만 쉘이 명령을 수락함을 보여줍니다.

MYPROMPT> strace -f -- bash --norc > /tmp/shout 2> /tmp/sherr
date

다른 터미널에서 파일을 봅니다. 내용은부르다예:

$ cat /tmp/shout
dim. 04 sept. 2022 16:19:24 CEST  <------- Result of the date command entered above

내용은셰르예:

$ cat /tmp/sherr
execve("/usr/bin/bash", ["bash", "--norc"], 0x7ffe73ea5da8 /* 59 vars */) = 0
[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
[...]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, 0x7ffc43496b30)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]
read(0,  <-------- No prompt is displayed

그러나 -ibash 명령줄에 옵션을 추가하는 것과 동일한 작업을 수행하는 경우:

MYPROMPT> strace -f -- bash --norc -i > /tmp/shout 2> /tmp/sherr

stderr에 프롬프트 표시:

$ cat /tmp/sherr
execve("/usr/bin/bash", ["bash", "--norc", "-i"], 0x7ffd8c1fa520 /* 59 vars */) = 0
[...]
ioctl(2, TCGETS, 0x7ffe1a8465f0)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]
ioctl(0, TIOCGWINSZ, {ws_row=37, ws_col=155, ws_xpixel=0, ws_ypixel=0}) = 0
[...]
write(2, "MYPROMPT> ", 10MYPROMPT> )              = 10  <------ Prompt displayed on stderr (fd = 2)
pselect6(1, [0], NULL, NULL, NULL, {[], 8}

관련 정보