bash에서 튜플을 반복합니까?


88

bash에서 튜플을 반복 할 수 있습니까?

예를 들어 다음이 작동하면 좋을 것입니다.

for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done

어떻게 든 튜플을 반복 할 수있는 해결 방법이 있습니까?


4
파이썬 배경에서 오는 이것은 실제로 매우 유용한 질문입니다!
존 지앙

5
4 년이 지난 지금도 더 좋은 방법이 없는지 궁금합니다. 세상에.
Giszmo

거의 8 년이 지난 지금도이 작업을 수행하는 더 좋은 방법이 없는지 궁금했습니다. 하지만이 2018 답변은 나에게 꽤 좋아 보인다 : stackoverflow.com/a/52228219/463994
MountainX

답변:


87
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5

set(부터 man builtins) 사용에 대해 :

옵션 처리 후에 남아있는 모든 인수는 위치 매개 변수의 값으로 처리되며 순서대로 $ 1, $ 2, ... $ n에 지정됩니다.

IFS=","모든 그래서 필드 분리 세트 $i로 분할 도착 $1하고 $2정확하게한다.

이 블로그를 통해 .

편집 : @SLACEDIAMOND가 제안한 더 정확한 버전 :

$ OLDIFS=$IFS; IFS=','; for i in c,3 e,5; do set -- $i; echo $1 and $2; done; IFS=$OLDIFS
c and 3
e and 5

7
IFS좋습니다. 명령 줄에서 실행하면 저장하고 원래 값으로 재설정해야한다는 점을 지적하고 싶습니다 . 또한 새로운 IFS것은 매 반복이 아니라 루프가 실행되기 전에 한 번 설정할 수 있습니다.
Eggxactly

1
어떤 내가 하이픈으로 시작 $의 경우,에 안전합니다set -- $i
글렌 잭맨

1
저장하는 대신 IFS다음 set명령에 대해서만 설정하십시오 for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done. 답변을 수정하십시오. 모든 독자가 나열된 솔루션 중 하나만 선택한다면 전체 개발 기록을 읽어야하는 것은 의미가 없습니다. 이 멋진 트릭에 감사드립니다!
cfi

내가 선언 tuples="a,1 b,2 c,3"하고 IFS=','편집 된 버전으로 넣으면 c,3 e,5사용 $tuples하는 대신 전혀 잘 인쇄되지 않습니다. 그러나 대신 for 루프에서 키워드 IFS=','바로 뒤에 넣으면 리터럴 값뿐만 do아니라 사용할 때도 잘 작동 $tuples합니다. 말할만한 가치가 있다고 생각했습니다.
Simonlbc

@Simonlbc는 for 루프가 IFS반복을 분할하는 데 사용 하기 때문 입니다. 이 같은 배열을 통해 루프 경우 즉 arr=("c,3" "e,5")넣어 IFS루프에 대한 이전의 값은 $i단지 것입니다 c그리고 e, 그것은 멀리 분할합니다 35그래서 set때문에 올바르게 구문 분석하지 않습니다 $i구문 분석에 아무것도하지 않습니다. 즉, 반복 할 값이 인라인되지 않은 경우는 IFS루프 내부에 배치되어야하며 외부 값은 반복 할 변수에 대해 의도 된 구분 기호를 존중해야합니다. 그 경우에는 $tuples단순히 IFS=기본값이며 공백으로 분할 되어야합니다 .
tore

25

이 솔루션이 제출 된 다른 솔루션보다 조금 더 깨끗하다고 ​​생각합니다. bash 스타일 가이드를 참조하여 구분 기호에서 문자열을 분할하고 개별 변수에 할당하는 방법을 설명합니다.

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}"
    echo "${item1}" and "${item2}"
done

17

을 설정 / 재설정하지 않고 @ eduardo-ivanec이 제공 한 답변에 따라 IFS간단히 다음과 같이 할 수 있습니다.

for i in "c 3" "e 5"
do
    set -- $i
    echo $1 and $2
done

출력 :

c and 3
e and 5

이 접근 방식은 받아 들여지고 가장 찬성되는 접근 방식보다 훨씬 간단 해 보입니다. @Eduardo Ivanec이 제안한 것과 달리 이런 식으로하지 않을 이유가 있습니까?
spurra

@spurra이 답변은 6 년이고 ½ 더 최근이며이를 기반으로합니다. 기한이있는 곳에서 크레딧을 받으세요.
Diego

1
@Diego 나는 그것을 알고 있습니다. 답변에 명시 적으로 기록되어 있습니다. 나는 받아 들여진 대답에 대해이 접근법을 사용하지 않을 이유가 있는지 물었다.
spurra

2
@spurra 기본 구분 기호 (공백, 탭 또는 줄 바꿈)가 어떤 이유로 작동하지 않는 경우 Eduardo의 답변을 사용하고 싶습니다 ( bash.cyberciti.biz/guide/$IFS )
Diego

11

연관 배열 (사전 / hashMap이라고도 함) 사용 :

declare -A pairs=(
  [c]=3
  [e]=5
)
for key in "${!pairs[@]}"; do
  value="${pairs[$key]}"
  echo "key is $key and value is $value"
done

bash4.0 이상에서 작동합니다.


쌍 대신 트리플이 필요한 경우 더 일반적인 접근 방식을 사용할 수 있습니다.

animals=(dog cat mouse)
declare -A sound=(
  [dog]=barks
  [cat]=purrs
  [mouse]=cheeps
)
declare -A size=(
  [dog]=big
  [cat]=medium
  [mouse]=small
)
for animal in "${animals[@]}"; do
  echo "$animal ${sound[$animal]} and it is ${size[$animal]}"
done

참고로, 이것은 GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)brew를 통해 설치된 Mac에서 작동하지 않았 으므로 YMMV입니다.
David

그러나 GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)Docker 컨테이너 내에서 Ubuntu 14.04에서 작동했습니다 .
David

이전 버전의 bash 또는이 기능을 지원하지 않는 버전이 색인 기반에서 작동하지 않는 것 같습니다. 여기서 키는 문자열이 아닌 숫자입니다. tldp.org/LDP/abs/html/declareref.html 하고, 대신에 -A우리가 -a.
David

데이비드, 그렇게 보인다. 나는 당신이 "연관성"을 얻기 위해 배열 인덱스를 시도 할 수 있다고 생각한다. 마찬가지로 declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)등의 유무는 아직 터미널에서 그것을 시도하지,하지만 난 생각이 작동합니다.
VasiliNovikov

7
c=('a' 'c')
n=(3    4 )

for i in $(seq 0 $((${#c[*]}-1)))
do
    echo ${c[i]} ${n[i]}
done

때때로 더 편리 할 수 ​​있습니다.

ugly주석에 언급 된대로 부품 을 설명하려면 :

seq 0 2 는 숫자 0 1 2의 시퀀스를 생성합니다. $ (cmd)는 명령 대체이므로이 예 seq 0 2에서는 숫자 시퀀스 인을 출력 합니다. 그러나 상한선은 무엇 $((${#c[*]}-1))입니까?

$ ((something))은 산술 확장이므로 $ ((3 + 4))는 7 등입니다. 우리의 표현은 ${#c[*]}-1, 그래서 뭔가-1. 우리가 무엇을 안다면 아주 간단 ${#c[*]}합니다.

c는 배열이고, c [*]는 전체 배열이고, $ {# c [*]}는 배열의 크기이며이 경우에는 2입니다. 이제 모든 것을 롤백합니다. for i in $(seq 0 $((${#c[*]}-1)))is for i in $(seq 0 $((2-1)))is for i in $(seq 0 1)입니다 for i in 0 1. 배열의 마지막 요소에는 Array-1의 길이 인 인덱스가 있기 때문입니다.


1
당신은해야합니다for i in $(seq 0 $(($#c[*]}-1))); do [...]
reox

1
와우, 이것은 "오늘 내가 본 임의의 문자의 추악한 무리"상을 수상했습니다. 이 가증 한 일이 정확히 무엇인지 설명해 줄 사람이 있습니까? 나는 해시 기호에서 길을 잃었습니다 ...
koniiiik 2014 년

1
@koniiiik : 설명이 추가되었습니다.
사용자 알 수 없음

6
$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done
c and 3
e and 5

3

GNU 병렬 사용 :

parallel echo {1} and {2} ::: c e :::+ 3 5

또는:

parallel -N2 echo {1} and {2} ::: c 3 e 5

또는:

parallel --colsep , echo {1} and {2} ::: c,3 e,5

1
이것에 대한 사랑이 없습니까? 잘 만든 관성 극복하고 설치gnu parallel
StephenBoesch

2
brew install parallel
StephenBoesch

2

printf프로세스 대체에서 사용 :

while read -r k v; do
    echo "Key $k has value: $v"
done < <(printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3')

Key key1 has value: val1
Key key2 has value: val2
Key key3 has value: val3

위의 bash. bash사용하지 않는 경우 간단한 파이프 라인을 사용하십시오.

printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3' |
while read -r k v; do echo "Key $k has value: $v"; done

1
예! Anubhava, 당신은 달콤한 천재!
Scott

1
do echo $key $value
done < file_discriptor

예를 들면 :

$ while read key value; do echo $key $value ;done <<EOF
> c 3
> e 5
> EOF
c 3
e 5

$ echo -e 'c 3\ne 5' > file

$ while read key value; do echo $key $value ;done <file
c 3
e 5

$ echo -e 'c,3\ne,5' > file

$ while IFS=, read key value; do echo $key $value ;done <file
c 3
e 5

0

조금 더 복잡하지만 유용 할 수 있습니다.

a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done

0

그러나 튜플이 연관 배열이 보유 할 수있는 k / v보다 크면 어떻게 될까요? 3 개 또는 4 개 요소이면 어떨까요? 이 개념을 확장 할 수 있습니다.

###---------------------------------------------------
### VARIABLES
###---------------------------------------------------
myVars=(
    'ya1,ya2,ya3,ya4'
    'ye1,ye2,ye3,ye4'
    'yo1,yo2,yo3,yo4'
    )


###---------------------------------------------------
### MAIN PROGRAM
###---------------------------------------------------
### Echo all elements in the array
###---
printf '\n\n%s\n' "Print all elements in the array..."
for dataRow in "${myVars[@]}"; do
    while IFS=',' read -r var1 var2 var3 var4; do
        printf '%s\n' "$var1 - $var2 - $var3 - $var4"
    done <<< "$dataRow"
done

그러면 출력은 다음과 같습니다.

$ ./assoc-array-tinkering.sh 

Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4

그리고 요소의 수는 이제 제한이 없습니다. 투표를 찾지 않습니다. 큰 소리로 생각하는 것뿐입니다. REF1 , REF2


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.