zsh: 포인터가 있는 리디렉션 및 파이프의 순서 이해

zsh: 포인터가 있는 리디렉션 및 파이프의 순서 이해

zsh에서는

echo "hello" 1>&1 1>&1 1>&1 | cat

hello를 8번 인쇄하는 동안

echo "hello" 1>&1 1>&1 1>&1 1>&1 | cat

hello를 16번 인쇄하세요. 따라서 hello는 2^n번 인쇄됩니다. 여기서 n은 1>&1위 파이프의 숫자 와 같습니다 .

나는 무엇을 찾고 있나요? 여러 소스에 따르면 파이프 + 리디렉션이 발생하는 순서는 다음과 같습니다.

"명령의 표준 입력, 표준 출력 또는 둘 다 명령의 일부로 리디렉션 연산자에 의해 지정된 리디렉션 이전에 파이프된 것처럼 처리됩니다."

소스 코드:https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/utilities/V3_chap02.html#tag_18_09_02,https://unix.stackexchange.com/a/672961/456507

대략적으로 말하면 리디렉션과 파이프가 처리되는 순서는 "파이프 다음으로 왼쪽에서 오른쪽으로"입니다.

zsh Libera 채팅 IRC의 누군가는 리디렉션을 처리하기 전에 "echo의 fd1이 cat의 fd0을 가리킨다"고 제안했습니다. 누군가 리디렉션을 포함하도록 이러한 표현 방식을 확장할 수 있습니까 1>&1? 사람들이 그것을 말로 표현할 수 있다면 사람들은 다른 리디렉션과 파이프에 대해서도 비슷한 표현을 할 수 있고 무슨 일이 일어나고 있는지 이해할 수 있다고 생각하기 때문에 이것이 중요합니다. 원하신다면 만들어진 C/C++ 포인터 용어를 자유롭게 사용해 보세요!

답변1

이 동작은 다음과 같습니다.여러 운영 체제zsh 특정 기능. 따라서 POSIX 사양과 같은 다른 문서는 별로 도움이 되지 않습니다. 지쉬여러 운영 체제가 꺼진 경우 POSIX는 이와 관련하여 호환되므로 POSIX 동작은 스토리의 일부만 제공하지만 일부만 제공합니다.

다중 OS이든 아니든 리디렉션을 이해하는 가장 자연스러운 방법은 명령형 방식으로 생각하는 것입니다. 명령 리디렉션은 왼쪽에서 오른쪽으로 처리되며(파이프 먼저 제외) 각 리디렉션은 프로세스 상태를 수정합니다. 이것이 쉘의 목적입니다.

  1. 처음에 명령에는 주변 코드와 동일한 열린 파일 설명자가 있습니다.

  2. 명령이 파이프 오른쪽에 있는 경우 파이프의 읽기 끝을 파일 설명자 0(표준 입력)에 연결합니다. 명령이 파이프의 왼쪽에 있는 경우( 포함 |&) 파이프의 쓰기 쪽을 파일 설명자 1(표준 출력)에 연결합니다.

  3. 리디렉션은 순차적으로 적용됩니다. 여기서는 주요 관련 사례만 나열합니다. 보다수동리디렉션 연산자의 전체 목록입니다. N0개 이상의 숫자를 나타내거나파일 설명자 변수. 파일 이름을 나타내는 M하나 이상의 숫자를 나타냅니다 FILENAME(프로세스 대체 가능).

    • N<&M파일 설명자 M을 N에 복사합니다. 즉, M이 연결된 모든 곳에 N을 연결합니다. 이전에 파일 설명자 N에 연결된 모든 것은 더 이상 중요하지 않습니다.
    • N<FILENAME입력을 위해 FILENAME을 열고 파일 설명자 N에 연결합니다. 이전에 파일 설명자 N에 연결된 모든 것은 더 이상 중요하지 않습니다.
    • N>&M, 형식 N>FILENAME과 유사 하지만 파일을 덮어쓰기 위해 열고 파일을 추가하기 위해 엽니다. 단, 다음과 같은 경우는 제외됩니다.N>>FILENAME<N>FILENAMEN>>FILENAME
      활성화(기본값) 되고 multios이 명령의 파일 설명자 N에 리디렉션(파이프 포함)이 이미 존재하는 경우 zsh는 파일 설명자 N을 M 또는 FILENAME에 연결하지 않고 대신 파이프 P를 생성하고 내장된 tee파이프에서 읽고 그 출력을 M 또는 FILENAME 및 파일 설명자 M의 내용에 복사하고 N을 파이프의 쓰기 끝에 연결하는 프로세스 와 같습니다 .즉, 파일 설명자 N에 대한 두 번째 리디렉션은 exec N> >(tee FILENAME >&N)명령 이전에 실행되는 것과 대략 동일합니다. (이것은 N> >(tee FILENAME >&N)간단한 경우 리디렉션을 적용하는 것과 동일하지만 동일한 파일 설명자에 세 개 이상의 리디렉션이 있는 경우에는 해당되지 않습니다.)
    • N<&-또는 N>&-파일 설명자 N을 닫습니다.
  4. 명령이 왼쪽에 있으면 |&리디렉션 2>&1이 수행됩니다. (이는 간단한 경우에는 배관 표준 오류와 동일하지만 복잡한 경우에는 항상 그런 것은 아닙니다.)

따라서 1>&1 1>&1 1>&1 …여러 운영 체제의 경우 다중 리디렉션을 사용하면 첫 번째 리디렉션 자체는 아무 작업도 수행하지 않습니다. 파일 설명자를 자체적으로 리디렉션하는 것은 아무 작업도 하지 않습니다(파일 설명자가 닫힐 때 오류가 발생하지 않는 한). 그러나 여전히 다중 OS 리디렉션으로 간주되므로 두 번째는 1>&1입력을 복사하고 두 출력 모두 원래 표준 출력으로 전송되는 Tee와 유사한 프로세스에 대한 파이프를 생성합니다. 세 번째는 1>&1입력을 복사하는 또 다른 티형 프로세스를 생성하고 두 출력 모두 이제 첫 번째 티의 입력인 표준 출력으로 이동하므로 데이터가 두 번 복사됩니다. 네 번째는 1>&1입력을 복제하고 두 출력은 두 번째 티로 이동하여 3번의 반복, 즉 입력의 8배가 됩니다. 등. 여러 리디렉션과 파이프를 사용하면 파이프가 하나의 리디렉션으로 계산되므로 첫 번째 1>&1는 파일 설명자 1에 대한 두 번째 리디렉션이고 복사를 시작하므로 8번 echo "hello" 1>&1 1>&1 1>&1 | cat인쇄됩니다 . hello그것은 동등하다

( exec 1> >(tee /dev/fd/1 1>&1);
  exec 1> >(tee /dev/fd/1 1>&1);
  exec 1> >(tee /dev/fd/1 1>&1);
  echo "hello" ) | cat

또는

{ { { { echo hello; } > >(tee /dev/fd/1 1>&1); } > >(tee /dev/fd/1 1>&1); } > >(tee /dev/fd/1 1>&1); } | tr a-z A-Z

연습 1: 다음 명령의 동작을 설명하세요.

/bin/echo hello 1>&1 1>&1 1>&-

1>&1 1>&1출력을 표준 출력으로 복사합니다. 그런 다음 1>&-표준 출력을 닫아 echo쓸 곳이 없도록 하고 불평합니다.

연습 2: 이 두 명령의 차이점을 설명하세요.

/bin/echo hello 1>&- 1>&2
/bin/echo hello 1>&1 1>&1 1>&- 1>&2

첫 번째 경우에는 파일 설명자 1에 닫히지 않는 리디렉션만 있으므로 multio 동작이 시작되지 않습니다. 1>&-fd 1을 닫고 1>&2일반적인 리디렉션이므로 명령은 다음 hello과 같이 fd 2에 인쇄됩니다 /bin/echo 1>&2. 두 번째 경우에는 fd 1에 여러 리디렉션이 있으므로 1>&-이를 닫은 후 zsh는 fd 1에 대해 여러 리디렉션을 수행합니다 1>&2. 여기에는 현재 fd 1에 연결되어 있지만 fd 1이 닫혀 있어 display 와 함께 실패하는 티형 프로세스를 분기하는 작업이 포함됩니다 multio failed for fd 1: bad file descriptor.

연습 2b: 설명

/bin/echo hello 1>&1 1>&- 1>&2

파일 설명자를 닫으면 is-the-the-first-redirection 상태가 재설정되므로 두 번째 리디렉션은 /bin/echo hello 1>&2.

관련 정보