이것은 Bash 매뉴얼 리디렉션 섹션의 오타입니까?


13
Note that the order of redirections is significant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error to the file dirlist, 
   while the command

          ls 2>&1 > dirlist

   directs  only  the  standard  output  to  file  dirlist,  because the 
   standard error was duplicated from the standard output before the standard
   output was redirected to dirlist.

이제 마지막 부분이 혼란 스럽습니다. 이 경우 표준 오류가 터미널에 인쇄되고 STDOUT이 dirlist 파일로 이동합니다. 그것이 일어날 일이지만, 내가 매뉴얼을 이해하는 방식이 아닙니다.

"표준 출력이 dirlist로 리디렉션 된 후 표준 오류가 표준 출력에서 ​​복제 되었기 때문에"라고 말해야합니다. STDOUT이 파일로 지정되기 전에 STDERR이 STDOUT으로 전송 된 경우 파일에 STDOUT 및 STDERR이 포함되어 있지 않습니까?

누군가 나를 위해 이것을 정리할 수 있습니까? 내 이해력이 열악합니까? 이 문맥에서 중복이라는 단어를 사용하는 것이 조금 이상해 보입니다. 아마도 그것은 저를 던지고 있습니다.



1
"값별"대 "참조 별"작업을 혼합하는 일반적인 경우입니다. 파일 디스크립터를 복제 할 때 이는 값별 조작입니다. 프로그래밍에서, a = 1; b = a; a = 2당신 a == 2 && b == 1이 사실 이라고 기대 한 후에 . 리디렉션 2>&1b = a할당 과 유사합니다 . 이는 참조가 아니라 값을 기준으로합니다. 2>&1영원 모두에 대해 파일 디스크립터 2를 파일 디스크립터 1에 결혼시키지 않습니다. 이들은 여전히 ​​동일한 파일을 가리키는 2 개의 개별 파일 디스크립터입니다.
jw013

답변:


23

여기서 중복 은 정말 중요한 부분입니다.

리디렉션 전에 파일 디스크립터가 어디로 가는지 봅시다. 이것은 일반적으로 현재 터미널입니다. 예 :

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

이제 ls -l리디렉션없이 전화하면 출력 및 오류 메시지가 아래 터미널로 이동합니다 /dev/pts/1.

먼저 STDOUT파일을 파일 ( ls -l > dirlist)로 리디렉션하면 다음과 같습니다.

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

그런 다음 파일 설명자 ( ) 의 복제본 으로 리디렉션 하면 다음STDERR복제본 으로 이동합니다 .STDOUTls -l > dirlist 2>&1STDERR/home/bon/dirlist

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

우리가 할 경우 첫 번째 리디렉션 STDERR의 중복에 STDOUT의 파일 기술자 ( ls -l 2>&1) :

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

그리고 다음 STDOUT 파일 (에 ls -l 2>&1 > dirlist), 우리는이를 얻을 것입니다 :

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

여기, STDERR여전히 터미널로 가고 있습니다.

맨 페이지의 순서가 정확하다는 것을 알 수 있습니다.


테스트 리디렉션

이제 직접 테스트 해 볼 수 있습니다. 를 사용 하면 현재 프로세스 ls -l /proc/$$/fd/에서 STDOUT(fd 1) 및 STDERR(fd 2)가 어디로 가고 있는지 볼 수 있습니다.

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

파일 디스크립터가 가리키는 위치를 나타내는 작은 쉘 스크립트를 작성해 봅시다. 이런 식으로, 우리는 항상 호출 할 때 상태를 얻습니다 ls.

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(을 사용하면 CtrlD파일 끝을 보내므로 cat에서 읽기 명령을 중지하십시오 STDIN.)

이제 다양한 조합의 리디렉션으로이 스크립트를 호출하십시오.

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

파일 디스크립터 1 (for STDOUT) 및 2 (for STDERR)가 다양 함을 알 수 있습니다. 재미있게도 리디렉션 STDIN하고 결과를 볼 수 있습니다.

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(독자에게 남은 질문 : 파일 디스크립터 255는 어디에 있습니까? ;-))


+1-훌륭한 답변. 매우 잘 쓰여지고 훌륭한 예. 감사합니다!!!
slm

나는 내 오해가 모든 후속 명령에 대해 리디렉션이 영구적이므로 나머지 줄에 대한 모든 STDERR이 STDOUT으로 이동한다고 생각합니다.
그레그 레 벤탈

2

아니요, 매뉴얼이 맞습니다.

처음에 1이 터미널을 가리키고 2가 터미널을 가리키는 경우 :

command  2>&1   1>somewhere

리디렉션 evaluatino는 왼쪽에서 오른쪽으로 발생합니다.

따라서 FIRST는 FIRST를 평가 2>&1하고 따라서 FIRST는 fd 1로 가리키는 fd (즉 the terminal, 일반적으로 / dev / tty 의 파일 디스크립터 )를 fd에 복사합니다 2.

그 시점에서 fd는 2이제 fd 1가 ( the terminal) 를 가리키는 곳 을 가리 킵니다.

그리고 그런 다음 평가 1>somewhere부분을, 따라서의 파일 기술자 복사합니다 somewhereFD에 1(그래서 그 시점에서, 전략 중 1지금 포인트를 somewhere, 및 FD 2여전히 점 the terminal)

따라서 1이 변경되기 전에 2가 1에서 복제되었으므로 실제로 1을 "어딘가"에, 2를 터미널에 인쇄합니다.

다른 순서 :

command  1>somewhere 2>&1

먼저 fd 1를로 리디렉션 somewhere한 다음 동일한 참조를 fd 2에 복사하므로 끝 2도를 가리 킵니다 somewhere. 그러나 그들은 지금부터 "연결"되지 않습니다. 각각은 여전히 ​​개별적으로 리디렉션 될 수 있습니다.

전의:

command  1>somewhere 2>&1
exec 2>/dev/null

그 끝에서 fd는를 1가리키고 somewherefd 2/dev/null

fd의 일반적인 이름 1은 STDOUT (표준 출력)이고 일반적인 fd 이름 2은 STDERR입니다 (표준 오류는 STDOUT을 방해하지 않고 오류를 표시하는 데 일반적으로 사용되므로 표준 오류 임)


@ Michael-mrozek : 편집 해 주셔서 감사합니다. 그러나 "중복"대신 "중복"대신 "복사"라고 말하면서 지금부터는 둘 다 "같은 것"이라고 믿게 만들 수 있습니다. ex : cmd 1>somewhere 2>&1 ; exec 2>/dev/null: exec 후 2 개만 / dev / null로 리디렉션되었습니다 (1은 여전히 ​​"어딘가"로 이동). "fd 1"대신 "what 1 point"를 말하는 방법을 생각해내는 데 도움이 필요하지만 ... 혼란 스럽기 때문에 ...
Olivier Dulac

1
무슨 뜻인지 잘 모르겠습니다. 당신은 그것을 "복사"에서 "중복"으로 바꾼 사람입니다. 내가 한 모든 것은 대문자를 사용하고 형식을 변경하는 것입니다. 단어를 바꾸지 않았습니다
Michael Mrozek

아 ... ^^ 미안합니다. 그리고 무엇을 더 정확하게 복사하도록 재구성하기 위해 다시 편집했습니다 ^^
Olivier Dulac

1

혼란스러운 부분은 stderr을 stdout으로 리디렉션하는 것이 실제로 두 개의 스트림을 연결하는 오해라고 생각합니다.

완벽하게 합리적인 아이디어이지만 글을 쓸 때 발생 2>&1하는 일은 stderr이 stdout이 무엇을 쓰고 있는지와 같은 장소 자체에 까 pee을합니다. 따라서 나중에 stdout에게 다른 곳으로 쓰라고 지시하면 이미 이동 한 stderr의 대상에는 영향을 미치지 않습니다.

나는 그것이 다소 반 직관적이라고 생각하지만 그것이 작동하는 방식입니다. 먼저 쓰고 싶은 곳을 설정 한 다음 모두에게 "저를 복사하십시오"라고 말하십시오. 그 희망은 분명히 ...


0

복사...

중요하지만 오히려 많은 혼란원천 이라는 점에서 의미가 있습니다 . 정말 간단합니다. 이 답변은 단지 "급진적 인"그림입니다.

허용되는 대답은 좋지만 너무 길며 "중복"을 강조합니다.

Q는 현명하게 다음과 같이 끝납니다.

단어의 중복 사용은 이러한 맥락에서 나에게 조금 이상한 것 같다. 아마도 그것은 저를 던지고 있습니다.

bash 표기법을 사용하고 변수 "1"과 "two"를 파일 핸들 "1"과 "2"로 정의합니다. (출력) 리디렉션 연산자 >는 할당 =입니다. &그리고 $"값"을 의미합니다.

man bash 예제 (기본 "1"추가)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

지다:

one=dirlist  two=$one

two=$one   one=dirlist

그리고 이것조차도 나에게 자동적이지 않으며 다른 사람들은 내가 추측합니다. 첫 번째 줄의 잎 당신과 함께 $one하고 $two모두 포함 "dirlist". 물론이야.

두 번째 줄은 쓸모없는 과제로 시작합니다. 둘 다 "TTY"(비트 기호)를 방향으로 정의하여 시작합니다 . 이 할당에 의해 값이 변경되지 않으며 파일 핸들과 같은 변수를 사용하면 마술로 연결된 것이 없습니다. 변수 two는 다음의 영향을받지 않습니다 one=dirlist. 당연히 아니지.

여기 (6 년 전) Sombody는 "복사"또는 "복제"대신 "지점"을 제안한 후 깨달았습니다.

이 복제 또는 포인터 의미론은 필요하지 않습니다. 아마도 더 많은주의가 필요한 것은 앰퍼샌드 일 것입니다. "가치"연산자 / 토큰 / 무엇이든.

콘솔 에서 놀라운 작업 번호를 얻는 방법을 찾고 있다면 "완료"메시지와 "2"라는 파일을 보너스로 받으면 다음과 같이됩니다.

ls 1>2& 2>/dev/null

자연스럽게 " 복사"/ "중복"1 ~ 2 로 읽은 다음 둘 다 함께 null 로 읽습니다 . 그러나 아이디어는 틀렸고 구문도 틀 렸습니다. (그러나 구문 오류는 없으며 유효합니다)

그것을 계획하는 올바른 방법은 둘 중 하나를 null로 리디렉션 한 다음 OTHER를 같은 장소로 리디렉션하는 것입니다.

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(앞의 "1"은 그대로 둘 수 있습니다)

(OK A. acc는 너무 길지는 않지만 너무 많은 목록입니다-또는 : 시각화가 훌륭하고 설명이 좋지 않습니다)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.