bash의“mapfile”을 파이프 할 수는 없지만… 왜 그렇습니까?


13

특정 디렉토리의 모든 파일을 bash 배열로 가져 오려고합니다 (이름에 줄 바꿈이없는 파일이 있다고 가정).

그래서:

myarr=()
find . -maxdepth 1  -name "mysqldump*" | mapfile -t myarr; echo "${myarr[@]}"

빈 결과!

파일을 사용하는 원형 교차로 방법을 사용하는 경우 임시 또는 다른 방법으로 :

myarr=()
find . -maxdepth 1  -name "mysqldump*" > X
mapfile -t myarray < X
echo "${myarray[@]}"

결과!

그런데 왜 mapfile파이프에서 제대로 읽지 않습니까?



모든 주위에 훌륭한 답변, 감사합니다. 파이프 라인 (각 스피어 레이트 프로세스에서 실행되는)의 실행 전략이 어떻게 "위쪽으로"유출되고 코드의 명백한 의미를 수정하여 기본적으로 파이프에 나타나는 모든 변수 앞에 "로컬"을 자동으로 적용합니다. 다른 프로그램에 대한 미친 접착제가 아닌 다른 언어에서는 버그가 될 것입니다.
David Tonhofer

2
당신이 할 수있는 코드를 제공하는 경우 shellcheck을 : 당신은 경고를받을 SC2030을 : "VAR의 수정이 (파이프 라인에 의한 서브 쉘합니다) 로컬"SC2031 : ".. VAR는 서브 쉘에서 수정 된 변화가 손실 될 수 있습니다" . 우수한.
David Tonhofer

사용하는 이유 findmapfile여기 모두에서가 아니라 단지 myarr=(mysqldump*)? 공백과 줄 바꿈이있는 파일 이름으로도 작동합니다.
BlackJack

1
딱 한 차례하는 것으로 나타났습니다 nullglob(에 옵션을 shopt -s nullglob위해에) myarr=(mysqldump*)배열로 끝나지에 ('mysqldump*')어떤 파일이 일치하지 경우.
David Tonhofer

답변:


25

보낸 사람 man 1 bash:

파이프 라인의 각 명령은 별도의 프로세스 (즉, 서브 쉘)로 실행됩니다.

이러한 서브 쉘은 기본 쉘에서 변수를 상속하지만 독립적입니다. 이것은 mapfile원래 명령에서 자체적으로 작동 한다는 것을 의미 합니다 myarr. 그런 다음 echo(파이프 외부에 있음) 빈 myarr(메인 쉘 )을 인쇄합니다 myarr.

이 명령은 다르게 작동합니다.

find . -maxdepth 1 -name "mysqldump*" | { mapfile -t myarr; echo "${myarr[@]}"; }

이 경우 mapfileecho동일한에서 작동 myarr(하지의 주요 쉘이다 myarr).

메인 쉘을 변경하려면 메인 쉘에서 정확하게 myarr실행해야합니다 mapfile. 예:

myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "${myarr[@]}"

방문자가 TL; DR 모멘트를 갖는 경우 Attie의 응답에 제공된 "프로세스 대체"링크를 추가했습니다.
David Tonhofer

11

Bash는 서브 쉘 환경에서 파이프 라인의 명령을 실행하므로 그 안에서 발생하는 변수 할당 등은 쉘의 나머지 부분에 표시되지 않습니다.

zsh와 ksh는 기본 쉘에서 마지막 부분을 실행하는 반면 대시 (데비안 /bin/sh)와 busybox sh도 비슷합니다. Bash shopt -s lastpipe에서도 동일한 작업을 수행 할 수 있지만 작업 제어가 비활성화 된 경우에만 작동하므로 기본적으로 대화식 쉘에서는 작동하지 않습니다.

그래서:

$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b

( read그리고 mapfile같은 문제가 있습니다.)

또는 Attie가 언급 한대로 일반화 된 파이프처럼 작동하며 Bash, ksh 및 zsh에서 지원되는 프로세스 대체를 사용하십시오 .

$ bash -c 'x=a; read x < <(echo b); echo $x'
b

POSIX는 파이프 라인의 일부가 서브 쉘에서 실행되는지 여부를 지정하지 않은 상태로 두므로, 어떤 쉘도 이것에서 "잘못"되었다고 말할 수 없습니다.


2
bash의 작업 제어를 비활성화하면 대화식 쉘에서도 lastpipe를 사용할 수 있습니다.set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
Cyrus

@ Cyrus, 아 맞아, 난 세부 사항을 잊어 버렸습니다, 감사합니다
ilkkachu

9

Kamil이 지적했듯이 파이프 라인의 각 요소는 별도의 프로세스입니다.

다음 프로세스 대체 를 사용 하여 현재 인터프리터에 호출이 남아 find있는 다른 프로세스에서 실행 mapfile하여 myarr나중에 액세스 할 수 있습니다 .

myarr=()
mapfile -t myarr < <( find . -maxdepth 1  -name "mysqldump*" )
echo "${myarr[@]}"

b < <( a )a | b파이프 라인 연결 방식 과 유사하게 작동 합니다. 차이점은 b" 여기 "에서 실행 됩니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.