code

"읽기"에 대한 파이프 입력이 "읽는 동안 ..."구성에 공급 될 때만 작동하는 이유는 무엇입니까?

codestyles 2020. 11. 5. 08:04
반응형

"읽기"에 대한 파이프 입력이 "읽는 동안 ..."구성에 공급 될 때만 작동하는 이유는 무엇입니까?


이 질문에 이미 답변이 있습니다.

다음과 같이 프로그램 출력에서 ​​환경 변수에 대한 입력을 읽으려고했습니다.

echo first second | read A B ; echo $A-$B 

결과는 다음과 같습니다.

-

A와 B는 항상 비어 있습니다. 나는 bash가 하위 셸에서 파이프 명령을 실행하고 기본적으로 입력을 읽을 수 없도록하는 것을 읽었습니다. 그러나 다음은 다음과 같습니다.

echo first second | while read A B ; do echo $A-$B ; done

작동하는 것처럼 보이며 결과는 다음과 같습니다.

first-second

누군가 여기에 논리가 무엇인지 설명해 주시겠습니까? while... done구조 내부의 명령 이 실제로 echo하위 쉘이 아닌 동일한 쉘에서 실행 됩니까?


stdin 에 대해 루프를 수행 하고 변수에 결과를 저장하는 방법

아래 (그리고 다른 도), 사용하여 당신 파이프 뭔가 |다른 명령에, 당신은 묵시적으로 만듭니다 포크 , 현재 세션의 자식이고 현재 세션의와 환경에 영향을 미칠 수없는 서브 쉘을.

그래서 이건:

TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
  while read A B;do
      ((TOTAL+=A-B))
      printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
    done
echo final total: $TOTAL

예상 한 결과를 얻지 못할 것입니다! :

  9 -   4 =    5 -> TOTAL=    5
  3 -   1 =    2 -> TOTAL=    7
 77 -   2 =   75 -> TOTAL=   82
 25 -  12 =   13 -> TOTAL=   95
226 - 664 = -438 -> TOTAL= -343
echo final total: $TOTAL
final total: 0

계산 된 TOTAL 은 메인 스크립트에서 재사용 할 수 없습니다.

포크 반전

사용하여 프로세스 대체를 , 여기에 문서 또는 여기에 문자열을 , 당신은 포크를 반전 할 수 :

여기 문자열

read A B <<<"first second"
echo $A
first

echo $B
second

여기 문서

while read A B;do
    echo $A-$B
    C=$A-$B
  done << eodoc
first second
third fourth
eodoc
first-second
third-fourth

루프 외부 :

echo : $C
: third-fourth

Here 명령

TOTAL=0
while read A B;do
    ((TOTAL+=A-B))
    printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
  done < <(
    printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664
)
  9 -   4 =    5 -> TOTAL=    5
  3 -   1 =    2 -> TOTAL=    7
 77 -   2 =   75 -> TOTAL=   82
 25 -  12 =   13 -> TOTAL=   95
226 - 664 = -438 -> TOTAL= -343

# and finally out of loop:
echo $TOTAL
-343

이제 기본 스크립트$TOTAL 에서 사용할 수 있습니다 .

명령 목록에 배관

But for working only against stdin, you may create a kind of script into the fork:

printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 | {
    TOTAL=0
    while read A B;do
        ((TOTAL+=A-B))
        printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
    done
    echo "Out of the loop total:" $TOTAL
  }

Will give:

  9 -   4 =    5 -> TOTAL=    5
  3 -   1 =    2 -> TOTAL=    7
 77 -   2 =   75 -> TOTAL=   82
 25 -  12 =   13 -> TOTAL=   95
226 - 664 = -438 -> TOTAL= -343
Out of the loop total: -343

Note: $TOTAL could not be used in main script (after last right curly bracket } ).

Using lastpipe bash option

As @CharlesDuffy correctly pointed out, there is a bash option used to change this behaviour. But for this, we have to first disable job control:

shopt -s lastpipe           # Set *lastpipe* option
set +m                      # Disabling job control
TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
  while read A B;do
      ((TOTAL+=A-B))
      printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
    done

  9 -   4 =    5 -> TOTAL= -338
  3 -   1 =    2 -> TOTAL= -336
 77 -   2 =   75 -> TOTAL= -261
 25 -  12 =   13 -> TOTAL= -248
226 - 664 = -438 -> TOTAL= -686

echo final total: $TOTAL
-343

This will work, but I (personally) don't like this because this is not standard and won't help to make script readable. Also disabling job control seem expensive for accessing this behaviour.

Note: Job control is enabled by default only in interactive sessions. So set +m is not required in normal scripts.

So forgotten set +m in a script would create different behaviours if run in a console or if run in a script. This will not going to make this easy to understand or to debug...


First, this pipe-chain is executed:

echo first second | read A B

then

echo $A-$B

Because the read A B is executed in a subshell, A and B are lost. If you do this:

echo first second | (read A B ; echo $A-$B)

then both read A B and echo $A-$B are executed in the same subshell (see manpage of bash, search for (list)


a much cleaner work-around...

read -r a b < <(echo "$first $second")
echo "$a $b"

This way, read isn't executed in a sub-shell (which would clear the variables as soon as that sub-shell has ended). Instead, the variables you want to use are echoed in a sub-shell that automatically inherits the variables from the parent shell.


What you are seeing is the separation between processes: the read occurs in a subshell - a separate process which cannot alter the variables in the main process (where echo commands later occur).

A pipeline (like A | B) implicitly places each component in a sub-shell (a separate process), even for built-ins (like read) that usually run in the context of the shell (in the same process).

"동안에 파이프"의 경우의 차이는 환상입니다. 동일한 규칙이 적용됩니다. 루프는 파이프 라인의 후반부이므로 서브 쉘에 있지만 전체 루프는 동일한 서브 쉘에 있으므로 프로세스 분리가 적용되지 않습니다.

참고 URL : https://stackoverflow.com/questions/13763942/why-piping-input-to-read-only-works-when-fed-into-while-read-construct

반응형