बैश के रीड-ए-फाइल कमांड प्रतिस्थापन को समझना


11

मैं यह समझने की कोशिश कर रहा हूं कि बैश निम्नलिखित पंक्ति में कैसे व्यवहार करता है:

$(< "$FILE")

बैश मैन पेज के अनुसार, यह इसके बराबर है:

$(cat "$FILE")

और मैं इस दूसरी पंक्ति के लिए तर्क की पंक्ति का अनुसरण कर सकता हूं। बैश पर चर विस्तार करता है $FILE, कमांड प्रतिस्थापन में प्रवेश करता है, के मान से गुजरता $FILEहै cat, बिल्ली $FILEमानक आउटपुट की सामग्री को आउटपुट करती है, कमांड प्रतिस्थापन अंदर से उत्पन्न मानक आउटपुट के साथ पूरी लाइन को बदलकर खत्म करता है, और बैश इसे निष्पादित करने का प्रयास करता है। एक साधारण आदेश।

हालाँकि, पहली पंक्ति के लिए मैंने ऊपर उल्लेख किया है, मैं इसे समझता हूं: बैश पर चर प्रतिस्थापन होता है $FILE, $FILEमानक इनपुट पर पढ़ने के लिए बैश खुलता है , किसी भी तरह मानक इनपुट को मानक आउटपुट , कमांड प्रतिस्थापन फिनिश और कॉपी किया जाता है , और बैश परिणामी मानक को निष्पादित करने का प्रयास करता है। उत्पादन।

क्या कोई मुझे समझा सकता है कि $FILEस्टड से स्टडआउट तक सामग्री कैसे जाती है?

जवाबों:


-3

<सीधे का एक पहलू नहीं है बैश आदेश प्रतिस्थापन । यह एक पुनर्निर्देशन ऑपरेटर (एक पाइप की तरह) है, जिसे कुछ गोले बिना कमांड के अनुमति देते हैं (POSIX इस व्यवहार को निर्दिष्ट नहीं करता है)।

शायद यह अधिक रिक्त स्थान के साथ अधिक स्पष्ट होगा:

echo $( < $FILE )

यह प्रभावी रूप से * अधिक POSIX-safe के समान है

echo $( cat $FILE )

... जो प्रभावी रूप से * भी है

echo $( cat < $FILE )

चलिए उस आखिरी संस्करण से शुरू करते हैं। यह catबिना किसी तर्क के चलता है, जिसका अर्थ है कि यह मानक इनपुट से पढ़ेगा। $FILEके कारण मानक इनपुट में पुनर्निर्देशित किया जाता है <, इसलिए catइसकी सामग्री को मानक आउटपुट में डाल दिया जाता है। $(command)Subsitution तो धक्का catके लिए बहस में की उत्पादन echo

में bash(लेकिन POSIX मानक में नहीं), आप <एक कमांड के बिना उपयोग कर सकते हैं । bash(और zshऔर kshनहीं लेकिन dash) की व्याख्या करेगा कि मानो cat <, हालांकि एक नया उपप्रक्रिया लागू बिना। जैसा कि यह शेल का मूल है, यह शाब्दिक रूप से बाहरी कमांड चलाने से तेज है cat* यही कारण है कि मैं कहता हूं "प्रभावी रूप से उसी के रूप में।"


तो अंतिम पैराग्राफ में जब आप कहते हैं " bashव्याख्या करेंगे कि cat filename" के रूप में , क्या आपका मतलब यह व्यवहार कमांड प्रतिस्थापन के लिए विशिष्ट है? क्योंकि अगर मैं < filenameखुद से दौड़ता हूं , तो बैश उसे बाहर नहीं निकालता है। यह कुछ भी नहीं उत्पादन और मुझे एक शीघ्र वापस कर देगा।
स्टेनली यू

अभी भी एक कमांड की जरूरत है। @cuonglm से अपने मूल पाठ को बदल दिया cat < filenameकरने के लिए cat filenameजो मैं विरोध और वापस ला सकते हैं।
एडम काट्ज

1
एक पाइप एक प्रकार की फ़ाइल है। शेल ऑपरेटर |दो सबप्रोसेस के बीच एक पाइप बनाता है (या, कुछ शेल के साथ, एक सबप्रोसेस से शेल के स्टैंडर्ड इनपुट के लिए)। शेल ऑपरेटर $(…)एक उपप्रोसेस से शेल में एक पाइप बनाता है (अपने मानक इनपुट के लिए नहीं)। शेल ऑपरेटर <में एक पाइप शामिल नहीं है, यह केवल एक फ़ाइल खोलता है और फ़ाइल डिस्क्रिप्टर को मानक इनपुट में स्थानांतरित करता है।
गिल्स एसओ- बुराई को रोकना '

3
< fileके रूप में ही नहीं है cat < file( zshजहां यह पसंद है को छोड़कर $READNULLCMD < file) < fileपूरी तरह से POSIX है और सिर्फ fileपढ़ने के लिए खुलता है और फिर कुछ भी नहीं करता है (इसलिए fileसीधे करीब है)। यह $(< file)या `< file`कि का एक विशेष ऑपरेटर है ksh, zshऔर bash(और व्यवहार को POSIX में अनिर्दिष्ट छोड़ दिया गया है)। देखें मेरा उत्तर जानकारी के लिए।
स्टीफन चेज़लस

2
@ StéphaneChazelas की टिप्पणी को अन्य प्रकाश में रखना: पहले सन्निकटन के लिए, $(cmd1) $(cmd2)आमतौर पर उसी के समान होगा $(cmd1; cmd2)। लेकिन मामले को देखो जहां cmd2है < file। यदि हम कहते हैं $(cmd1; < file), फ़ाइल पढ़ा नहीं गया है, लेकिन $(cmd1) $(< file), यह है। इसलिए यह कहना गलत है कि $(< file)यह केवल एक साधारण मामला है $(command)जिसकी कमान है < file।   कमांड प्रतिस्थापन $(< …)का एक विशेष मामला है, और पुनर्निर्देशन का सामान्य उपयोग नहीं है।
स्कॉट

14

$(<file)(साथ काम भी करता है `<file`) कोर्न शेल के एक विशेष ऑपरेटर द्वारा कॉपी किया गया zshऔर bash। यह कमांड प्रतिस्थापन की तरह दिखता है, लेकिन यह वास्तव में नहीं है।

POSIX गोले में, एक साधारण कमांड है:

< file var1=value1 > file2 cmd 2> file3 args 3> file4

सभी भाग वैकल्पिक हैं, आप केवल पुनर्निर्देशन कर सकते हैं, केवल कमांड, केवल असाइनमेंट या संयोजन।

यदि पुनर्निर्देशन हैं, लेकिन कोई आदेश नहीं है, तो पुनर्निर्देशन किया जाता है (इसलिए > fileखुल जाएगा और छोटा हो जाएगा file), लेकिन तब कुछ भी नहीं होता है। इसलिए

< file

fileपढ़ने के लिए खोलता है, लेकिन तब कुछ भी नहीं होता है क्योंकि कोई आदेश नहीं है। तो fileफिर बंद है और यह बात है। यदि $(< file)एक साधारण कमांड प्रतिस्थापन था , तो इसका विस्तार कुछ भी नहीं होगा।

में POSIX विनिर्देशन , में $(script), अगर scriptकेवल पुनर्निर्देशन के होते हैं, कि अनिर्दिष्ट परिणाम का उत्पादन । कि कोर्न शेल के उस विशेष व्यवहार की अनुमति है।

Ksh में (यहां परीक्षण किया गया ksh93u+), यदि स्क्रिप्ट में एक और केवल एक साधारण कमांड है (हालांकि टिप्पणियों को पहले और बाद में अनुमति दी गई है) जिसमें केवल पुनर्निर्देशन (कोई आदेश, कोई असाइनमेंट) नहीं है और यदि पहला पुनर्निर्देशन एक स्टड है (fd) 0) इनपुट केवल ( <, <<या <<<) पुनर्निर्देशन, इसलिए:

  • $(< file)
  • $(0< file)
  • $(<&3)(यह भी $(0>&3)वास्तव में एक ही ऑपरेटर के प्रभाव में है)
  • $(< file > foo 2> $(whatever))

लेकिन नहीं:

  • $(> foo < file)
  • $(0<> file)
  • $(< file; sleep 1)
  • $(< file; < file2)

फिर

  • सभी लेकिन पहले पुनर्निर्देशन को नजरअंदाज किया जाता है (उन्हें दूर रखा जाता है)
  • और यह फ़ाइल / heredoc / herestring की सामग्री (या जो कुछ भी फ़ाइल का उपयोग करने वाला फ़ाइल विवरणक से पढ़ा जा सकता है) की सीमा तक फैलता है <&3, अनुगामी न्यूलाइन वर्णों को घटाता है।

जैसे $(cat < file)कि इसके अलावा उपयोग करना

  • रीडिंग आंतरिक रूप से शेल द्वारा की जाती है और इसके द्वारा नहीं cat
  • कोई पाइप और न ही अतिरिक्त प्रक्रिया शामिल है
  • ऊपर के परिणाम के रूप में, क्योंकि अंदर कोड एक उपधारा में नहीं चलाया जाता है, इसके बाद कोई भी संशोधन शेष रहता है (जैसे $(<${file=foo.txt})या $(<file$((++n))))
  • रीड एरर (हालाँकि फाइल खोलने या फाइल डिस्क्रिप्टर को डुप्लिकेट करते समय त्रुटियाँ नहीं हैं) को चुपचाप नजरअंदाज कर दिया जाता है।

में zsh, यह सिवाय इसके कि उस विशेष व्यवहार केवल शुरू हो रहा है जब केवल एक फ़ाइल इनपुट पुनर्निर्देशन है ही है ( <fileया 0< fileनहीं, कोई <&3, <<<here, < a < b...)

हालाँकि, अन्य गोले का अनुकरण करते समय:

< file
<&3
<<< here...

जब आदेश के बाहर केवल इनपुट पुनर्निर्देशन होते हैं, तो कमांड प्रतिस्थापन के बाहर, (डिफ़ॉल्ट रूप से एक पेजर) zshचलाता है $READNULLCMD, और जब इनपुट और आउटपुट दोनों पुनर्निर्देशन होते हैं, तो $NULLCMD( catडिफ़ॉल्ट रूप से), इसलिए भले ही $(<&3)उस विशेष के रूप में मान्यता प्राप्त न हो। ऑपरेटर, यह अभी भी काम करेगा, kshहालांकि इसे करने के लिए एक पेजर को आमंत्रित करके (यह कि पेजर अभिनय कर रहा है catक्योंकि इसके स्टडआउट एक पाइप होगा)।

हालांकि, जबकि ksh'एस $(< a < b)की सामग्री के लिए विस्तार होगा aमें, zshहै, यह की सामग्री के लिए फैलता है aऔर b(या बस bअगर multiosविकल्प अक्षम होता है), $(< a > b)की नक़ल की aकरने के लिए bऔर कुछ भी नहीं है, आदि का विस्तार

bash एक समान ऑपरेटर है लेकिन कुछ अंतरों के साथ:

  • टिप्पणियों की अनुमति पहले है लेकिन बाद में नहीं:

    echo "$(
       # getting the content of file
       < file)"
    

    काम करता है लेकिन:

    echo "$(< file
       # getting the content of file
    )"
    

    विस्तार कुछ भी नहीं।

  • जैसे zsh, केवल एक फ़ाइल स्टड पुनर्निर्देशन, हालांकि इसमें कोई गिरावट नहीं है $READNULLCMD, इसलिए $(<&3), $(< a < b)पुनर्निर्देशन करें, लेकिन विस्तार कुछ भी नहीं करें।

  • किसी कारण के लिए, जब bashयह आह्वान नहीं करता है cat, तब भी यह एक ऐसी प्रक्रिया की तलाश करता है जो फ़ाइल की सामग्री को एक पाइप के माध्यम से खिलाती है, जो इसे अन्य गोले की तुलना में बहुत कम अनुकूलन बनाती है। यह एक बिलियन होगा $(cat < file)जहां प्रभाव की तरह है ।catcat
  • उपरोक्त के परिणामस्वरूप, किसी भी परिवर्तन को बाद में खो दिया जाता है ( $(<${file=foo.txt})उदाहरण के लिए, ऊपर उल्लेखित है, कि $fileअसाइनमेंट आगे की तरफ खो गया है)।

में bash, IFS= read -rd '' var < file (यह भी काम करता है zsh) एक पाठ फ़ाइल की सामग्री को एक चर में पढ़ने के लिए एक अधिक प्रभावी तरीका है । इसमें अनुगामी न्यूलाइन वर्णों को संरक्षित करने का भी लाभ है। यह भी देखें $mapfile[file]में zsh(में zsh/mapfileमॉड्यूल और केवल नियमित रूप से फ़ाइलों के लिए) है, जिसकी द्विआधारी फाइलों के साथ काम करता है।

ध्यान दें कि pdksh- आधारित वेरिएंट में kshksh93 की तुलना में कुछ विविधताएँ हैं। ब्याज में, mksh(उन pdksh व्युत्पन्न गोले में से एक), में

var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)

इसमें अनुकूलित किया गया है कि यहाँ दस्तावेज़ की सामग्री (अनुगामी पात्रों के बिना) को एक अस्थायी फ़ाइल या पाइप के बिना विस्तारित किया जाता है जैसा कि अन्यथा उपयोग किया जा रहा है अन्यथा यहाँ दस्तावेजों के लिए मामला है, जो इसे एक प्रभावी मल्टी-लाइन उद्धरण सिंटैक्स बनाता है।

के सभी संस्करणों के लिए पोर्टेबल होना ksh, zshऔर bash, केवल $(<file)टिप्पणियों और असर को ध्यान में रखते हुए सीमित करना है कि भीतर किए गए चर में संशोधन किया जा सकता है या संरक्षित नहीं किया जा सकता है।


क्या यह सही है कि $(<)फ़ाइल नाम पर एक ऑपरेटर है? है <में $(<)एक पुनर्निर्देशन ऑपरेटर नहीं है, या अपने आप ही एक ऑपरेटर, और पूरे ऑपरेटर का हिस्सा होना चाहिए $(<)?
टिम

@ टिम, यह बहुत मायने नहीं रखता कि आप उन्हें कैसे कॉल करना चाहते हैं। $(<file)के fileरूप में एक समान तरीके से सामग्री का विस्तार करने के लिए है $(cat < file)। यह कैसे किया जाता है यह शेल से शेल में भिन्न होता है जो उत्तर में लंबाई पर वर्णित होता है। यदि आप चाहें, तो आप कह सकते हैं कि यह एक विशेष ऑपरेटर है जो तब ट्रिगर होता है जब एक कमांड प्रतिस्थापन (सिंटैक्टिकली) जैसा दिखता है, जिसमें एक एकल स्टड पुनर्निर्देशन (सिंटैक्टिकली) जैसा दिखता है, लेकिन फिर से शेल और विविधताओं के साथ शेल के आधार पर यहां सूचीबद्ध किया गया है। ।
स्टीफन चेज़लस

@ स्टीफनचेज़लस: आकर्षक, हमेशा की तरह; मैंने इसे बुकमार्क कर लिया है। तो, n<&mऔर n>&mएक ही बात करते हैं? मुझे नहीं पता था, लेकिन मुझे लगता है कि यह बहुत आश्चर्यजनक नहीं है।
स्कॉट

@ सच, ​​हाँ, वे दोनों एक करते हैं dup(m, n)। मैं stdio और कुछ का उपयोग करके ksh86 के कुछ सबूत देख सकता हूं fdopen(fd, "r" or "w"), इसलिए यह तब मायने रखता होगा। लेकिन एक खोल में stdio का उपयोग करना थोड़ा समझ में आता है, इसलिए मुझे उम्मीद नहीं है कि आपको कोई आधुनिक शेल मिलेगा जहां इससे फर्क पड़ेगा। एक अंतर यह है कि है >&nहै dup(n, 1)(छोटे के लिए 1>&n,), जबकि <&nहै dup(n, 0)(छोटे के लिए 0<&n)।
स्टीफन चेज़लस

सही। को छोड़कर, ज़ाहिर है, फ़ाइल डिस्क्रिप्टर डुप्लीकेशन कॉल के दो-तर्क रूप को कहा जाता है dup2(); dup()केवल एक तर्क लेता है और, open()सबसे कम उपलब्ध फ़ाइल डिस्क्रिप्टर का उपयोग करता है। (आज मुझे पता चला कि एक dup3()समारोह है ।)
स्कॉट

8

क्योंकि bashयह आंतरिक रूप से आपके लिए है, फ़ाइल नाम का विस्तार किया है और फ़ाइल को मानक आउटपुट में रखता है, जैसे कि यदि आप करना चाहते थे $(cat < filename)। यह एक बैश फीचर है, शायद आपको bashयह जानने के लिए कि यह कैसे काम करता है, यह जानने के लिए सोर्स कोड पर गौर करना होगा ।

यहाँ इस सुविधा को संभालने के लिए फ़ंक्शन ( bashस्रोत कोड, फ़ाइल से builtins/evalstring.c):

/* Handle a $( < file ) command substitution.  This expands the filename,
   returning errors as appropriate, then just cats the file to the standard
   output. */
static int
cat_file (r)
     REDIRECT *r;
{
  char *fn;
  int fd, rval;

  if (r->instruction != r_input_direction)
    return -1;

  /* Get the filename. */
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing++;
  fn = redirection_expand (r->redirectee.filename);
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing--;

  if (fn == 0)
    {
      redirection_error (r, AMBIGUOUS_REDIRECT);
      return -1;
    }

  fd = open(fn, O_RDONLY);
  if (fd < 0)
    {
      file_error (fn);
      free (fn);
      return -1;
    }

  rval = zcatfd (fd, 1, fn);

  free (fn);
  close (fd);

  return (rval);
}

एक नोट जो $(<filename)इसके बिल्कुल बराबर नहीं है $(cat filename); यदि फ़ाइल नाम डैश से शुरू होता है तो बाद वाला विफल हो जाएगा -

$(<filename)मूल रूप से था ksh, और करने के लिए जोड़ा गया है bashसे Bash-2.02


1
cat filenameयदि फ़ाइल नाम डैश से शुरू होता है तो असफल हो जाएगा क्योंकि बिल्ली विकल्प स्वीकार करती है। आप के साथ सबसे आधुनिक प्रणालियों पर काम कर सकते हैं cat -- filename
एडम काटज़

-1

कमांड प्रतिस्थापन को हमेशा की तरह चलाने और उस बिंदु पर आउटपुट को डंप करने के बारे में सोचें जहां आप कमांड चला रहे हैं।

आदेशों के आउटपुट का उपयोग अन्य कमांड के लिए तर्क के रूप में किया जा सकता है, एक चर को सेट करने के लिए, और यहां तक ​​कि लूप के लिए तर्क सूची बनाने के लिए भी।

foo=$(echo "bar")चर $fooका मान सेट करेगा bar; कमांड का आउटपुट echo bar

कमान प्रतिस्थापन


1
मेरा मानना ​​है कि यह इस सवाल से बिल्कुल स्पष्ट है कि ओपी कमांड प्रतिस्थापन की मूल बातें समझता है; सवाल विशेष मामले के बारे में है $(< file), और उसे सामान्य मामले पर एक ट्यूटोरियल की आवश्यकता नहीं है। यदि आप यह कह रहे हैं कि $(< file)केवल एक साधारण मामला है $(command), जिसकी आज्ञा है < file, तो आप वही बात कह रहे हैं, जो आदम काट्ज कह रहे हैं , और आप दोनों गलत हैं।
स्कॉट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.