วนซ้ำ tuples ใน bash?


89

เป็นไปได้ไหมที่จะวนซ้ำสิ่งที่ทับซ้อนในทุบตี?

ตัวอย่างเช่นจะเป็นการดีหากสิ่งต่อไปนี้ได้ผล:

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

มีวิธีแก้ปัญหาที่ทำให้ฉันวนรอบสิ่งที่เพิ่มขึ้นได้หรือไม่?


4
มาจากพื้นหลัง python นี่เป็นคำถามที่มีประโยชน์มากจริงๆ!
John Jiang

5
เมื่อมองไปที่สี่ปีต่อมาฉันสงสัยว่ายังไม่มีวิธีใดที่ดีกว่านี้ในการทำเช่นนี้ พระเจ้าช่วย.
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
ในกรณีที่ $ i ขึ้นต้นด้วยยัติภังค์จะปลอดภัยกว่าset -- $i
glenn jackman

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มันพิมพ์ไม่ดีเลย แต่ถ้าฉันใส่IFS=','หลังdoคีย์เวิร์ดใน for loop มันจะทำงานได้ดีเมื่อใช้$tuplesเช่นเดียวกับค่า litteral แค่คิดก็คุ้มแล้ว
Simonlbc

@Simonlbc นั่นเป็นเพราะ for loop ใช้IFSเพื่อแบ่งการทำซ้ำ เช่นถ้าคุณห่วงมากกว่าอาร์เรย์เหมือนarr=("c,3" "e,5")และใส่IFSก่อนที่จะมีการห่วงค่าของการ$iจะเป็นเพียงcและeมันจะแยกออกไป3และ5เพื่อsetจะไม่แยกอย่างถูกต้องเพราะ$iจะไม่ได้มีอะไรที่จะแยก ซึ่งหมายความว่าหากค่าที่จะวนซ้ำไม่อยู่ในบรรทัดIFSควรใส่ไว้ในลูปและค่าภายนอกควรเป็นไปตามตัวคั่นที่ต้องการสำหรับตัวแปรที่จะวนซ้ำ ในกรณี$tuplesนี้ควรเป็นเพียงIFS=ค่าเริ่มต้นและแยกตามช่องว่าง
ถึง

26

ฉันเชื่อว่าโซลูชันนี้สะอาดกว่าวิธีอื่น ๆ ที่ส่งมาเล็กน้อย h / t คู่มือสไตล์ทุบตีนี้เพื่อแสดงให้เห็นว่าสามารถใช้การอ่านเพื่อแยกสตริงที่ตัวคั่นและกำหนดให้กับตัวแปรแต่ละตัวได้อย่างไร

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

20

จากคำตอบที่ได้รับจาก @ 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

13

ใช้ Associative array (หรือที่เรียกว่า dictionary / 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

FYI สิ่งนี้ใช้ไม่ได้สำหรับฉันบน Mac GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)ซึ่งติดตั้งผ่านการชงดังนั้น YMMV
David

อย่างไรก็ตามมันทำงานบนGNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)Ubuntu 14.04 ภายในคอนเทนเนอร์นักเทียบท่า
เดวิด

ดูเหมือนว่า bash รุ่นเก่ากว่าหรือรุ่นที่ไม่รองรับคุณลักษณะนี้จะไม่สามารถใช้งานดัชนีได้ โดยที่คีย์เป็นตัวเลขแทนที่จะเป็นสตริง tldp.org/LDP/abs/html/declareref.htmlและแทนที่จะเรามี-A -a
เดวิด

เดวิดดูเหมือนอย่างนั้น ฉันคิดว่าคุณสามารถลองดัชนีอาร์เรย์เพื่อรับ "การเชื่อมโยง" ได้ ชอบ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))คืออะไร?

$ ((somthing)) คือการขยายเลขคณิตดังนั้น $ ((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)is for i in 0 1. เนื่องจากองค์ประกอบสุดท้ายในอาร์เรย์มีดัชนีซึ่งเป็นความยาวของ Array - 1


1
คุณควรทำfor i in $(seq 0 $(($#c[*]}-1))); do [...]
reox

1
ว้าวนี่ชนะรางวัล“ Ugliest Bunch of Arbitrary Characters I've Seen Today” ใครสนใจที่จะอธิบายว่าสิ่งที่น่ารังเกียจนี้ทำอะไร? ฉันหลงทางที่เครื่องหมายแฮช ...
koniiiik

1
@koniiiik: เพิ่มคำอธิบายแล้ว
ผู้ใช้ไม่ทราบ

"Neu-Perl: The Revenge" ฉันคิดว่ามันน่าจะชัดเจนกว่าใน perl :-)
Buzz Moschetti


3

การใช้ GNU Parallel:

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


0

ในกรณีที่คำจำกัดความทูเพิลของฉันซับซ้อนกว่าฉันต้องการให้คำจำกัดความเหล่านี้เป็นกรรมพันธุ์:

while IFS=", " read -ra arr; do
  echo "${arr[0]} and ${arr[1]}"
done <<EOM
c, 3
e, 5
EOM

รวมนี้บ่วงผ่านสายของ heredocกับแยกเส้นที่บางส่วนของตัวละครที่ต้องการแยก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.