파이프를 다른 파이프에 연결한 결과가 예상대로 작동하지 않습니다.

파이프를 다른 파이프에 연결한 결과가 예상대로 작동하지 않습니다.

저는 bash 프로그래밍이 처음이고 파이프와 그룹화된 명령을 사용하여 여러 인수를 전달하는 방법을 이해하려고 몇 가지 명령을 시도하던 중 이 이상한 동작을 발견하여 혼란스러웠습니다. 내가 원하는 것을 달성할 수 있는 다른 방법이 있다는 것을 알고 있지만 왜 이런 일이 발생하는지 이해하려고 노력하고 있습니다.

write 내장 기능 프로그램을 사용하여 SSH 서버에 연결된 사용자(사용자 ID를 USER로, TTY를 TTY로 생각)에게 메시지를 보내려고 합니다.

다음을 사용하면 메시지를 잘 보낼 수 있지만

$ echo "some message" | write USER TTY

그러나 USER 및 TTY를 전달하는 다른 파이프를 사용하려고 하면 메시지가 전송되지 않습니다.

$ echo "some message" | { echo "USER TTY" | xargs -o write; }

결과를 보면 bash는 첫 번째 부분( $ echo "some message")을 무시하고 명령 실행 후 메시지를 요구하는 것으로 보입니다.

{ echo "USER TTY" | xargs -o write; }그리고 동일한 작업을 수행한다는 점에 유의하세요 write USER TTY(분명히? 여기에는 제가 인식하지 못하는 차이점이 있는 것으로 생각됩니다).

다시 말하지만, 이 작업을 수행하는 더 쉬운 방법이 있다는 것을 알고 있지만 명령 그룹화, 파이핑 및 입력 인수를 함수에 전달하는 측면에서 bash가 어떻게 작동하는지 이해하고 싶습니다. 이 분야에 대한 의견을 주시면 감사하겠습니다.


내가 숙제를 요구하는 것을 의심하는 사람들을 위해 사람들이 이 일에 관심을 갖는 것을 보니 반갑습니다. 저는 실제로 내 SSH 서버의 모든 사용자에게 메시지를 보내기 위해 별칭을 만들려고 노력하고 있습니다. 이것이 더 wall쉽다고 생각하지만 어렵습니다. 흥미롭습니다. 내가 여기서 언급하는 내용을 명확히 하기 위해서입니다.

답변1

당신의

echo "some message" | { echo "USER TTY" | xargs -o write; }

이 명령은 다음을 수행하기 때문에 작동하지 않습니다.

                        |-------------------------------------------|
echo "some message"   --+->   echo "USER TTY"  -->  xargs -o write; |
                        |-------------------------------------------|

표준 입력은 xargs에서 오는 파이프입니다 echo "USER TTY". gets echo "some message"는 복합 명령으로 파이프됩니다 . 이는  { echo "USER TTY" | xargs -o write; }실제로 gets가 명령 echo "some message"으로 파이프 되지 않음 echo "USER TTY"을 의미합니다 . PS  옵션을 지정했으므로  표준 입력이 터미널로 설정됩니다. 당신이 말하고 싶다면 이것은 효과가 있습니다writesome message
-oxargswrite

echo "USER TTY" | xargs -o write

write그런 다음 메시지를 입력하면 가장 바깥쪽 메시지에 연결되지 않습니다 echo.

당신이 좀 가지고 있다면Command B산출USER그리고 TTY (하지만 한 쌍만) 시도해 볼 수 있습니다

echo "어떤 메시지" 쓰기 $(명령 B)

고쳐 쓰다:다음은 가상의 상황에 적용될 수 있습니다.Command B한 쌍만 출력 USER그리고 TTY, 그러나 실제 상황에는 적합하지 않을 수 있습니다. 읽어보시고 아래의 토론을 살펴보세요.

말씀하신 대로 bash를 사용하고 있고 GNU xargs (Linux의 표준, 다른 Unix 계열 운영 체제에서도 사용 가능)가 있는 경우 다음을 시도해 볼 수도 있습니다.

echo "어떤 메시지" xargs -a <(명령 B) 쓰다

리치가 제안한대로 xargs를 사용하여 실행된 명령의 stdin에서 읽기.

 POSIX가 해당 옵션을 지정하지 않기 xargs때문에  GNU가 있을 수도 있다는 것을 방금 깨달았습니다 .-o


더 많은 아이디어:

  • 이것은 문제의 일부를 해결할 수 있습니다. 그런데, 어떤 예도 생각할 수 없습니다.

    명령 A|명령 B|CommandC;
    와는 다르다
    명령 A|명령 B|CommandC
    그룹화가 차이를 만드는 곳이 바로 여기입니다. 나는 명백한 것을 간과하고 있기를 바라고 누군가가 나에게 예를 가르쳐주기를 기대하고 있습니다. 비슷하게,
    {명령 A|명령 B;CommandC
    동등합니다.

  • 다음과 같은 파이프

    명령 A|명령 B|CommandC
    유용할 수도 있습니다. 간단한 예를 들어보세요:

    $ echo GET | tr BEG VAC | od -cb
    0000000   C   A   T  \n
            103 101 124 012
    0000004
    
  • 이런 파이프

    명령 A|명령 B|매개변수(옵션)  명령 Y
    유용하다고 생각하시면
    {명령 A|명령 B; } |매개변수(옵션)  명령 Y
    즉, 입력을 제공합니다. 당신이 그렇다고 생각한다면Command A | Command Bxargs
    명령 A  |명령 B|매개변수(옵션)  명령 Y;
    즉, Command B에 입력을 제공 xargs하고  Command A에 입력을 제공하면  위에서 설명한 이유로 작동하지 않습니다. 와 사이에 데이터 흐름을 설정할 Command Y방법이 없습니다  .Command ACommand Y

  • 하지만,

    명령 A  |매개변수(옵션)  -a <(명령 B)  명령 Y
    여러 가지 이유로 문제가 발생합니다. 여기서 작업은  (잠재적으로) 여러 번 실행 xargs됩니다 . Command Y그러나 다중 실행을 위한 메커니즘이 없으므로 Command A입력을 여러 번 제공할 수 없습니다.Command Y

    정말로 여러 번 실행하고 싶다면 이렇게 할 수 있습니다Command A | Command Y

    명령 B|매개변수(옵션)  쉬-씨'명령 A|명령 Y'다양하다
    예를 들어,

    $ echo S F u o n b | xargs -n2 sh -c 'date | tr "$1" "$2"; sleep 2' sh
    Fun, Mar 27, 2022 11:11:07 AM
    Son, Mar 27, 2022 11:11:09 AM
    Sub, Mar 27, 2022 11:11:11 AM
    

    이것은 실행됩니다

    date | tr S F
    date | tr u o
    date | tr n b
    

    date초가 다르기 때문에( 07, ,  09 )  11실제로는 3번 실행되는 것을 알 수 있습니다.

    이것은 귀하의 경우에 작동합니다. (위의 예에서는 Command Y 실제로 원하는/필요한 것과 마찬가지로 두 매개변수에서 동시에 매개변수를 가져옵니다.) 다음과 같은 출력을 생성하는 경우(예: ) 다음을 수행할 수 있습니다.Command BwriteCommand Bname1 tty1 name2 tty2 name3 tty3 …fred tty17 wilma tty42 barney ttyS23

    명령 Bxargs-n2 |(어쩌면 다른 옵션이 있을 수도 있음)  sh -c '에코"몇 가지 소식들" | 쓰기 "$1" "$2"' sh

  • Command A그러나 (귀하의 경우) 여러 번 실행할 필요가 없다면 echo "some message"(매번 동일한 출력이 생성되기 때문에) 이 사실을 활용하고 Command A한 번 실행하여 출력을 저장할 수 있습니다.

    에코"몇 가지 소식들>tmp 파일;
            명령 Bxargs-n2 |(어쩌면 다른 옵션이 있을 수도 있음)  sh -c '"$1" "$2" 쓰기 < tmpfile' sh;
            rm 임시 파일

  • 따라서 위의 솔루션은 Command A다중 실행을 방지하지만 여전히 사용자당 한 번씩 셸을 실행합니다. 이는 바람직하지 않습니다. xargs쉘을 여러 번 시작하지 않고는 이 상황을 처리할 수 있는 방법이 없습니다 . (누군가가 지적해주길 기대한다.) 쉘을 사용하지 않고는 처리하기 어렵다.그래도,xargs"간단한" 명령만 처리할 수 있기 때문에 리디렉션( <)이나 파이프( |)는 없습니다.  하지만,이를 처리하는 대신 xargs쉘을 보다 창의적으로 사용할 수 있습니다. 각 라인이 Command B한 쌍의 출력을 생성한다고 가정합니다(예:namen ttynfred tty17 (새 팀) wilma tty42 (새 팀) barney ttyS23 (새 팀) ) 그러면 할 수 있습니다

    에코"몇 가지 소식들>tmp 파일;
                  명령 B|ut를 읽을 때 "$u" "$t" < tmpfile;
                  rm 임시 파일


질문의 몇 가지 핵심 사항을 자세히 살펴보고 해결하세요.

  • 존재하다

    에코"몇 가지 소식들" | { 에코"사용자 텔레타이피스트" | xargs -o 쓰기; }
    쉘은 실제로 그 부분을 무시하지 않습니다.echo "some message"그 자체.  그러나 답변의 첫 번째 부분에서 설명했듯이 해당 부분에 효과적으로 연결됩니다. 따라서 표준 입력을 읽지 않습니다.echo "USER TTY"echomessage진행되지 않았다.

  • { echo "USER TTY" | xargs -o write; }그리고 동일한 작업을 수행한다는 점에 유의하세요 write USER TTY (분명히? 여기에는 제가 인식하지 못하는 차이점이 있을 것으로 생각됩니다).

    좋습니다. 아직 명확하지 않은 경우를 대비해 두 변형 모두 write인수를 사용하여 프로그램을 호출합니다.USER그리고 TTY. 표준 I/O 스트림을 수정하지 않고 단순히 기존 stdin에서 읽는 간단한 명령입니다. 이는 tty, 파이프 또는 파일일 수 있습니다. 이 변종은 stdin을 리디렉션하는 역할을 하기 때문에 stdin을 리디렉션합니다. 일반적으로(지정하지 않은 경우) stdin은 실행될 명령의 인수 소스이므로 더 큰 환경에서는 널리 사용되는 stdin에 액세스할 수 없습니다. write USER TTYxargswrite/dev/tty-o-axargsxargs정보for  write) - 그래서 만들 수 없습니다저것실행자가 사용할 수 있는 데이터 스트림입니다. 따라서 -aor 를  -o지정  하지 않으면 xargs실행기의 표준 입력이 로 설정됩니다  /dev/null.

  • 알아채다

    명령 B| xargs -n2 -o 쓰기
    약간 작동하지만 입력을 입력해야 합니다.정보write반복 사이에 메시지를 저장할 방법이 없이 각 사용자에 대해 한 번 호출되므로 각 사용자 에 대해 한 번 호출됩니다. 너 할 수 있다이론적으로는 여러 사용자에게 서로 다른 메시지를 보내려는 경우 이 옵션을 사용할 수 있지만 어떤 사용자와 대화하고 있는지에 대한 힌트나 피드백을 제공하지 않으므로 실용적이지 않습니다.

답변2

첫 번째 포인트

  • echo "<command arguments>" | command매개변수를 입력으로 에코하고 있기 때문에 이를 사용할 수 없습니다 . 명령 인수의 입력을 사용하려면 xargs를 "변환기"로 사용하여 입력을 명령줄 인수로 변환해야 합니다.

xargs쉘(예: bash)의 일부가 아닌 외부 바이너리입니다. xargs에는 여러 버전이 있지만 -o이 플래그가 있는 버전은 GNU 사용자 공간에서 찾을 수 있습니다. 일반적으로 Linux에서는 man <command name>. 에서웹 버전매뉴얼:

이 매뉴얼 페이지에는 xargs의 GNU 버전이 문서화되어 있습니다. xargs는 표준 입력에서 다음으로 구분된 항목을 읽습니다.공백(큰따옴표, 작은따옴표 또는 백슬래시로 보호할 수 있습니다.) 또는 개행 문자, 초기 인수를 사용하여 명령(기본값은 echo)을 한 번 이상 실행하고 표준 입력에서 읽은 항목을 실행합니다.

xargs가 하는 일은 입력을 테이블의 행과 같은 필드로 나누는 것입니다. 각 필드에 대해 xargs(기본적으로)는 시스템 정의 한계까지 인수를 프로그램에 전달합니다. 공백과 개행 문자를 구분 기호(예: |테이블)로 처리합니다. 입력 "USER TTY"에는 USER와 TTY 사이에 공백이 있으므로 xargs는 아래와 같은 테이블 행과 같은 것을 생성합니다.

ARG1 ARG2
사용자 텔레타이피스트

두 번째 포인트

  • 명령을 실행할 때마다 파일을 파이프하려면 아래와 같이 xargs를 사용하여 파일을 실행 명령으로 파이프하십시오. 그러나 이렇게 하면 새 셸이 열리므로 현재 세션의 환경 변수를 사용할 수 없습니다.
echo "USER TTY" | xargs -I REPL sh -c 'echo "some message" | write REPL'

분석: -I플래그는 REPL명령이 실행될 때 필드로 대체되는 매개변수를 사용합니다.

사용자 텔레타이피스트

그래서 xargs가 실행될 것입니다

sh -c 'echo "some message" | write USER TTY'

관련 정보