Bash 프로세스 대체: 표준 입력을 이식성 있게 참조하는 방법은 무엇입니까?

Bash 프로세스 대체: 표준 입력을 이식성 있게 참조하는 방법은 무엇입니까?

여기서 문제는용감한다음 문장. 내가 이것을 하려고 한다고 가정해보자:

diff -u <(some command) {mystery-syntax}

stdin의 출력을 some command터미널의 복사-붙여넣기와 같은 일부 데이터와 비교하고 있습니다. 이와 같이:

$ diff -u <(echo foo) {mystery-syntax}
bar
[Ctrl-D]
--- /dev/fd/63  2021-11-04 11:28:19.360366909 -0700
+++ /dev/fd/62  2021-11-04 11:28:19.360366909 -0700
@@ -1 +1 @@
-foo
+bar

{mystery-syntax}메타 토큰은 어떤 실제 구문을 다루나요?

diff유틸리티는 표준 입력 지정 규칙을 사용하지 않으므로 -사용할 수 없습니다.

한 가지 대답은 다음과 같습니다 /dev/fd/0.

$ diff -u <(echo foo) /dev/fd/0
bar
[Ctrl-D]
--- /dev/fd/63  2021-11-04 11:31:02.837040697 -0700
+++ /dev/fd/0   2021-11-04 11:30:56.807964644 -0700
@@ -1 +1 @@
-foo
+bar

좋아요, 그래서 우리는 Linux 기반 시스템에서 한 방향으로 작동하도록 했습니다 /dev/fd.하지만 파일 시스템을 참조하지 않는 방법이 있습니까 /dev(이것은 시스템별 구현 세부 사항입니다)?나는 예외가 있다고 생각합니다 /dev/fd. 설명서에는 다음과 같은 내용이 나와 있습니다.

프로세스 대체는 명명된 파이프(FIFO) 또는 /dev/fd 명명된 파일 열기 방법을 지원하는 시스템에서 지원됩니다.

예를 들면 다음과 같습니다.

diff <(echo foo) <(cat)

어떤 이유로든 작동하지 않습니다. <(cat)프로세스가 필요한 출력만 대체 하더라도 cat여전히 cat표준 입력 없이 호출됩니다.

$ diff -u <(echo foo) <(cat)
cat: -: Input/output error
--- /dev/fd/63  2021-11-04 11:33:59.303347814 -0700
+++ /dev/fd/62  2021-11-04 11:33:59.303347814 -0700  
@@ -1 +0,0 @@
-foo

여기에도 "똑똑한" 행동이 있습니다. 명시적으로 입력 /dev/fd/0으로 리디렉션하더라도 여전히 지루합니다.cat

$ diff -u <(echo foo) <(cat < /dev/fd/0)
cat: -: Input/output error

하지만! 여기 문서를 표준 입력으로 제공하면 cat방해하지 않습니다.

$ diff -u <(echo foo) <(cat <<<"bar")
--- /dev/fd/63    2021-11-04 11:40:03.612616179 -0700
+++ /dev/fd/62    2021-11-04 11:40:03.612616179 -0700
@@ -1 +1 @@
-foo
+bar

답변1

diff명령줄에 제공된 파일 이름 중 하나가 -(대시)인 경우 유틸리티는 표준 입력에서 해당 위치를 읽습니다.

$ echo hello | diff -u <( echo ok ) -
--- /dev/fd/12  Fri Nov 12 21:25:18 2021
+++ -   Fri Nov 12 21:25:18 2021
@@ -1 +1 @@
-ok
+hello

GNU diff매뉴얼에서:

a가 FILE이면 -표준 입력을 읽습니다.

OpenBSD 매뉴얼에서:

file1또는 이면 file2표준 -입력이 대신 사용됩니다.

POSIX 표준에서:

file1, file2
비교할 파일의 경로 이름입니다. file1OR file2피연산자가 이면 표준 -입력을 대신 사용해야 합니다.

답변2

diff -u <(echo foo) <(cat)bash프로세스 대체 명령이 포그라운드로 가져오지 않아 터미널 장치에서 읽을 수 없기 때문에 대화형으로 작동하지 않습니다 . cat &WHERE가 백그라운드에서 실행되는 경우 cat(터미널 장치의 포그라운드 프로세스 그룹이 아님)와 마찬가지로 실행 중인 프로세스는 cat제어 터미널에서 데이터를 읽으려고 시도하자마자 신호에 의해 일시 중지됩니다.SIGTTIN

echo hello | diff -u <(echo ok) <(cat)이는 두 개의 하위 쉘로 구성된 전체 파이프가 전경에 있고 cat어쨌든 터미널 대신 파이프에서 읽혀지기 때문에 작동합니다. 프로세스 교체는 포그라운드에 있는 하위 셸(이 파이프라인의 두 번째 구성 요소)에 의해 시작되기 때문에 포그라운드에서 끝납니다.

bash-5.0$ diff -u <(echo ok) <(ps -o pid,ppid,pgid,args)
--- /dev/fd/63  2021-11-12 22:39:41.985207894 +0000
+++ /dev/fd/62  2021-11-12 22:39:41.985207894 +0000
@@ -1 +1,6 @@
-ok
+    PID    PPID    PGID COMMAND
+   9779    9772    9779 /bin/zsh
+1957388    9779 1957388 bash --norc
+1958861 1957388 1957388 bash --norc
+1958862 1957388 1958862 diff -u /dev/fd/63 /dev/fd/62
+1958863 1958861 1957388 ps -o pid,ppid,pgid,args
--- /dev/fd/63  2021-11-12 22:39:01.017237420 +0000
+++ /dev/fd/62  2021-11-12 22:39:01.017237420 +0000
@@ -1 +1,7 @@
-ok
+    PID    PPID    PGID COMMAND
+   9779    9772    9779 /bin/zsh
+1957388    9779 1957388 bash --norc
+1958325 1957388 1958324 diff -u /dev/fd/63 /dev/fd/62
+1958326 1958325 1958324 [bash] <defunct>
+1958327 1958325 1958324 bash --norc
+1958328 1958327 1958324 ps -o pid,ppid,pgid,args

ps위의 첫 번째 사례가 어떻게 다른 프로세스 그룹에서 실행되고 두 번째 사례가 나머지 프로세스와 동일한 프로세스 그룹에서 실행되는지 확인하세요 .

실행하는 경우:

bash-5.0$ (diff -u <(echo ok) <(ps -o pid,ppid,pgid,args))
--- /dev/fd/63  2021-11-12 22:42:04.761112701 +0000
+++ /dev/fd/62  2021-11-12 22:42:04.761112701 +0000
@@ -1 +1,6 @@
-ok
+    PID    PPID    PGID COMMAND
+1960605    9772 1960605 /bin/zsh
+1960612 1960605 1960612 bash --norc
+1960757 1960612 1960757 diff -u /dev/fd/63 /dev/fd/62
+1960759 1960757 1960757 bash --norc
+1960760 1960759 1960757 ps -o pid,ppid,pgid,args

포그라운드에서 완전한 서브셸을 시작하면 모든 것이 정상으로 돌아갑니다.

따라서 여기서 가장 간단한 해결책은 서브셸에서 파이프를 실행하는 것입니다.

어쨌든 이것은 대화형 셸(터미널 작업 제어가 실행되고 있음)과 stdin이 세션의 제어 터미널인 경우에만 작동합니다.

diff -u <(echo ok) <(cat)일반적으로 이식성만 고려하는 스크립트에서는 쉘이 비대화형이고 작업 제어를 수행하지 않기 때문에 stdin이 터미널이더라도 문제가 되지 않습니다 .

그러나 이는 cat유용하지 않으며 다음과 같은 UUOC입니다.

cat | diff -u <(echo ok) -

또는

cat | diff -u <(echo ok) /dev/stdin

bash 또는 zsh에서 임시 명명된 파이프를 사용하는 것과 다른 명령에 대해 /dev/fd/x/ 를 지원하지 않는 시스템에서는 stdin 지정이 지원 되지 않습니다 (그러나 이 기능이 제공되는 ksh93u 이전 버전의 ksh에서는 지원되지 않음). 따라서 해당 시스템에서는 계속 작동합니다. 일하다./dev/stdindiff-<(cat)diff -u <(echo ok) <(cat)

이식성에 대해 언급했으므로 에서는 다음을 zsh수행해야 합니다.

echo hello | { diff <(echo ok) <(cat); }

to 에 대한 표준 입력 cat도 파이프에서 나옵니다 echo.

또한 참고하십시오:

echo hello | cmd /dev/stdin # or /dev/fd/0, /proc/self/fd/0²

ksh93은 파이프대신 소켓 쌍을 사용하기 ksh93때문에 Linux에서는 작동하지 않습니다 .3, Linux(및 Cygwin)에서는 다른 시스템과 달리 표준 입력에서 여는 것과 동일한 파일을 열므로 연결할 수 없습니다. .|/dev/stdindup(0)open()

더 많은 관련 Q&A 읽기:


bash¹ 오늘날에는 거의 없으며, 없는 시스템보다 없는 시스템이 더 많을 것입니다./dev/fd/x

² 실제로 파일을 열지 않고도 표준 입력을 의미하는 gawk것으로 자체적으로 처리하거나 처리할 수 있는 명령과 같은 명령은 제외됩니다 ./dev/stdin-

3 Linux의 파이프 구현에서는 ksh93이 일부 (모호한) 최적화를 수행하는 데 필요한 것이 무엇인지 확인할 수 없기 때문입니다.

관련 정보