बश में स्क्रिप्ट में घोषित चर कैसे सूचीबद्ध करें?


98

बैश में मेरी स्क्रिप्ट में, बहुत सारे चर हैं, और मुझे उन्हें फ़ाइल करने के लिए सहेजने के लिए कुछ करना होगा। मेरा प्रश्न यह है कि मेरी स्क्रिप्ट में घोषित सभी चर को कैसे सूचीबद्ध किया जाए और इस तरह से सूची प्राप्त करें:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

जवाबों:


151

set चर का उत्पादन करेगा, दुर्भाग्य से यह भी कार्यों को परिभाषित करता है।

सौभाग्य से POSIX मोड केवल चर का उत्पादन करता है:

( set -o posix ; set ) | less

lessजहां आप विकल्प चाहते हैं, वहां तक पहुंचना या अनुप्रेषित करना।

तो बस स्क्रिप्ट में घोषित चर पाने के लिए:

( set -o posix ; set ) >/tmp/variables.before
source script
( set -o posix ; set ) >/tmp/variables.after
diff /tmp/variables.before /tmp/variables.after
rm /tmp/variables.before /tmp/variables.after

(या कम से कम उस पर आधारित कुछ :-))


जब मैं पोस्ट कर रहा था तब आपने संपादित किया था। -o posixअब एक अंतर के साथ अच्छा कॉल केवल चर शामिल होंगे।
ezpz

8
अस्थायी फ़ाइलों का उपयोग किए बिना VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;:। यह एक रेडी-टू-सेव फॉर्मेट में var का आउटपुट देगा। सूची में वे चर शामिल होंगे जिन्हें स्क्रिप्ट बदल गई (यह निर्भर करता है कि क्या यह वांछनीय है)
ivan_pozdeev

1
@ErikAronesty यदि आप किसी वातावरण को फिर से बनाना चाहते हैं source, तो आपको आउटपुट के साथ ऐसा करने में सक्षम होना चाहिए declare -p
छह

2
यदि आप अस्थायी फ़ाइलों के उपयोग का सहारा लिए बिना (या किसी भी समान आदेश) से पहले और बाद के वातावरण की तुलना करना चाहते हैं, तो आप निम्न के साथ ऐसा कर सकते हैं:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
छह

1
@ कैसर नहीं, मैंने केवल गैर-रिक्त सरणियों की कोशिश की। खाली सरणियों के मामले में आप सही हैं - वे मुद्रित नहीं हैं।
सोकोवि

41
compgen -v

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


2
अफसोस की बात है compgen -vकि वैश्विक चर भी सूचीबद्ध हैं जो स्थानीय रूप से परेशान थे। यकीन नहीं होता कि यह लंबे समय से बग या वांछित व्यवहार है।
मिशैल गोरनी

11
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

यह सभी शेल वैरिएबल नामों को प्रिंट करना होगा। आप अपनी फ़ाइल को सोर्स करने से पहले और बाद में "सेट" की तरह एक सूची प्राप्त कर सकते हैं कि कौन से चर नए हैं (जैसा कि अन्य उत्तरों में बताया गया है)। लेकिन ध्यान रखें कि इस तरह के फ़िल्टरिंग में अंतर के साथ कुछ चर भी फ़िल्टर किए जा सकते हैं जिनकी आपको ज़रूरत है लेकिन आपकी फ़ाइल को सोर्स करने से पहले मौजूद थे।

आपके मामले में, यदि आप जानते हैं कि आपके चर के नाम "VARIABLE" से शुरू होते हैं, तो आप अपनी स्क्रिप्ट को स्रोत बना सकते हैं और कर सकते हैं:

for var in ${!VARIABLE@}; do
   printf "%s%q\n" "$var=" "${!var}"
done

अद्यतन: शुद्ध कैश समाधान के लिए (कोई बाहरी कमांड का उपयोग नहीं किया गया):

for i in _ {a..z} {A..Z}; do
   for var in `eval echo "\\${!$i@}"`; do
      echo $var
      # you can test if $var matches some criteria and put it in the file or ignore
   done 
done

1
+1 और एक ऑनलाइनर संस्करण (एक एकल eval के साथ):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj

4

उपरोक्त कुछ उत्तरों के आधार पर, इसने मेरे लिए काम किया:

before=$(set -o posix; set | sort);

स्रोत फ़ाइल :

comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq) 

3

यदि आप पोस्ट-प्रोसेस कर सकते हैं, (जैसा कि पहले ही उल्लेख किया गया है) तो आप setअपनी स्क्रिप्ट की शुरुआत और अंत में (प्रत्येक एक अलग फ़ाइल में) कॉल कर सकते हैं और दो फाइलों पर एक अंतर कर सकते हैं। एहसास है कि यह अभी भी कुछ शोर होगा।

आप इसे प्रोग्रामेटिक रूप से भी कर सकते हैं। आउटपुट को केवल अपने वर्तमान दायरे तक सीमित करने के लिए, आपको एक चर को वेरिएबल क्रिएशन पर लागू करना होगा। उदाहरण के लिए

store() {
    export ${1}="${*:2}"
    [[ ${STORED} =~ "(^| )${1}($| )" ]] || STORED="${STORED} ${1}"
}

store VAR1 abc
store VAR2 bcd
store VAR3 cde

for i in ${STORED}; do
    echo "${i}=${!i}"
done

कौन सी पैदावार

VAR1=abc
VAR2=bcd
VAR3=cde

2

यहां @GinkgoFr उत्तर के समान कुछ है, लेकिन @Tino या @DejayClayton द्वारा पहचानी गई समस्याओं के बिना, और @ डगलस लीडर के चतुर set -o posixबिट की तुलना में अधिक मजबूत है :

+ function SOLUTION() { (set +o posix; set) | sed -ne '/^\w\+=/!q; p;'; }

अंतर यह है कि यह समाधान पहली गैर-चर रिपोर्ट के बाद STOPS है, उदाहरण के लिए पहले फ़ंक्शन द्वारा रिपोर्ट किया गया set

BTW: "टिनो" समस्या हल हो गई है। भले ही POSIX को बंद कर दिया गया हो और फ़ंक्शन द्वारा रिपोर्ट किया गया हो set, sed ...समाधान का भाग केवल चर रिपोर्ट (जैसे VAR=VALUEलाइनों) के माध्यम से अनुमति देता है । विशेष रूप से, A2है नहीं नकली तौर पर यह उत्पादन में किया जाता है।

+ function a() { echo $'\nA2=B'; }; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A9=999

और: "DejayClayton" समस्या हल हो गई है (परिवर्तनशील मूल्यों में एम्बेडेड newlines आउटपुट को बाधित नहीं करता है - प्रत्येक VAR=VALUEको एक एकल आउटपुट लाइन मिलती है):

+ A1=$'111\nA2=222'; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A1=$'111\nA2=222'
A9=999

नोट: @DouglasLeeder द्वारा प्रदान किया गया समाधान "DejayClayton" समस्या (एम्बेडेड सुर्खियों के साथ मूल्यों) से ग्रस्त है। नीचे, A1गलत है और A2बिल्कुल नहीं दिखाना चाहिए।

$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999

अंतिम रूप से: मुझे नहीं लगता कि bashमामलों का संस्करण है, लेकिन यह हो सकता है। मैंने इस पर अपना परीक्षण / विकास किया:

$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)

पोस्ट-एससीआरआईपीटी: ओपी के कुछ अन्य जवाबों को देखते हुए, मुझे <100% यकीन है कि set हमेशा मूल्य के भीतर नई \nसूचियों को परिवर्तित करता है , जो कि यह समाधान "डेजेकेल्टन" समस्या से बचने पर निर्भर करता है। शायद यह एक आधुनिक व्यवहार है? या एक संकलन-समय भिन्नता? या एक set -oया shoptविकल्प की स्थापना? यदि आप इस तरह के बदलाव जानते हैं, तो कृपया एक टिप्पणी जोड़ें ...


0

स्क्रिप्ट का उपयोग करने का प्रयास करें (इसे "ls_vars" कहते हैं):

  #!/bin/bash
  set -a
  env > /tmp/a
  source $1
  env > /tmp/b
  diff /tmp/{a,b} | sed -ne 's/^> //p'

chmod + x इसे, और:

  ls_vars your-script.sh > vars.files.save

0

सुरक्षा दृष्टिकोण से, या तो @ akostadinov का उत्तर या @ JuvenXu का उत्तरset , संभावित संभावित सुरक्षा दोष के कारण, कमांड के अनस्ट्रक्चर्ड आउटपुट पर भरोसा करने के लिए बेहतर है :

#!/bin/bash

function doLogic()
{
    local COMMAND="${1}"
    if ( set -o posix; set | grep -q '^PS1=' )
    then
        echo 'Script is interactive'
    else
        echo 'Script is NOT interactive'
    fi
}

doLogic 'hello'   # Script is NOT interactive
doLogic $'\nPS1=' # Script is interactive

उपरोक्त फ़ंक्शन चर की उपस्थिति के लिए जांच करने के लिए doLogicउपयोग करता setहैPS1 यह निर्धारित करने के लिए कि स्क्रिप्ट संवादात्मक है या नहीं (कभी भी ध्यान रखें कि यह उस लक्ष्य को पूरा करने का सबसे अच्छा तरीका है; यह सिर्फ एक उदाहरण है।)

हालांकि, का उत्पादन set अनस्ट्रक्चर्ड है, जिसका अर्थ है कि कोई भी वैरिएबल जिसमें एक न्यूलाइन है, परिणाम को पूरी तरह से दूषित कर सकता है।

यह, निश्चित रूप से, एक संभावित सुरक्षा जोखिम है। इसके बजाय, अप्रत्यक्ष चर नाम विस्तार के लिए या तो बैश के समर्थन का उपयोग करें, या compgen -v


0

इसे आज़माएँ: set | egrep "^\w+="( | lessपाइपिंग के साथ या उसके बिना )

पहला प्रस्तावित समाधान, ( set -o posix ; set ) | lessकाम करता है, लेकिन इसकी एक खामी है: यह नियंत्रण कोड को टर्मिनल तक पहुंचाता है, इसलिए वे ठीक से प्रदर्शित नहीं होते हैं। उदाहरण के लिए, यदि कोई चर (संभावना) है IFS=$' \t\n', तो हम देख सकते हैं:

IFS='
'

…बजाय।

मेरा egrepसमाधान इसे (और अंततः अन्य सिमिलर्स को) ठीक से प्रदर्शित करता है।


8 साल हो गए हैं, आप जानते हैं
lauriys

डाउनवोट किया गया क्योंकि यह सादा गलत bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A डिस्प्ले है A=B"-> फेल!
टीनो

अजीब परीक्षण जो केवल "_" (पिछले कमांड के लिए अंतिम तर्क) छद्म चर को लगता है, लेकिन दूसरों पर विफल नहीं होता है, खासकर IFS। इसे "बैश -c" के तहत चलाना भी महत्वपूर्ण नहीं बना सकता है। आपको कम से कम नीच के बजाय तटस्थ रहना चाहिए।
गिंगकोफ्र

0

मैंने शायद पहले भी जवाब चुराया है ... वैसे भी एक कवक के रूप में थोड़ा अलग:

    ##
    # usage source bin/nps-bash-util-funcs
    # doEchoVars
    doEchoVars(){

        # if the tmp dir does not exist
        test -z ${tmp_dir} && \
        export tmp_dir="$(cd "$(dirname $0)/../../.."; pwd)""/dat/log/.tmp.$$" && \
        mkdir -p "$tmp_dir" && \
        ( set -o posix ; set )| sort >"$tmp_dir/.vars.before"


        ( set -o posix ; set ) | sort >"$tmp_dir/.vars.after"
        cmd="$(comm -3 $tmp_dir/.vars.before $tmp_dir/.vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
        echo -e "$cmd"
    } 

0

printenvआदेश:

printenvenvironment variablesअपने मूल्यों के साथ सभी को प्रिंट करता है।

शुभ लाभ...


0

ऐसा करने का सरल तरीका यह है कि अपनी स्क्रिप्ट को चलाने से पहले सिस्टम वातावरण चर सेट करके हार्ड मोड का उपयोग करें और अपनी स्क्रिप्ट के केवल छाँटने के लिए भिन्न का उपयोग करें:

# Add this line at the top of your script :
set > /tmp/old_vars.log

# Add this line at the end of your script :
set > /tmp/new_vars.log

# Alternatively you can remove unwanted variables with grep (e.g., passwords) :
set | grep -v "PASSWORD1=\|PASSWORD2=\|PASSWORD3=" > /tmp/new_vars.log

# Now you can compare to sort variables of your script :
diff /tmp/old_vars.log /tmp/new_vars.log | grep "^>" > /tmp/script_vars.log

अब आप /tmp/script_vars.log में अपनी स्क्रिप्ट के चर पुनः प्राप्त कर सकते हैं। या कम से कम उस पर आधारित कुछ!


0

पार्टी में थोड़ी देर, लेकिन यहाँ एक और सुझाव है:

#!/bin/bash

set_before=$( set -o posix; set | sed -e '/^_=*/d' )

# create/set some variables
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c

set_after=$( set -o posix; unset set_before; set | sed -e '/^_=/d' )
diff  <(echo "$set_before") <(echo "$set_after") | sed -e 's/^> //' -e '/^[[:digit:]].*/d'

Diff + sed पाइपलाइन कमांड लाइन वांछित प्रारूप में सभी स्क्रिप्ट-निर्धारित वेरिएबल (ओ पी की पोस्ट में विनिर्दिष्ट है) आउटपुट:

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