एक तर्क (स्क्रिप्टिंग के बिना) पर दो कमांड चलाएं


28

मैं उस इनपुट को दो बार टाइप किए बिना, एक इनपुट पर दो कमांड कैसे कर सकता हूं?

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

stat fileName

फ़ाइल कमांड, बताती है कि फाइल किस प्रकार की है:

file fileName

आप इसे इस तरह से एक पंक्ति में कर सकते हैं:

stat fileName ; file fileName

हालाँकि, आपको फ़ाइलनाम को दो बार लिखना होगा।

आप एक ही इनपुट पर दोनों कमांड कैसे निष्पादित कर सकते हैं (इनपुट टाइप किए बिना या इनपुट के दो बार वेरिएबल)?

लिनक्स में, मुझे पता है कि आउटपुट कैसे करें, लेकिन आप पाइप इनपुट कैसे करते हैं?


14
बस स्पष्ट होने के लिए, वे "इनपुट" नहीं हैं, वे तर्क (उर्फ पैरामीटर) हैं। इनपुट के माध्यम से पाइप किया जा सकता है <(फ़ाइल से बाईं ओर |इनपुट ) या (स्ट्रीम से दाईं ओर इनपुट)। एक अंतर है।
गोल्डीलॉक्स

6
आपके प्रश्न का उत्तर नहीं, लेकिन हो सकता है कि आपकी समस्या का उत्तर: बाश में, yank-last-argकमांड (डिफ़ॉल्ट शॉर्टकट Meta-.या Meta-_, Metaअक्सर बाईं Altकुंजी हो) पूर्ववर्ती कमांड लाइन से अंतिम पैरामीटर को वर्तमान में कॉपी करेगा। इसलिए, stat fileNameआपको निष्पादित करने के बाद file [Meta-.]और fileNameफिर से टाइप करने की आवश्यकता नहीं है । मैं हमेशा इसका इस्तेमाल करता हूं।
डबुन

2
और अंतर यह है कि <और >कर रहे हैं पुनर्निर्देशन , नहीं पाइप । एक पाइपलाइन दो प्रक्रियाओं को जोड़ती है, जबकि पुनर्निर्देशन बस स्टड / स्टडआउट को पुन: सौंपता है।
bsd

@bdowning: हाँ, लेकिन आप पाइप को पुनर्निर्देशित कर रहे हैं हालांकि आप नहीं हैं। आप डिस्क पर एक फ़ाइल से पढ़ने के लिए पाइपलाइन की शुरुआत को पुनर्निर्देशित कर सकते हैं, या आप डिस्क पर एक फ़ाइल पर लिखने के लिए एक पाइपलाइन के अंत को पुनर्निर्देशित कर सकते हैं। फिर भी पाइपिंग। ;-)
जेम्स हाई

जवाबों:


22

यहाँ एक और तरीका है:

$ stat filename && file "$_"

उदाहरण:

$ stat /usr/bin/yum && file "$_"
  File: ‘/usr/bin/yum’
  Size: 801         Blocks: 8          IO Block: 4096   regular file
Device: 804h/2052d  Inode: 1189124     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:rpm_exec_t:s0
Access: 2014-06-11 22:55:53.783586098 +0700
Modify: 2014-05-22 16:49:35.000000000 +0700
Change: 2014-06-11 19:15:30.047017844 +0700
 Birth: -
/usr/bin/yum: Python script, ASCII text executable

यही कारण है कि में काम करता है bashऔर zsh। यह भी काम करता है mkshऔर dashकेवल जब इंटरैक्टिव है। AT & T ksh में, वह केवल तभी काम करता है जब वह file "$_"एक से भिन्न रेखा पर हो stat


1
!$क्योंकि काम नहीं करेगा !$पिछले इतिहास घटना के अंतिम शब्द (पहले दर्ज किए कमांड लाइन की तो, स्टेट आदेश की नहीं) के लिए फैलता है।
स्टीफन चेज़लस

@ स्टीफनचेज़ेलस: ओह, हां, मेरी गलती है, मैंने इसे हटा दिया।
congonglm

32

संभवतः इसके लिए मेरे पोर-पोरों का उपयोग किया जा रहा है, यहाँ बैश ब्रेस विस्तार और eval का हैकी संयोजन है जो इस चाल को करने के लिए लगता है

eval {stat,file}" fileName;"


2
ब्रेस विस्तार की उत्पत्ति हुई csh, नहीं bashbashसे नकल की ksh। यह कोड सभी csh, tsh, ksh, zsh, मछली और बैश में काम करेगा।
स्टीफन चेजलस

1
बहुत बुरा आप बोली के कारण "टैब आउट" (स्वतः पूर्ण) फ़ाइलनाम की क्षमता खो देते हैं।
लोनिविब

क्षमा करें, लेकिन मैं विरोध नहीं कर सकता है या तो
tomd

क्षमा करें, यहाँ क्या समस्या है eval? (शीर्ष टिप्पणियों का संदर्भ देते हुए)
user13107

28

के साथ zsh, आप गुमनाम कार्यों का उपयोग कर सकते हैं:

(){stat $1; file $1} filename

esलंबोदर के साथ :

@{stat $1; file $1} filename

आप भी कर सकते हैं:

{ stat -; file -;} < filename

( statपहले के रूप fileमें पहुँच समय को अद्यतन करेगा)।

मैं करूंगा:

f=filename; stat "$f"; file "$f"

हालाँकि, यह वैरिएबल के लिए है।


3
{ stat -; file -;} < filenameजादू है। statयह देखने के लिए जाँच की जानी चाहिए कि इसका स्टड एक फ़ाइल से जुड़ा है और फ़ाइल की विशेषताओं की रिपोर्ट कर रहा है? संभवत: सूचक को file
स्टड

1
@ 1_CR, हाँ। अपने fd 0.stat -statfstat()
स्टीफन चेज़लस

4
{ $COMMAND_A -; $COMMAND_B; } < filenameसंस्करण सामान्य स्थिति में काम नहीं करेगा अगर $ COMMAND_A वास्तव में किसी भी इनपुट पढ़ता है; उदाहरण { cat -; cat -; } < filenameफ़ाइल नाम की सामग्री को केवल एक बार प्रिंट करेगा।
मैक्स नानसी

2
stat -Coreutils-8.0 (Oct 2009) के बाद से विशेष रूप से संभाला जाता है, इससे पहले के संस्करणों में आपको एक त्रुटि मिलेगी (जब तक कि आपके पास -पिछले प्रयोग से कॉल की गई फ़ाइल नहीं होगी , जिस स्थिति में आपको गलत परिणाम मिलेंगे ...)
mr .सुपरैटिक

1
@ mr.spuratic। हां, और BSDs, IRIX और zsh आँकड़े इसे पहचान नहीं पाएंगे। Zsh और IRIX स्टेट के साथ, आप stat -f0इसके बजाय उपयोग कर सकते हैं ।
स्टीफन चेज़लस

9

ये सभी उत्तर मुझे अधिक या कम सीमा तक स्क्रिप्टिंग जैसे लगते हैं। ऐसे समाधान के लिए जिसमें वास्तव में कोई स्क्रिप्टिंग शामिल नहीं है, और यह मानता है कि आप एक शेल में टाइपिंग कीबोर्ड पर बैठे हैं, आप कोशिश कर सकते हैं:

स्टेट कोई Enter
फ़ाइल फ़ाइल Esc_   Enter

बैश, zsh और ksh में, कम से कम, दोनों vi और emacs मोड में, एस्केप को दबाएं और फिर अंडरस्कोर अंतिम शब्द को पिछली लाइन से वर्तमान लाइन के लिए एडिट बफर में डालें।

या, आप इतिहास प्रतिस्थापन का उपयोग कर सकते हैं:

स्टेट कोई Enter
फ़ाइल। $!Enter

बैश, zsh, csh, tsh, और अधिकांश अन्य गोले में, !$पिछली कमांड लाइन के अंतिम शब्द से बदल दिया जाता है जब वर्तमान कमांड लाइन पार्स होती है।


क्या अन्य विकल्प zsh / bash में उपलब्ध हैं जो पसंद हैं Esc _? क्या आप कृपया आधिकारिक दस्तावेज के साथ लिंक कर सकते हैं?
अभिजीत रस्तोगी

1
बैश: gnu.org/software/bash/manual/bashref.html#Command-Line-Editing और "M-_" की खोज करें
Godlygeek

1
zsh: linux.die.net/man/1/zshzle और "इन्सर्ट-लास्ट-वर्ड" खोजें
Godlygeek

1
@shadyabhi ^ बैश और zsh के मानक कीबाइंडिंग दोनों के लिए लिंक। ध्यान दें कि बैश सिर्फ रीडलाइन का उपयोग कर रहा है, इसलिए (अधिकांश) बैश किसी भी एप्लिकेशन में काम करेगा जो रीडलाइन लाइब्रेरी का उपयोग करता है।
गॉडलीजेक

3

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

echo filename | tee >(xargs stat) >(xargs file) | cat

कई अन्य उत्तरों के विपरीत यह लिनक्स के तहत बैश और अधिकांश अन्य गोले में काम करेगा। मेरा सुझाव है कि यह एक चर का एक अच्छा उपयोग मामला है, लेकिन अगर आप पूरी तरह से एक का उपयोग नहीं कर सकते हैं तो यह केवल पाइप और सरल उपयोगिताओं के साथ करने का सबसे अच्छा तरीका है (यह मानते हुए कि आपके पास विशेष रूप से फैंसी शेल नहीं है)।

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

संपादित करें

जैसा कि टिप्पणियों में सुझाव दिया गया है कि इस उत्तर में कुछ खामियां हैं, पहला आउटपुट इंटरलेसिंग की क्षमता है। यह इस प्रकार तय किया जा सकता है:

echo filename | tee >(xargs stat) >(( wait $!; xargs file )) | cat

यह कमांड को बदले में चलाने के लिए मजबूर करेगा और आउटपुट को कभी भी इंटरलेक्ट नहीं किया जाएगा।

दूसरा मुद्दा प्रक्रिया प्रतिस्थापन से बचना है जो कुछ गोले में उपलब्ध नहीं है, इसे टी के बजाय टीपीई के उपयोग से प्राप्त किया जा सकता है:

echo filename | tpipe 'xargs stat' | ( wait $!; xargs file )

यह लगभग किसी भी शेल के लिए पोर्टेबल होना चाहिए (मुझे उम्मीद है) और अन्य प्रशंसा में मुद्दों को हल करता है, हालांकि मैं इसे स्मृति से आखिरी लिख रहा हूं क्योंकि मेरे वर्तमान सिस्टम में tpipe उपलब्ध नहीं है।


1
प्रक्रिया प्रतिस्थापन एक ksh सुविधा है जो केवल ksh(सार्वजनिक डोमेन संस्करण में नहीं) में काम करती है , bashऔर zsh। ध्यान दें कि statऔर fileसमवर्ती रूप से चलेगा, इसलिए संभवतः उनके आउटपुट को इंटरव्यू किया जाएगा (मुझे लगता है कि आप | catउस प्रभाव को सीमित करने के लिए आउटपुट बफ़रिंग के लिए मजबूर कर रहे हैं ?)।
स्टीफन चेज़लस

@ StéphaneChazelas आप बिल्ली के बारे में सही हैं, इसका उपयोग करते समय मैं कभी भी इसका उपयोग करने के लिए इंटरलेस्ड इनपुट का मामला उत्पन्न करने में कामयाब नहीं हुआ हूं। हालाँकि मैं क्षण भर में अधिक पोर्टेबल समाधान का उत्पादन करने के लिए कुछ करने की कोशिश कर रहा हूं।
20

3

यह उत्तर दूसरों के एक जोड़े के समान है:

स्टेट फाइलनाम   && फ़ाइल! #: 1

अन्य उत्तरों के विपरीत, जो पिछली कमांड से अंतिम तर्क को स्वचालित रूप से दोहराते हैं , यह आपको गिनने की आवश्यकता है:

ls- बड़ा फ़ाइल नाम   && फ़ाइल! #: 2

कुछ अन्य उत्तरों के विपरीत, इसके लिए उद्धरण की आवश्यकता नहीं है:

stat "useless cat"  &&  file !#:1

या

echo Sometimes a '$cigar' is just a !#:3.

2

यह अन्य उत्तरों के एक जोड़े के समान है, लेकिन यह एक की तुलना में अधिक पोर्टेबल है और इस एक की तुलना में बहुत सरल है :

sh -c 'स्टेट "$ 0"; फ़ाइल "$ 0" फ़ाइल नाम

या

sh -c 'स्टेट "$ 1"; फ़ाइल "$ 1" ' फ़ाइल नाम

जिसमें shसौंपा गया है $0। यद्यपि यह टाइप करने के लिए तीन अक्षर अधिक है, यह (दूसरा) फ़ॉर्म बेहतर है क्योंकि $0यह वह नाम है जो आप उस इनलाइन स्क्रिप्ट को देते हैं। ऐसा नहीं है कि स्क्रिप्ट के लिए खोल द्वारा उदाहरण के लिए प्रयोग किया जाता है, त्रुटि संदेश में, जैसे जब fileया statनहीं पाया जा सकता है या किसी भी कारण निष्पादित नहीं किया जा सकता है।


अगर आप करना चाहते हैं

स्टेट फ़ाइलनाम 1  फ़ाइल नाम 2  फ़ाइल नाम 3 …; फ़ाइल नाम 1  फ़ाइल नाम 2  फ़ाइल नाम 3

फ़ाइलों की एक मनमानी संख्या के लिए, करते हैं

sh -c 'स्टेट "$ @"; फ़ाइल "$ @" ' फ़ाइल नाम 1  फ़ाइल नाम 2  फ़ाइल नाम 3 ...

जो काम करता है क्योंकि "$@"के बराबर है "$1" "$2" "$3" …


वह $0नाम है जो आप उस इनलाइन स्क्रिप्ट को देना चाहते हैं। यह उस स्क्रिप्ट के लिए शेल द्वारा त्रुटि संदेशों में उदाहरण के लिए उपयोग किया जाता है। जैसे किसी कारण से कब fileया statनहीं मिल सकता है या निष्पादित नहीं किया जा सकता है। इसलिए, आप यहाँ कुछ सार्थक चाहते हैं। यदि प्रेरित नहीं है, तो बस उपयोग करें sh
स्टीफन चेज़लस

1

मुझे लगता है कि आप एक गलत सवाल पूछ रहे हैं, कम से कम आप क्या करने की कोशिश कर रहे हैं। statऔर fileकमांड ऐसे पैरामीटर ले रहे हैं जो किसी फ़ाइल का नाम है न कि उसकी सामग्री। यह बाद में उस कमांड पर है जो आपके द्वारा निर्दिष्ट नाम से पहचानी गई फ़ाइल को पढ़ना है।

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

आपके मामले में आपको एक पाइप की आवश्यकता नहीं है क्योंकि आपको फ़ाइल नाम के रूप में इनपुट प्रदान करना है। और यहां तक ​​कि अगर वे उपकरण ( statऔर file) स्टड को पढ़ेंगे, तो पाइप फिर से प्रासंगिक नहीं है क्योंकि इनपुट को किसी भी उपकरण द्वारा बदल नहीं दिया जाना चाहिए जब वह दूसरे पर जाता है।


1

यह वर्तमान निर्देशिका में सभी फ़ाइल नामों के लिए काम करना चाहिए।

sh -c "$(printf 'stat -- "$1";file -- "$1";shift%.0b\n' *)" -- *

1
मान लें कि फ़ाइल नामों में दोहरा उद्धरण, बैकस्लैश, डॉलर, बैकटिक, }... वर्ण नहीं हैं और इसके साथ शुरू नहीं होता है -। कुछ printfकार्यान्वयन (zsh, bash, ksh93) एक है %q। इसके साथ zsh, यह हो सकता है:printf 'stat %q; file %1$q\n' ./* | zsh
स्टीफन चेज़लस

@StephaneChezales - हार्डकॉट की संभावना को छोड़कर अब यह सब तय हो गया है। मुझे sed s///लगता है कि मैं एक अनुमान के साथ संभाल सकता हूं , लेकिन इसके दायरे के बारे में - और अगर लोग इसे अपने फाइलनाम में डाल रहे हैं तो उन्हें खिड़कियों का उपयोग करना चाहिए।
मोकेसर

@ स्टीफनचेज़ेलस - कोई बात नहीं - मैं भूल गया कि उन लोगों को संभालना कितना आसान था।
मिकसर्व

सख्ती से बोलना, यह पूछे गए सवाल का जवाब नहीं है: "आप एक ही इनपुट पर दोनों आदेशों को कैसे निष्पादित कर सकते हैं (इनपुट टाइप किए बिना ... दो बार )?" (जोर दिया गया)। आपका उत्तर वर्तमान निर्देशिका की सभी फाइलों पर लागू होता है - और इसमें *दो बार शामिल हैं । आप भी कह सकते हैं stat -- *; file -- *
स्कॉट

@ सेट - इनपुट दो बार टाइप नहीं किया गया - *विस्तारित है। विस्तार के *साथ साथ दो आदेशों हर बहस में के लिए * इनपुट है। देख? printf commands\ %s\;shift *
माइकल्स

1

यहां 'इनपुट' के लिए कुछ अलग संदर्भ हैं, इसलिए मैं पहले इसे ध्यान में रखते हुए कुछ परिदृश्य दूंगा। सबसे छोटे रूप में प्रश्न के आपके त्वरित उत्तर के लिए :

stat testfile < <($1)> outputfile

उपर्युक्त, टेस्टफाइल पर एक स्टेटमेंट करेगा, इसे STDOUT में ले (रीडायरेक्ट) करेगा और इसमें अगले विशेष फ़ंक्शन (<() भाग) को शामिल करेगा, फिर जो कुछ भी था, उसके अंतिम परिणामों को एक नई फ़ाइल (आउटपुटफाइल) में आउटपुट करेगा। फ़ाइल को कॉल किया जाता है, फिर बाश बिल्ट-इन (प्रत्येक बार $ 1 के बाद संदर्भित किया जाता है, जब तक कि आप निर्देशों का एक नया सेट शुरू नहीं करते)।

आपका प्रश्न बहुत अच्छा है, और ऐसा करने के लिए कई उत्तर और तरीके हैं, लेकिन यह वास्तव में जो आप विशेष रूप से कर रहे हैं, उसके साथ बदलता है।

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

run program < <($output_from_program)> my_own.log

उस ज्ञान को लेना और उसमें विस्तार करना आपको चीजों को बनाने की अनुमति देता है जैसे:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

यह आपकी वर्तमान निर्देशिका में एक साधारण ls--A प्रदर्शन करेगा, फिर ls -A से प्रत्येक परिणाम के माध्यम से लूप करने के लिए बताएं (और यहाँ यह कहाँ मुश्किल है!) उन परिणामों में से प्रत्येक में "grep" और केवल पिछला प्रदर्शन करें! प्रिंटफ (लाल रंग में) अगर यह वास्तव में "उस" के साथ एक फ़ाइल मिला। यह grep के परिणामों को एक नई टेक्स्ट फ़ाइल, files_that_matched_thatword में भी लॉग करेगा।

उदाहरण आउटपुट:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

index.html

उन सभी ने केवल ls-a परिणाम को मुद्रित किया, कुछ खास नहीं। इस समय के लिए इसे जोड़ने के लिए कुछ जोड़ें:

echo "thatword" >> newfile

अब इसे फिर से चलाएं:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

files_that_matched_thatword  index.html  newfile

Found a file: newfile:thatword

जबकि शायद आप वर्तमान में देख रहे हैं की तुलना में अधिक थकाऊ जवाब, मेरा मानना ​​है कि इस तरह के आस-पास के काम को ध्यान में रखते हुए आपको भविष्य के प्रयासों में बहुत अधिक लाभ होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.