저는 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 옵션을 지정했으므로 표준 입력이 터미널로 설정됩니다. 당신이 말하고 싶다면 이것은 효과가 있습니다write
some message
-o
xargs
write
echo "USER TTY" | xargs -o write
write
그런 다음 메시지를 입력하면 가장 바깥쪽 메시지에 연결되지 않습니다 echo
.
당신이 좀 가지고 있다면Command B
산출USER
그리고 TTY
(하지만 한 쌍만) 시도해 볼 수 있습니다
echo "어떤 메시지" 쓰기 $(명령 B)
고쳐 쓰다:다음은 가상의 상황에 적용될 수 있습니다.Command B
한 쌍만 출력 USER
그리고 TTY
, 그러나 실제 상황에는 적합하지 않을 수 있습니다. 읽어보시고 아래의 토론을 살펴보세요.
말씀하신 대로 bash를 사용하고 있고 GNUxargs
(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 B
xargs
명령 A |명령 B|매개변수(옵션) 명령 Y;
즉,Command B
에 입력을 제공xargs
하고Command A
에 입력을 제공하면 위에서 설명한 이유로 작동하지 않습니다. 와 사이에 데이터 흐름을 설정할Command Y
방법이 없습니다 .Command A
Command 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 B
write
Command B
name1 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 ttyn
fred tty17
(새 팀)wilma tty42
(새 팀)barney ttyS23
(새 팀) ) 그러면 할 수 있습니다에코"몇 가지 소식들>tmp 파일; 명령 B|ut를 읽을 때 "$u" "$t" < tmpfile; rm 임시 파일
질문의 몇 가지 핵심 사항을 자세히 살펴보고 해결하세요.
존재하다
에코"몇 가지 소식들" | { 에코"사용자 텔레타이피스트" | xargs -o 쓰기; }
쉘은 실제로 그 부분을 무시하지 않습니다.echo "some message"
그 자체. 그러나 답변의 첫 번째 부분에서 설명했듯이 해당 부분에 효과적으로 연결됩니다. 따라서 표준 입력을 읽지 않습니다.echo "USER TTY"
echo
message
진행되지 않았다.-
{ echo "USER TTY" | xargs -o write; }
그리고 동일한 작업을 수행한다는 점에 유의하세요write USER TTY
(분명히? 여기에는 제가 인식하지 못하는 차이점이 있을 것으로 생각됩니다).좋습니다. 아직 명확하지 않은 경우를 대비해 두 변형 모두
write
인수를 사용하여 프로그램을 호출합니다.USER
그리고TTY
. 표준 I/O 스트림을 수정하지 않고 단순히 기존 stdin에서 읽는 간단한 명령입니다. 이는 tty, 파이프 또는 파일일 수 있습니다. 이 변종은 stdin을 리디렉션하는 역할을 하기 때문에 stdin을 리디렉션합니다. 일반적으로(지정하지 않은 경우) stdin은 실행될 명령의 인수 소스이므로 더 큰 환경에서는 널리 사용되는 stdin에 액세스할 수 없습니다.write USER TTY
xargs
write
/dev/tty
-o
-a
xargs
xargs
정보forwrite
) - 그래서 만들 수 없습니다저것실행자가 사용할 수 있는 데이터 스트림입니다. 따라서-a
or 를-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'