หลายตัวแปรสำหรับลูป


12

มีวิธีการระบุตัวแปรหลายตัว (ไม่ใช่แค่จำนวนเต็ม) ในการforวนซ้ำbashหรือไม่? ฉันอาจมี 2 ไฟล์ที่มีข้อความใด ๆ ที่ฉันต้องทำงานด้วย

สิ่งที่ฉันต้องการตามหน้าที่คืออะไร:

for i in $(cat file1) and j in $(cat file2); do command $i $j; done

ความคิดใด ๆ

คำตอบ:


11

ก่อนอื่นอย่าอ่านบรรทัดด้วยสำหรับเนื่องจากมีปัญหาที่หลีกเลี่ยงไม่ได้หลายประการกับการอ่านบรรทัดผ่านการแยกคำ

สมมติว่าไฟล์ที่มีความยาวเท่ากันหรือหากคุณต้องการวนซ้ำจนกว่าจะอ่านไฟล์ที่สั้นกว่าสองไฟล์ก็จะสามารถแก้ปัญหาได้ง่าย

while read -r x && read -r y <&3; do
    ...
done <file1 3<file2

การรวบรวมโซลูชันทั่วไปที่ยากขึ้นเข้าด้วยกันเป็นเรื่องยากเพราะเมื่อreadคืนเท็จและเหตุผลอื่น ๆ อีกหลายประการ ตัวอย่างนี้สามารถอ่านจำนวนกระแสข้อมูลโดยพลการและส่งคืนหลังจากอินพุตสั้นหรือยาวที่สุด

#!/usr/bin/env bash

# Open the given files and assign the resulting FDs to arrName.
# openFDs arrname file1 [file2 ...]
openFDs() {
    local x y i arr=$1

    [[ -v $arr ]] || return 1
    shift

    for x; do
        { exec {y}<"$x"; } 2>/dev/null || return 1
        printf -v "${arr}[i++]" %d "$y"
    done
}

# closeFDs FD1 [FD2 ...]
closeFDs() {
    local x
    for x; do
        exec {x}<&-
    done
}

# Read one line from each of the given FDs and assign the output to arrName.
# If the first argument is -l, returns false only when all FDs reach EOF.
# readN [ -l ] arrName FD1 [FD2 ...]
readN() {
    if [[ $1 == -l ]]; then
        local longest
        shift
    else
        local longest=
    fi

    local i x y status arr=$1
    [[ -v $arr ]] || return 1
    shift

    for x; do
        if IFS= read -ru "$x" "${arr}[i]" || { unset -v "${arr}[i]"; [[ ${longest+_} ]] && return 1; }; then
            status=0
        fi
        ((i++))
    done
    return ${status:-1}
}

# readLines file1 [file2 ...]
readLines() {
    local -a fds lines
    trap 'closeFDs "${fds[@]}"' RETURN
    openFDs fds "$@" || return 1

    while readN -l lines "${fds[@]}"; do
        printf '%-1s ' "${lines[@]}"
        echo
    done
}

{
    readLines /dev/fd/{3..6} || { echo 'error occured' >&2; exit 1; }
} <<<$'a\nb\nc\nd' 3<&0 <<<$'1\n2\n3\n4\n5' 4<&0 <<<$'x\ny\nz' 5<&0 <<<$'7\n8\n9\n10\n11\n12' 6<&0

# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:

ดังนั้นขึ้นอยู่กับว่าจะreadNได้รับ-lผลลัพธ์เป็นอย่างใดอย่างหนึ่ง

a 1 x 7
b 2 y 8
c 3 z 9
d 4 10
5 11
12

หรือ

a 1 x 7
b 2 y 8
c 3 z 9

การอ่านหลายสตรีมในลูปโดยไม่ต้องบันทึกทุกอย่างลงในหลาย ๆ อาเรย์นั้นไม่ใช่เรื่องธรรมดา mapfileถ้าคุณเพียงต้องการที่จะอ่านอาร์เรย์ที่คุณควรมีลักษณะที่


||ไม่ได้ผลสำหรับฉัน มันประมวลผลfile1 แล้ว file2แต่จะเก็บไฟล์ที่ตรงกับ&&ที่ออกจากในขณะที่วงแรกเมื่อEOF - GNU bash 4.1.5
Peter.O

ฉันไม่มีเวลาว่างในการทดสอบ - ใช่คุณอาจต้องการองค์ประกอบทั้งสองต่อการวนซ้ำ ||"รายการ" ผู้ประกอบการมีการลัดวงจรเช่นในภาษามากที่สุด แน่นอนว่าเรื่องนี้ได้รับการตอบเป็นพันครั้งก่อน ...
ormaaj

ประเด็นนี้ไม่ใช่ความถี่ของความถี่ที่บางสิ่งถูกกล่าวถึงมาก่อน แต่ก็คือว่ารหัสที่คุณกำลังแสดงในคำตอบของคุณไม่ได้ทำงาน ตาม " อาจต้องการองค์ประกอบทั้งสองอย่างของคุณ" ดูเหมือนว่าคุณยังไม่ได้ทดสอบ
Peter.O

@ Peter.O ฉันรู้ มันได้รับการแก้ไขแล้ว
ormaaj

2

Do-ทำ; เสร็จสิ้น - คุณต้องการสอง fors และสอง dones:

for i in $(< file1); do for j in $(< file2); do echo $i $j;done ; done

แน่นอนว่า File2 จะถูกประมวลผลสำหรับแต่ละคำใน file1 หนึ่งครั้ง

การใช้ <แทนที่จะเป็นแมวน่าจะเป็นประสิทธิภาพที่ไม่ได้มาตรฐานในกรณีส่วนใหญ่ ไม่เกี่ยวข้องกับกระบวนการย่อย (ไม่แน่ใจเกี่ยวกับประโยคสุดท้าย)

ไม่มีการบีบอัด:

for i in $(< file1)
do
  for j in $(< file2)
  do
    echo $i $j
  done
done

หากคุณไม่ชอบที่จะอ่านคำ แต่เป็นบรรทัดและรักษาช่องว่างให้ใช้ในขณะที่และอ่าน:

while read line; do while read second; do echo "${line}${second}"; done <file2 ; done <file1

หากคุณต้องการต่อท้าย 2 ไฟล์และไม่ซ้อน:

cat file1 file2 | while read line; do echo "${line}"; done

หากคุณต้องการ zip ไฟล์ 2 ไฟล์ใช้ paste:

paste file1 file2

1
คุณบอกว่าไม่มีกระบวนการย่อยที่ไม่เกี่ยวข้อง ไม่ใช่ subshell () หรือไม่ ฉันไม่ต้องการที่จะเป็นจี้ แต่ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้นตอนนี้ เช่นฉันรู้ว่าเมื่อฉันมีสคริปต์และฉันไม่ต้องการให้ polute เชลล์ปัจจุบันด้วย cd ของอินสแตนซ์ฉันทำมันภายใน () ยังทำงานอยู่ (sleep 4) & ตัวอย่างเช่นตามด้วย ps แสดงให้ฉันเห็นกระบวนการยังคงทำงานอยู่ คุณช่วยอธิบายได้ไหม
Silverrocker

ฉันขอโทษฉันแสดงได้ดีมากโดยไม่มีพื้นฐานที่นี่ ฉันคิดว่าฉันได้ยินเช่นนั้น แต่บางทีมันอาจจะเป็นเพียงเมื่อเทียบกับ< catและฉันไม่แน่ใจว่าจะทดสอบอย่างไรและเมื่อใดจึงจะมีความสำคัญทางความหมาย หากไพพ์ถูกเรียกใช้ตัวแปรในกระบวนการย่อยจะไม่ทำให้เกิดฟองขึ้นกับกระบวนการหลัก แต่เราไม่มีตัวแปรที่นี่ ฉันอ่านประโยคที่สำคัญจนมีใครซักคนอธิบายได้
ผู้ใช้ที่ไม่รู้จัก

0

whileมีไวยากรณ์ interesitng คุณสามารถใส่คำสั่งหลายคำสั่งก่อนหน้าdo ... whileลูปและกรณีที่เป็นปัญหาอาจจำเป็นต้องใช้คุณสมบัตินี้ขึ้นอยู่กับความต้องการเฉพาะของคุณ: คุณอ่านจนจบไฟล์ที่ยาวที่สุดหรือเฉพาะที่สั้นที่สุด

ยกตัวอย่างเช่นread || readเพียงไม่ทำงาน (ตามความต้องการของคำถามของ) becase เมื่ออ่านไฟล์แรกคือtrueอ่านไฟล์ที่สองคือการข้ามจนกว่าไฟล์แรกได้รับการอ่านตั้งแต่ต้นจนจบ ... แล้วเพราะสถานะ ยังคงtrueอยู่ขณะที่ลูปดำเนินการต่อและอ่านไฟล์ที่สองตั้งแต่ต้นจนจบ

read && readจะอ่านไฟล์พร้อมกัน (แบบซิงโครนัส) หากคุณต้องการอ่านเท่าที่ไฟล์ที่สั้นที่สุด อย่างไรก็ตามหากคุณต้องการอ่านทั้งสองไฟล์eofคุณจำเป็นต้องทำงานกับwhile'sข้อกำหนดด้านไวยากรณ์เช่น โดยคำสั่งทันทีก่อนที่จะdo whileลูปจะสร้างโค้ดส่งคืนที่ไม่เป็นศูนย์เพื่อแยกออกจากลูป while

นี่คือตัวอย่างของวิธีการอ่านไฟล์ทั้งสองไปยังeof

while IFS= read -r line3 <&3 || ((eof3=1))
      IFS= read -r line4 <&4 || ((eof4=1))
      !((eof3 & eof4))
do
    echo "$line3, $line4"
done 3<file3 4<file4

(คุณอาจต้องการทดสอบ eof3 และ eof4 ก่อนการอ่าน แต่มีความคิดทั่วไปโดยเฉพาะอย่างยิ่งในสภาพจริง / เท็จขั้นสุดท้าย

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