2009-12-31 13 views
22

मैं यह करने के लिए कोई फ़ाइल या नहीं करने के लिए stdin पुनर्निर्देशित करने के लिए तय करने के लिए कोशिश कर रहा था में अनुप्रेषित:बैश: मानक इनपुट गतिशील रूप से एक स्क्रिप्ट

[ ...some condition here... ] && input=$fileName || input="&0" 
./myScript < $input 

लेकिन वह काम नहीं करता चर $ इनपुट है जब क्योंकि "& 0", बैश इसे फ़ाइल नाम के रूप में व्याख्या करता है।

हालांकि, मैं अभी कर सकता है:

if [ ...condition... ];then 
    ./myScript <$fileName 
else 
    ./myScript 

समस्या यह है कि ./myScript वास्तव में एक लंबा आदेश पंक्ति है कि मैं नकल नहीं करना चाहती है, और न ही मैं इसके लिए एक समारोह बनाने के लिए करना चाहते हैं क्योंकि यह इतना लंबा नहीं है (यह इसके लायक नहीं है)।

तो यह मेरे लिए हुआ यह करने के लिए:

[ ...condition... ] && input=$fileName || input= #empty 
cat $input | ./myScript 

लेकिन वह एक और आदेश और एक पाइप (अर्थात एक subshell) को चलाने के लिए की आवश्यकता है।
क्या कोई और तरीका है जो सरल और अधिक कुशल है?

+4

यह हमेशा कार्यों बनाने के लायक है। –

+2

कभी भी "हमेशा" न कहें। कार्य एक अमूर्तता का स्तर जोड़ते हैं जो हमेशा वांछनीय नहीं होता है। – GetFree

+1

शायद। इस मामले में मुझे लगता है कि यह वांछनीय है। –

उत्तर

20

सबसे पहले सभी stdin फ़ाइल के बजाय फ़ाइल descriptor 0 (शून्य) है (जो stdout है)।

आप फ़ाइल वर्णनकर्ता नकल या इस तरह सशर्त फ़ाइल नामों का उपयोग कर सकते हैं:

[[ some_condition ]] && exec 3<$filename || exec 3<&0 

some_long_command_line <&3 
+0

बस मुझे क्या चाहिए। यदि मैं फ़ंक्शन के अंदर नई फ़ाइल डिस्क्रिप्टर बनाता हूं, तो फ़ंक्शन समाप्त होने के बाद भी यह मौजूद होगा? – GetFree

+0

हां, लेकिन फाइल में पॉइंटर भी होगा। दूसरे शब्दों में, यदि आप फ़ंक्शन में फ़ाइल के अंत की तलाश में हैं तो भी आप फ़ंक्शन के बाहर होंगे। 'परीक्षण() {exec 3

2

के बारे में
function runfrom { 
    local input="$1" 
    shift 
    case "$input" in 
     -) "[email protected]" ;; 
     *) "[email protected]" < "$input" ;; 
    esac 
} 

मैं ऋण चिह्न का उपयोग किया है कैसे मानक इनपुट निरूपित करने के लिए है क्योंकि यह है कि कई यूनिक्स कार्यक्रमों के लिए पारंपरिक है।

अब आप

[ ... condition ... ] && input="$fileName" || input="-" 
runfrom "$input" my-complicated-command with many arguments 

बारे में मैं इन कार्यों/आदेशों जो तर्क के रूप में आदेशों ले पाते हैं (जैसे xargs(1)) बहुत उपयोगी हो सकता है, और वे अच्छी तरह से लिखें।

1

उपयोग eval:

 
$ ./myDemux myScript 
pl- lrep/nib/rsu/ !# 
esrever = _$ 

$ ./myDemux 
foo 
oof 
bar 
rab 
baz 
zab 

ध्यान दें कि यह आदानों में रिक्त स्थान भी संभालती है:

#! /bin/bash 

[ $# -gt 0 ] && input="'"$1"'" || input="&1" 

eval "./myScript <$input" 

यह सरल स्टैंड-इन के लिए myScript

#! /usr/bin/perl -lp 
$_ = reverse 

निम्नलिखित उत्पादन का उत्पादन

 
$ ./myDemux foo\ bar 
eman eht ni ecaps a htiw elif 
myScript करने के लिए नीचे पाइप इनपुट करने के लिए

, process substitution का उपयोग करें:

 
$ ./myDemux <(md5sum /etc/issue) 
eussi/cte/ 01672098e5a1807213d5ba16e00a7ad0 

ध्यान दें कि यदि आप उत्पादन सीधे

 
$ md5sum /etc/issue | ./myDemux 

में के रूप में पाइप करने की कोशिश है, यह इनपुट पर इंतजार कर रखती हूँ टर्मिनल, जबकि ephemient's answer में यह कमी नहीं है।

#! /bin/bash 

[ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin 
eval "./myScript <$input" 
+0

'command1 <(command2)',' command1 <<< $ (command2) 'और 'command2 के बीच क्या अंतर है | कमांड 1' ?? – GetFree

+0

पहला प्रक्रिया प्रतिस्थापन है, जिसका दस्तावेज ऊपर से जुड़ा हुआ है। दूसरा 'कमांड 2' के आउटपुट को 'कमांड 1' के आउटपुट भेजने के लिए यहाँ स्ट्रिंग ('<<<') के साथ कमांड प्रतिस्थापन ('$ (...)') बनाता है। अंतिम आदेश चलाने के लिए सामान्य फोर्क-निष्पादन अनुक्रम के माध्यम से चला जाता है लेकिन एक पाइप बनाता है जिसका लेखन अंत 'कमांड 2' के मानक आउटपुट को प्रतिस्थापित करता है और जिसका पठन अंत 'command1' के मानक इनपुट को प्रतिस्थापित करता है। –

2

आप सावधान हैं, तो आप 'eval' का उपयोग कर सकते हैं और अपना पहला विचार:

एक मामूली परिवर्तन वांछित व्यवहार पैदा करता है।

[ ...some condition here... ] && input=$fileName || input="&1" 
eval ./myScript < $input 

हालांकि, आप कहते हैं कि 'माइस्क्रिप्ट' वास्तव में एक जटिल कमांड आमंत्रण है; यदि इसमें तर्क शामिल हैं जिनमें रिक्त स्थान हो सकते हैं, तो आपको 'eval' का उपयोग करने का निर्णय लेने से पहले बहुत सावधान रहना चाहिए।

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

और भी बेहतर myScript डिजाइन करने के लिए इतना है कि यह एक नियमित रूप से यूनिक्स फिल्टर की तरह काम करता है। यह डिज़ाइन लंबे और ध्वनि अनुभव पर आधारित है - और इसलिए इस तरह की समस्याओं से निपटने से बचने के लिए अनुकरण करने योग्य है।

7
(
    if [ ...some condition here... ]; then 
     exec <$fileName 
    fi 
    exec ./myscript 
) 

एक सबहेल में, सशर्त रूप से stdin को रीडायरेक्ट करें और स्क्रिप्ट को निष्पादित करें।

+3

+1। यदि इसमें रिक्त स्थान हैं तो '$ fileName' के आस-पास उद्धरण रखें। –

4

मानक इनपुट भी तो का उपयोग कर के रूप में एक फ़ाइल नाम काम करेंगे, विशेष उपकरण फ़ाइल /dev/stdin द्वारा प्रतिनिधित्व किया जा सकता है।

file="/dev/stdin" 
./myscript < "$file" 
+0

जब मैं रूट नहीं हूं तो मुझे "अनुमति अस्वीकार" त्रुटि मिलती है। – GetFree

+1

यह संभवतः किसी प्रकार की गलत कॉन्फ़िगरेशन की तरह लगता है; '/ dev/stdin' (लगभग) हमेशा उपयोग योग्य होना चाहिए। विशेष रूप से, मेरा मानना ​​है कि लिनक्स और सोलारिस दोनों इसे '/ proc/self/fd/0' के लिए एक सिम्लिंक के रूप में कार्यान्वित करते हैं, और केवल एक ही समस्या जो मैं सोच सकता हूं यदि यूआईडी बदल गई है। – ephemient

+0

ठीक है, इसलिए एक "सामान्य" स्थिति होगी: टर्मिनल उपयोगकर्ता ए के स्वामित्व में है, स्क्रिप्ट उपयोगकर्ता बी के रूप में चल रही है, stdin टर्मिनल से जुड़ा हुआ है। क्या यह आप कर रहे हैं? – ephemient

0

लोगों को आप के लिए बहुत लंबे लिपियों दिखाते हैं, लेकिन .... आप बैश जाल मिल :) आप बैश में सब कुछ बोली चाहिए। उदाहरण के लिए , आप & 0 नाम की सूची फ़ाइल चाहते हैं।

filename = '& 0' # राइट ls $ filename #wrong! यह विकल्प $ filename और ls "$ filename" # राइट

अन्य, रिक्त स्थान वाली फ़ाइलें।

फ़ाइल नाम = ls $ फ़ाइल नाम #wrong, बैश पहले कटौती और पिछले अंतरिक्ष 'रिक्त स्थान के साथ कुछ फ़ाइल', और साथ और रिक्त स्थान शब्द ls "$ फ़ाइल नाम" righ के बीच कई रिक्त स्थान को कम

ही में है आपकी लिपि कृपया बदलने के लिए:

./myScript < $input 

./myScript < "$input" 

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

लेकिन/dev/stdin के बारे में क्या? यह तब उपयोग योग्य होता है जब आपने stdin को रीडायरेक्ट किया है और वास्तविक stdin पर कुछ प्रिंट करना चाहते हैं।

हां, तो अपनी स्क्रिप्ट इस तरह दिखाना चाहिए:

[ ...some condition here... ] && input="$fileName" || input="&0" 
./myScript < "$input" 
संबंधित मुद्दे