zsh 및 Bash의 문자열 반복

zsh 및 Bash의 문자열 반복

이 Bash 루프를 변환하고 싶습니다.

x="one two three"

for i in ${x}
do
    echo ${i}
done

이런 식으로 Bash와 zsh를 함께 사용하십시오

이 솔루션은 다음과 같이 작동합니다.

x=( one two three )

for i in ${x[@]}
do
    echo ${i}
done

어쨌든 x문자열에서 배열로 수정 중입니다.

$xzsh가 문자열이고 Bash와 호환되는 방식으로 zsh에서 반복하는 방법이 있습니까?

zsh가 Bash를 에뮬레이트할 수 있다는 것을 알고 있지만 setopt shwordsplit설정할 수는 없습니다.애드 혹for 루프는 Bash에서 작동하지 않기 때문입니다.

`

답변1

$x가 문자열이고 Bash와 호환되는 경우 zsh에서 $x를 반복하는 방법이 있습니까?

예! . var 확장은 zsh에서 (기본적으로) 분할되지 않지만명령 확장자는 다음과 같습니다.. 따라서 Bash와 zsh 모두에서 다음을 사용할 수 있습니다.

 x="one two three"

 for i in $( echo "$x" )
 do
    echo "$i"
 done

실제로 위 코드는 모든 Bourne 쉘 자손에서 동일하게 작동합니다(그러나 원래 Bourne에서는 작동하지 않으므로 변경하여 작동하도록 하십시오) $(…).`…`

위 코드에는 여전히 와일드카드 및 에코 사용과 관련된 몇 가지 문제가 있습니다. 계속 읽어보세요.



zsh에서는 var 확장과 같은 것이 $var기본적으로 분할되지 않습니다(글로브도 마찬가지).
이 코드는 zsh에서 문제 없이 작동합니다(pwd의 모든 파일로 확장되지는 않음).

var="one * two"
printf "<%s> " ${var}; echo

그러나 var는 IFS 값으로 나누어지지 않습니다.

zsh의 경우 다음 방법 중 하나를 사용하여 IFS에서 분할을 수행할 수 있습니다.

 1. Call splitting explicitly: `echo ${=var}`.
 2. Set SH_WORD_SPLIT option: `set -y; echo ${var}`.
 3. Using read to split to a list of vars (or an array with `-A`).

그러나 이러한 옵션 중 어느 것도 bash(또는 해당 문제에 대해 ksh를 제외한 다른 쉘 -A)로 이식 가능하지 않습니다.

두 쉘에서 공유하는 이전 구문을 사용하면 read도움이 될 수 있습니다.
하지만 이는 다음에만 적용됩니다.문자 구분 기호(IFS 아님) 입력 문자열에 구분 기호가 있는 경우에만 다음과 같습니다.

 # ksh, zsh and bash(3.0+)
 t1(){  set -f;
        while read -rd "$delimiter" i; do
            echo "|$i|"
        done <<<"$x"
     }

$1하나는 어디에 있습니까?캐릭터분리 기호.

*?여전히 와일드카드 문자( , 및 ) 확장이 적용되므로 [이 필요합니다. set -f또한 배열 변수를 설정할 수도 있습니다 outarr.

 # for either zsh or ksh
 t2(){ set -f; IFS="$delimiter" read -d $'\3' -A outarr < <(printf '%s\3' "$x"); }

bash도 같은 생각을 가지고 있습니다.

 # bash
 t3(){ local -; set -f; mapfile -td "$1" outarr < <(printf '%s' "$x"); }

set -fBash 기능에서 복원된 효과를 사용하십시오 local -.

이 개념은 대시와 같은 제한된 쉘로 확장될 수도 있습니다.

 # valid for dash and bash
 t4(){  local -; set -f;
        while read -r i; do
             printf '%s' "$i" | tr "$delimiter"'\n' '\n'"$delimiter"; echo
        done <<-EOT
$(echo "$x" | tr "$delimiter"'\n' '\n'"$delimiter")
EOT
     } 

아니요 <<<, 아니요 <(…), 아니요 read -A또는 readarray사용되었지만 작동합니다(예:캐릭터입력에 공백, 줄 바꿈 및/또는 제어 문자를 포함하는 구분 기호입니다.

하지만 간단히 이렇게 하는 것이 훨씬 쉽습니다.

 t9(){ set -f; outarr=(   $(printf '%s' "$x")   ); }

안타깝게도 zsh는 그 local -값을 이해하지 못하므로 다음과 같이 복원해야 합니다.set -f

 t99(){ oldset=$(set +o); set -f; outarr=( $( printf '%s' "$x" ) ); eval "$oldset"; }

위의 함수는 다음을 통해 호출할 수 있습니다.

 IFS=$1 delimiter=$1 $2

여기서 첫 번째 매개변수 $는 구분 기호(및 IFS)이고 두 번째 매개변수는 호출할 함수(t1, t2, ... t9, t99)입니다. 이 호출은 함수 호출 중에 IFS 값만 설정하고 호출된 함수가 종료되면 원래 값으로 반환합니다.

답변2

if type emulate >/dev/null 2>/dev/null; then emulate ksh; fi

zsh sh_word_split에서는 emulate.

답변3

eval (=evil)을 사용하는 것이 두렵지 않다면:

x="one two three"
eval "x=($x)"
for i in ${x[@]}; do 
    echo $i
done

관련 정보