बैश में फ़ाइल या STDIN से कैसे पढ़ें?


244

निम्न पर्ल स्क्रिप्ट ( my.pl) कमांड लाइन आर्ग्स पर फ़ाइल से या STDIN से पढ़ सकती है:

while (<>) {
   print($_);
}

perl my.plSTDIN से पढ़ेगा, जबकि perl my.pl a.txtसे पढ़ेगा a.txt। यह बहुत सुविधाजनक है।

आश्चर्य है कि क्या बैश में एक समान है?

जवाबों:


409

निम्न समाधान एक फ़ाइल से पढ़ता है यदि स्क्रिप्ट को फ़ाइल नाम के साथ पहले पैरामीटर के रूप में कहा जाता है $1अन्यथा मानक इनपुट से।

while read line
do
  echo "$line"
done < "${1:-/dev/stdin}"

प्रतिस्थापन ${1:-...}को $1परिभाषित किया जाता है, अन्यथा स्वयं की प्रक्रिया के मानक इनपुट का फ़ाइल नाम उपयोग किया जाता है।


1
अच्छा लगा, यह काम करता है। एक और सवाल यह है कि आप इसके लिए एक उद्धरण क्यों जोड़ते हैं? "$ {1: - / proc / $ {$} / fd / 0}"
दगांग

15
कमांड लाइन पर आपके द्वारा आपूर्ति किया जाने वाला फ़ाइल नाम रिक्त हो सकता है।
फ्रिट्ज जी। मेहनर

3
क्या उपयोग करने /proc/$$/fd/0और के बीच कोई अंतर है /dev/stdin? मैंने देखा कि उत्तरार्द्ध अधिक सामान्य प्रतीत होता है और अधिक सीधा दिखता है।
पताह

19
-rअपने readआदेश में जोड़ने के लिए बेहतर है , ताकि यह गलती से \ चरस न खाए ; while IFS= read -r lineव्हाट्सएप को अग्रणी और अनुगामी बनाने के लिए उपयोग करें।
mklement0

1
@NeDark: यह उत्सुक है; मैंने अभी सत्यापित किया कि यह उस प्लेटफ़ॉर्म पर काम करता है, यहां तक ​​कि उपयोग करते समय /bin/sh- क्या आप इसके अलावा bashया किसी अन्य शेल का उपयोग कर रहे हैं sh?
mklement0

119

शायद सबसे सरल समाधान एक विलय पुनर्निर्देशित ऑपरेटर के साथ स्टड को पुनर्निर्देशित करना है:

#!/bin/bash
less <&0

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

फ़ाइल विवरणक पुनर्निर्देशन के बारे में और पढ़ें


1
काश मैं तुम्हें देने के लिए और अधिक upvotes था, मैं इस साल के लिए देख रहा हूँ।
मार्कस डाउनिंग

13
<&0इस स्थिति में उपयोग करने का कोई लाभ नहीं है - आपका उदाहरण इसके साथ या इसके बिना भी काम करेगा - प्रतीत होता है कि, उपकरण जो आप एक bash स्क्रिप्ट के भीतर से डिफ़ॉल्ट रूप से आह्वान करते हैं, वही स्टड स्वयं स्क्रिप्ट के रूप में देखते हैं (जब तक कि स्क्रिप्ट पहले इसका सेवन नहीं करती)।
mklement0

@ mkelement0 तो अगर कोई उपकरण आधा इनपुट बफर पढ़ता है, तो क्या अगला उपकरण जो मुझे मिलता है, उसे बाकी मिलेगा?
असद सईदुद्दीन

"मिसिंग फाइलनेम (" कम
हेल्प

5
इस उत्तर में "या फ़ाइल से" भाग कहाँ है?
सेबेस्टियन

84

यहाँ सबसे आसान तरीका है:

#!/bin/sh
cat -

उपयोग:

$ echo test | sh my_script.sh
test

चर को स्टडिन असाइन करने के लिए , आप उपयोग कर सकते हैं: STDIN=$(cat -)या बस के STDIN=$(cat)रूप में ऑपरेटर आवश्यक नहीं है ( @ mklement0 टिप्पणी के अनुसार )।


मानक इनपुट से प्रत्येक पंक्ति को पार्स करने के लिए , निम्न स्क्रिप्ट आज़माएँ:

#!/bin/bash
while IFS= read -r line; do
  printf '%s\n' "$line"
done

फ़ाइल या स्टडिन से पढ़ने के लिए (यदि तर्क मौजूद नहीं है), आप इसे इस तक बढ़ा सकते हैं:

#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")

टिप्पणियाँ:

- read -r- किसी विशेष तरीके से बैकस्लैश चरित्र का इलाज न करें। इनपुट लाइन का हिस्सा बनने के लिए प्रत्येक बैकलैश पर विचार करें।

- बिना सेटिंग के IFS, डिफ़ॉल्ट रूप से Spaceऔर Tabलाइनों की शुरुआत और अंत में (ट्रिम की गई) अनदेखी की जाती है।

- खाली लाइनों को प्रिंट करने से बचने printfके echoलिए इसका उपयोग करें जब लाइन में एकल -e, -nया हो -E। हालाँकि इसमें एक वर्कअराउंड है env POSIXLY_CORRECT=1 echo "$line"जिसका उपयोग करके आपके बाहरी GNU echoको निष्पादित करता है जो इसका समर्थन करता है। देखें: मैं "-ई" कैसे गूंजूं?

देखें: जब कोई तर्क पारित नहीं किया जाता है तो स्टड कैसे पढ़ें? स्टैकओवरफ्लो एसई पर


आप को सरल बना सकता [ "$1" ] && FILE=$1 || FILE="-"है FILE=${1:--}। (वक्रोक्ति: पर्यावरण चर के साथ नाम टकराव से बचने के लिए सभी अपरकेस शेल चर से बचने के लिए बेहतर है ।)
mklement0

मेरा सौभाग्य; वास्तव में, ${1:--} है POSIX अनुरूप, तो यह गोले सभी POSIX की तरह में काम करना चाहिए। ऐसे सभी गोले में जो काम नहीं करेगा वह प्रक्रिया प्रतिस्थापन है ( <(...)); उदाहरण के लिए, यह bash, ksh, zsh में काम करेगा, लेकिन डैश में नहीं। इसके अलावा, -rअपने readआदेश में जोड़ने के लिए बेहतर है , ताकि यह गलती से \ चरस न खाए ; व्हाट्सएप IFS= को अग्रणी और अनुगामी बनाने के लिए तैयार करना।
mklement0

4
वास्तव में आपका कोड अभी भी इस वजह से टूटता है echo: यदि एक पंक्ति में शामिल हैं -e, -nया -E, यह नहीं दिखाया जाएगा। इसे ठीक करने के लिए, आप का उपयोग करना चाहिए printf: printf '%s\n' "$line"। मैंने इसे अपने पिछले संपादन में शामिल नहीं किया ... जब मैं इस त्रुटि को ठीक करता हूं तो अक्सर मेरे संपादन रोलबैक हो जाते हैं :(
gourourf_gniourf

1
नहीं, यह विफल नहीं है। और --बेकार है अगर पहला तर्क है'%s\n'
gniourf_gniourf

1
मेरे द्वारा आपका जवाब ठीक है (मेरा मतलब है कि मैं कोई भी बग या अवांछित सुविधाओं से अवगत नहीं हूं) - हालांकि यह कई तर्कों का इलाज नहीं करता है जैसा कि पेरेंट करता है। वास्तव में, यदि आप कई तर्क को संभालना चाहते हैं, तो आप जोनाथन लेफ़लर के उत्कृष्ट उत्तर को लिखना समाप्त कर देंगे - वास्तव में आप बेहतर होंगे क्योंकि आप इसके IFS=साथ readऔर printfइसके बजाय का उपयोग करेंगे echo:)
ग्नौरफ_ग्निऑरफ

19

मुझे लगता है कि यह सीधा-सीधा तरीका है:

$ cat reader.sh
#!/bin/bash
while read line; do
  echo "reading: ${line}"
done < /dev/stdin

-

$ cat writer.sh
#!/bin/bash
for i in {0..5}; do
  echo "line ${i}"
done

-

$ ./writer.sh | ./reader.sh
reading: line 0
reading: line 1
reading: line 2
reading: line 3
reading: line 4
reading: line 5

4
यह या तो स्टड या एक फ़ाइल तर्क से पढ़ने के लिए पोस्टर द्वारा आवश्यकता को फिट नहीं करता है, यह सिर्फ स्टड से पढ़ता है।
nash

2
छोड़कर @ नैश के वैध आपत्ति अलग: readstdin से पढ़ता है डिफ़ॉल्ट रूप से है, तो वहाँ कोई जरूरत के लिए < /dev/stdin
mklement0

13

echoसमाधान नई लाइनों जब भी कहते हैं IFSइनपुट धारा टूट जाता है। @ fgm के उत्तर को थोड़ा संशोधित किया जा सकता है:

cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"

क्या आप बता सकते हैं कि "इको सॉल्यूशन जब भी आईएफएस इनपुट स्ट्रीम को तोड़ता है" तो आप क्या कहते हैं? मामले में आप की बात कर रहे थे readके व्यवहार: जबकि read करता संभावित वर्ण द्वारा कई टोकन में विभाजित। इसमें निहित है $IFS, यह केवल एक एकल टोकन देता है यदि आप केवल एक एकल चर नाम निर्दिष्ट करते हैं (लेकिन डिफ़ॉल्ट रूप से ट्रिम्स और अग्रणी और अनुगामी व्हाट्सएप)।
mklement0

@ mklement0 मैं आपके साथ व्यवहार पर 100% सहमत हूं readऔर $IFS- झंडे के echoबिना खुद नई लाइनें जोड़ता हूं -n। "इको यूटिलिटी किसी भी निर्दिष्ट ऑपरेंड को लिखती है, जो सिंगल ब्लैंक (` ') कैरेक्टर्स द्वारा अलग किया जाता है और उसके बाद एक न्यूलाइन (`\ n') कैरेक्टर को स्टैंडर्ड आउटपुट में लाया जाता है।"
डेविड सूथर

समझ गया। हालाँकि, पर्ल लूप का अनुकरण करने के लिए आपको इसके द्वारा जोड़े गए अनुगामी की आवश्यकता होती\n है echo: पर्ल के पास रीड लाइन की समाप्ति रेखा $_ शामिल है \n, जबकि बैश readनहीं है। (हालांकि, @gniourf_gniourf कहीं और इंगित करता है, और अधिक मजबूत दृष्टिकोण के printf '%s\n'बदले में उपयोग करना है echo)।
mklement0

8

प्रश्न में पर्ल लूप कमांड लाइन पर सभी फ़ाइल नाम तर्कों से या किसी फ़ाइल के निर्दिष्ट न होने पर मानक इनपुट से पढ़ता है । यदि कोई फ़ाइल निर्दिष्ट नहीं है, तो मैं जो उत्तर देखता हूं, वे सभी किसी एकल फ़ाइल या मानक इनपुट को संसाधित करते हैं।

यद्यपि अक्सर यूयूओसी (बेकार उपयोग cat) के रूप में सटीक रूप से व्युत्पन्न किया जाता है , ऐसे समय होते हैं जब catनौकरी के लिए सबसे अच्छा उपकरण होता है, और यह यकीन है कि यह उनमें से एक है:

cat "$@" |
while read -r line
do
    echo "$line"
done

इसका एकमात्र नकारात्मक पहलू यह है कि यह एक उप-शेल में चलने वाली पाइपलाइन बनाता है, इसलिए whileलूप में चर असाइनमेंट जैसी चीजें पाइपलाइन के बाहर पहुंच योग्य नहीं हैं। जिस bashतरह से चारों ओर प्रक्रिया प्रतिस्थापन है :

while read -r line
do
    echo "$line"
done < <(cat "$@")

यह whileलूप को मुख्य शेल में छोड़ता है , इसलिए लूप में सेट किए गए वेरिएबल लूप के बाहर पहुंचते हैं।


1
कई फाइलों के बारे में उत्कृष्ट बिंदु । मुझे नहीं पता कि संसाधन और प्रदर्शन के निहितार्थ क्या होंगे, लेकिन अगर आप बैश, ksh, या zsh पर नहीं हैं और इसलिए प्रक्रिया प्रतिस्थापन का उपयोग नहीं कर सकते हैं, तो आप यहां कमांड प्रतिस्थापन के साथ कोशिश कर सकते हैं (3 में फैली) लाइनें) >>EOF\n$(cat "$@")\nEOF। अंत में, एक वक्रोक्ति: पर्ल में while IFS= read -r lineक्या while (<>)होता है, इसका एक बेहतर अनुमान है (व्हाट्सएप को अग्रणी और अनुगामी बना देता है - हालांकि पर्ल भी अनुगामी रहता है \n)।
mklement0

4

ओपी में दिए गए कोड के साथ पर्ल का व्यवहार, कोई भी या कई तर्क नहीं ले सकता है, और यदि कोई तर्क एकल हाइफ़न है, तो -इसे स्टडिन के रूप में समझा जाता है। इसके अलावा, हमेशा फ़ाइल नाम के साथ संभव है $ARGV। अब तक दिए गए उत्तरों में से कोई भी वास्तव में इन मामलों में पर्ल के व्यवहार की नकल नहीं करता है। यहाँ एक शुद्ध बैश संभावना है। चाल का execउचित उपयोग करना है।

#!/bin/bash

(($#)) || set -- -
while (($#)); do
   { [[ $1 = - ]] || exec < "$1"; } &&
   while read -r; do
      printf '%s\n' "$REPLY"
   done
   shift
done

में उपलब्ध है $1

यदि कोई तर्क नहीं दिया जाता है, तो हम कृत्रिम -रूप से पहले स्थितीय पैरामीटर के रूप में सेट होते हैं । हम फिर मापदंडों पर लूप करते हैं। यदि कोई पैरामीटर नहीं है -, तो हम फ़ाइल नाम से मानक इनपुट को पुनर्निर्देशित करते हैं exec। यदि यह पुनर्निर्देशन सफल होता है तो हम एक whileलूप के साथ लूप करते हैं। मैं मानक REPLYचर का उपयोग कर रहा हूं , और इस मामले में आपको रीसेट करने की आवश्यकता नहीं है IFS। यदि आप कोई दूसरा नाम चाहते हैं, तो आपको ऐसा अवश्य करना चाहिए IFS(जब तक, निश्चित रूप से, आप ऐसा नहीं चाहते हैं और जानते हैं कि आप क्या कर रहे हैं):

while IFS= read -r line; do
    printf '%s\n' "$line"
done

2

अधिक सटीकता से...

while IFS= read -r line ; do
    printf "%s\n" "$line"
done < file

2
मुझे लगता है कि यह मूल रूप से stackoverflow.com/a/6980232/45375 पर एक टिप्पणी है , उत्तर नहीं। जोड़ने: टिप्पणी स्पष्ट करने के लिए IFS=और -r करने के लिए readआदेश सुनिश्चित करें कि प्रत्येक पंक्ति पढ़ी जाती है असंशोधित (प्रमुख और रिक्त स्थान को अनुगामी सहित)।
mklement0

2

कृपया निम्नलिखित कोड आज़माएँ:

while IFS= read -r line; do
    echo "$line"
done < file

1
ध्यान दें कि यहां तक ​​कि संशोधित किया गया है, यह मानक इनपुट से या कई फ़ाइलों से नहीं पढ़ा जाएगा, इसलिए यह प्रश्न का पूर्ण उत्तर नहीं है। (उत्तर प्रस्तुत होने के 3 साल से अधिक समय बाद मिनटों के मामले में दो संपादनों को देखना भी आश्चर्यजनक है।)
जोनाथन लेफ़लर

@JonathanLeffler को इस तरह के एक पुराने (और वास्तव में अच्छा नहीं) के जवाब के लिए खेद है ... लेकिन मैं इस गरीब readको IFS=और उसके स्वस्थ उद्धरण के बिना -rगरीबों $lineको देखकर खड़ा नहीं हो सकता ।
ग्नौरफ_ग्निऑरफ

1
@gniourf_gniourf: मैं read -rसंकेतन को नापसंद करता हूं । IMO, POSIX को गलत मिला; विकल्प को बैकस्लैश को पीछे हटाने के लिए विशेष अर्थ को सक्षम करना चाहिए, इसे अक्षम नहीं करना चाहिए - ताकि मौजूदा स्क्रिप्ट (POSIX मौजूद होने से पहले) टूट न -rजाए क्योंकि छोड़ा गया था। मैं, हालांकि, यह IEEE 1003.2 1992 का हिस्सा था, जो कि POSIX खोल और उपयोगिताओं के मानक संस्करण का हिस्सा था, लेकिन यह तब भी एक अतिरिक्त के रूप में चिह्नित किया गया था, इसलिए यह लंबे समय से चले आ रहे अवसरों के बारे में है। मैं कभी परेशानी में नहीं आया क्योंकि मेरा कोड उपयोग नहीं करता है -r; मैं भाग्यशाली होना चाहिए। इस पर मेरी उपेक्षा करो।
जोनाथन लेफ़लर

1
@JonathanLeffler मैं वास्तव में सहमत हूं कि -rमानक होना चाहिए। मैं मानता हूं कि यह उन मामलों में होने की संभावना नहीं है जहां इसका उपयोग नहीं करने से परेशानी होती है। हालांकि, टूटा हुआ कोड टूटा हुआ कोड है। मेरा संपादन पहली बार उस खराब $lineवैरिएबल से शुरू हुआ था जो अपने उद्धरणों को बुरी तरह से याद करता था। मैंने उस पर readसमय निर्धारित किया । मैंने ठीक नहीं किया echoक्योंकि यह उस तरह का संपादन है जो वापस लुढ़क जाता है। :(
ग्नौरफ_ग्निऑरफ

1

कोड ${1:-/dev/stdin}केवल पहले तर्क को समझेगा, इसलिए, इस बारे में कैसे।

ARGS='$*'
if [ -z "$*" ]; then
  ARGS='-'
fi
eval "cat -- $ARGS" | while read line
do
   echo "$line"
done

1

मुझे इनमें से कोई उत्तर स्वीकार्य नहीं लगता। विशेष रूप से, स्वीकृत उत्तर केवल पहले कमांड लाइन पैरामीटर को संभालता है और बाकी को अनदेखा करता है। पर्ल प्रोग्राम जो यह अनुकरण करने की कोशिश कर रहा है कि सभी कमांड लाइन मापदंडों को संभालता है। तो स्वीकृत जवाब भी सवाल का जवाब नहीं देता है। अन्य उत्तर बैश एक्सटेंशन का उपयोग करते हैं, अनावश्यक 'कैट' कमांड जोड़ते हैं, केवल आउटपुट के लिए इको इनपुट के सरल मामले के लिए काम करते हैं, या बस अनावश्यक रूप से जटिल हैं।

हालाँकि, मुझे उन्हें कुछ श्रेय देना होगा क्योंकि उन्होंने मुझे कुछ विचार दिए। ये रहा पूरा जवाब:

#!/bin/sh

if [ $# = 0 ]
then
        DEFAULT_INPUT_FILE=/dev/stdin
else
        DEFAULT_INPUT_FILE=
fi

# Iterates over all parameters or /dev/stdin
for FILE in "$@" $DEFAULT_INPUT_FILE
do
        while IFS= read -r LINE
        do
                # Do whatever you want with LINE here.
                echo $LINE
        done < "$FILE"
done

1

मैंने उपरोक्त सभी उत्तरों को संयोजित किया और एक शेल फ़ंक्शन बनाया जो मेरी आवश्यकताओं के अनुरूप होगा। यह मेरी 2 विंडोज 10 मशीनों के एक साइबरविन टर्मिनल से है जहां मेरे बीच एक साझा फ़ोल्डर था। मुझे निम्नलिखित को संभालने में सक्षम होना चाहिए:

  • cat file.cpp | tx
  • tx < file.cpp
  • tx file.cpp

जहां एक विशिष्ट फ़ाइल नाम निर्दिष्ट किया गया है, मुझे कॉपी के दौरान उसी फ़ाइलनाम का उपयोग करने की आवश्यकता है। जहां इनपुट डेटा स्ट्रीम को थ्रू पाइप किया गया है, तो मुझे एक अस्थायी फ़ाइल नाम उत्पन्न करना होगा जिसमें घंटे मिनट और सेकंड हों। साझा किए गए मुख्य फ़ोल्डर में सप्ताह के दिनों के सबफ़ोल्डर हैं। यह संगठनात्मक उद्देश्यों के लिए है।

निहारना, मेरी जरूरतों के लिए परम स्क्रिप्ट:

tx ()
{
  if [ $# -eq 0 ]; then
    local TMP=/tmp/tx.$(date +'%H%M%S')
    while IFS= read -r line; do
        echo "$line"
    done < /dev/stdin > $TMP
    cp $TMP //$OTHER/stargate/$(date +'%a')/
    rm -f $TMP
  else
    [ -r $1 ] && cp $1 //$OTHER/stargate/$(date +'%a')/ || echo "cannot read file"
  fi
}

अगर कोई ऐसा तरीका है जिसे आप आगे भी इसे अनुकूलित करने के लिए देख सकते हैं, तो मैं जानना चाहूंगा।


0

निम्नलिखित मानक के साथ काम करता है sh( dashडेबियन पर परीक्षण किया गया) और काफी पठनीय है, लेकिन यह स्वाद की बात है:

if [ -n "$1" ]; then
    cat "$1"
else
    cat
fi | commands_and_transformations

विवरण: यदि पहला पैरामीटर गैर-रिक्त है तो catवह फ़ाइल, अन्यथा catमानक इनपुट। तब पूरे ifस्टेटमेंट के आउटपुट को प्रोसेस किया जाता है commands_and_transformations


IMHO सबसे अच्छा जवाब है क्योंकि यह सही समाधान की ओर इशारा करता है cat "${1:--}" | any_command:। शेल वेरिएबल्स को पढ़ना और उन्हें गूंजना छोटी फाइलों के लिए काम कर सकता है लेकिन इतनी अच्छी तरह से स्केल नहीं करता है।
एंड्रियास स्पिंडलर

[ -n "$1" ]करने के लिए सरल किया जा सकता [ "$1" ]
एजीसी

0

यह टर्मिनल पर उपयोग करना आसान है:

$ echo '1\n2\n3\n' | while read -r; do echo $REPLY; done
1
2
3

-1

कैसा रहेगा

for line in `cat`; do
    something($line);
done

के आउटपुट catको कमांड लाइन में रखा जाएगा। कमांड लाइन का अधिकतम आकार है। इसके अलावा यह लाइन से लाइन नहीं पढ़ेगा, लेकिन शब्द से शब्द।
Notinlist
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.