कैसे करें और इंस फाइल को बैश एरे चर में परिवर्तित करें?


13

मैं एक इंस फ़ाइल को बैश ऐरे चर में बदलने की कोशिश कर रहा हूँ। नमूना आईएनआई निम्नानुसार है:

[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

तो ये बन जाते हैं:

session[foobar]=foo
path[foobar]=/some/path
session[barfoo]=bar

और इसी तरह।

अभी, मैं केवल इस आदेश के साथ आ सकता है

awk -F'=' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" $2 }'

इसके अलावा, एक और समस्या यह है कि यह रिक्त स्थान =को ध्यान में नहीं रखता है । मुझे लगता sedहै कि शायद इस नौकरी के लिए बेहतर अनुकूल है, लेकिन मुझे नहीं पता कि अनुभाग नाम के लिए एक अस्थायी चर को कैसे पकड़ें और स्टोर करें sed

तो किसी भी विचार यह कैसे करना है?


यदि ऐसा करने का कोई अन्य कारगर तरीका है, तो अपना समाधान भी पोस्ट करने के लिए स्वतंत्र महसूस करें :)
Flint


सरल समाधान के लिए, जांचें: मैं शेल स्क्रिप्ट के भीतर INI मान कैसे प्राप्त करूं? स्टैकओवरफ्लो एसई पर।
केनोरब

जवाबों:


10

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

awk -F ' *= *' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" "\"" $2 "\"" }'

ध्यान दें कि आप खश शो (केवल $ 1 और अनुभाग पर) को हटाने की जगह भी कर सकते हैं क्योंकि बैश चर नामों में स्थान नहीं हो सकते।

यदि मान समान चिह्न हैं, तो भी, यह विधि काम नहीं करेगा।

एक अन्य तकनीक एक बैश while readलूप का उपयोग करना और असाइनमेंट को निष्पादित करना है क्योंकि फ़ाइल को पढ़ा जाता है, declareजिसका उपयोग करके अधिकांश दुर्भावनापूर्ण सामग्री से सुरक्षित है।

foobar=1
barfoo=2  # or you could increment an index variable each time a section is found
while IFS='= ' read var val
do
    if [[ $var == \[*] ]]
    then
        section=$var
    elif [[ $val ]]
    then
        declare "$var$section=$val"
    fi
done < filename

फिर, साहचर्य सरणियों को काफी आसानी से समर्थन किया जा सकता है।


1
बहुत अच्छी जानकारी और मुझे विशेष रूप से दूसरी तकनीक पसंद है क्योंकि यह बाहरी कमांड पर भरोसा करने के बजाय फ़ंक्शन में निर्मित बैश का उपयोग करता है।
फ्लिंट

@TonyBarganski: कि एक AWK कॉल को एक दूसरे में पाइप करने के बजाय संशोधित किया जा सकता है।
अगली सूचना तक रोक दिया गया।

10

मैं इस नौकरी के लिए सरल अजगर स्क्रिप्ट का उपयोग करूंगा क्योंकि इसने INI पार्सर में बनाया है :

#!/usr/bin/env python

import sys, ConfigParser

config = ConfigParser.ConfigParser()
config.readfp(sys.stdin)

for sec in config.sections():
    print "declare -A %s" % (sec)
    for key, val in config.items(sec):
        print '%s[%s]="%s"' % (sec, key, val)

और फिर बैश में:

#!/bin/bash

# load the in.ini INI file to current BASH - quoted to preserve line breaks
eval "$(cat in.ini  | ./ini2arr.py)"

# test it:
echo ${barfoo[session]}

यकीन है, जाग में छोटे कार्यान्वयन हैं, लेकिन मुझे लगता है कि यह अधिक पठनीय और बनाए रखने में आसान है।


3
4.2 से पहले के बैश संस्करणों में, इसे भरने से पहले एक सहयोगी सरणी घोषित करना आवश्यक है, जैसेprint "declare -A %s" % (sec)
फेलिक्स ईव

2
इसके बजाय eval:source <(cat in.ini | ./ini2arr.py)
अगली सूचना तक रोक दिया गया।

3

यदि आप अतिरिक्त रिक्त स्थान को खत्म करना चाहते हैं, तो आप अंतर्निहित फ़ंक्शन का उपयोग कर सकते हैं gsub। उदाहरण के लिए, आप जोड़ सकते हैं:

gsub(/ /, "", $1);

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

gsub(/^ /, "", $1);
gsub(/ $/, "", $1);

शांत चाल। नहीं पता था कि इस तरह का बिल्ट-इन फंक्शन :)
फ्लिंट

0

यहाँ एक शुद्ध बैश समाधान है।

यह एक नया और बेहतर संस्करण है जिसे चिल्डेक्स ने पोस्ट किया है:

https://github.com/albfan/bash-ini-parser

प्रारंभिक उदाहरण का पालन करना वास्तव में आसान है: इसे डाउनलोड करने के बाद, बस फाइलों को कॉपी करें bash-ini-parser, और scripts/file.iniउसी निर्देशिका में, फिर एक क्लाइंट परीक्षण स्क्रिप्ट बनाएं जो मैंने उसी निर्देशिका के नीचे भी प्रदान की है।

source ./bash-ini-parser
cfg_parser "./file.ini"
cfg_section_sec2
echo "var2=$var2"
echo "var5[*]=${var5[*]}"
echo "var5[1]=${var5[1]}"

यहाँ मैं bash-ini-parser स्क्रिप्ट में किए गए कुछ और सुधार हैं ...

अगर आप विंडोज लाइन एंडिंग के साथ ही यूनिक्स के साथ आईएनआई फाइल को पढ़ने में सक्षम होना चाहते हैं, तो इस लाइन को तुरंत cfg_parser फ़ंक्शन में जोड़ें, जो फ़ाइल को पढ़ता है:

ini=$(echo "$ini"|tr -d '\r') # remove carriage returns

यदि आप उन फ़ाइलों को पढ़ना चाहते हैं जिनमें प्रतिबंधात्मक पहुंच की अनुमति है, तो इस वैकल्पिक फ़ंक्शन को जोड़ें:

# Enable the cfg_parser to read "locked" files
function sudo_cfg_parser {

    # Get the file argument
    file=$1

    # If not "root", enable the "sudo" prefix
    sudoPrefix=
    if [[ $EUID -ne 0 ]]; then sudoPrefix=sudo; fi

    # Save the file permissions, then "unlock" the file
    saved_permissions=$($sudoPrefix stat -c %a $file)
    $sudoPrefix chmod 777 $file

    # Call the standard cfg_parser function
    cfg_parser $file

    # Restore the original permissions
    $sudoPrefix chmod $saved_permissions $file  
}

की वजह से कम करना पड़ा chmod 777। जबकि सबसे अच्छा एक छायादार अभ्यास, निश्चित रूप से ini फ़ाइल को निष्पादन योग्य बनाने की कोई आवश्यकता नहीं है। एक बेहतर तरीका यह होगा sudoकि फाइल को पढ़ने के लिए इस्तेमाल किया जाए, न कि अनुमतियों के साथ गड़बड़ करने के लिए।
रिचल्व

@ रिचलव ठीक है। मैं डाउन वोट स्पष्टीकरण की सराहना करता हूं। लेकिन, इस का एक छोटा सा हिस्सा है, जो कम से कम एक पूरे के रूप में सवाल का जवाब देने के रूप में महत्व है। "उत्तर" लिंक है: github.com/albfan/bash-ini-parser । नीचे की बजाय पूरी चीज़ को वोट करें, इसके लिए जो पहले से ही एक वैकल्पिक आवरण फ़ंक्शन लेबल है, आप एक संपादन का सुझाव दे सकते थे।
BuvinJ

0

हमेशा पायथन के कॉन्फिगरेशन के आस-पास होने के कारण, एक शेल हेल्पर फ़ंक्शन का निर्माण कर सकता है:

get_network_value()
{
    cat <<EOF | python
import ConfigParser
config = ConfigParser.ConfigParser()
config.read('network.ini')
print (config.get('$IFACE','$param'))
EOF
}

$IFACEऔर $paramपैरामीटर क्रमशः खंड हैं।

यह सहायक तब जैसे कॉल की अनुमति देता है:

address=`param=address get_network_value` || exit 1
netmask=`param=netmask get_network_value` || exit 1
gateway=`param=gateway get_network_value` || exit 1

उम्मीद है की यह मदद करेगा!


0

यदि आपके पास Git उपलब्ध है और मुख्य नामों में अंडरस्कोर का उपयोग न कर पाने की बाधा के साथ ठीक है, तो आप git configएक सामान्य-उद्देश्य INI पार्सर / संपादक के रूप में उपयोग कर सकते हैं ।

यह चारों ओर से मुख्य / मान जोड़े बाहर पार्स करने संभाल लेंगे =और छोड़ें तुच्छ खाली स्थान के, प्लस आप टिप्पणी (दोनों मिल ;और #) और प्रकार बलात्कार मूल रूप से मुक्त करने के लिए। मैंने नीचे ओपी के इनपुट .iniऔर वांछित आउटपुट (बैश साहचर्य सरणियों) के लिए एक पूर्ण कार्य उदाहरण शामिल किया है ।

हालाँकि, इस तरह एक कॉन्फिगर फाइल दी गई है

; mytool.ini
[section1]
    inputdir = ~/some/dir
    enablesomefeature = true
    enablesomeotherfeature = yes
    greeting = Bonjour, Monde!

[section2]
    anothersetting = 42

... बशर्ते आपको बस एक त्वरित-गंदे समाधान की आवश्यकता है, और एक बैश साहचर्य सरणी में सेटिंग्स होने के विचार से शादी नहीं की है, आप कम से कम के साथ दूर हो सकते हैं:

eval $(git config -f mytool.ini --list | tr . _)

# or if 'eval' skeeves you out excessively
source <(git config -f mytool.ini --list | tr . _)

जो sectionname_variablenameवर्तमान परिवेश में नामित पर्यावरण चर बनाता है। यह, निश्चित रूप से, केवल तभी काम करता है जब आप भरोसा कर सकते हैं कि आपके किसी भी मूल्य में कभी भी एक अवधि या व्हाट्सएप नहीं होगा (अधिक मजबूत समाधान के लिए नीचे देखें)।

अन्य सरल उदाहरण

टाइपिंग को बचाने के लिए शेल फ़ंक्शन का उपयोग करके मनमाना मान प्राप्त करना:

function myini() { git config -f mytool.ini; }

बैस मैन पेज के अनुसार, एक उपनाम भी ठीक होगा, लेकिन वे भी शेल स्क्रिप्ट [ 1 ] में सामान्य रूप से विस्तारित नहीं होते हैं , और वैसे भी उपनामों को शेल कार्यों द्वारा "" [ 2 ] के रूप में फैलाया जाता है ।

myini --list
# result:
# section1.inputdir=~/some/dir
# section1.enablesomefeature=true
# section1.enablesomeotherfeature=yes
# section2.anothersetting=42

myini --get section1.inputdir
# result:
# ~/some/dir

--typeविकल्प के साथ , आप पूर्णांक, बूलियन, या पथ के रूप में विशिष्ट सेटिंग्स को "स्वचालित रूप से विस्तारित" कर सकते हैं ~:

myini --get --type=path section1.inputdir  # value '~/some/dir'
# result:
# /home/myuser/some/dir

myini --get --type=bool section1.enablesomeotherfeature  # value 'yes'
# result:
# true

थोड़ा अधिक मजबूत त्वरित और गंदा उदाहरण

वर्तमान परिवेश में सभी वैरिएबल mytool.iniउपलब्ध कराएं SECTIONNAME_VARIABLENAME, आंतरिक व्हॉट्सएप को प्रमुख मूल्यों में संरक्षित करना:

source <(
    git config -f mytool.ini --list \
      | sed 's/\([^.]*\)\.\(.*\)=\(.*\)/\U\1_\2\E="\3"/'
)

अंग्रेजी में sed अभिव्यक्ति क्या कर रही है, है

  1. एक अवधि तक गैर-अवधि वर्णों का एक गुच्छा ढूंढना, जो कि \1तब याद रखना
  2. एक समान चिह्न तक वर्णों का एक समूह ढूंढना, जैसा कि \2, और याद रखना
  3. बराबरी पर हस्ताक्षर के बाद सभी पात्रों को खोजना \3
  4. अंत में, प्रतिस्थापन स्ट्रिंग में
    • अनुभाग नाम + चर नाम ऊपरी आवरण वाला है, और
    • मान भाग को दोहरे-उद्धृत किया जाता है, यदि इसमें ऐसे अक्षर हैं जो खोल के लिए विशेष अर्थ रखते हैं यदि अयोग्य (जैसे व्हॉट्सएप)

\Uऔर \Eप्रतिस्थापन स्ट्रिंग में दृश्यों (जो अपर-केस कि प्रतिस्थापन स्ट्रिंग के भाग) जीएनयू हैं sedविस्तार। MacOS और BSD पर, आप -eएक ही प्रभाव को प्राप्त करने के लिए कई अभिव्यक्तियों का उपयोग करेंगे ।

खंड नाम (जो git configअनुमति देता है) में एम्बेडेड कोट्स और व्हाट्सएप से निपटना पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है।:)

बैश साहचर्य सरणी में कुंजियों के रूप में अनुभाग नामों का उपयोग करना

दिया हुआ:

; foo.ini
[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

इसका परिणाम यह होगा कि ओपी आपसे रिक्वेस्ट करने के लिए, सेड रिप्लेसमेंट एक्सप्रेशन में से कुछ कैप्चर को रीक्रिएट करके, और जीएनयू सेड के बिना ठीक काम करेगा:

source <(
    git config -f foo.ini --list \
      | sed 's/\([^.]*\)\.\(.*\)=\(.*\)/declare -A \2["\1"]="\3"/'
)

मैं भविष्यवाणी करता हूं कि वास्तविक दुनिया की .iniफ़ाइल के लिए उद्धृत करने के साथ कुछ चुनौतियां हो सकती हैं , लेकिन यह उपलब्ध उदाहरण के लिए काम करता है। परिणाम:

declare -p {session,path}
# result:
# declare -A session=([barfoo]="bar" [foobar]="foo" )
# declare -A path=([barfoo]="/some/path" [foobar]="/some/path" )
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.