쉘스크립트에서 버그를 추적하는 동안 이 코드 조각에서 다음 동작을 발견했습니다.
declare -a filelist
readarray filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
sha256sum ${filelist[$file]} | head -c 64
done
배열이 filelist
큰따옴표로 묶이지 않으면 명령이 성공합니다. 저는 코딩을 개선하기 위해 ShellCheck를 사용해 왔으며 다음과 같은 제안을 받았습니다.
큰따옴표는 와일드카드와 단어 분리기를 방지합니다.
이 경우에는 단어 분할에 대해 걱정하지 않지만 다른 많은 경우에는 걱정하기 때문에 코드 일관성을 유지하려고 노력합니다. 그러나 배열을 큰따옴표로 묶으면 명령이 실패합니다. 코드를 단일 요소로 줄이면 다음과 같은 결과가 나타납니다.
bash-5.0# sha256sum ${filelist[0]} | head -c 64
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
bash-5.0# sha256sum "${filelist[0]}" | head -c 64
sha256sum: can't open 'file1
': No such file or directory
분명히... 큰따옴표를 사용할 수 없습니다. 이 경우 단어 분리는 문제가 되지 않기 때문입니다. 하지만 앞으로도 그럴 수도 있을 것 같아서 포스팅을 하고 싶었습니다.
내 질문은 두 부분으로 구성됩니다.
- 위의 큰따옴표 배열 외에 단어 분할을 방지하는 "모범 사례" 방법이 있습니까?
배열의 작은따옴표는 어디에서 왔나요?편집: 작은따옴표가 없습니다. 작은따옴표는 열 수 없는 파일 이름을 표시하는 오류입니다.
또한 궁금해서 echo ${filelist[0]}
포함되지 않은 추가 개행 문자가 포함되는 이유는 무엇입니까 echo "${filelist[0]}"
?
답변1
참조 배열 확장에는 전혀 문제가 없습니다.
물론, 결과를 알고 받아들인다면 인용하지 않아도 괜찮습니다. 따옴표가 없는 확장자는 분할 및 와일드카드의 영향을 받습니다. 그리고 코드에서 ${filelist[…]}
IFS 문자 제거(문자열에 <space>
, <tab>
또는 가 포함된 경우 분할 <newline>
)를 수행해야 합니다.
이것이 인용되지 않은 확장이 수행하는 작업이며, 후행 <newline>
.
무엇만들다문제는 를 사용할 때 readarray
각 배열 요소에서 후행 구분 기호를 제거하지 않는다는 것입니다. 이렇게 하면 오류 메시지에 반영된 후행이 유지됩니다
.<newline>
사용할 수 있는 것은 다음과 같습니다.
readarray -t filelist < <(ls -A)
이 -t
옵션은 각 파일 이름에서 후행 줄 바꿈을 모두 제거합니다.
-t 읽은 각 줄에서 후행 구분 기호(기본 줄 바꿈)를 제거합니다.
그러나 코드에는 다른 문제가 있습니다.
배열을 선언하거나 지울 필요가 없습니다
filelist
. 이는 기본적으로 readarray에 의해 수행됩니다. 이는 다른 상황에서도 필요합니다.구문 분석된 출력이 필요하지 않습니다
ls
. 사실 이는 나쁜 생각입니다. 배열의 파일 목록을 얻는 가장 간단한 방법은 다음과 같습니다.filelist=( ./* )
그리고 더 나은 결과를 얻으려면 디렉터리를 사용하지 않는 것이 좋습니다.
for file in ./*; do [[ -f $file ]] && filelist+=( "$file" ) done
루프 내에서는
$file
var 값을 사용해야 합니다.for file in "${filelist[@]}"; do sha256sum "$file" | head -c 64 done
for file in "${!filelist[@]}"; do
어떤 목록을 사용하지 않는 한열쇠정렬.전체 목록을 처리하면 됩니다.하나sha256sum에 전화하세요.
sha256sum "${filelist[@]}" | cut -c -64
개선된 스크립트는 다음과 같습니다.
filelist=() # declare filelist as an array and empty it.
for file in ./*; do
if [[ -f $file ]]; then
filelist+=( "$file" )
fi
done
declare -r filelist # declare filelist as readonly.
sha256sum "${filelist[@]}" | cut -c -64
답변2
이 경우에는 분사에 대해 걱정하지 않습니다
글쎄요, 사실 당신은그것에 의지하다배열 항목에서 후행 줄 바꿈을 제거하십시오!
배쉬의 readarray
( mapfile
)구분 기호는 기본적으로 유지됩니다. 매뉴얼 페이지나 명령줄 도움말에는 명확하게 나와 있지 않은 것 같지만 옵션이 있습니다.제거하다구분 기호는 기본적으로 제거되지 않습니다.
-t Remove a trailing delim (default newline) from each line read.
따라서 배열의 실제 문자열은 다음과 같습니다.file1[newline]
따옴표가 없으면 단어 분리는 후행 공백을 제거하여 개행을 수정합니다. 그러나 파일 이름에 공백이 포함되어 있으면 토큰화로 인해 평소처럼 공백이 엉망이 됩니다. 큰따옴표로 묶은 배열은 이를 방지합니다. 첫 번째 질문에 답하려면,모범 사례는 큰따옴표입니다., 여기에는 필요하지 않은 추가 개행 문자가 있습니다.
$@
( 약간 혼란스러운 예외는 큰따옴표로 묶인 배열 입니다. 여기서 큰따옴표로 묶인 문자열은 각 배열 요소에 대해 하나씩 여러 단어를 생성합니다.)
명령줄 ${filelist[$file]}
에서도 사용할 수 있습니다 . sha256sum
이것은 작동하지 않습니다. file
인덱스가 아닌 배열에서 받은 값이 이미 포함되어 있습니다.
최소한의 수정으로 다음과 같이 작동할 수 있습니다.
declare -a filelist
readarray -t filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
sha256sum "$file" | head -c 64
done
declare
(나는 명시적인 것도 실제로 필요하다고 생각하지 않습니다 .)
ls
위의 문제는 본 내용과 관련이 없습니다 .파일 이름을 한 줄에 하나씩 파일에 저장하고 이 옵션을 사용하지 않고 readarray
/를 사용하여 mapfile
파일을 읽는 경우에도 -t
동일한 문제가 발생합니다 . (또는 의 출력을 읽는 경우 find
, 이 경우에는 사용할 수 있습니다 find -exec
.)
물론, 이것은예쓸모없는 사용 ls
의 일부 버전 은 ls
출력에서 파일 이름을 손상시킬 수 있습니다. (내 생각에는 GNU ls가 파이프로 출력할 때 이 작업을 수행하지 않는 것 같습니다.)
Bash에서는 배열을 glob으로 채울 수 있습니다.
shopt -s dotglob
filelist=(*)
for file in *; do ...
또는 배열에 저장하지 않고 glob에서 루프를 실행합니다.
shopt -s dotglob
for file in *; do ...
꼭 필요하다는 점 참고하세요shopt -s dotglob
*
몇 가지 서류를 얻으려면, 쉘에 따라 다릅니다.
답변3
코드 조각을 기반으로 한 문제 중 하나는 구문 분석 중일 수 있습니다 ls
. 이는 위험하고 수많은 문제로 가득 차 있으므로 피하는 것이 가장 좋습니다.
대신에
declare -a filelist
readarray filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
더 간단하고 안전합니다!
for file in *; do
이 경우:
for file in *; do
sha256sum "${file}" | head -c 64
done
readarray
또한 줄바꿈을 포함하여 호출할 때 전달된 리터럴 데이터를 보존하는 데 도움이 됩니다. 따라서 인용된 값을 에코하면 개행 문자가 유지됩니다. 인용하지 않으면 쉘은 이를 토큰 간 공백으로 무시합니다. 이것이 sha256sum
실패의 이유이기도 합니다. 이름이 지정된 파일이 있는 경우 foo
해당 파일에 해당하지 않는 readarray
값을 전달하십시오 . foo\n
일부 변수 값이 실수로 삭제되었기 때문에 이를 역참조하면 문제가 "수정"됩니다.