नामों की सूची में 'ls> ls.out' का कारण 'loutout' क्यों शामिल है?


26

$ ls > ls.outवर्तमान निर्देशिका में फ़ाइलों के नामों की सूची में 'लेआउट' को शामिल करने का कारण क्यों बनता है? इसे क्यों चुना गया? अन्यथा क्यों नहीं?


3
शायद इसलिए कि यह पहली बार एक फ़ाइल लेस्आउट बनाता है और फिर इसे आउटपुट लिखता है
दिमित्री पॉडबोर्स्की

1
यदि आप समावेशन से बचना चाहते हैं तो आप आउटपुट फ़ाइल को हमेशा अलग निर्देशिका में संग्रहीत कर सकते हैं। उदाहरण के लिए आप मूल निर्देशिका चुन सकते हैं (बशर्ते कि आप फ़ाइल सिस्टम के मूल में न हों) ls > ../ls.out
एल्डर गीक

जवाबों:


36

कमांड का मूल्यांकन करते समय >पुनर्निर्देशन को पहले हल किया जाता है: इसलिए समय के lsअनुसार आउटपुट फ़ाइल पहले ही बनाई जा चुकी है।

यही कारण है कि एक >ही कमांड के भीतर रीडायरेक्शन का उपयोग करके एक ही फाइल को पढ़ना और लिखना फ़ाइल को छोटा करता है; जब तक कमांड चलती है तब तक फ़ाइल पहले से ही काट दी गई है:

$ echo foo >bar
$ cat bar
foo
$ <bar cat >bar
$ cat bar
$ 

इससे बचने के लिए टोटके:

  • <<<"$(ls)" > ls.out (पुनर्निर्देशन हल होने से पहले चलाने के लिए आवश्यक किसी भी आदेश के लिए काम करता है)

    बाहरी स्थान का मूल्यांकन करने से पहले कमांड प्रतिस्थापन चलाया जाता है, इसलिए बनाए जाने lsसे पहले चलाया ls.outजाता है:

    $ ls
    bar  foo
    $ <<<"$(ls)" > ls.out
    $ cat ls.out 
    bar
    foo
    
  • ls | sponge ls.out (पुनर्निर्देशन हल होने से पहले चलाने के लिए आवश्यक किसी भी आदेश के लिए काम करता है)

    spongeफ़ाइल को केवल तब ही लिखता है जब पाइप के बाकी हिस्सों को निष्पादित करना समाप्त हो lsजाता है , इसलिए बनाने से पहले चलाया ls.outजाता है ( पैकेज के spongeसाथ प्रदान किया जाता moreutilsहै):

    $ ls
    bar  foo
    $ ls | sponge ls.out
    $ cat ls.out 
    bar
    foo
    
  • ls * > ls.out( ls > ls.outविशिष्ट मामले के लिए काम करता है)

    पुनर्निर्देशन हल होने से पहले फ़ाइल नाम का विस्तार किया जाता है, इसलिए lsइसके तर्कों पर चलेगा, जिसमें शामिल नहीं होगा ls.out:

    $ ls
    bar  foo
    $ ls * > ls.out
    $ cat ls.out 
    bar
    foo
    $
    

कार्यक्रम / स्क्रिप्ट / जो कुछ भी चलाया जाता है, उससे पहले पुनर्निर्देशन क्यों हल किया जाता है, मुझे ऐसा करने के लिए अनिवार्य कोई विशिष्ट कारण नहीं दिखता है , लेकिन मुझे दो कारण दिखाई देते हैं कि ऐसा करना बेहतर क्यों है :

  • एसटीडीआई पुनर्निर्देशित नहीं करने से पहले ही कार्यक्रम / स्क्रिप्ट / जो कुछ भी हो जब तक एसटीडी पुनर्निर्देशित नहीं हो जाता है;

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

तो पहले मामले में समय की बर्बादी और दूसरे मामले में समय और स्मृति की बर्बादी।

यह सिर्फ मेरे साथ होता है, मैं दावा नहीं कर रहा हूं कि ये वास्तविक कारण हैं; लेकिन मुझे लगता है कि सभी में, अगर कोई एक विकल्प था, तो वे उपरोक्त कारणों के लिए वैसे भी पुनर्निर्देशन के साथ जाएंगे।


1
ध्यान दें कि पुनर्निर्देशन के दौरान शेल वास्तव में डेटा को नहीं छूता है (इनपुट पुनर्निर्देशन या आउटपुट पुनर्निर्देशन पर)। यह सिर्फ फाइल को खोलता है और प्रोग्राम के नीचे फाइल डिस्क्रिप्टर को पास करता है।
पीटर ग्रीन

11

से man bash:

REDIRECTION

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

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

फ़ाइल रखने से बचने के लिए, मेरा सुझाव है कि आप आउटपुट का नाम पहले पाइप पर दें, और फिर फ़ाइल करने के लिए। &उपयोगकर्ता पर टर्मिनल पर नियंत्रण वापस करने के उपयोग पर ध्यान दें

DIR:/xieerqi
skolodya@ubuntu:$ mkfifo /tmp/namedPipe.fifo                                                                         

DIR:/xieerqi
skolodya@ubuntu:$ ls > /tmp/namedPipe.fifo &
[1] 14167

DIR:/xieerqi
skolodya@ubuntu:$ cat /tmp/namedPipe.fifo > ls.out

पर क्यों?

इस बारे में सोचें - आउटपुट कहां होगा? एक प्रोग्राम की तरह कार्य करता है printf, sprintf, puts, जो सभी के लिए डिफ़ॉल्ट जाने से stdout, लेकिन अगर फ़ाइल पहली जगह में मौजूद नहीं है उनके उत्पादन फाइल करने के लिए चला गया हो सकता है? यह पानी की तरह है। क्या आप पहले नल के नीचे ग्लास लगाए बिना एक गिलास पानी प्राप्त कर सकते हैं?


10

मैं वर्तमान उत्तरों से असहमत नहीं हूं। आउटपुट फ़ाइल को कमांड चलने से पहले खोलना होगा या कमांड को अपना आउटपुट लिखने के लिए कहीं भी नहीं होगा।

ऐसा इसलिए है क्योंकि हमारी दुनिया में "सब कुछ एक फ़ाइल है" । स्क्रीन पर आउटपुट SDOUT (उर्फ फ़ाइल डिस्क्रिप्टर 1) है। टर्मिनल पर लिखने के लिए एक आवेदन के लिए, यह fd1 खोलता है और इसे एक फाइल की तरह लिखता है।

जब आप किसी शेल में किसी एप्लिकेशन के आउटपुट को रीडायरेक्ट करते हैं, तो आप fd1 बदल रहे हैं, इसलिए यह वास्तव में फ़ाइल पर इंगित कर रहा है। जब आप पाइप करते हैं तो आप एक एप्लिकेशन के STDOUT को दूसरे का STDIN (fd0) बनने के लिए बदल देते हैं।


लेकिन यह सब अच्छा है, लेकिन आप यह आसानी से देख सकते हैं कि यह कैसे काम करता है strace। यह बहुत भारी सामान है लेकिन यह उदाहरण काफी छोटा है।

strace sh -c "ls > ls.out" 2> strace.out

भीतर strace.outहम निम्नलिखित हाइलाइट देख सकते हैं:

open("ls.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

इस ls.outरूप में खुलता है fd3। ही लिखो। ट्रंकट्स (अधिलेखित) यदि मौजूद हैं, अन्यथा बनाता है।

fcntl(1, F_DUPFD, 10)                   = 10
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0

यह थोड़ी जुगलबंदी है। हम fd10 से STDOUT (fd1) को बंद करते हैं और इसे बंद कर देते हैं। ऐसा इसलिए है क्योंकि हम इस आदेश के साथ वास्तविक STDOUT में कुछ भी आउटपुट नहीं कर रहे हैं। यह ls.outमूल को लिखने के हैंडल को बंद करने और बंद करने से समाप्त होता है।

stat("/opt/wine-staging/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/home/oli/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/ls", 0x7ffc6bf028c0)    = -1 ENOENT (No such file or directory)
stat("/usr/bin/ls", 0x7ffc6bf028c0)     = -1 ENOENT (No such file or directory)
stat("/sbin/ls", 0x7ffc6bf028c0)        = -1 ENOENT (No such file or directory)
stat("/bin/ls", {st_mode=S_IFREG|0755, st_size=110080, ...}) = 0

यह यह निष्पादन योग्य के लिए खोज कर रहा है। एक सबक शायद एक लंबा रास्ता नहीं है;)

clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0961324a10) = 31933
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31933
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31933, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn()                          = 31933
dup2(10, 1)                             = 1
close(10)                               = 0

फिर आज्ञा चलती है और जनक प्रतीक्षा करते हैं। इस ऑपरेशन के दौरान किसी भी STDOUT ने वास्तव में खुले फ़ाइल हैंडल पर मैप किया होगा ls.out। जब बच्चा जारी करता है SIGCHLD, तो यह माता-पिता की प्रक्रिया को उसके समाप्त होने को बताता है और यह फिर से शुरू हो सकता है। यह थोड़ा और करतब दिखाने के साथ खत्म होता है ls.out

इतनी करतूत क्यों है ? नहीं, मैं पूरी तरह से निश्चित नहीं हूं।


बेशक आप इस व्यवहार को बदल सकते हैं। आप मेमोरी wth को बफर कर सकते हैं जैसे spongeकि और वह कार्यवाही कमांड से अदृश्य होगा। हम अभी भी फ़ाइल डिस्क्रिप्टर को प्रभावित कर रहे हैं, लेकिन फ़ाइल-सिस्टम-दृश्य तरीके से नहीं।

ls | sponge ls.out

6

शेल में पुनर्निर्देशन और पाइप ऑपरेटरों के कार्यान्वयन के बारे में एक अच्छा लेख भी है । यह दिखाता है कि पुनर्निर्देशन को किस $ ls > ls.outतरह लागू किया जा सकता है ताकि यह दिख सके:

main(){
    close(1); // Release fd no - 1
    open("ls.out", "w"); // Open a file with fd no = 1
    // Child process
    if (fork() == 0) {
        exec("ls"); 
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.