파일 검색을 사용 find
하고 해당 파일을 Bash 배열에 넣어 다른 작업 (예: ls
또는 해당 파일)을 수행하려고 합니다. 그러나 파이프 입력을 통한 출력이 읽히지 않는 grep
이유를 이해할 수 없습니다 .readarray
find
현재 디렉토리에 두 개의 파일이 있고 출력은 다음과 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)
이건 뭐랑 비슷한데?그렉의 배쉬 FAQ 020자세한 설명과이 답변은 다음과 같습니다.
공백이나 개행 문자가 포함된 이상한 이름의 파일(NUL을 포함하지 않는 이름)에는 문제가 없습니다. 결과는 배열로 설정되며 이는 추가 처리에 유용합니다.
답변3
readarray
표준 입력에서도 읽을 수 있습니다.
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
비대화형 쉘에서만 작동한다는 점도 알아두세요.
파이프 교체(프로세스 교체)의 또 다른 일반적인 < <(...)
문제는 자식 프로세스의 성공 여부를 확인하기 어렵다는 것입니다.