मेरी शेल स्क्रिप्ट के लिए कॉन्फ़िग फ़ाइल का उपयोग करें


26

मुझे अपनी स्वयं की स्क्रिप्ट के लिए एक विन्यास फाइल बनाने की आवश्यकता है: यहाँ एक उदाहरण है:

स्क्रिप्ट:

#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2

की सामग्री /home/myuser/test/config:

nam="Mark"
sur="Brown"

यह काम करता है!

मेरा प्रश्न: यह ऐसा करने का सही तरीका है या अन्य तरीके हैं?


चर शीर्ष पर होना चाहिए। मुझे आश्चर्य है कि यह काम करता है। वैसे भी, आपको एक कॉन्फ़िग फ़ाइल की आवश्यकता क्यों है? क्या आप इन चर का उपयोग कहीं और करने की योजना बना रहे हैं?
फहीम मीठा

फहीम, मुझे चरों की जरूरत है क्योंकि मेरी स्क्रिप्ट के कई विकल्प हैं: एक कॉन्फिग फाइल का इस्तेमाल करने से स्क्रिप्ट रच जाएगी। धन्यवाद
पोल Hallen

5
IMHO इसका जुर्माना। मैं इस तरह से करूँगा।
टिनि डे

abcdeयह भी इस तरह से करता है और यह एक बहुत बड़ा कार्यक्रम है (शेल स्क्रिप्ट के लिए)। आप इसे यहाँ देख सकते हैं ।
लुकास

जवाबों:


19

sourceयह सुरक्षित नहीं है क्योंकि यह मनमाना कोड निष्पादित करेगा। यह आपके लिए चिंता का विषय नहीं हो सकता है, लेकिन यदि फ़ाइल की अनुमति गलत है, तो फाइल सिस्टम के साथ हमलावर के लिए यह संभव हो सकता है कि कोड को एक विशेषाधिकार प्राप्त उपयोगकर्ता के रूप में कोड को निष्पादित करने के लिए एक अन्यथा सुरक्षित स्क्रिप्ट जैसे भरी हुई फ़ाइल द्वारा लोड की गई एक कॉन्फ़िगर फ़ाइल के रूप में हो। init स्क्रिप्ट।

अब तक, सबसे अच्छा समाधान जिसकी पहचान करने में मैं सक्षम हूं, वह है अनाड़ी पुनर्निवेश-द-व्हील समाधान:

myscript.conf

password=bar
echo rm -rf /
PROMPT_COMMAND='echo "Sending your last command $(history 1) to my email"'
hostname=localhost; echo rm -rf /

का उपयोग करते हुए source, यह echo rm -rf /दो बार चलेगा , साथ ही साथ चल रहे उपयोगकर्ता को बदल देगा $PROMPT_COMMAND। इसके बजाय, यह करें:

myscript.sh (बैश 4)

#!/bin/bash
typeset -A config # init array
config=( # set default values in config array
    [username]="root"
    [password]=""
    [hostname]="localhost"
)

while read line
do
    if echo $line | grep -F = &>/dev/null
    then
        varname=$(echo "$line" | cut -d '=' -f 1)
        config[$varname]=$(echo "$line" | cut -d '=' -f 2-)
    fi
done < myscript.conf

echo ${config[username]} # should be loaded from defaults
echo ${config[password]} # should be loaded from config file
echo ${config[hostname]} # includes the "injected" code, but it's fine here
echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have
               # been looking for, but they're sandboxed inside the $config array

myscript.sh (मैक / बैश 3-संगत)

#!/bin/bash
config() {
    val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d '=' -f 2-)

    if [[ $val == __DEFAULT__ ]]
    then
        case $1 in
            username)
                echo -n "root"
                ;;
            password)
                echo -n ""
                ;;
            hostname)
                echo -n "localhost"
                ;;
        esac
    else
        echo -n $val
    fi
}

echo $(config username) # should be loaded from defaults
echo $(config password) # should be loaded from config file
echo $(config hostname) # includes the "injected" code, but it's fine here
echo $(config PROMPT_COMMAND) # also respects variables that you may not have
               # been looking for, but they're sandboxed inside the $config array

कृपया जवाब दें कि क्या आपको मेरे कोड में कोई सुरक्षा शोषण मिला है।


1
FYI करें यह एक Bash वर्जन 4.0 सॉल्यूशन है, जो दुख की बात है कि Apple द्वारा लगाए गए लाइसेंसिंग के मुद्दों के अधीन है और Macs पर डिफ़ॉल्ट रूप से उपलब्ध नहीं है
Sukima

@ सुकिमा अच्छी बात है। मैंने एक संस्करण जोड़ा है जो बैश 3 के साथ संगत है। इसकी कमजोरी यह है कि यह *इनपुट में ठीक से हैंडल नहीं करेगा , लेकिन फिर बैश में इस चरित्र को अच्छी तरह से क्या हैंडल करता है?
मिकेल

यदि पासवर्ड में बैकस्लैश है तो पहली स्क्रिप्ट विफल हो जाती है।
Kusalananda

@ कुसलानंद अगर बैकस्लैश बच गया तो क्या होगा? my\\password
मिकेल

10

यहाँ एक साफ और पोर्टेबल संस्करण है जो मैक और लिनक्स दोनों पर बैश 3 और इसके साथ संगत है।

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

config.cfg :

myvar=Hello World

config.cfg.defaults :

myvar=Default Value
othervar=Another Variable

config.shlib (यह एक पुस्तकालय है, इसलिए कोई भी शेबंग-लाइन नहीं है):

config_read_file() {
    (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}

config_get() {
    val="$(config_read_file config.cfg "${1}")";
    if [ "${val}" = "__UNDEFINED__" ]; then
        val="$(config_read_file config.cfg.defaults "${1}")";
    fi
    printf -- "%s" "${val}";
}

test.sh (या कोई स्क्रिप्ट जहां आप कॉन्फिगर वैल्यू पढ़ना चाहते हैं) :

#!/usr/bin/env bash
source config.shlib; # load the config library functions
echo "$(config_get myvar)"; # will be found in user-cfg
printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing!
myvar="$(config_get myvar)"; # how to just read a value without echoing
echo "$(config_get othervar)"; # will fall back to defaults
echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn't set anywhere

परीक्षण स्क्रिप्ट की व्याख्या:

  • ध्यान दें कि test.sh में config_get के सभी उपयोग दोहरे उद्धरण चिह्नों में लिपटे हुए हैं। प्रत्येक config_get को दोहरे उद्धरण चिह्नों में लपेटकर, हम यह सुनिश्चित करते हैं कि चर मान में पाठ को झंडे के रूप में कभी भी गलत नहीं समझा जाएगा। और यह सुनिश्चित करता है कि हम व्हॉट्सएप को ठीक से संरक्षित करें, जैसे कि कॉन्फ़िगरेशन मान में एक पंक्ति में कई रिक्त स्थान।
  • और वो printfलाइन क्या है ? ठीक है, यह कुछ ऐसा है जिसके बारे में आपको पता होना चाहिए: echoमुद्रण पाठ के लिए एक बुरा आदेश है जिसका आपके पास कोई नियंत्रण नहीं है। यहां तक ​​कि अगर आप दोहरे उद्धरण चिह्नों का उपयोग करते हैं, तो यह झंडे की व्याख्या करेगा। सेटिंग myvar(इन config.cfg) करने का प्रयास करें -eऔर आप एक खाली रेखा देखेंगे, क्योंकि echoसोचेंगे कि यह एक ध्वज है। लेकिन printfउस समस्या नहीं है। printf --"इस प्रिंट, और झंडे के रूप में कुछ भी व्याख्या नहीं है" कहते हैं, और "%s\n"कहते हैं, "पीछे न्यू लाइन के साथ एक स्ट्रिंग के रूप में उत्पादन को स्वरूपित, और अंत में अंतिम पैरामीटर प्रारूप करने के लिए printf के लिए मूल्य है।
  • यदि आप स्क्रीन पर मूल्यों को प्रतिध्वनित नहीं करने जा रहे हैं, तो आप उन्हें सामान्य रूप से असाइन करेंगे, जैसे myvar="$(config_get myvar)";। यदि आप उन्हें स्क्रीन पर प्रिंट करने जा रहे हैं, तो मैं सुझाव देता हूं कि किसी भी गूंज-असंगत तारों के खिलाफ प्रिंटफ़ का उपयोग पूरी तरह से सुरक्षित हो जो उपयोगकर्ता के कॉन्फ़िगरेशन में हो सकता है। लेकिन इको ठीक है अगर उपयोगकर्ता द्वारा प्रदान किया गया वैरिएबल स्ट्रिंग का पहला वर्ण नहीं है जिसे आप गूँज रहे हैं, क्योंकि यह एकमात्र ऐसी स्थिति है जहाँ "झंडे" की व्याख्या की जा सकती है, इसलिए ऐसा कुछ echo "foo: $(config_get myvar)";सुरक्षित है, क्योंकि "फू" नहीं है एक डैश के साथ शुरू करें और इसलिए प्रतिध्वनि बताता है कि बाकी स्ट्रिंग के लिए झंडे नहीं हैं। :-)

@ user2993656 यह बताने के लिए धन्यवाद कि मेरे मूल कोड में अभी भी मेरा निजी कॉन्फिगर नाम (environment.cfg) था, बजाय सही एक के। जैसा कि आपने किया "इको-एन" एडिट के लिए, जो उपयोग किए गए शेल पर निर्भर करता है। मैक / लिनक्स बैश पर, "इको-एन" का अर्थ "नई पंक्ति को पीछे किए बिना प्रतिध्वनि" है, जो मैंने अनुगामी सुर्खियों से बचने के लिए किया था। लेकिन यह इसके बिना बिल्कुल समान काम करता है, इसलिए संपादन के लिए धन्यवाद!
gw0

वास्तव में, मैं बस के माध्यम से चला गया और इसे गूंज के बजाय प्रिंटफ का उपयोग करने के लिए फिर से लिखा था, जो यह सुनिश्चित करता है कि हम विन्यास मूल्यों में "झंडे" को गलत तरीके से गूंजने के जोखिम से छुटकारा पा लेंगे।
gw0

मुझे वास्तव में यह संस्करण पसंद है। मैं config.cfg.defaultsफोन करने के समय उन्हें परिभाषित करने के एवज में गिरा $(config_get var_name "default_value")tritarget.org/static/...
Sukima

7

कॉन्फ़िगरेशन फ़ाइल को पार्स करें, इसे निष्पादित न करें।

मैं वर्तमान में काम पर एक आवेदन लिख रहा हूँ जो एक अत्यंत सरल XML कॉन्फ़िगरेशन का उपयोग करता है:

<config>
    <username>username-or-email</username>
    <password>the-password</password>
</config>

शेल स्क्रिप्ट ("एप्लिकेशन") में, यह वही है जो मैं उपयोगकर्ता नाम पर प्राप्त करने के लिए करता हूं (कम या ज्यादा, मैंने इसे शेल फ़ंक्शन में रखा है):

username="$( xml sel -t -v '/config/username' "$config_file" )"

xmlआदेश है XMLStarlet है, जो सबसे Unices लिए उपलब्ध है।

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

यदि आप JSON पसंद करते हैं, तो jqशेल JSON पार्सर का उपयोग करना आसान है।

मेरी कॉन्फ़िगरेशन फ़ाइल JSON में कुछ इस तरह दिखाई देगी:

{                                 
  "username": "username-or-email",
  "password": "the-password"      
}                

और फिर मुझे स्क्रिप्ट में उपयोगकर्ता नाम मिलेगा:

username="$( jq -r '.username' "$config_file" )"

स्क्रिप्ट को निष्पादित करने के कई फायदे और नुकसान हैं। मुख्य नुकसान सुरक्षा हैं, अगर कोई कॉन्फिग फ़ाइल को बदल सकता है तो वे कोड निष्पादित कर सकते हैं, और इसे बेवकूफ प्रमाण बनाना कठिन है। फायदे गति हैं, एक साधारण परीक्षण पर यह pk चलाने के लिए एक कॉन्फिग फ़ाइल को स्रोत से 10,000 गुना अधिक तेज है, और लचीलापन, जो कोई भी बंदर पैचिंग पसंद करता है वह इसकी सराहना करेगा।
इकारस

@icarus आप आमतौर पर कितनी बड़ी कॉन्फ़िगरेशन फ़ाइलों का सामना करते हैं, और आपको एक सत्र में उन्हें कितनी बार पार्स करने की आवश्यकता है? ध्यान दें कि एक बार में कई मान XML या JSON से बाहर हो सकते हैं।
Kusalananda

आमतौर पर केवल कुछ (1 से 3) मान। यदि आप evalकई मान सेट करने के लिए उपयोग कर रहे हैं, तो आप कॉन्फ़िगर फ़ाइल :-) के चयनित भागों को निष्पादित कर रहे हैं।
इकारस

1
@icarus मैं सरणियाँ सोच रहा था ... evalकुछ भी करने की जरूरत नहीं। मौजूदा पार्सर (भले ही यह बाहरी उपयोगिता हो) के साथ एक मानक प्रारूप का उपयोग करने का प्रदर्शन मजबूती के मुकाबले नगण्य है, कोड की मात्रा, उपयोग की आसानी, और रखरखाव।
Kusalananda

1
"कॉन्फिग फाइल को पार्स करने के लिए +1, इसे निष्पादित न करें"
Iiridayn

4

सबसे आम, कुशल और सही तरीके का उपयोग करना है source, या .एक आशुलिपि के रूप में। उदाहरण के लिए:

source /home/myuser/test/config

या

. /home/myuser/test/config

हालांकि, कुछ विचार करने के लिए, सुरक्षा मुद्दे हैं जो एक अतिरिक्त बाहरी-खट्टा कॉन्फ़िगरेशन फ़ाइल का उपयोग कर सकते हैं, यह देखते हुए कि अतिरिक्त कोड डाला जा सकता है। अधिक जानकारी के लिए, इस समस्या का पता लगाने और हल करने के तरीके सहित, मैं http://wiki.bash-hackers.org/howto/conffile#secure_it के it सिक्योर इट ’अनुभाग पर एक नज़र डालने की सलाह दूंगा


5
मुझे उस लेख के लिए उच्च उम्मीदें थीं (मेरे खोज परिणामों में भी), लेकिन दुर्भावनापूर्ण कोड को फ़िल्टर करने के लिए regex का उपयोग करने के प्रयास के लेखक का सुझाव व्यर्थता में एक अभ्यास है।
मिकेल

डॉट के साथ प्रक्रिया, एक पूर्ण पथ की आवश्यकता है? रिश्तेदार के साथ यह काम नहीं करता है
डेविड

2

मैं अपनी स्क्रिप्ट में इसका उपयोग करता हूं:

sed_escape() {
  sed -e 's/[]\/$*.^[]/\\&/g'
}

cfg_write() { # path, key, value
  cfg_delete "$1" "$2"
  echo "$2=$3" >> "$1"
}

cfg_read() { # path, key -> value
  test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1
}

cfg_delete() { # path, key
  test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1"
}

cfg_haskey() { # path, key
  test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null
}

हर चरित्र संयोजन का समर्थन करना चाहिए, सिवाय इसके =कि उनमें कोई चाबी नहीं हो सकती है, क्योंकि वह सेपरेटर है। और कुछ भी काम करता है।

% cfg_write test.conf mykey myvalue
% cfg_read test.conf mykey
myvalue
% cfg_delete test.conf mykey
% cfg_haskey test.conf mykey || echo "It's not here anymore"
It's not here anymore

इसके अलावा, यह पूरी तरह से सुरक्षित है क्योंकि यह किसी भी प्रकार का उपयोग नहीं करता है source/eval


0

मेरे परिदृश्य के लिए, sourceया .ठीक था, लेकिन मैं FOO=bar myscript.shकॉन्फ़िगर किए गए चर पर पूर्वता लेते हुए स्थानीय पर्यावरण चर (यानी ) का समर्थन करना चाहता था । मैं यह भी चाहता था कि विन्यास फाइल उपयोगकर्ता को संपादन योग्य लगे और किसी को भी सहजता से कॉन्फ़िगर की गई फ़ाइलों के लिए उपयोग किया जाए, और इसे यथासंभव छोटी / सरल बनाए रखने के लिए, मेरी बहुत छोटी स्क्रिप्ट के मुख्य जोर से विचलित न होने के लिए।

मैंने ये ढूंढ निकाला:

CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh
if [ ! -f $CONF ]; then
    cat > $CONF << CONF
VAR1="default value"
CONF
fi
. <(sed 's/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/' < $CONF)

अनिवार्य रूप से - यह चर परिभाषाओं के लिए जाँच करता है (व्हाट्सएप के बारे में बहुत लचीला होने के बिना) और उन पंक्तियों को फिर से लिखता है ताकि मान उस चर के लिए डिफ़ॉल्ट में परिवर्तित हो जाए, और यदि XDG_CONFIG_HOMEचर ऊपर पाया जाता है, तो चर असम्बद्ध है। यह कॉन्फ़िगर फ़ाइल के इस परिवर्तित संस्करण का स्रोत है और जारी है।

भविष्य के काम sedस्क्रिप्ट को और अधिक मजबूत बना सकते हैं , अजीब लगने वाली रेखाओं को फ़िल्टर कर सकते हैं या परिभाषाएँ आदि नहीं हैं, लाइन टिप्पणियों के अंत में नहीं टूटेंगे - लेकिन यह मेरे लिए अभी के लिए पर्याप्त है।


0

यह रसीला और सुरक्षित है:

# Read common vars from common.vars
# the incantation here ensures (by env) that only key=value pairs are present
# then declare-ing the result puts those vars in our environment 
declare $(env -i `cat common.vars`)

यह -iसुनिश्चित करता है कि आप केवल चर प्राप्त करेंcommon.vars


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