$! > >(...) 외부 명령과 함께 사용하기 위한 프로세스 대체에 대해 PID가 설정되지 않았습니다.

$! > >(...) 외부 명령과 함께 사용하기 위한 프로세스 대체에 대해 PID가 설정되지 않았습니다.

Bash 4.4.19(1) - 출시됨

아래에는 로깅 애플리케이션의 기초가 되는 간단한 스크립트가 있습니다. 여러 가지 이유로 프로세스 대체를 사용해야 했습니다.

이것이 runner애플리케이션의 핵심이며, 프로세스 교체가 비동기식이므로 루프를 사용하여 일관성을 유지하는 데 성공했습니다 while. 완벽하게 작동합니다.

bash <filename> <function>불행히도 작동하지 않는 상황을 발견했습니다. ' '를 실행할 때

따라서 재생산하려면 2개의 파일이 필요합니다.

필요하다:

  1. 왜 이런 일이 발생합니까?
  2. 유사한 상황을 수용하기 위해 while 루프를 어떻게 수정할 수 있습니까?

단순화된 스크립트는 다음과 같습니다.

test.sh

#!/bin/bash

2sub() {
local in=$(cat); echo -e "$in";
}   
runner () {
 "${@}" 1> >(2sub)
 while [ -e /proc/$! ]; do sleep 0.1; done     # <<< LOOP WAIT FOR $!
}
remotesub() {
 bash ./test2.sh remotesub2
}

echo -e "running\n"; 
    runner bash ./test2.sh remotesub2 # LOOPS
    # runner remotesub # A POSSIBLE BYPASS/SOLUTION? But why?
echo -e "done!\n"

test2.sh

     remotesub2() {
         echo -e "'${BASH_VERSION}'"
         return 0
     }

     "$@"

우회로:

bash <filename> <function>스크립트에서 볼 수 있듯이 문제를 함수 안에 래핑하여 문제를 우회할 수 있습니다.전달 함수to runner. 이것이 직접적인 방법 대신에 작동하는 이유는 여기 누군가가 알고 있다고 확신합니다.

이 문제를 해결하고 이러한 경우를 처리하기 위해 대기 루프를 수행하는 더 좋은 방법이 있는지 알려주세요.

해결책:

가장 좋은 솔루션은 mosvy가 제안한 솔루션입니다. 감사해요. 를 사용하면 { "${@}"; }명령을 별도의 작은 기능으로 래핑할 필요가 없어져 고통스러울 수 있습니다. 또한 더 큰 코드로 여러 시간 동안 테스트한 후 하위 프로세스를 신중하게 종료하면 이 작업이 불필요하다는 결론을 내렸습니다 while [ -e /proc/$! ]; do sleep 0.1; done. 라인은 다음으로 대체됩니다.wait $!;

답변1

제가 정확히 이해한다면, 왜 $!내부에서 실행되는 프로세스의 PID가 내장 명령이나 함수의 명령줄의 일부일 때만 설정되고 >(...), 내장 명령줄의 일부일 때는 설정되지 않는지 궁금하실 것입니다. 명령 또는 기능의 일부는 외부 명령입니다.

단순화된 예:

$ bash -c 'true > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
psubst=12392
in=12392

$ bash -c '/bin/true > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
in=12751
psubst=

이는 외부 명령을 사용하는 경우 bash이를 실행하기 위해 별도의 프로세스가 분기되고 해당 프로세스에서 실행 중인 프로세스가 >(...)해당 프로세스의 하위로 실행되기 때문에 발생합니다.멋진완전히 통제할 수 없는 스크립트의 하위 항목입니다.

외부 명령이 종료되면 해당 하위 명령(아직 실행 중인 경우)이 pid 1(init)에 의해 채택되므로 스크립트에서 해당 PID를 검색하는 데 여전히 사용할 수 있는 모든 링크가 끊어집니다.

해결 방법은 명령줄의 모든 프로세스 교체가 스크립트의 하위 프로세스로 실행되어 를 통해 전달되도록 하는 래퍼 함수를 ​​사용하는 것입니다 pgrep -P "$$".

또한 외부 명령을 {...}블록에 넣고 블록의 출력을 리디렉션하는 것이 작동하는 것 같습니다.

$ bash -c 'func(){ /bin/true; }; func > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
in=3574
psubst=3574
$  bash -c '{ /bin/true; } > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
in=3435
psubst=3435

두 해결 방법 모두 현재 구현의 작동 방식에 따라 다릅니다. bash언젠가는 사소한 그룹 명령이나 기능을 최적화하고 이러한 가정을 깨기로 결정할 수도 있습니다.

PID를 마지막 프로세스 교체로 설정하는 것은 $!NET에서 사용할 수 없는 문서화되지 않은 기능입니다 bash.

관련 정보