लूप का उपयोग करते हुए दो इनपुट फ़ाइलों से कैसे पढ़ें


27

मैं जानना चाहता था कि क्या एक समय में एक पंक्ति में लूप रहते हुए दो इनपुट फ़ाइलों से पढ़ने का कोई तरीका है। उदाहरण के लिए, मैं कहता हूं कि मेरे पास दो फाइलें हैं FileAऔर FileB

FileA:

[jaypal:~/Temp] cat filea
this is File A line1
this is File A line2
this is File A line3

FileB:

[jaypal:~/Temp] cat fileb
this is File B line1
this is File B line2
this is File B line3

वर्तमान नमूना स्क्रिप्ट:

[jaypal:~/Temp] cat read.sh 
#!/bin/bash
while read lineA
    do echo $lineA 
    while read lineB
        do echo $lineB 
        done < fileb
done < filea

निष्पादन:

[jaypal:~/Temp] ./read.sh 
this is File A line1
this is File B line1
this is File B line2
this is File B line3
this is File A line2
this is File B line1
this is File B line2
this is File B line3
this is File A line3
this is File B line1
this is File B line2
this is File B line3

समस्या और वांछित आउटपुट:

यह पूरी तरह से FileB में प्रत्येक पंक्ति के लिए FileB पर लूप करता है। मैंने जारी रखने, तोड़ने, बाहर निकलने का उपयोग करने की कोशिश की, लेकिन उनमें से कोई भी उस आउटपुट को प्राप्त करने के लिए नहीं है जो मैं देख रहा हूं। मैं चाहूंगा कि स्क्रिप्ट फाइल ए से सिर्फ एक लाइन पढ़े और फिर फाइलबी से एक लाइन और लूप से बाहर निकले और फाइल ए की दूसरी लाइन और फाइल बी की दूसरी लाइन जारी रखें। निम्न स्क्रिप्ट के समान कुछ -

[jaypal:~/Temp] cat read1.sh 
#!/bin/bash
count=1
while read lineA
    do echo $lineA 
        lineB=`sed -n "$count"p fileb`
        echo $lineB
        count=`expr $count + 1`
done < filea

[jaypal:~/Temp] ./read1.sh 
this is File A line1
this is File B line1
this is File A line2
this is File B line2
this is File A line3
this is File B line3

क्या लूप के साथ इसे प्राप्त करना संभव है?


@Codadict द्वारा एक महान समाधान यहाँ है: stackoverflow.com/a/4011824/4095830 ->paste -d '\n' file1 file2
whoan

जवाबों:


32

यदि आप यह सुनिश्चित करने के लिए जानते हैं कि पहली फ़ाइल में कुछ वर्ण कभी नहीं होगा तो आप पेस्ट का उपयोग कर सकते हैं।

डिफ़ॉल्ट सीमांकक टैब का उपयोग करके पेस्ट का उदाहरण:

paste file1 file2 | while IFS="$(printf '\t')" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

का उपयोग कर पेस्ट का उदाहरण @:

paste -d@ file1 file2 | while IFS="@" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

ध्यान दें कि यदि चरित्र पहली फ़ाइल में नहीं होने की गारंटी है तो यह पर्याप्त है। ऐसा इसलिए है क्योंकि अंतिम चर को भरते समय readउपेक्षा होगी IFS। इसलिए @दूसरी फाइल में होने पर भी विभाजित नहीं किया जाएगा।

यकीनन क्लीनर कोड के लिए कुछ बैश सुविधाओं का उपयोग करके पेस्ट का उदाहरण:

while IFS=$'\t' read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done < <(paste file1 file2)

उपयोग की जाने वाली बैश विशेषताएं: उपसमूह समस्या में लूप से बचने के लिए एएनसी सी स्ट्रिंग ( $'\t') और प्रक्रिया प्रतिस्थापन ( <(...)) ।

यदि आप यह निश्चित नहीं कर सकते हैं कि कोई भी चरित्र दोनों फ़ाइलों में कभी नहीं होगा तो आप फ़ाइल डिस्क्रिप्टर का उपयोग कर सकते हैं ।

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done 3<file1 4<file2

ज्यादा परीक्षण नहीं किया गया। खाली लाइनों पर टूट सकता है।

फ़ाइल डिस्क्रिप्टर नंबर 0, 1 और 2 पहले से ही क्रमशः स्टड, स्टडआउट और स्टडर के लिए उपयोग किए जाते हैं। 3 और ऊपर से फ़ाइल डिस्क्रिप्टर मुफ्त (आमतौर पर) हैं। बैश मैनुअल 9 से अधिक फ़ाइल डिस्क्रिप्टर का उपयोग करने से चेतावनी देता है, क्योंकि वे "आंतरिक रूप से उपयोग किए जाते हैं"।

ध्यान दें कि ओपन फ़ाइल डिस्क्रिप्टर शेल कार्यों और बाहरी कार्यक्रमों को विरासत में मिला है। ओपन फाइल डिस्क्रिप्टर को प्राप्त करने वाले फ़ंक्शंस और प्रोग्राम फ़ाइल डिस्क्रिप्टर से पढ़ सकते हैं (और लिख सकते हैं)। आपको सभी फ़ाइल डिस्क्रिप्टर को बंद करने का ध्यान रखना चाहिए जो किसी फ़ंक्शन या बाहरी प्रोग्राम को कॉल करने से पहले आवश्यक नहीं हैं।

यहाँ एक ही कार्यक्रम है जो वास्तविक कार्य (छपाई) के साथ मेटा-वर्क (समानांतर में दो फ़ाइलों से लाइन द्वारा रीडिंग लाइन) से अलग है।

work() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  work "$f1" "$f2"
done 3<file1 4<file2

अब हम दिखावा करते हैं कि काम कोड पर हमारा कोई नियंत्रण नहीं है और जो भी कारण हो, उस कोड को फाइल डिस्क्रिप्टर 3 से पढ़ने की कोशिश करता है।

unknowncode() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
  read -r yoink <&3 && printf 'yoink: %s\n' "$yoink"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  unknowncode "$f1" "$f2"
done 3<file1 4<file2

यहाँ एक उदाहरण आउटपुट है। ध्यान दें कि पहली फ़ाइल से दूसरी पंक्ति लूप से "चोरी" है।

f1: file1 line1
f2: file2 line1
yoink: file1 line2
f1: file1 line3
f2: file2 line2

यहां बताया गया है कि बाहरी कोड (या उस मामले के लिए कोई भी कोड) को कॉल करने से पहले आपको फ़ाइल डिस्क्रिप्टर को कैसे बंद करना चाहिए।

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  # this will close fd3 and fd4 before executing anycode
  anycode "$f1" "$f2" 3<&- 4<&-
  # note that fd3 and fd4 are still open in the loop
done 3<file1 4<file2

17

अलग-अलग फ़ाइल डिस्क्रिप्टर पर दो फाइलें खोलें । readअंतर्निहित डिस्क्रिप्टर के इनपुट को पुनर्निर्देशित करें जो आप चाहते हैं वह फ़ाइल से जुड़ा है। Bash / ksh / zsh में, आप read -u 3इसके बजाय लिख सकते हैं read <&3

while IFS= read -r lineA && IFS= read -r lineB <&3; do
  echo "$lineA"; echo "$lineB"
done <fileA 3<fileB

यह स्निपेट तब बंद हो जाता है जब सबसे छोटी फ़ाइल को संसाधित किया जाता है। लूप करते समय एक IFS में दो फाइलें पढ़ना देखें - क्या इस मामले में एक शून्य भिन्न परिणाम प्राप्त करने का एक तरीका है? यदि आप दोनों फाइलों के अंत तक प्रसंस्करण रखना चाहते हैं।

यह भी देखें कि आप एक अतिरिक्त फ़ाइल डिस्क्रिप्टर का उपयोग कब करेंगे? फाइल डिस्क्रिप्टर पर अतिरिक्त जानकारी के लिए, और IFS = के बजाय IFS = read` को इतनी बार क्यों इस्तेमाल किया जाता है; पढ़े जाने के दौरान ..? के स्पष्टीकरण के लिए IFS= read -r


फाइल डिस्क्रिप्टर पर अतिरिक्त लिंक के लिए @Gilles धन्यवाद।
जयपाल सिंह

@ गिल्स शायद मैंने आपको गलत समझा, लेकिन मैं लूप प्रक्रिया को पूरी तरह से सबसे लंबी फ़ाइल नहीं बना सका (जो मेरे मामले में हमेशा $ फ़ाइल है), इसलिए मैंने इसे एक अलग प्रश्न में बनाया, जा रहा है: क्या लूप लिखने का एक तरीका है वह अंतर इनपुट और आउटपुट के बीच कोई अंतर नहीं देखता है? unix.stackexchange.com/questions/26780/… निकटतम मैं प्राप्त कर सकता था अंतर केवल अंतर की एक पंक्ति पा रहा था।
ixtmixilix

3

मुझे पता है कि आप एक शेल स्क्रिप्ट चाहते हैं, लेकिन आप pasteकमांड देख सकते हैं ।


धन्यवाद @lutzky pasteशांत भी है।
जयपाल सिंह

2

नीचे दिए गए आदेश का प्रयास करें:

paste -d '\n' inp1.txt inp2.txt > outfile.txt

0

वैकल्पिक रूप से, मुझे लगता है कि आप फ़ाइल के प्रत्येक लाइन को सरणी [लाइन_ऑफ_फाइल_इंडेक्स] में बैश के मैपफाइल कमांड का उपयोग करते हुए फ़ाइल को एक सरणी चर में घसीट सकते हैं। हालाँकि, मुझे यकीन नहीं है कि यह केवल बैश 3 उच्च या बैश 4 के लिए है।

http://wiki.bash-hackers.org/commands/builtin/mapfile

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