보통 paste
인쇄둘인접한 열에 있는 명명된(또는 이에 상응하는) 파일은 다음과 같습니다.
paste <(printf '%s\n' a b) <(seq 2)
산출:
a 1
b 2
하지만 두 파일 이 /dev/stdin
및 ./dev/stderr
우리가 가지고 있다고 가정하자비부족비상자두 줄을 출력하는 프로그램표준 출력그리고 두 줄표준 에러. 설명을 위해 다음 함수를 사용하여 이를 시뮬레이션할 수 있습니다.
bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }
지금 달려라annotate-output
, (내부에개발 스크립트캡슐화됨데비안/우분투/등.) 작동하는지 보여주기 위해:
annotate-output bash -c 'bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }; bb'
22:06:17 I: Started bash -c bb() { seq 2 | tee >(sed s/^/e/ > /dev/stderr) ; }; bb
22:06:17 O: 1
22:06:17 E: e1
22:06:17 O: 2
22:06:17 E: e2
22:06:17 I: Finished with exitcode 0
그래서 작동합니다. 피드 :bb
paste
bb | paste /dev/stdin /dev/stderr
산출:
1 e1
e2
^C
정지됨 - ^C
눌려졌다는 의미Control-C그만두다.
|
다음 중 하나로 변경해 ;
도 작동하지 않습니다.
bb ; paste /dev/stdin /dev/stderr
산출:
1
2
e1
e2
^C
또한 일시 중지됨 - ^C
눌려졌음을 의미합니다.Control-C그만두다.
원하는 출력:
1 e1
2 e2
을 사용하여 수행할 수 있습니까 paste
? 그렇지 않다면 왜 안 됩니까?
답변1
/dev/stderr을 파이프로 사용할 수 없는 이유는 무엇입니까?
문제는 그렇지 paste
도 않고 그렇지도 않습니다 /dev/stdin
. 그것은 와 함께 간다 /dev/stderr
.
모든 명령은 하나의 개방형 입력 설명자(0: 표준 입력)와 두 개의 출력(1: 표준 출력 및 2: 표준 오류)으로 생성됩니다. 일반적 으로 각각 /dev/stdin
및 이름을 사용하여 액세스할 수 있지만 다음을 참조하세요./dev/stdout
/dev/stderr
/dev/stdin, /dev/stdout 및 /dev/stderr의 이식성은 얼마나 됩니까?. (포함 paste
) 많은 명령 도 파일 이름을 -
STDIN으로 해석합니다.
개별적으로 실행 하면 bb
STDOUT과 STDERR은 모두 콘솔이며 일반적으로 명령 출력이 나타납니다. 라인은 (귀하의 설명과 같이) 다른 설명자를 통과 annotate-output
하지만 동일한 위치에 끝납니다.
하나와 두 번째 명령을 추가하면 |
파이프가 생성됩니다.
bb | paste /dev/stdin /dev/stderr
|
의 출력을 bb
의 입력에 연결하도록 쉘에 지시합니다 paste
. paste
먼저 를 읽으십시오 /dev/stdin
. 이것은 (일부 심볼릭 링크를 통해) 자신의 표준 입력 설명자(쉘이 방금 연결한)로 해석되어 행이 1
통과하도록 합니다.
그러나 쉘/파이프라인은 STDERR에 영향을 미치지 않습니다. bb
여전히 e1
e2
콘솔로 전송 중입니다. 동시에 paste
동일한 콘솔에서 데이터를 읽으려고 하면 콘솔이 정지됩니다(무언가를 입력할 때까지).
당신의 링크텍스트 편집기를 사용하여 /dev/stdout을 읽을 수 없는 이유는 무엇입니까?./dev/stderr
두 번째 파이프를 만드는 방법
표준 출력과 표준 오류를 생성하는 명령이 있고 paste
이 두 줄을 서로 옆에 두기를 원합니다. 이는 각 열에 하나씩 두 개의 동시 파이프라인을 의미합니다. 쉘 파이프는 ... | ...
이들 중 하나를 제공합니다. 두 번째 파이프를 직접 생성하고 .redirect를 사용하여 STDERR을 해당 파이프로 리디렉션해야 합니다 2>filename
.
mkfifo RHS
bb 2>RHS | paste /dev/stdin RHS
이것이 스크립트 내에서 사용되는 경우 FIFO를 임시 디렉토리에 배치하고 사용 후 삭제하는 것이 좋습니다.
답변2
annotate-output
paste
이 작업을 수행할 수 있는 이유는 특별한 작업(예: 명령의 표준 오류를 fifo로 리디렉션)을 수행하고 있기 때문입니다.전적으로절대 안돼 - 단지 paste
왜냐하면아니요입력을 받는 명령을 실행하며 입력이나 출력을 리디렉션할 수 없습니다.
그러나 annotate-output에서 사용하는 것과 똑같은 트릭을 사용하여 래퍼를 작성할 수 있습니다.
pasteout(){
f=$(mktemp -u) || return
mkfifo -m 600 -- "$f" || return
"$@" 2>"$f" | paste -- - "$f"
rm -f -- "$f"
}
pasteout bb
하지만 교착상태에 빠지기 쉬우니 주의하세요. 예를 들어, bb
파이프가 보유할 수 있는 것보다 더 많은 stdout을 생성하고 처음에 읽은 추가 양을 더한 값을 paste
생성하지만 오류 출력을 생성하지 않는 경우 paste
fifo에 대한 입력을 기다리며 차단되고 해당 stdout 출력이 삭제되지 않습니다 bb
. bb
파이프의 write()도 중단 됩니다 .
답변3
전체 생산 라인에는 분석해야 할 몇 가지 문제가 있습니다. 즉:
seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr
표준 에러
첫 번째는 마지막 명령입니다. 오직표준 출력파이프 연결 가능:
$ seq2 | paste -
1
2
$ seq2 | paste - -
1 2
읽을 내용 없음 stderr
:
$ seq 2 | paste - /dev/stderr
1 ^C
^C
읽을 내용을 차단하고 남기지 않기 때문에 필요합니다 stderr
.
일부 출력을 생성하더라도 stderr
파이핑되지 않습니다.
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr
1 3
4
이전과 마찬가지로 1
이 인쇄되고 paste
블록은 기다립니다 stderr
.
다른 2개의 숫자는 콘솔로 직접 이동하여 (독립적으로) 인쇄됩니다.
stderr
파이프라인의 마지막 명령에 일부 입력을 제공할 수 있습니다.
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr 2</dev/null
1
2
3
4
2>/dev/null
그건 그렇고, 이는 차단 명령에 사용된 두 번째 파일 설명자를 피하는 것과 정확히 같습니다 paste
. 그러나 인쇄된 값은 seq 3 4
에서가 아니라 리디렉션에서 콘솔로 직접 가져옵니다 paste
.
$ { seq 2; seq 3 4 >/dev/tty; } | paste - /dev/stderr 2</dev/null
1
2
3
4
이는 다음을 방지하지 않습니다.
$ seq 2 | tee >(sed 's/^/e/' > /dev/stderr) |
paste /dev/stdin /dev/stderr 2</dev/null
1
2
e1
e2
주문하다
둘째, 의 출력이 tee
"순서대로" 이루어질 필요는 없습니다.`tee` 및 `bash` 프로세스 교체 순서
그리고 실제로 프로세스 대체의 출력은 "순서대로"일 필요는 없습니다. 프로세스 교체 출력이 고장났습니다.
$ echo one; echo two > >(cat); echo three;
one
three
two
실제로 일부 예에서는 여러 번 시도하면 다른 주문을 받을 수도 있습니다.프로세스 교체를 통해 동시에 실행 중인 독립 프로세스의 비결정적 출력
$ printf '%s\n' {0..1000} | tee >(head -n2) >(sort -grk1,1 | head -n3) >/dev/null
1000
999
998
0
1
따라서 아니요. 절차적 교체 및 붙여넣기를 통해 수행할 수 없습니다.
몇 가지 명령을 실행해야 합니다.
$ seq 2 | { while read a; do printf "%s %s\n" "$a" "e$a" ; done; }
1 e1
2 e2
BB
따라서 bb 함수에는 기본적으로 다음이 포함됩니다.
| tee >(sed 's/^/e/')
이는 다음을 사용하여 테스트할 수 있습니다.
$ printf '%s\n' {0..1000} | tee >(sort -grk1,1 | head -n3 >&2) | head -n 2
0
1
291
290
289
0, 1, 1000, 999, 998을 이 순서대로 인쇄해야 하지만 그렇지 않은 경우가 많습니다.
즉: 그렇습니다본질적으로불안정한.
안정적인 실제 솔루션.
bb에 대한 유일한 안전한 솔루션은 프로세스 교체를 피하는 것입니다.
그리고 {…}
stdout 및 stderr 캡처를 활용하세요. 예를 들면 다음과 같습니다.
$ bash -c '{ echo test-str >/dev/stderr; }' 2>/dev/null
출력이 없습니다. 2개를 제거하여 확인하세요.
이것은 bb에서 작동합니다.
$ bb() { seq 5 | tee /dev/stderr | sed 's/^/e/'; }
그리고 fifo를 사용하여 다음을 붙여넣습니다.
$ mkfifo out2
$ bb 2>out2 | paste out2 -
1 e1
2 e2
3 e3
4 e4
5 e5
fifo 파일을 삭제하려면 트랩을 설정하고 fifo 파일을 생성하기 전에 해당 파일이 존재하는지 테스트해야 합니다.
내가 테스트한 모든 쉘에서 이식 가능한 것 같습니다(Almquist 구문과 호환 가능). 아직 완전히 테스트되지 않았습니다. 다른 사용자에게 확인해 주세요. 알려지지 않은 놀라움이 있을 수 있습니다.