पाइप में स्क्रिप्ट का उपयोग करते समय उपयोगकर्ता इनपुट कैसे पढ़ें


10

सामान्य समस्या

मैं एक स्क्रिप्ट लिखना चाहता हूं जो उपयोगकर्ता के साथ बातचीत करती है, भले ही यह पाइप की एक श्रृंखला के बीच में हो।

ठोस उदाहरण

इसके विपरीत, यह एक fileया लेता है stdin, लाइनों को प्रदर्शित करता है (लाइन नंबर के साथ), उपयोगकर्ता को एक चयन या लाइन नंबर इनपुट करने के लिए कहता है, और फिर संबंधित लाइनों को प्रिंट करता है stdout। चलिए इस स्क्रिप्ट को कहते हैं selector। फिर मूल रूप से, मैं ऐसा करने में सक्षम होना चाहता हूं

grep abc foo | selector > myfile.tmp

अगर fooहोता है

blabcbla
foo abc bar
quux
xyzzy abc

फिर selectorमुझे विकल्प पर (टर्मिनल में नहीं myfile.tmp!) प्रस्तुत करता है

1) blabcbla
2) foo abc bar
3) xyzzy abc
Select options:

जिसके बाद मैं टाइप करता हूं

2-3

और अंत के साथ

foo abc bar
xyzzy abc

की सामग्री के रूप में myfile.tmp

मुझे एक चयनकर्ता स्क्रिप्ट मिली है और चल रही है, और मूल रूप से यह पूरी तरह से काम कर रहा है अगर मैं इनपुट और आउटपुट को रीडायरेक्ट नहीं करता हूं। इसलिए

selector foo

जैसा मैं चाहता हूँ वैसा ही व्यवहार करता है। हालांकि, जब उपरोक्त उदाहरण में एक साथ चीजों को पाइप करते हैं, selectorतो प्रस्तुत विकल्पों को प्रिंट myfile.tmpकरता है और ग्रीप किए गए इनपुट से चयन पढ़ने की कोशिश करता है।

मेरा दृष्टिकोण

मैंने के -uझंडे का उपयोग करने की कोशिश की readहै

exec 4< /proc/$PPID/fd/0
exec 4> /proc/$PPID/fd/1
nl $INPUT >4
read -u4 -p"Select options: "

लेकिन यह वह नहीं करता है जो मुझे उम्मीद थी कि यह होगा।

प्रश्न: मुझे वास्तविक उपयोगकर्ता सहभागिता कैसे मिलेगी?


एक स्क्रिप्ट बनाते हैं और आउटपुट को वेरिएबल में सेव करते हैं, और फिर वर्तमान उपयोगकर्ता चाहते हैं कि आप चाहते हैं ??
हैकाहॉलिक

@ हेकहॉलिक - मुझे यकीन नहीं है कि आपका क्या मतलब है। मैं एक ऐसी स्क्रिप्ट चाहता हूं जिसे किसी भी तरह के पाइपलाइन सीक्वेंस (यानी यूनिक्स तरीके से) में रखा जा सके। मैंने ऊपर एक विस्तृत उदाहरण दिया है, लेकिन यह निश्चित रूप से एकमात्र उपयोग मामला नहीं है जो मेरे पास है।
jmc 21

1
का प्रयोग करेंcmd | { some processing; read var </dev/tty; } | cmd
mikeserv

@mikeserv - दिलचस्प है! मेरे पास अब alias selector='{ TMPFILE=$(mktemp); cat > $TMPFILE; nl -s") " $TMPFILE | column -c $(tput cols); read -e -p"Select options: " < /dev/tty; rangeselect -v range="$REPLY" $TMPFILE; rm $TMPFILE; }'बहुत अच्छा काम है। हालाँकि grep b foo | selector | wc -lयहाँ पर टूट जाता है। किसी भी विचार कैसे तय करने के लिए? वैसे, rangeselectजो मैंने इस्तेमाल किया है वह पास्टबिन . com / VAxTSSHs पर पाया जा सकता है । यह एक सरल AWK स्क्रिप्ट है, जो किसी दी गई श्रेणी की लिनन्यूट्स के अनुरूप फाइल की लाइनों को प्रिंट करता है। (रंग "3-10, 12,14,16-20" जैसी चीजें हो सकते हैं।)
jmc

1
ऐसा नहीं aliasहै, बल्कि selector() { all of that stuff...; }एक समारोह में। कार्य के एक सरल कमांड में एक यौगिक कमांड पैक करते समय, सरल कमांड काalias नाम बदलें ।
21

जवाबों:


8

उपयोग करना /proc/$PPID/fd/0अविश्वसनीय है: selectorप्रक्रिया के माता-पिता के पास इसके इनपुट के रूप में टर्मिनल नहीं हो सकता है।

एक मानक पथ है जो हमेशा वर्तमान प्रक्रिया के टर्मिनल को संदर्भित करता है /dev/tty:।

nl "$INPUT" >/dev/tty
read -p"Select options: " </dev/tty

या

exec </dev/tty >/dev/tty
nl "$INPUT"
read -p"Select options: "

1
धन्यवाद, जो मेरी समस्या हल करता है। उत्तर थोड़ा न्यूनतर है, हालांकि। मुझे लगता है कि सवाल में टिप्पणियों में mikeserv की कुछ सलाह शामिल करने से फायदा हो सकता है।
जेएमसी

2

मैंने एक छोटा सा फ़ंक्शन लिखा है: यह इस बारे में जवाब नहीं देगा कि आपने पाइप की चेनिंग के बारे में क्या पूछा है, लेकिन आपकी समस्या का समाधान करेगा।

inf() ( [ -n "$ZSH_VERSION" ] && emulate sh
        unset n i c; set -f; tab='      ' IFS='
';      _in()   until [ "$((i+=1))" -gt 5 ] && exit 1
                printf '\nSelect: '
                read -r c && [ -n "${c##*[!- 0-9]*}" ]
                do echo "Invalid selection."
                done
        _out()  for n do i=; [ "$n" = . ]  &&
                printf '"${%d#*$tab}" ' $c ||
                until c="${c#*.} ${i:=${n%%-*}}"
                [ "$((i+=1))" -gt "${n#*-}" ]
                do :; done; done
set -- $(grep "$@"|nl -w1 -s "$tab"|tee /dev/tty)
i=$((($#<1)*5)); _in </dev/tty >/dev/tty
eval "printf '%s\n' $(c=$c\ . IFS=\ ;_out $c)"
)

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

grepइसका आउटपुट पास करता है, nlजो प्रत्येक पंक्ति को संख्या देता है और जो इसके आउटपुट को पास करता है, जो इसके आउटपुट को teeडुप्लिकेट करता है stdoutऔर दोनों को /dev/tty। इसका मतलब यह है कि पाइपलाइन से आउटपुट एक साथ फ़ंक्शन के तर्क सरणी दोनों में मुद्रित किया जाता है जहां यह \newlines पर और टर्मिनल के रूप में काम करता है।

यदि किसी कार्य में पिछली क्रिया से कम से कम 1 परिणाम होता है, तो अगला _in()फ़ंक्शन फ़ंक्शन के readचयन का प्रयास करता है । चयन में केवल स्थान द्वारा अलग किए गए नंबर शामिल हो सकते हैं, या फिर संख्या द्वारा अलग-अलग संख्याएँ हो सकती हैं -। अगर कुछ और है read (एक रिक्त रेखा सहित) यह फिर से कोशिश करेगा - लेकिन केवल, पहले की तरह, अधिकतम पांच बार।

अंतिम _out()फ़ंक्शन उपयोगकर्ता के चयन को पार्स करता है और उसमें किसी भी सीमा का विस्तार करता है। यह "${[num]}"प्रत्येक के लिए फॉर्म में इसके परिणामों को प्रिंट करता है - जिससे inf()'s arg' ऐरे में संग्रहीत लाइनों के मान से मेल खाता है । यह आउटपुट evalएड के रूप में होता है, printfजिससे उपयोगकर्ता द्वारा चुनी गई लाइनों को प्रिंट करता है।

यह स्पष्ट रूप readसे टर्मिनल से है और केवल Select:मेनू को प्रिंट करता है stderrऔर इसलिए यह काफी पाइपलाइन अनुकूल है। उदाहरण के लिए, निम्नलिखित कार्य:

seq 100 |inf 3|grep 8
1       3
2       13
3       23
4       30
5       31
6       32
7       33
8       34
9       35
10      36
11      37
12      38
13      39
14      43
15      53
16      63
17      73
18      83
19      93

Select: 6 9 12-18
38
83

लेकिन आप अपने द्वारा दिए गए किसी भी विकल्प का उपयोग कर सकते हैं grepऔर जितने भी फ़ाइलनाम आप इसे सौंप सकते हैं। यही है, आप किसी भी लेकिन एक तरह का उपयोग कर सकते हैं - इसके पार्सिंग इनपुट के साइड-इफेक्ट के रूप में इसके साथ $IFSकाम नहीं करेंगे यदि आप रिक्त लाइनों की खोज कर रहे हैं। लेकिन कौन रिक्त लाइनों की एक क्रमांकित सूची से चयन करना चाहेगा?

अंतिम ध्यान दें कि क्योंकि यह फ़ंक्शन के तर्क सरणी में संग्रहीत संख्यात्मक स्थितीय मापदंडों में सीधे संख्यात्मक उपयोगकर्ता इनपुट का अनुवाद करके काम करता है, फिर आउटपुट उपयोगकर्ता का चयन करता है, जैसा कि कई बार उपयोगकर्ता इसे चुनता है, और उपयोगकर्ता जिस भी क्रम में चयन करता है। यह।

उदाहरण के लिए:

seq 1000 | inf 00\$

1       100
2       200
3       300
4       400
5       500
6       600
7       700
8       800
9       900
10      1000

Select: 4-8 1 1 3-6
400
500
600
700
800
100
100
300
400
500
600

@mikeserv यह केवल एक विचार था, संपूर्ण स्क्रिप्ट नहीं, और एक बात, आप परीक्षण के बारे में बात कर रहे हैं, मूल फ़ाइल केवल डिस्क में है, इसलिए आप उनसे ले रहे हैं। इसलिए मुझे लगता है कि यह एक मुद्दा या इसे परीक्षण करने के लिए अतिरिक्त प्रयास नहीं है
Hackaholic

@ mikeserv हां, आप सही हैं, मैंने सभी चीजों को मान्य नहीं किया है, जैसे अनुचित इनपुट और सभी। अपनी बात के लिए धन्यवाद
Hackaholic

@mikeserv मुझे शेल प्रोग्रामिंग के सभी मूल पता है, क्या आप मुझे उन्नत करने के लिए जाने के लिए मार्गदर्शन कर सकते हैं
हैकहॉलिक

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