जब मेरा कार्य एक पाइपलाइन में कहा जाता है, तो पर्यावरण चर सेट नहीं होते हैं


10

पर्यावरण चर सेट करने के लिए मेरे पास निम्न पुनरावर्ती कार्य है:

function par_set {
  PAR=$1
  VAL=$2
  if [ "" != "$1" ]
  then
    export ${PAR}=${VAL}
    echo ${PAR}=${VAL}
    shift
    shift
    par_set $*
  fi
}

अगर मैं इसे स्वयं कहता हूं, तो यह दोनों चर और गूँज को stdout में सेट करता है:

$ par_set FN WORKS
FN=WORKS
$ echo "FN = "$FN
FN = WORKS

फ़ाइल के लिए पुन: प्रेषित करना भी काम करता है:

$ par_set REDIR WORKS > out
cat out
REDIR=WORKS
$ echo "REDIR = "$REDIR
REDIR = WORKS

लेकिन, अगर मैं किसी अन्य कमांड पर stdout करता हूं, तो चर सेट नहीं होता है:

$ par_set PIPE FAILS |sed -e's/FAILS/BARFS/'
PIPE=BARFS
$ echo "PIPE = "$PIPE
PIPE =

पाइप फ़ंक्शन को चर को निर्यात करने से क्यों रोकता है? क्या अस्थायी फ़ाइलों या नामित पाइपों का सहारा लिए बिना इसे ठीक करने का कोई तरीका है?

हल किया:

काम कोड गिल्स के लिए धन्यवाद:

par_set $(echo $*|tr '=' ' ') > >(sed -e's/^/  /' >> ${LOG})

इससे स्क्रिप्ट को इस प्रकार बुलाया जा सकता है:

$ . ./script.sh PROCESS_SUB ROCKS PIPELINES=NOGOOD
$ echo $PROCESS_SUB
ROCKS
$ echo $PIPELINES
NOGOOD
$ cat log
7:20140606155622162731431:script.sh:29581:Parse Command Line parameters.  Params must be in matched pairs separated by one or more '=' or ' '.
  PROCESS_SUB=ROCKS
  PIPELINES=NOGOOD

पूर्ण कोड में रुचि रखने पर प्रोजेक्ट को बिटकॉइन https://bitbucket.org/adalby/monitor-bash पर होस्ट किया गया ।

जवाबों:


8

एक पाइप लाइन के प्रत्येक भाग (यानी पाइप के प्रत्येक पक्ष) एक अलग प्रक्रिया (एक subshell कहा जाता है, जब एक खोल में चलाता कांटे स्क्रिप्ट का हिस्सा चलाने के लिए एक उपप्रक्रिया)। में par_set PIPE FAILS |sed -e's/FAILS/BARFS/', दPIPE चर उपप्रक्रिया कि पाइप के बाएं ओर कार्यान्वित में सेट है। यह परिवर्तन मूल प्रक्रिया में परिलक्षित नहीं होता है (पर्यावरण चर प्रक्रियाओं के बीच स्थानांतरण नहीं करते हैं, वे केवल सबप्रोसेस द्वारा विरासत में मिले हैं।

पाइप का बायाँ भाग सदैव एक उपधारा में चलता है। कुछ गोले (ATT ksh, zsh) माता-पिता के गोले में दाईं ओर चलते हैं; अधिकांश उप-भाग में दाईं ओर भी चलते हैं।

यदि आप स्क्रिप्ट के एक भाग के आउटपुट को पुनर्निर्देशित करना चाहते हैं और उस भाग को मूल खोल में, ksh / bash / zsh में चलाते हैं, तो आप प्रक्रिया प्रतिस्थापन का उपयोग कर सकते हैं ।

par_set PROCESS SUBSTITUTION > >(sed s/ION/ED/)

किसी भी POSIX शेल के साथ, आप आउटपुट को नामित पाइप पर पुनर्निर्देशित कर सकते हैं।

mkfifo f
<f grep NAMED= &
par_set NAMED PIPE >f

ओह, और आप चर प्रतिस्थापन के आसपास उद्धरण याद कर रहे हैं , जैसे आपका कोड चीजों पर टूट जाता है par_set name 'value with spaces' star '*'

export "${PAR}=${VAL}"

par_set "$@"

जीत के लिए प्रक्रिया प्रतिस्थापन! मुझे पता था कि मैं एक नामित पाइप या अस्थायी फ़ाइल का उपयोग कर सकता हूं, लेकिन वे बदसूरत हैं, खराब संगामिति हैं, और पीछे एक गड़बड़ छोड़ते हैं यदि स्क्रिप्ट मर जाती है (जाल पिछले एक के साथ मदद करता है)। अंतरिक्ष की बात इरादतन है। सम्मेलन द्वारा, कमांड लाइन पर पारित चर नाम / मूल्य जोड़े में होते हैं और '=' और / या '' द्वारा अलग किए जाते हैं।
एंड्रयू

3

यह काम नहीं करता है क्योंकि पाइप का प्रत्येक भाग एक सब-इन में चलता है bash, और एक सब-वे में सेट किए गए चर उस उप-समूह के लिए स्थानीय हैं।

अपडेट करें:

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

कुछ संदर्भ:

http://mywiki.wooledge.org/BashFAQ/024
https://stackoverflow.com/q/15541321/3565972
https://stackoverflow.com/a/15383353/3565972
http://forums.opoduse.org/showthread .php / 458,979-कैसे निर्यात-चर-इन-subshell-बैक-आउट करने के लिए माता-पिता


मुझे लगा कि यह एक संभावना है, लेकिन फ़ंक्शन को वर्तमान शेल में निष्पादित करना चाहिए। मैंने फ़ंक्शन में "इको $ $" जोड़कर परीक्षण किया। par_set STILL नाखून | sed -e "s / ^ / sedpid = $$, fnpid = /" outputs sedpid = 15957, fnpid = 15957.
एंड्रयू

@Andrew इस stackoverflow.com/a/20726041/3565972 पर देखें । जाहिरा तौर पर, $$माता-पिता और बच्चे के गोले दोनों के लिए समान है। आप $BASHPIDसबसाइड पीआईडी ​​प्राप्त करने के लिए उपयोग कर सकते हैं । जब मैं echo $$ $BASHPIDभीतर होता par_setहूं तो मुझे अलग-अलग तरह की मुद्राएं मिलती हैं।
सवंतो

@ और फिर भी वर्कअराउंड खोजने की कोशिश कर रहा है, लेकिन असफल हो रहा है! =)
सवंतो

@ Savanto-धन्यवाद। मुझे नहीं पता था कि $ $ बनाम $ BASHPID या पाइप मजबूरन सबहेल्ड।
एंड्रयू

0

आप उपखंडों को इंगित करते हैं - जो कि एक पाइपलाइन के बाहर शेल में कुछ परिशोधन के साथ मिल सकता है - लेकिन समस्या का कठिन हिस्सा पाइपलाइन की संगति के साथ करना है

पाइपलाइन की सभी प्रक्रिया सदस्य एक ही बार शुरू करते हैं , और इस समस्या को समझना आसान हो सकता है यदि आप इसे इस तरह से देखते हैं:

{ sleep 1 ; echo $((f=1+2)) >&2 ; } | echo $((f))
###OUTPUT
0
...
3

पाइपलाइन प्रक्रियाएं चर मानों को विरासत में नहीं दे सकती हैं क्योंकि वे पहले से ही बंद हैं और चर कभी भी सेट होने से पहले चल रहे हैं।

मैं वास्तव में यह नहीं समझ सकता कि आपके कार्य का क्या मतलब है - वह कौन सा उद्देश्य है जो exportपहले से ही पूरा नहीं करता है? या बस भी var=val? उदाहरण के लिए, यहाँ लगभग वही पाइपलाइन फिर से है:

pipeline() { 
    { sleep 1
      echo "f=$((f=f+1+2))" >&3
    } | echo $((f)) >&2
} 3>&1

f=4 pipeline

###OUTPUT

4
...
f=7

और साथ export:

export $(f=4 pipeline) ; pipeline

###OUTPUT:

4
7
...
f=10

तो आपकी चीज़ काम कर सकती है:

par_set $(echo PIPE FAILS | 
    sed 's/FAIL/WORK/;/./w /path/to/log')

जो एक फ़ाइल sedके आउटपुट में लॉग इन करता है और इसे शेल-स्प्लिट के रूप में आपके फंक्शन में डिलीवर करता है "$@"

या, वैकल्पिक रूप से:

$ export $(echo PIPE FAILS | sed 's/ FAIL/=WORK/')
$ par_set $PIPE TWICE_REMOVED
$ echo "WORKS = "$WORKS
WORKS = TWICE_REMOVED

अगर मैं आपके फंक्शन को लिखने जा रहा हूँ, हालाँकि, यह शायद इस तरह दिखेगा:

_env_eval() { 
    while ${2+:} false ; do
       ${1:+export} ${1%%["${IFS}"]*}="${2}" || :
       shift 2
    done
}

यह सच है, लेकिन अप्रासंगिक है: पाइप के समाप्त होने के बाद एंड्रयू पाइप के दूसरी तरफ नहीं बल्कि चर का उपयोग करने की कोशिश कर रहा है।
गिल्स एसओ- बुराई को रोकें '

@ गिल्स - अच्छी तरह से वह ऐसा कर सकता है। |pipeline | sh
माइकस

@ गिल्स - वास्तव में, आपको जरूरत भी नहीं है sh। वह पहले से ही उपयोग कर रहा है export
मिकसेर

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

1
@ mikeserv- सभी टिप्पणियों के लिए धन्यवाद। मैं प्रक्रिया प्रतिस्थापन के गिल्स सुझाव के साथ गया था, लेकिन मेरे कोड के लिए बहस करने से मुझे बेहतर प्रोग्रामर बना दिया गया।
एंड्रयू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.