다음 명령을 실행할 때:
#!/bin/bash
while IFS= read -r -d '' file; do
files+=$file
done < <(find -type f -name '*.c' -print0)
echo "${files[@]}"
나는 다음과 같은 결과를 얻지 못합니다.
#!/bin/bash
find_args="-type f '*.c' -print0"
while IFS= read -r -d '' file; do
files+=$file
done < <(find $find_args)
echo "${files[@]}"
두 번째 장면을 첫 번째 장면과 동일하게 수정하려면 어떻게 해야 합니까?
내 이해는 큰따옴표 안에 작은따옴표가 있기 때문에 작은따옴표가 이스케이프되어 다음과 같은 잘못된 확장이 생성된다는 것입니다.
find -type f -name ''\''*.c'\'' -print0
답변1
블레어 총리의 답변정확하지만 여기서 실제로 무슨 일이 일어나고 있는지 분석하려면 (누락된 master 데이터베이스의 철자 오류를 무시하고 -name
):
#!/bin/bash
while IFS= read -r -d '' file; do
files+=$file
done < <(find -type f -name '*.c' -print0)
echo "${files[@]}"
프로세스 대체( )를 통해 시작된 쉘에서 <(...)
bash는 다음 명령을 구문 분석합니다.
find -type f -name '*.c' -print0
glob이 *.c
참조되기 때문에 bash는아니요확장하세요. 그러나 작은따옴표는 제거됩니다. 따라서 find
프로세스가 시작되면 표시되는 매개변수 목록은 다음과 같습니다.
-type
f
-name
*.c
-print0
이러한 매개변수는 구분 기호로 구분됩니다.널 바이트, 공백이나 줄바꿈 없이. 이것은 쉘 수준이 아닌 C 수준에 있습니다. 이는 C 언어를 사용하여 execve()
프로그램을 실행하는 방식과 관련이 있습니다 .
이제 이를 다음 코드 조각과 비교해 보세요.
#!/bin/bash
find_args="-type f -name '*.c' -print0"
while IFS= read -r -d '' file; do
files+=$file
done < <(find $find_args)
echo "${files[@]}"
변수 값은 find_args
다음과 같이 설정됩니다.
-type f -name '*.c' -print0
(큰따옴표는 값의 일부가 아니지만 작은따옴표 문자는예.)
명령이 find $find_args
실행 되면 man bash
토큰은 다음에 따라 $find_args
매개변수 확장이 적용됩니다 .이어서분사이어서경로명 확장(일명 전역 확장).
매개변수 확장 후에는 -type f -name '*.c' -print0
.뒤쪽에참조가 삭제되었습니다. 따라서 작은따옴표는 제거되지 않습니다.
단어 분할 후에는 다음과 같은 개별 단어를 얻게 됩니다.
-type
f
-name
'*.c'
-print0
그 다음에경로명 확장. 물론 '*.c'
일반적으로 파일 이름에 작은따옴표를 넣지 않기 때문에 어떤 것과도 일치할 가능성이 없으므로 결과는 다음과 같습니다.가능한'*.c'
리터럴 모드로 전달 되므로 find
기본 -name
데이터베이스는 모든 파일에서 실패합니다. (이름이 작은따옴표로 시작하고 세 글자로 끝나는 파일이 있는 경우에만 성공합니다 .c'
.)
편집: 실제로 그러한 파일이 존재하는 경우 glob은 '*.c'
해당 파일 및 기타 그러한 파일과 일치하도록 확장됩니다.확장[실제 파일 이름] find
은무늬. 따라서 -print0
마스터 노드에 도달할지 여부는 (a)하나그러한 파일 이름 및 (b) 해당 파일 이름(glob으로 해석됨)이 일치하는지 여부.
예:
touch "'something.c'"
그때 달리면전반적인 상황 '*.c'
확장되면 'something.c'
기본 find
파일 -name 'something.c'
도 일치하여 인쇄됩니다.
실행하면 touch "'namewithcharset[a].c'"
glob은 '*.c'
쉘에 의해 glob으로 확장되지만 find
기본은-name 'namewithcharset[a].c'
아니요일치 자체 - 일치만 하고 'namewithcharseta.c'
존재하지 않으므로 -print0
도달할 수 없습니다.
을 실행하면 touch "'x.c'" "'y.c'"
glob이 '*.c'
다음으로 확장됩니다.둘 다find
filename은 유효한 기본 파일 이름이 아니기 때문에 출력 오류를 일으킬 수 있습니다 'y.c'
(그리고 하이픈으로 시작하지 않기 때문에 그럴 수 없습니다).
nullglob
이 옵션을 설정 하면 다른 동작이 발생합니다.
또한보십시오:
답변2
-name
(철자 오류가 있습니다. 두 번째 예에서는 플래그를 생략했습니다.)
한 가지 방법은 매개변수를 배열에 넣고 해당 배열을 적절하게 전달하는 것입니다 find
.
#!/bin/bash
find_args=(-type f -name '*.c' -print0)
while IFS= read -r -d '' file; do
files+=$file
done < <(find "${find_args[@]}")
echo "${files[@]}"
형식은 ${foo[@]}
배열의 모든 요소로 확장되며, 각 요소는 단일 문자열로 확장되지 않고 별도의 단어입니다. 이는 원본 스크립트의 의도에 더 가깝습니다.
답변3
말한 것 외에도 다음이 필요합니다.
- 변수는
$files
기본적으로 스칼라이므로 배열로 선언하고var+=something
스칼라에 대해 문자열 연결(또는 스칼라가 할당된 경우 산술 덧셈)을 수행합니다.정수속성). 또는var+=(something)
구문을 사용하십시오(변수를 자동으로 배열로 변환합니다). - 변수를 초기화합니다(설정되지 않거나 빈 목록). 그렇지 않으면 환경에서 초기 값을 상속받을 수 있습니다.
행위:
files=()
while ...
files+=$file # or files+=("$file")
done
files
변수가 이전에 스크립트에서 연관 배열로 선언되지 않은 경우(이 경우 오류가 files+=something
발생함 files["0"]+=something
) files+=("$files")
이것으로 충분합니다 .
files
스크립트가 이전에 연관 배열로 정의되지 않았음 을 보장할 수 없는 경우 다음을 수행해야 할 수 있습니다.
typeset -a files=()
대신에 변수의 범위를 둘러싸는 함수로 제한하는 부작용이 있습니다. 전역 범위에서 변수를 선언하기 typeset -ga files=()
때문에 문제에 대한 해결 방법으로 제대로 작동하지 않습니다 . 어떤 경우에는 변수를 설정 해제하는 대신 외부 범위(연관 배열)에서 변수를 표시하는 것이 가능하기 때문에 작동하지 않을 수도 있습니다 .bash
unset files; files=()
unset files
files