Git을 사용하여 디렉토리의 파일에 대한 모든 단계되지 않은 수정 사항의 bash 배열을 얻으려고 합니다. 다음 코드는 디렉터리에서 수정된 모든 파일을 인쇄합니다.
git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4-
이 인쇄
"Directory Name/File B.txt"
"File A.txt"
나는 사용하려고
arr1=($(git status --porcelain | grep "^.\w" | cut -c 4-))
그러나 이후에
for a in "${arr1[@]}"; do echo "$a"; done
${arr1[@]}
(주변에 따옴표가 있거나 없이 인쇄됨)
"Directory
Name/File
B.txt"
"File
A.txt"
나도 시도했다
git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4- | readarray arr2
그러나 이후에
for a in "${arr2[@]}"; do echo "$a"; done
(따옴표 포함 및 제외 ${arr2[@]}
) 아무것도 인쇄하지 않습니다. 미리 사용해도 declare -a arr2
아무런 효과가 없습니다.
내 질문은: 이 값을 배열로 어떻게 읽을 수 있습니까? (이것은 내 아르고스 플러그인에서 사용됩니다.Git 바, 혹시라도 내 코드를 모두 볼 수 있도록).
답변1
긴 이야기 짧게
배쉬에서:
readarray -t arr2 < <(git … )
printf '%s\n' "${arr2[@]}"
귀하의 질문에는 두 가지 다른 질문이 있습니다
껍질이 깨졌습니다.
이 작업을 수행할 때:arr1=($(git … ))
"명령 확장"은 따옴표가 없으므로 쉘 분할 및 glob의 영향을 받습니다.
쉘 분할이 수행하는 작업을 정확히 보려면 printf를 사용하십시오.
$ printf '<%s> ' $(echo word '"one simple sentence"') <word> <"one> <simple> <sentence">
이는 다음을 통해 피할 수 있습니다.인용하다:
$ printf '<%s> ' "$(echo word '"one simple sentence"')" <word "one simple sentence">
그러나 이것은 또한 원하는 개행 분할을 방지합니다.
파이프라인
실행 시:git … | … | … | readarray arr2
배열 변수
arr2
가 설정되었지만,|
파이프()가 닫히면 사라집니다.마지막 서브셸에 머무르는 경우 다음 값을 사용할 수 있습니다.
$ printf '%s\n' "First value." "Second value." | { readarray -t arr2; printf '%s\n' "${arr2[@]}"; } First value. Second value.
arr2
그러나 파이프라인을 떠난 후에는 그 가치가 유지되지 않습니다.
해결책
줄 바꿈을 분할하려면 읽기를 사용해야 하지만 파이프는 사용할 수 없습니다.
오래된 것부터 새로운 것까지:
반지 모양.
배열이 없는 오래된 쉘의 경우(위치 인수가 있는 유일한 준 배열):set -- while IFS='' read -r value; do set -- "$@" "$value" done <<-EOT $(printf '%s\n' "First value." "Second value.") EOT printf '%s\n' "$@"
배열 설정(ksh, zsh, bash)
i=0; arr1=() while IFS='' read -r value; do arr1+=("$value") done <<-EOT $(printf '%s\n' "First value." "Second value.") EOT printf '%s\n' "${arr1[@]}"
Here-string here document() 대신 here-string()을 사용할 수 있습니다 .<<
<<<
i=0; arr1=() while IFS='' read -r value; do arr1+=("$value") done <<<"$(printf '%s\n' "First value." "Second value.")" printf '%s\n' "${arr1[@]}"
프로세스 대체
이를 지원하는 셸(ksh, zsh, bash)에서 이를 사용하여<( … )
여기에서 문자열을 바꿀 수 있습니다.i=0; arr1=() while IFS='' read -r value; do arr1+=("$value") done < <(printf '%s\n' "First value." "Second value.") printf '%s\n' "${arr1[@]}"
차이점은
<( )
NUL 바이트를 내보내는 기능인 반면, 여기서 문자열은 NUL을 삭제(또는 경고)할 수 있습니다. 기본적으로 여기에서는 후행 개행 문자가 문자열에 추가됩니다. AFAIK 다른 것이 있을 수도 있습니다.readarray는 다음 용도로
사용됩니다 .readarray
bash
[ㅏ](일명mapfile
) 루프를 피하세요:readarray -t arr2 < <(printf '%s\n' "First value." "Second value.") printf '%s\n' "${arr2[@]}"
[ㅏ]ksh에서는 사용하기 전에 변수를 지우는 를 사용해야 read -A
하지만 개행 문자를 분할하고 전체 입력을 한 번에 읽으려면 약간의 "마법"이 필요합니다.
IFS=$'\n' read -d '' -A arr2 < <(printf '%s\n' "First value." "Second value.")
필요할 것이예요mapfile
zsh에서 모듈 로드비슷한 일을 해보세요.
답변2
readarray로 파이프하면 arr2 배열을 올바르게 채우는 하위 쉘이 시작된 다음 종료됩니다. readarray에 대한 입력으로 프로세스 대체를 사용합니다.
readarray -t arr2 < <(git ...)
답변3
당신은 가깝습니다
이것은 "공백 문자가 포함된 파일 이름" 문제입니다.
기본적으로 구분 기호는 공백 문자입니다. 이는 IFS
환경 변수에 의해 설정됩니다.
환경 변수를 임시로 변경하려면 다음 명령을 사용하십시오.
ifs_backup=$IFS
IFS=$(echo -en "\n\b")
그런 다음 이 명령의 출력은 다음과 같습니다.
for a in "${arr1[@]}"; do echo "$a"; done
할 것이다:
"Directory Name/File B.txt"
"File A.txt"
복구 IFS
:
IFS=$ifs_backup
답변4
코드 내부 부분에 따옴표를 추가합니다.
arr1=("$(git status --porcelain | grep "^.\w" | cut -c 4-)")