$ ls > ls.out
वर्तमान निर्देशिका में फ़ाइलों के नामों की सूची में 'लेआउट' को शामिल करने का कारण क्यों बनता है? इसे क्यों चुना गया? अन्यथा क्यों नहीं?
ls > ../ls.out
$ ls > ls.out
वर्तमान निर्देशिका में फ़ाइलों के नामों की सूची में 'लेआउट' को शामिल करने का कारण क्यों बनता है? इसे क्यों चुना गया? अन्यथा क्यों नहीं?
ls > ../ls.out
जवाबों:
कमांड का मूल्यांकन करते समय >
पुनर्निर्देशन को पहले हल किया जाता है: इसलिए समय के 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
$
कार्यक्रम / स्क्रिप्ट / जो कुछ भी चलाया जाता है, उससे पहले पुनर्निर्देशन क्यों हल किया जाता है, मुझे ऐसा करने के लिए अनिवार्य कोई विशिष्ट कारण नहीं दिखता है , लेकिन मुझे दो कारण दिखाई देते हैं कि ऐसा करना बेहतर क्यों है :
एसटीडीआई पुनर्निर्देशित नहीं करने से पहले ही कार्यक्रम / स्क्रिप्ट / जो कुछ भी हो जब तक एसटीडी पुनर्निर्देशित नहीं हो जाता है;
एसटीडी के बारे में पहले से पुनर्निर्देशित नहीं करना चाहिए, शेल को प्रोग्राम के स्क्रिप्ट / / जो भी आउटपुट है जब तक एसटीडी के पुनर्निर्देशित नहीं किया जाता है;
तो पहले मामले में समय की बर्बादी और दूसरे मामले में समय और स्मृति की बर्बादी।
यह सिर्फ मेरे साथ होता है, मैं दावा नहीं कर रहा हूं कि ये वास्तविक कारण हैं; लेकिन मुझे लगता है कि सभी में, अगर कोई एक विकल्प था, तो वे उपरोक्त कारणों के लिए वैसे भी पुनर्निर्देशन के साथ जाएंगे।
से 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
, लेकिन अगर फ़ाइल पहली जगह में मौजूद नहीं है उनके उत्पादन फाइल करने के लिए चला गया हो सकता है? यह पानी की तरह है। क्या आप पहले नल के नीचे ग्लास लगाए बिना एक गिलास पानी प्राप्त कर सकते हैं?
मैं वर्तमान उत्तरों से असहमत नहीं हूं। आउटपुट फ़ाइल को कमांड चलने से पहले खोलना होगा या कमांड को अपना आउटपुट लिखने के लिए कहीं भी नहीं होगा।
ऐसा इसलिए है क्योंकि हमारी दुनिया में "सब कुछ एक फ़ाइल है" । स्क्रीन पर आउटपुट 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
शेल में पुनर्निर्देशन और पाइप ऑपरेटरों के कार्यान्वयन के बारे में एक अच्छा लेख भी है । यह दिखाता है कि पुनर्निर्देशन को किस $ 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");
}
}