파이프 라인을 사용하는 동안 명령을 읽어야하는 이유는 무엇입니까?
이 명령 df .
은 우리가 어떤 장치에 있는지 보여줄 수 있습니다. 예를 들면
me@ubuntu1804:~$ df .
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdb1 61664044 8510340 49991644 15% /home
이제 문자열을 얻고 싶습니다 /dev/sdb1
.
나는 이렇게 시도했지만 작동하지 않았다 : df . | read a; read a b; echo "$a"
이 명령은 나에게 빈 출력을 주었다. 그러나 df . | (read a; read a b; echo "$a")
예상대로 작동합니다.
지금은 좀 혼란 스러워요.
나는 그것이 (read a; read a b; echo "$a")
서브 쉘 이라는 것을 알고 있지만, 왜 여기서 서브 쉘을 만들어야하는지 모르겠습니다. 내 이해대로 x|y
의 출력 x
을의 입력으로 리디렉션합니다 y
. read a; read a b; echo $a
입력을받을 수 없지만 서브 쉘은받을 수있는 이유 는 무엇 입니까?
답변
여기서 주요 문제는 명령을 올바르게 그룹화하는 것입니다. 서브 쉘은 부차적 인 문제입니다.
x|y
의 출력을x
입력으로 리디렉션합니다.y
예,하지만 x | y; z
의 출력을 및 x
둘 다로 리디렉션하지는 않습니다 .y
z
에서 df . | read a; read a b; echo "$a"
, 파이프 라인 만 연결 df .
하고 read a
, 다른 명령은 파이프 라인에 연결되지. 당신은 그룹에 있습니다 read
: S 함께 df . | { read a; read a b; }
또는 df . | (read a; read a b)
파이프 라인은 둘 다에 연결하기.
그러나 이제 하위 셸 문제가 발생합니다. 파이프 라인의 명령은 하위 셸에서 실행되므로 변수를 설정해도 상위 셸에 영향을주지 않습니다. 따라서 echo
명령은 read
s 와 동일한 서브 쉘에 있어야 합니다. 그래서 : df . | { read a; read a b; echo "$a"; }
.
이제 파이프 라인의 명령이 어쨌든 하위 셸에서 실행되기 때문에 여기에서 사용 ( ... )
하거나 { ...; }
특별한 차이가 없습니다.
대안은 프로세스 대체를 사용하는 것입니다 .
{ read header; read filesystem rest; } < <(df .)
echo "$filesystem"
<(...)
프로세스 치환은 (서브 쉘)에 포함 된 스크립트를 실행하지만 제 필요 그래서, 파일명 같은 역할 <
브레이스에 스크립트 (스크립트의 출력 인) 컨텐츠를 리디렉션. 그룹화 된 명령은 현재 shel;에서 실행됩니다.
이것을 읽기가 까다로울 수 있지만 임의의 공백을 중괄호와 프로세스 대체에 넣을 수 있습니다.
{
read header
read filesystem rest
} < <(
df .
)
echo "$filesystem"
그리고 외부 도구를 사용하여 파일 시스템을 추출하는 것이 더 쉬울 수 있습니다.
filesystem=$( df . | awk 'NR == 2 {print $1}' )
첫 번째 명령
df . | read a; read a b; echo "$a"
효과적으로 해석됩니다
( df . | read a ) ; read a b; echo "$a"
따라서 파이프 라인은 read a
명령 에만 공급됩니다 .
파이프 라인에서 여러 읽기를 원하므로 명령을 함께 그룹화해야합니다.
이제는 서브 쉘일 필요가 없습니다. 그룹 화일 수 있습니다 ..
bash-4.2$ df | { read a ; read a b ; echo $a ; }
devtmpfs
더 일반적으로 루프를 원할 수 있습니다.
bash-4.2$ df | while read a > do > read a b > echo $a
> done
devtmpfs
tmpfs
/dev/vda3
/dev/vdb
두 번째 문제가 bash
있고 파이프 라인의 오른쪽이 서브 쉘을 실행하고 있으므로 $a
$b
값은 while
루프 외부에서 액세스 할 수 없지만 다른 문제입니다!