Bash: 'find' 출력을 'readarray'로 파이프합니다.

Bash: 'find' 출력을 'readarray'로 파이프합니다.

나는 을 사용하여 파일을 검색 find하고 해당 파일을 Bash 배열에 넣어 다른 작업을 수행할 수 있도록 하려고 합니다 . 하지만 출력이 파이프로 연결될 때 왜 출력을 읽지 않는지 알 수 없습니다 .lsgrepreadarrayfind

현재 디렉토리에 두 개의 파일 이 있고 file1.txt. file2.txt따라서 find출력은 다음과 같습니다.

$ find . -name "file*"
./file1.txt
./file2.txt

그래서 나는 그것을 두 요소가 문자열 "./file1.txt""./file2.txt"(물론 따옴표 없이)인 배열로 파이프하고 싶습니다.

나는 몇 가지 다른 것 중에서 이것을 시도했습니다.

$ declare -a FILES
$ find . -name "file*" | readarray FILES
$ echo "${FILES[@]}"; echo "${#FILES[@]}"

0

출력 에서 볼 수 있듯이 echo내 배열은 비어 있습니다.

그렇다면 내가 여기서 정확히 무엇을 잘못하고 있는 걸까요? 왜 의 출력을 표준 입력으로 readarray읽고 find해당 문자열을 배열에 넣지 않습니까?

답변1

파이프라인을 사용할 때 bash는 서브셸¹에서 명령을 실행합니다. 따라서 배열은 채워지지만 하위 쉘에 있으므로 상위 쉘에서는 이에 액세스할 수 없습니다. 또한 -t파일 이름의 일부가 아니기 때문에 해당 줄 구분 기호를 배열 구성원에 저장하지 않는 옵션을 원할 수도 있습니다 .

프로세스 대체 사용:

readarray -t FILES < <(find .)

경로에 개행 문자가 있는 파일에서는 작동하지 않습니다. 그렇지 않다는 것을 보장할 수 없다면 개행으로 구분된 레코드 대신 NUL로 구분된 레코드를 사용하는 것이 좋습니다.

readarray -td '' < <(find . -print0)

(이 -d옵션은 bash 4.4에 추가되었습니다)


¹ 옵션을 사용할 때 마지막 파이프 구성 요소는 제외 lastpipe하지만 이는 의 비대화형 호출에만 해당됩니다 bash.

답변2

올바른 해결책은 다음과 같습니다.

unset a; declare -a a
while IFS= read -r -u3 -d $'\0' file; do
    a+=( "$file" )        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

그거랑 비슷한데그렉의 BashFAQ 020자세하게 설명하고이 답변은 다음과 같습니다.

공백이나 새 줄이 있는 이상한 이름의 파일(이름에 NUL이 포함되지 않음)에는 문제가 없습니다. 그리고 결과는 배열로 설정되므로 추가 처리에 유용합니다.

답변3

readarraystdin에서도 읽을 수 있습니다

readarray FILES <<< "$(find . -name "file*")"; echo "${#FILES[@]}"

답변4

shopt -s lastpipe원래 질문에 표시된 솔루션을 사용 하면 마지막 부분 이후의 부분이 |대부분의 스크립트와 동일한 프로세스에서 실행됩니다.

shopt -s lastpipe

# This is just the code form the original question.
# Of course it could be done with -print0, etc.
declare -a FILES
find . -name "name*" | readarray -t FILES
echo find and readarray exit status: "${PIPESTATUS[@]}"
echo "${FILES[@]}"
echo "${#FILES[@]}"

lastpipe그러나 이는 프로젝트 전체 표준으로 만들 수 있는 경우에만 실용적입니다 (아마도 set -u등에 대한 공통 설정이 이미 있을 수 있습니다). 또한 lastpipe비대화형 쉘에서만 작동한다는 것도 알아두세요.

또 다른 일반적인 파이프 교체 < <(...)(프로세스 대체)는 자식 프로세스가 성공했는지 확인하기가 상당히 어렵다는 문제가 있습니다.

관련 정보