लूप्स के लिए बहुक्रियाशील


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 तो करें 2 , लेकिन यह साथ सिंक्रनाइज़ फ़ाइलों को रखना होता है &&, जो बाहर निकल जाता है , जबकि पहले पर पाश EOF । - GNU बैश 4.1.5
पीटर।

मेरे पास परीक्षण करने के लिए एक स्वतंत्र क्षण नहीं था - हां आप शायद प्रति तत्व दोनों तत्वों को चाहते हैं। ||"सूची" ऑपरेटर अधिकांश भाषाओं में तरह शॉर्ट-सर्किट है। निश्चित रूप से यह एक हजार बार पहले उत्तर दिया गया है ...
ormaaj

बिंदु इस बात की आवृत्ति नहीं है कि पहले कितनी बार कुछ का उल्लेख किया गया है। बल्कि, यह है कि वर्तमान में आप अपने जवाब में जो कोड दिखा रहे हैं वह काम नहीं करता है । आपके " शायद दोनों तत्वों को चाहते हैं .." के आधार पर , ऐसा लगता है कि आपने अभी भी इसका परीक्षण नहीं किया है।
पीटर।

@ पीटर। मैं जानता हूँ। यह तय हो गया है।
ormaaj

2

कर दिया; किया - आपको दो फ़ोर और दो डोन की आवश्यकता है:

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

यदि आप 2 फ़ाइलों को ज़िप करना चाहते हैं, तो पेस्ट का उपयोग करें:

paste file1 file2

1
आप कहते हैं कि कोई उपप्रजाति अनपेक्षित नहीं है। () एक उपधारा नहीं है? मैं पेंडेंट नहीं होना चाहता, लेकिन मैं अनिश्चित हूं कि अब क्या हो रहा है। उदाहरण के लिए, मुझे पता है कि जब मेरे पास एक स्क्रिप्ट होती है और मैं नहीं चाहता कि यह cd के उदाहरण के साथ वर्तमान शेल को पूरी तरह से खोल दे, तो मैं इसे एक () के अंदर करता हूं। इसके अलावा (स्लीप 4) और उदाहरण के लिए पीएस द्वारा पीछा किया जाना मुझे दिखाता है कि प्रक्रिया अभी भी चल रही है। क्या आप कृपया विस्तार से बता सकते हैं।
सिल्वरकोर जू

अच्छा - मुझे क्षमा करें, मैं यहाँ मौलिक रूप से बहुत अधिक अभिनय कर रहा हूँ। मुझे लगा कि मैंने ऐसा सुना है, लेकिन शायद यह केवल <तुलना है cat। और मैं अनिश्चित हूं कि इसका परीक्षण कैसे किया जाए, और जब यह महत्वपूर्ण रूप से महत्वपूर्ण हो जाएगा। यदि पाइप लगाए जाते हैं, तो सबप्रोसेस में वेरिएबल्स मुख्य प्रक्रिया तक नहीं फैलते हैं, लेकिन हमारे यहां वैरिएबल नहीं हैं। मैं महत्वपूर्ण वाक्य के माध्यम से हड़ताल करता हूं, जब तक कि कोई इसे स्पष्ट नहीं कर सकता।
उपयोगकर्ता अज्ञात

0

whileएक इंटरसिटिंग सिंटैक्स है। आप do ... whileलूप से पहले कई कमांड डाल सकते हैं , और प्रश्न में मामले को अपनी विशेष आवश्यकताओं के आधार पर इस सुविधा को टालना पड़ सकता है: क्या आप सबसे लंबी फ़ाइल के अंत में पढ़ते हैं, या केवल सबसे छोटी अवधि के अंत तक।

उदाहरण के लिए, read || readबस काम नहीं करता (प्रश्न की आवश्यकताओं के अनुसार), जब पहली फ़ाइल का एक रीड होता है true, तब समाप्त हो जाता है , जब तक कि पहली फ़ाइल को शुरू से अंत तक पढ़ा नहीं गया है, तब तक दूसरी फ़ाइल के रीड को छोड़ दिया जाता है ... फिर, क्योंकि स्थिति अभी भी है true, जबकि लूप जारी है और शुरू से अंत तक दूसरी फ़ाइल पढ़ता है।

read && readयदि आप केवल सबसे छोटी फ़ाइल के रूप में पढ़ना चाहते हैं, तो समवर्ती (समकालिक) फ़ाइलों को पढ़ेंगे। हालाँकि, यदि आप दोनों फ़ाइलों को पढ़ना चाहते हैं eof, तो आपको while'sसिंटैक्स आवश्यकताओं के साथ काम करने की आवश्यकता है, अर्थात। आदेश से तुरंत पहलेdo while एक गैर शून्य वापसी कोड के उत्पादन पाश जबकि लूप से बाहर तोड़ने के लिए।

यहाँ एक उदाहरण है कि दोनों फ़ाइलों को ईओफ़ में कैसे पढ़ा जाए

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 का परीक्षण करना चाह सकते हैं, लेकिन सामान्य विचार है, विशेष रूप से अंतिम सच / झूठी स्थिति में।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.