शेल में एक चर में एक फ़ाइल कैसे पढ़ें?


488

मैं एक फ़ाइल पढ़ना चाहता हूं और इसे चर में सहेजना चाहता हूं, लेकिन मुझे चर रखने की जरूरत है और न कि केवल फ़ाइल का प्रिंट आउट लेने की आवश्यकता है। मैं यह कैसे कर सकता हूँ? मैंने यह स्क्रिप्ट लिखी है, लेकिन यह वैसा नहीं है जैसा मुझे चाहिए था:

#!/bin/sh
while read LINE  
do  
  echo $LINE  
done <$1  
echo 11111-----------  
echo $LINE  

मेरी स्क्रिप्ट में, मैं फ़ाइल नाम को एक पैरामीटर के रूप में दे सकता हूं, इसलिए, यदि फ़ाइल में "आआआ" है, उदाहरण के लिए, यह इसे प्रिंट करेगा:

aaaa
11111-----

लेकिन यह सिर्फ स्क्रीन पर फ़ाइल को प्रिंट करता है, और मैं इसे एक चर में सहेजना चाहता हूं! क्या इसे करने का कोई आसान तरीका है?


1
यह एक सादा पाठ लगता है। यदि यह एक बाइनरी फ़ाइल था, आप की आवश्यकता होगी इस के परिणाम के रूप में catया $(<someFile)एक अधूरी उत्पादन में परिणाम होगा (आकार वास्तविक फ़ाइल से कम है)।
कुंभ राशि

जवाबों:


1052

क्रॉस-प्लेटफ़ॉर्म में, shआपके द्वारा उपयोग किए जाने वाले सबसे कम-सामान्य-भाजक :

#!/bin/sh
value=`cat config.txt`
echo "$value"

में bashया zsh, लागू बिना एक चर में एक पूरे फ़ाइल को पढ़ने के लिए cat:

#!/bin/bash
value=$(<config.txt)
echo "$value"

किसी फ़ाइल catमें घुसना bashया घुसना बिल्ली का बेकार उपयोगzsh माना जाएगा ।

ध्यान दें कि नए विकल्प को संरक्षित करने के लिए कमांड प्रतिस्थापन को उद्धृत करना आवश्यक नहीं है।

देखें: बैश हैकर का विकी - कमांड प्रतिस्थापन - विशेषता


4
ठीक है, लेकिन यह बकवास है, श नहीं; यह सभी मामलों में फिट नहीं हो सकता है।
मूला

14
नहीं चाहेंगे value="`cat config.txt`"और value="$(<config.txt)"मामले में सुरक्षित हो कि config.txt स्पेस हैं?
मार्टिन वॉन विटिच

13
ध्यान दें कि catऊपर का उपयोग करना हमेशा एक बेकार उपयोग नहीं माना जाता है cat। उदाहरण के लिए, < invalid-file 2>/dev/nullएक त्रुटि संदेश होगा जिसके लिए रूट नहीं किया जा सकता है /dev/null, जबकि cat invalid-file 2>/dev/nullठीक से रूट किया जाता है /dev/null
देजय क्लेटन

16
मेरे जैसे नए शेल स्क्रिप्टर्स के लिए, ध्यान दें कि बिल्ली संस्करण वापस टिक्सेस का उपयोग करता है, एकल उद्धरण नहीं! उम्मीद है कि इससे किसी को आधे घंटे का समय लगेगा जो मुझे यह पता लगाने में लगा।
इरिकसनला

7
मेरे जैसे नए बैशर्स के लिए: ध्यान दें कि value=$(<config.txt)अच्छा है, लेकिन value = $(<config.txt)बुरा है। उन स्थानों के लिए बाहर देखो।
ArtHare

88

यदि आप पूरी फ़ाइल को एक चर में पढ़ना चाहते हैं:

#!/bin/bash
value=`cat sources.xml`
echo $value

यदि आप इसे लाइन-बाय-लाइन पढ़ना चाहते हैं:

while read line; do    
    echo $line    
done < file.txt

2
@brain: क्या होता है यदि फ़ाइल config.cpp है और बैकस्लैश सम्‍मिलित है; दोहरे उद्धरण और उद्धरण?
user2284570

2
आपको चर को डबल-कोट करना चाहिए echo "$value"। अन्यथा, शेल मूल्य पर व्हॉट्सएप टोकन और वाइल्डकार्ड विस्तार का प्रदर्शन करेगा।
ट्रिपल

3
@ user2284570 read -rबस के बजाय का उपयोग करें read- हमेशा, जब तक कि आपको विशेष रूप से अजीब विरासत व्यवहार की आवश्यकता नहीं होती है।
ट्रिपल

74

दो महत्वपूर्ण नुकसान

जिन्हें अब तक अन्य उत्तरों द्वारा अनदेखा किया गया था:

  1. कमांड विस्तार से नईलाइन को निकालना
  2. एनयूएल चरित्र हटाने

कमांड विस्तार से नईलाइन को निकालना

यह एक समस्या है:

value="$(cat config.txt)"

प्रकार के समाधान, लेकिन readआधारित समाधान के लिए नहीं ।

कमांड विस्तार ने नई लाइनों को हटा दिया:

S="$(printf "a\n")"
printf "$S" | od -tx1

आउटपुट:

0000000 61
0000001

यह फाइलों से पढ़ने की भोली विधि को तोड़ता है:

FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"

POSIX वर्कअराउंड: अतिरिक्त विस्तार को कमांड एक्सटेंशन में जोड़ें और बाद में इसे हटा दें:

S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1

आउटपुट:

0000000 61 0a 0a
0000003

लगभग POSIX वर्कअराउंड: ASCII सांकेतिक शब्दों में बदलना। निचे देखो।

एनयूएल चरित्र हटाने

चर में NUL वर्णों को संग्रहीत करने के लिए कोई बैन बैश तरीका नहीं है

यह विस्तार और दोनों को प्रभावित करता है read समाधान , और मुझे इसके लिए कोई अच्छा समाधान नहीं पता है।

उदाहरण:

printf "a\0b" | od -tx1
S="$(printf "a\0b")"
printf "$S" | od -tx1

आउटपुट:

0000000 61 00 62
0000003

0000000 61 62
0000002

हा, हमारा एनयूएल चला गया है!

समाधान:

  • ASCII सांकेतिक शब्दों में बदलना। निचे देखो।

  • बैश एक्सटेंशन $""शाब्दिक का उपयोग करें :

    S=$"a\0b"
    printf "$S" | od -tx1

    केवल शाब्दिक के लिए काम करता है, इसलिए फ़ाइलों से पढ़ने के लिए उपयोगी नहीं है।

नुकसान के लिए समाधान

वेरिएबल में फ़ाइल का एक यूएनकोड बेस 64 एनकोडेड संस्करण स्टोर करें और हर उपयोग से पहले डीकोड करें:

FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"

आउटपुट:

0000000 61 00 0a
0000003

uuencode और udecode POSIX 7 हैं, लेकिन डिफ़ॉल्ट रूप से ( sharutilsपैकेज) 12 Ubuntu में नहीं है ... मैं bash प्रक्रिया के लिए POSIX 7 विकल्प नहीं देखता<() किसी अन्य फ़ाइल पर लिखने के अलावा प्रतिस्थापन एक्सटेंशन के ...

बेशक, यह धीमा और असुविधाजनक है, इसलिए मुझे लगता है कि असली उत्तर यह है: यदि इनपुट फ़ाइल में NUL वर्ण हो सकते हैं तो बैश का उपयोग न करें।


2
धन्यवाद केवल इसने मेरे लिए काम किया क्योंकि मुझे नई कहानियों की जरूरत थी।
जेसन लिवेसे

1
@CiroSantilli: अगर FILE कॉन्फ़िग.कैप है और इसमें बैकस्लैश शामिल हैं तो क्या होगा; दोहरे उद्धरण और उद्धरण?
user2284570

@ user2284570 मुझे नहीं पता था, लेकिन यह पता लगाना आसान है S="$(printf "\\\'\"")"; echo $S:। आउटपुट: \'"। तो यह काम करता है =)
Ciro Santilli 冠状

@CiroSantilli: 5511 लाइनों पर? क्या आप सुनिश्चित हैं कि कोई स्वचालित तरीका नहीं है?
user2284570

@ user2284570 मुझे समझ में नहीं आता, जहां 5511 लाइनें हैं? $()विस्तार से नुकसान , मेरे उदाहरण से पता चलता है कि $()विस्तार के साथ काम करता है \'"
सिरो सेंटिल्ली 郝海东 i iro i 法轮功 ''


2

जैसा कि Ciro Santilli के नोट्स में कमांड सब्स्टिट्यूशन का उपयोग करके नई अनुगामी को छोड़ दिया जाएगा। अनुगामी पात्रों को जोड़ने का उनका तरीका बहुत अच्छा है, लेकिन काफी समय तक इसका उपयोग करने के बाद मैंने फैसला किया कि मुझे एक समाधान की आवश्यकता है जो कमांड प्रतिस्थापन का बिल्कुल भी उपयोग नहीं करता है।

मेरा दृष्टिकोण अब बिलिन के झंडेread के साथ-साथ printfस्टड की सामग्री को सीधे एक चर में पढ़ने के लिए उपयोग करता है ।-v

# Reads stdin into a variable, accounting for trailing newlines. Avoids needing a subshell or
# command substitution.
read_input() {
  # Use unusual variable names to avoid colliding with a variable name
  # the user might pass in (notably "contents")
  : "${1:?Must provide a variable to read into}"
  if [[ "$1" == '_line' || "$1" == '_contents' ]]; then
    echo "Cannot store contents to $1, use a different name." >&2
    return 1
  fi

  local _line _contents
   while read -r _line; do
     _contents="${_contents}${_line}"$'\n'
   done
   _contents="${_contents}${_line}" # capture any content after the last newline
   printf -v "$1" '%s' "$_contents"
}

यह newlines के साथ या इसके बिना इनपुट का समर्थन करता है।

उदाहरण उपयोग:

$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file

महान! मुझे बस आश्चर्य होता है, कि क्यों न हम कुछ नया इस्तेमाल करना चाहें _contents="${_contents}${_line}\n "?
Eenoku

1
आप के बारे में पूछ रहे हैं $'\n'? यह आवश्यक है, अन्यथा आप शाब्दिक \ और nपात्रों को जोड़ रहे हैं । आपके कोड ब्लॉक में अंत में एक अतिरिक्त स्थान है, यह सुनिश्चित करने के लिए कि यह जानबूझकर नहीं है, लेकिन यह हर बाद की लाइन को एक अतिरिक्त व्हाट्सएप के साथ इंडेंट करेगा।
dimo414

खैर, स्पष्टीकरण के लिए धन्यवाद!
Eenoku

-3

आप लूप के लिए एक बार में 1 लाइन तक पहुंच सकते हैं

#!/bin/bash -eu

#This script prints contents of /etc/passwd line by line

FILENAME='/etc/passwd'
I=0
for LN in $(cat $FILENAME)
do
    echo "Line number $((I++)) -->  $LN"
done

फ़ाइल के लिए संपूर्ण सामग्री की प्रतिलिपि बनाएँ (पंक्ति कहिए। शश); निष्पादित

chmod +x line.sh
./line.sh

आपका forलूप लाइनों पर लूप नहीं करता है, यह शब्दों पर लूप करता है। के मामले में /etc/passwd, ऐसा होता है कि प्रत्येक पंक्ति में केवल एक शब्द होता है। हालाँकि, अन्य फ़ाइलों में प्रति पंक्ति कई शब्द हो सकते हैं।
MPB
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.