Bash 스크립트의 파이프를 포함하여 전체 명령줄에 액세스할 수 있습니까?

Bash 스크립트의 파이프를 포함하여 전체 명령줄에 액세스할 수 있습니까?

예를 들어 명령줄은 다음과 같습니다.

test.sh arg1 | grep "xyz"

bash 스크립트 test.sh에서 다음 grep을 포함한 전체 명령줄을 얻을 수 있습니까?

답변1

아니요

bash(또는 쉘)는 두 가지 다른 명령을 분기합니다.

  1. test.sh arg1
  2. grep "xyz"

test.shgrep을 따르는 방법을 모릅니다.

그러나 테스트를 통해 파이프라인 "내부"에 있음을 알 수 있습니다./proc/self/fd/1

테스트 파일

#!/bin/bash

file /proc/self/fd/1

작동 모드는 다음과 같습니다.

> ./test.sh
/proc/self/fd/1: symbolic link to /dev/pts/0
> ./test.sh | cat
/proc/self/fd/1: broken symbolic link to pipe:[25544239]

(편집자) 참조Mulu의 코멘트당신이 파이프라인에 있는지 아는 것에 대해.

당신이 그런 곤경에 처해 있는지 알 필요는 없습니다. 출력이 TTY인지 확인하십시오.[ -t 1 ] https://unix.stackexchange.com/a/401938/70524

답변2

이렇게 할 방법이 없습니다일반적으로 말하면.

그러나 대화형 bash쉘은 히스토리 메커니즘과 트랩을 활용하여 DEBUG환경 변수를 통해 실행하도록 전체 명령줄을 "알려줄" 수 있습니다.

$ trap 'export LC=$(fc -nl -0); LC=${LC#? }' DEBUG
$ sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true
last_command={sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true}

답변3

를 사용하면 /proc/self/fd파이프라인에 있는지 여부와 파이프라인의 ID를 확인할 수 있습니다. 일치하는 파이프를 반복하면 /proc/\*/fd파이프 반대쪽 끝의 PID를 찾을 수 있습니다. PID를 사용하면 /proc/$PID/cmdline파일 설명자에서 프로세스를 읽고 반복하여 파이프 대상을 찾습니다.

$ cat | cat | cat &
$ ps
  PID TTY          TIME CMD
 6942 pts/16   00:00:00 cat
 6943 pts/16   00:00:00 cat
 6944 pts/16   00:00:00 cat
 7201 pts/16   00:00:00 ps
20925 pts/16   00:00:00 bash
$ ls -l /proc/6942/fd
lrwx------. 1 tim tim 64 Jul 24 19:59 0 -> /dev/pts/16
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581130]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6943/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581130]'
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6944/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 1 -> /dev/pts/16
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16

또한 운이 좋다면 파이프라인의 다양한 명령이 연속적인 PID를 얻게 되므로 작업이 좀 더 쉬워질 것입니다.

실제로 이 작업을 수행할 스크립트는 없지만 개념을 시연했습니다.

답변4

또 다른 접근 방식은 자동 변수에 액세스하는 것일 수 있지만 $BASH_COMMAND이는 본질적으로 불안정하고 원하는 값을 캡처하기 어렵습니다.

eval제 생각 에는 다음과 같은 특별한 방법으로 명령줄을 호출하는 것을 통해서만 이를 잡을 수 있다고 생각합니다 .

CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'

여기서는 $BASH_COMMAND확장되어 eval문자열의 비트도 지워지므로 결과 문자열이 보조 $CMD변수에 "스냅샷"됩니다.

작은 예:

$ cat test.sh
#!/bin/sh

printf 'you are running %s\n' "$CMD"
sleep 1
echo bye bye
$
$ CMD="${BASH_COMMAND##* eval }" eval './test.sh | { grep -nH "."; }'
(standard input):1:you are running './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$

물론 sh -c다음과 같은 방법으로 스크립트를 호출할 때도 작동합니다(실제로는 더 좋습니다) bash -c.

$
$ CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):1:you are running CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$

여기서는 지워진 변수가 없습니다.

관련 정보