ฉันจะรับค่า INI ภายในเชลล์สคริปต์ได้อย่างไร


104

ฉันมีไฟล์ parameters.ini เช่น:

[parameters.ini]
    database_user    = user
    database_version = 20110611142248

ฉันต้องการอ่านและใช้เวอร์ชันฐานข้อมูลที่ระบุในไฟล์ parameters.ini จากภายใน bash shell script เพื่อให้ฉันสามารถประมวลผลได้

#!/bin/sh    
# Need to get database version from parameters.ini file to use in script    
php app/console doctrine:migrations:migrate $DATABASE_VERSION

ฉันจะทำอย่างไร


3
คำตอบใด ๆ เหล่านี้เกี่ยวข้องกับส่วนทั้งหมดหรือไม่?
ManuelSchneid3r

คำตอบ:


87

วิธีการ grepping สำหรับบรรทัดนั้นจากนั้นใช้ awk

version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)

6
ซึ่งจะรวมช่องว่างหลัง '='

11
หากต้องการตัดช่องว่างให้เพิ่ม| tr -d ' 'ที่ส่วนท้าย
kenorb

23
นี่ไม่ใช่ทางออกที่ดีจริงๆ ลองนึกถึงการมี 2 [parameters.ini] โดยแต่ละส่วนมีตัวแปร "database_version" คุณจะได้รับค่าสองครั้งแล้ว
nerdoc

4
ใช่โปรดพิจารณาตัวแยกวิเคราะห์ ini ที่เชี่ยวชาญเช่น crudini เนื่องจากมีกรณีขอบจำนวนมากที่ไม่ได้รับการจัดการด้านบน
pixelbeat

3
ยังคงมีประโยชน์และเร็วกว่าสำหรับไฟล์ ini พื้นฐาน
Cyril N.

58

คุณสามารถใช้ bash native parser เพื่อตีความค่า ini โดย:

$ source <(grep = file.ini)

ไฟล์ตัวอย่าง:

[section-a]
  var1=value1
  var2=value2
  IPS=( "1.2.3.4" "1.2.3.5" )

echo $var1ตัวแปรการเข้าถึงคุณเพียงแค่พิมพ์พวกเขา คุณยังสามารถใช้อาร์เรย์ตามที่แสดงด้านบน ( echo ${IPS[@]})

หากคุณต้องการเพียงค่าเดียวเพียงแค่ grep สำหรับมัน:

source <(grep var1 file.ini)

สำหรับการสาธิตการตรวจสอบบันทึกนี้ที่ asciinema

เป็นเรื่องง่ายเพราะคุณไม่จำเป็นต้องให้ไลบรารีภายนอกใด ๆ ในการแยกวิเคราะห์ข้อมูล แต่ก็มีข้อเสียบางประการ ตัวอย่างเช่น:

  • หากคุณมีช่องว่างระหว่าง=(ชื่อตัวแปรและค่า) คุณต้องตัดช่องว่างก่อนเช่น

      $ source <(grep = file.ini | sed 's/ *= */=/g')
    

    หรือถ้าคุณไม่สนใจช่องว่าง (รวมทั้งตรงกลาง) ให้ใช้:

      $ source <(grep = file.ini | tr -d ' ')
    
  • หากต้องการสนับสนุน;ความคิดเห็นให้แทนที่ด้วย#:

      $ sed "s/;/#/g" foo.ini | source /dev/stdin
    
  • ไม่รองรับส่วนต่างๆ (เช่นถ้าคุณต้อง[section-name]กรองออกตามที่แสดงด้านบนเช่นgrep =) เช่นเดียวกันสำหรับข้อผิดพลาดอื่น ๆ ที่ไม่คาดคิด

    หากคุณต้องการที่จะอ่านค่าเฉพาะตามมาตราเฉพาะการใช้งานgrep -A, sed, awkหรือex)

    เช่น

      source <(grep = <(grep -A5 '\[section-b\]' file.ini))
    

    หมายเหตุ: -A5จำนวนแถวที่จะอ่านในส่วนนี้อยู่ที่ไหน แทนที่sourceด้วยcatเพื่อแก้ไขข้อบกพร่อง

  • หากคุณมีข้อผิดพลาดในการแยกวิเคราะห์ให้เพิกเฉยโดยเพิ่ม: 2>/dev/null

ดูสิ่งนี้ด้วย:


2
แต่ ... source <(grep = <(grep -A5 '\[section-b\]' file.ini))สิ่งนี้จะใช้ไม่ได้: [sec a] a = 1 b = 2 c = 3 [sec b] a = 2 b = 3 [sec c] a = 0 ที่ซึ่งไม่มีกฎที่แน่นอนด้วยเส้น
Psychozoic

ฉันพยายามใช้แหล่งที่มา แต่เมื่อฉันสะท้อน $ var1 มันกลับไม่มีอะไรเลย ทำไม?
อ. Gh

@ A.Gh ฉันไม่แน่ใจใช้ได้กับฉัน ตรวจสอบให้แน่ใจว่าคุณใช้ Bash shell ดู: asciinema.org/a/306481
kenorb

สิ่งนี้จะดูสวยงาม แต่ล้มเหลวในการทำให้ใช้งานได้ใน OS X (Catalina) มันทำงานได้จากพร้อมรับคำสั่งใน zsh (เปลือกเริ่มต้นปัจจุบัน) syntax error near unexpected token '('แต่เมื่อฉันใส่มันเป็นสคริปต์ที่ผมได้รับข้อผิดพลาด ด้วยการทุบตีมันจะล้มเหลวอย่างเงียบ ๆ ทั้งจากพร้อมต์และสคริปต์
MiRin

30

Bash ไม่มีตัวแยกวิเคราะห์สำหรับไฟล์เหล่านี้ เห็นได้ชัดว่าคุณสามารถใช้คำสั่ง awk หรือการเรียก sed ได้สองสามครั้ง แต่ถ้าคุณเป็นนักบวชและไม่ต้องการใช้เชลล์อื่นคุณสามารถลองใช้รหัสที่คลุมเครือต่อไปนี้:

#!/usr/bin/env bash
cfg_parser ()
{
    ini="$(<$1)"                # read the file
    ini="${ini//[/\[}"          # escape [
    ini="${ini//]/\]}"          # escape ]
    IFS=$'\n' && ini=( ${ini} ) # convert to line-array
    ini=( ${ini[*]//;*/} )      # remove comments with ;
    ini=( ${ini[*]/\    =/=} )  # remove tabs before =
    ini=( ${ini[*]/=\   /=} )   # remove tabs after =
    ini=( ${ini[*]/\ =\ /=} )   # remove anything with a space around =
    ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix
    ini=( ${ini[*]/%\\]/ \(} )    # convert text2function (1)
    ini=( ${ini[*]/=/=\( } )    # convert item to array
    ini=( ${ini[*]/%/ \)} )     # close array parenthesis
    ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
    ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
    ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
    ini[0]="" # remove first element
    ini[${#ini[*]} + 1]='}'    # add the last brace
    eval "$(echo "${ini[*]}")" # eval the result
}

cfg_writer ()
{
    IFS=' '$'\n'
    fun="$(declare -F)"
    fun="${fun//declare -f/}"
    for f in $fun; do
        [ "${f#cfg.section}" == "${f}" ] && continue
        item="$(declare -f ${f})"
        item="${item##*\{}"
        item="${item%\}}"
        item="${item//=*;/}"
        vars="${item//=*/}"
        eval $f
        echo "[${f#cfg.section.}]"
        for var in $vars; do
            echo $var=\"${!var}\"
        done
    done
}

การใช้งาน:

# parse the config file called 'myfile.ini', with the following
# contents::
#   [sec2]
#   var2='something'
cfg.parser 'myfile.ini'

# enable section called 'sec2' (in the file [sec2]) for reading
cfg.section.sec2

# read the content of the variable called 'var2' (in the file
# var2=XXX). If your var2 is an array, then you can use
# ${var[index]}
echo "$var2"

ทุบตี INI-parser สามารถพบได้ที่เว็บไซต์บล็อกโรงเรียนเก่า DevOps


3
แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป
alecxe

8
ปกติฉันเป็นคนที่แสดงความคิดเห็นแบบนี้ ทั้งหมดที่ฉันสามารถพูดได้คือฉันยังเด็กและโง่ :-)
Fredrik Pihl

1
หากคุณชอบตัวอย่างนี้มีการ enhacement ที่github.com/albfan/bash-ini-parser
albfan

3
ในการทำงานอย่างถูกต้องต้องใช้ cfg_parser แทน cfg.parser
Wes

1
TYPO: "cfg.parser" ควรเป็น "cfg_parser"
Setop

29

Sed one-liner ที่คำนึงถึงส่วนต่างๆ ไฟล์ตัวอย่าง:

[section1]
param1=123
param2=345
param3=678

[section2]
param1=abc
param2=def
param3=ghi

[section3]
param1=000
param2=111
param3=222

สมมติว่าคุณต้องการ param2 จาก section2 เรียกใช้สิ่งต่อไปนี้:

sed -nr "/^\[section2\]/ { :l /^param2[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" ./file.ini

จะให้คุณ

def

3
sed -nr "/ ^ \ [SECTION2 \] / {: l /^\s*[^#].*/ p; n; / ^ \ [/ q; bl;}" file.conf # เพื่อรับทั้งส่วน ไม่มีความคิดเห็นสำหรับไฟล์รูปแบบ. config ที่มี [SECTION2] และ # บรรทัดความคิดเห็นสไตล์แฮช จากนั้น grep สำหรับ paramname หากคุณต้องการเพียงพารามิเตอร์เดียว
gaoithe

ใช้ที่อยู่ช่วง sed ได้ดีกว่าอ่านบรรทัดถัดไป:"/^\[section2\]/,/^\[/{...}"
แอ่ง

1
ถ้าใน mac A: brew install gnu-sedแล้วใช้gsed(อย่างอื่นsed: illegal option -- r)
frnhr

ใครช่วยอธิบายว่า sed -nr "/^\[SECTION2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" นิพจน์ทำงานอย่างไร ขอบคุณ
foo_l

21

เพียงรวมไฟล์. ini ลงใน bash body:

ไฟล์example.ini :

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

ไฟล์example.sh

#!/bin/bash
#Including .ini file
. example.ini
#Test
echo "${DBNAME}   ${DBUSER}  ${DBPASSWORD}"

3
นี่ควรเป็นคำตอบที่เลือก มันทำงานกับ file.properties และทนต่อความผิดพลาด (ไฟล์ที่มีบรรทัดว่างภายใน) ขอบคุณ
Anthony

21
ไม่จัดการส่วน [section] ของไฟล์ INI
Setop

นี่คือคำตอบที่ดีที่สุด!
JavaSheriff

17
หวังว่าจะไม่มีใครเพิ่ม "rm -rf /" ลงในไฟล์ ini :(
HeyMan

1
ปลอดภัยกว่ามากใน sub-shell: $ (. example.ini; echo $ DBNAME)
Rich Remer

14

วิธีแก้ปัญหาทั้งหมดที่ฉันเห็นจนถึงตอนนี้ยังมีการแสดงความคิดเห็น อันนี้ไม่ได้ถ้ารหัสความคิดเห็นคือ;:

awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /database_version/) print $2}' file.ini

2
นี่ควรเป็นคำตอบที่ได้รับการยอมรับเนื่องจาก a) จัดการกับบรรทัดที่แสดงความคิดเห็น b) ง่าย ๆ :)
Sudar

1
นี่มันยอดเยี่ยมมาก ty @PenguinLust! การใช้งาน: 1. อนุญาตให้แสดงความคิดเห็นแบบเต็มบรรทัดโดยมีคำนำหน้าอัฒภาค (ไม่อนุญาตให้แสดงความคิดเห็นในบรรทัดท้ายบรรทัด) 2. พื้นที่สีขาวไม่ได้ถูกตัดออกจากผลลัพธ์ (ดังนั้นหากไฟล์ ini มี 'a = 1' การค้นหาของสคริปต์สำหรับ 'a' จะประเมินเป็น '1')
AnneTheAgile

1
หากต้องการตัดช่องว่างให้เพิ่ม| tr -d ' 'ที่ส่วนท้าย
kenorb

ปัญหานี้มีปัญหาเดียวกับคำตอบที่แนะนำ มันค้นหาทุกอินสแตนซ์ของ "database_version"
Nubcake

12

อีกวิธีหนึ่งที่เป็นไปได้

dbver=$(sed -n 's/.*database_version *= *\([^ ]*.*\)/\1/p' < parameters.ini)
echo $dbver

8

แสดงค่าของmy_keyในmy_file ในรูปแบบ:

sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
  • -n - อย่าพิมพ์อะไรโดยค่าเริ่มต้น
  • -e - ดำเนินการนิพจน์
  • s/PATTERN//p - แสดงอะไรก็ได้ตามรูปแบบนี้ในรูปแบบ:
  • ^ - รูปแบบเริ่มต้นที่จุดเริ่มต้นของบรรทัด
  • \s - อักขระเว้นวรรค
  • * - ศูนย์หรือหลายตัว (อักขระเว้นวรรค)

ตัวอย่าง:

$ cat my_file
# Example INI file
something   = foo
my_key      = bar
not_my_key  = baz
my_key_2    = bing

$ sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
bar

ดังนั้น:

ค้นหารูปแบบที่บรรทัดขึ้นต้นด้วยอักขระช่องว่างศูนย์หรือหลายตัวตามด้วยสตริงmy_keyตามด้วยอักขระช่องว่างศูนย์หรือหลายตัวเครื่องหมายเท่ากับแล้วจึงเป็นศูนย์หรืออักขระช่องว่างอีกครั้ง แสดงเนื้อหาที่เหลือในบรรทัดนั้นตามรูปแบบนั้น


ตัวอย่างของคุณใช้ไม่ได้ (ไม่ได้barพิมพ์ออกมา) อย่างน้อยก็ใน Unix / OSX
kenorb

7

เช่นเดียวกับคำตอบอื่น ๆ ของ Python คุณสามารถทำได้โดยใช้-cแฟล็กเพื่อดำเนินการตามลำดับของคำสั่ง Python ที่ให้ไว้ในบรรทัดคำสั่ง:

$ python3 -c "import configparser; c = configparser.ConfigParser(); c.read('parameters.ini'); print(c['parameters.ini']['database_version'])"
20110611142248

สิ่งนี้มีข้อดีคือต้องการไลบรารีมาตรฐาน Python เท่านั้นและข้อดีของการไม่เขียนไฟล์สคริปต์แยกต่างหาก

หรือใช้เอกสารที่นี่เพื่อให้อ่านง่ายขึ้นดังนั้น:

#!/bin/bash
python << EOI
import configparser
c = configparser.ConfigParser()
c.read('params.txt')
print c['chassis']['serialNumber']
EOI

serialNumber=$(python << EOI
import configparser
c = configparser.ConfigParser()
c.read('params.txt')
print c['chassis']['serialNumber']
EOI
)

echo $serialNumber

จะเกิดอะไรขึ้นถ้าฉันต้องการดึงทั้งส่วนเป็น Array โดยใช้คำสั่งนี้
Debopam Parua

7

sed

คุณสามารถใช้sedเพื่อแยกวิเคราะห์ไฟล์การกำหนดค่า ini โดยเฉพาะอย่างยิ่งเมื่อคุณมีชื่อส่วนเช่น:

# last modified 1 April 2001 by John Doe
[owner]
name=John Doe
organization=Acme Widgets Inc.

[database]
# use IP address in case network name resolution is not working
server=192.0.2.62
port=143
file=payroll.dat

เพื่อให้คุณสามารถใช้sedสคริปต์ต่อไปนี้เพื่อแยกวิเคราะห์ข้อมูลด้านบน:

# Configuration bindings found outside any section are given to
# to the default section.
1 {
  x
  s/^/default/
  x
}

# Lines starting with a #-character are comments.
/#/n

# Sections are unpacked and stored in the hold space.
/\[/ {
  s/\[\(.*\)\]/\1/
  x
  b
}

# Bindings are unpacked and decorated with the section
# they belong to, before being printed.
/=/ {
  s/^[[:space:]]*//
  s/[[:space:]]*=[[:space:]]*/|/
  G
  s/\(.*\)\n\(.*\)/\2|\1/
  p
}

สิ่งนี้จะแปลงข้อมูล ini เป็นรูปแบบแบนนี้:

owner|name|John Doe
owner|organization|Acme Widgets Inc.
database|server|192.0.2.62
database|port|143
database|file|payroll.dat

ดังนั้นมันจะง่ายต่อการแยกใช้sed, awkหรือreadโดยมีชื่อส่วนในทุกบรรทัด

เครดิตและแหล่งที่มา: ไฟล์คอนฟิกูเรชันสำหรับเชลล์สคริปต์ Michael Grünewald


หรือคุณสามารถใช้โปรเจ็กต์นี้: chilladx/config-parserตัวแยกวิเคราะห์การกำหนดค่าโดยใช้sed.


นี่มันเยี่ยมมาก! ฉันกำลังคิดที่จะแบนมันแบบนั้น แต่นี่มันไกลกว่าที่ฉันจะแฮ็คด้วยกันหลายไมล์!
กรินช์

6

คุณอาจใช้crudiniเครื่องมือเพื่อรับค่า ini เช่น:

DATABASE_VERSION=$(crudini --get parameters.ini '' database_version)

โปรดทราบว่ามันขึ้นอยู่กับ Python ดังนั้นอาจไม่เหมาะสำหรับแอพพลิเคชัน Linux
Craig McQueen

นี่เป็นส่วนหนึ่งของ repos Fedora มาตรฐาน (ทดสอบด้วย 31) yum install crudini
shrewmouse

5

สำหรับคน (เช่นฉัน) ที่ต้องการอ่านไฟล์ INI จากเชลล์สคริปต์ (อ่านเชลล์ไม่ใช่ทุบตี) - ฉันได้เคาะไลบรารีตัวช่วยเล็ก ๆ น้อย ๆ ซึ่งพยายามทำอย่างนั้น:

https://github.com/wallyhall/shini (ใบอนุญาตของ MIT ทำตามที่คุณต้องการฉันได้เชื่อมโยงด้านบนรวมถึงอินไลน์เนื่องจากโค้ดค่อนข้างยาว)

ค่อนข้าง "ซับซ้อน" กว่าsedบรรทัดง่ายๆที่แนะนำข้างต้น - แต่ทำงานบนพื้นฐานที่คล้ายกันมาก

ฟังก์ชั่นอ่านไฟล์ทีละบรรทัด - ค้นหาเครื่องหมายส่วน ( [section]) และการประกาศคีย์ / ค่า ( key=value)

ในที่สุดคุณจะได้รับการติดต่อกลับไปยังฟังก์ชันของคุณเอง - ส่วนคีย์และค่า


@CraigMcQueen - คืนนี้ฉันได้เพิ่มการสนับสนุนการเขียนคุณภาพระดับอัลฟา มันไม่ "สมบูรณ์" ด้วยจินตนาการใด ๆ !
เก่ง

ยอดเยี่ยม! :-) Major
Jonathan

2

บางคำตอบไม่เคารพความคิดเห็น บางส่วนไม่เคารพส่วนต่างๆ บางคนรู้จักไวยากรณ์เดียวเท่านั้น (เฉพาะ ":" หรือเฉพาะ "=") คำตอบ Python บางคำตอบล้มเหลวในเครื่องของฉันเนื่องจากความแตกต่างของคำอธิบายภาพหรือการนำเข้าโมดูล sys ไม่สำเร็จ ทั้งหมดค่อนข้างสั้นเกินไปสำหรับฉัน

ดังนั้นฉันจึงเขียนของฉันเองและถ้าคุณมี Python ที่ทันสมัยคุณอาจเรียกสิ่งนี้จาก Bash shell ของคุณ มีข้อได้เปรียบในการปฏิบัติตามข้อกำหนดการเข้ารหัส Python ทั่วไปและยังให้ข้อความแสดงข้อผิดพลาดและความช่วยเหลือที่สมเหตุสมผล หากต้องการใช้ให้ตั้งชื่อเช่น myconfig.py (อย่าเรียกว่า configparser.py หรืออาจพยายามนำเข้าเอง) ทำให้สามารถเรียกใช้งานได้และเรียกมันว่า

value=$(myconfig.py something.ini sectionname value)

นี่คือรหัสของฉันสำหรับ Python 3.5 บน Linux:

#!/usr/bin/env python3
# Last Modified: Thu Aug  3 13:58:50 PDT 2017
"""A program that Bash can call to parse an .ini file"""

import sys
import configparser
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="A program that Bash can call to parse an .ini file")
    parser.add_argument("inifile", help="name of the .ini file")
    parser.add_argument("section", help="name of the section in the .ini file")
    parser.add_argument("itemname", help="name of the desired value")
    args = parser.parse_args()

    config = configparser.ConfigParser()
    config.read(args.inifile)
    print(config.get(args.section, args.itemname))

2

ความเรียบง่ายที่ซับซ้อน

ini ไฟล์

test.ini

[section1]
name1=value1
name2=value2
[section2]
name1=value_1
  name2  =  value_2

bash script พร้อมอ่านและดำเนินการ

/ bin / parseini

#!/bin/bash

set +a
while read p; do
  reSec='^\[(.*)\]$'
  #reNV='[ ]*([^ ]*)+[ ]*=(.*)'     #Remove only spaces around name
  reNV='[ ]*([^ ]*)+[ ]*=[ ]*(.*)'  #Remove spaces around name and spaces before value
  if [[ $p =~ $reSec ]]; then
      section=${BASH_REMATCH[1]}
  elif [[ $p =~ $reNV ]]; then
    sNm=${section}_${BASH_REMATCH[1]}
    sVa=${BASH_REMATCH[2]}
    set -a
    eval "$(echo "$sNm"=\""$sVa"\")"
    set +a
  fi
done < $1

จากนั้นในสคริปต์อื่นฉันจะจัดหาผลลัพธ์ของคำสั่งและสามารถใช้ตัวแปรใดก็ได้ภายใน

test.sh

#!/bin/bash

source parseini test.ini

echo $section2_name2

ในที่สุดจากบรรทัดคำสั่งผลลัพธ์จึงเป็นเช่นนั้น

# ./test.sh 
value_2

ทางออกที่ดี! ขอบคุณ!
Michael

2

นี่คือเวอร์ชันของฉันซึ่งแยกวิเคราะห์ส่วนต่างๆและเติมข้อมูลอาร์เรย์ที่เชื่อมโยงส่วนกลางg_iniPropertiesด้วย โปรดทราบว่าสิ่งนี้ใช้ได้กับbash v4.2 ขึ้นไปเท่านั้น

function parseIniFile() { #accepts the name of the file to parse as argument ($1)
    #declare syntax below (-gA) only works with bash 4.2 and higher
    unset g_iniProperties
    declare -gA g_iniProperties
    currentSection=""
    while read -r line
    do
        if [[ $line = [*  ]] ; then
            if [[ $line = [* ]] ; then 
                currentSection=$(echo $line | sed -e 's/\r//g' | tr -d "[]")  
            fi
        else
            if [[ $line = *=*  ]] ; then
                cleanLine=$(echo $line | sed -e 's/\r//g')
                key=$currentSection.$(echo $cleanLine | awk -F: '{ st = index($0,"=");print  substr($0,0,st-1)}')
                value=$(echo $cleanLine | awk -F: '{ st = index($0,"=");print  substr($0,st+1)}')
                g_iniProperties[$key]=$value
            fi
        fi;
    done < $1
}

และนี่คือโค้ดตัวอย่างโดยใช้ฟังก์ชันด้านบน:

parseIniFile "/path/to/myFile.ini"
for key in "${!g_iniProperties[@]}"; do
    echo "Found key/value $key = ${g_iniProperties[$key]}"
done

1

สคริปต์นี้จะได้รับพารามิเตอร์ดังนี้:

หมายความว่าหาก ini ของคุณมี:

pars_ini.ksh <path to ini file> <name of Sector in Ini file> <the name in name = value to return>

เช่น. วิธีการเรียก:


[สิ่งแวดล้อม]

a = x

[DataBase_Sector]

DSN = บางอย่าง


จากนั้นโทร:

pars_ini.ksh /users/bubu_user/parameters.ini DataBase_Sector DSN

สิ่งนี้จะดึง "บางสิ่ง" ต่อไปนี้

สคริปต์ "pars_ini.ksh":

\#!/bin/ksh

\#INI_FILE=path/to/file.ini

\#INI_SECTION=TheSection

\# BEGIN parse-ini-file.sh

\# SET UP THE MINIMUM VARS FIRST

alias sed=/usr/local/bin/sed

INI_FILE=$1

INI_SECTION=$2

INI_NAME=$3

INI_VALUE=""


eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \

    -e 's/;.*$//' \

    -e 's/[[:space:]]*$//' \

    -e 's/^[[:space:]]*//' \

    -e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \

   < $INI_FILE  \

    | sed -n -e "/^\[$INI_SECTION\]/,/^\s*\[/{/^[^;].*\=.*/p;}"`


TEMP_VALUE=`echo "$"$INI_NAME`

echo `eval echo $TEMP_VALUE`

1

ฉันเขียนสคริปต์ python ที่ง่ายและรวดเร็วเพื่อรวมไว้ใน bash script ของฉัน

ตัวอย่างเช่นไฟล์ ini ของคุณถูกเรียกfood.ini และในไฟล์คุณสามารถมีบางส่วนและบางบรรทัด:

[FRUIT]
Oranges = 14
Apples = 6

คัดลอกสคริปต์ Python 6 บรรทัดขนาดเล็กนี้และบันทึกเป็นไฟล์ configparser.py

#!/usr/bin/python
import configparser
import sys
config = configparser.ConfigParser()
config.read(sys.argv[1])
print config.get(sys.argv[2],sys.argv[3])

ตอนนี้ในสคริปต์ทุบตีของคุณคุณสามารถทำได้เช่น

OrangeQty=$(python configparser.py food.ini FRUIT Oranges)

หรือ

ApplesQty=$(python configparser.py food.ini FRUIT Apples)
echo $ApplesQty

สิ่งนี้คาดการณ์ล่วงหน้า:

  1. คุณติดตั้ง Python แล้ว
  2. คุณติดตั้งไลบรารี configparser แล้ว (ควรมาพร้อมกับการติดตั้ง std python)

หวังว่ามันจะช่วยได้ : ¬)


ฉันกำลังมองหาสิ่งที่ทำได้เพียงแค่นี้ดังนั้นฉันจึงทำตามตัวอย่างและมันก็ใช้ได้ดี ฉันลืมฉันเขียนสิ่งนี้ !!!! ฉันพยายามโหวตให้ตัวเอง แต่อนิจจาฉันไม่สามารถโหวตให้ตัวเองได้ !!! ha ha
joe_evans

0

เวอร์ชันซับเดียวของฉัน

#!/bin/bash
#Reader for MS Windows 3.1 Ini-files
#Usage: inireader.sh

# e.g.: inireader.sh win.ini ERRORS DISABLE
# would return value "no" from the section of win.ini
#[ERRORS]
#DISABLE=no
INIFILE=$1
SECTION=$2
ITEM=$3
cat $INIFILE | sed -n /^\[$SECTION\]/,/^\[.*\]/p | grep "^[:space:]*$ITEM[:space:]*=" | sed s/.*=[:space:]*//

0

เพิ่งเขียน parser ของตัวเองเสร็จ ฉันพยายามใช้ตัวแยกวิเคราะห์ต่างๆที่พบที่นี่ดูเหมือนว่าจะไม่มีทั้ง ksh93 (AIX) และ bash (Linux)

เป็นรูปแบบการเขียนโปรแกรมแบบเก่า - แยกวิเคราะห์ทีละบรรทัด ค่อนข้างเร็วเนื่องจากใช้คำสั่งภายนอกไม่กี่คำสั่ง ช้าลงเล็กน้อยเนื่องจากการประเมินทั้งหมดที่จำเป็นสำหรับชื่อไดนามิกของอาร์เรย์

ini รองรับไวยากรณ์พิเศษ 3 แบบ:

  • includefile = ini file -> โหลดไฟล์ addnal ini มีประโยชน์สำหรับการแยกไฟล์ ini ในหลาย ๆ ไฟล์หรือใช้การกำหนดค่าบางส่วนซ้ำ
  • includedir = ไดเร็กทอรี -> เหมือนกับ includefile แต่รวมไดเร็กทอรีที่สมบูรณ์
  • includeection = section -> คัดลอกส่วนที่มีอยู่ไปยังส่วนปัจจุบัน

ฉันใช้ไวยากรณ์ของ thoses ทั้งหมดเพื่อให้มีไฟล์ ini ที่ซับซ้อนและใช้ซ้ำได้ มีประโยชน์ในการติดตั้งผลิตภัณฑ์เมื่อติดตั้งระบบปฏิบัติการใหม่ - เราทำเช่นนั้นมาก

สามารถเข้าถึงค่าได้ด้วย $ {ini [$ section. $ item]} ต้องกำหนดอาร์เรย์ก่อนที่จะเรียกสิ่งนี้

มีความสุข. หวังว่าจะเป็นประโยชน์สำหรับคนอื่น ๆ !

function Show_Debug {
    [[ $DEBUG = YES ]] && echo "DEBUG $@"
    }

function Fatal {
    echo "$@. Script aborted"
    exit 2
    }
#-------------------------------------------------------------------------------
# This function load an ini file in the array "ini"
# The "ini" array must be defined in the calling program (typeset -A ini)
#
# It could be any array name, the default array name is "ini".
#
# There is heavy usage of "eval" since ksh and bash do not support
# reference variable. The name of the ini is passed as variable, and must
# be "eval" at run-time to work. Very specific syntax was used and must be
# understood before making any modifications.
#
# It complexify greatly the program, but add flexibility.
#-------------------------------------------------------------------------------

function Load_Ini {
    Show_Debug "$0($@)"
    typeset ini_file="$1"
# Name of the array to fill. By default, it's "ini"
    typeset ini_array_name="${2:-ini}"
    typeset section variable value line my_section file subsection value_array include_directory all_index index sections pre_parse
    typeset LF="
"
    if [[ ! -s $ini_file ]]; then
        Fatal "The ini file is empty or absent in $0 [$ini_file]"
    fi

    include_directory=$(dirname $ini_file)
    include_directory=${include_directory:-$(pwd)}

    Show_Debug "include_directory=$include_directory"

    section=""
# Since this code support both bash and ksh93, you cannot use
# the syntax "echo xyz|while read line". bash doesn't work like
# that.
# It forces the use of "<<<", introduced in bash and ksh93.

    Show_Debug "Reading file $ini_file and putting the results in array $ini_array_name"
    pre_parse="$(sed 's/^ *//g;s/#.*//g;s/ *$//g' <$ini_file | egrep -v '^$')"
    while read line; do
        if [[ ${line:0:1} = "[" ]]; then # Is the line starting with "["?
# Replace [section_name] to section_name by removing the first and last character
            section="${line:1}"
            section="${section%\]}"
            eval "sections=\${$ini_array_name[sections_list]}"
            sections="$sections${sections:+ }$section"
            eval "$ini_array_name[sections_list]=\"$sections\""
            Show_Debug "$ini_array_name[sections_list]=\"$sections\""
            eval "$ini_array_name[$section.exist]=YES"
            Show_Debug "$ini_array_name[$section.exist]='YES'"
        else
            variable=${line%%=*}   # content before the =
            value=${line#*=}       # content after the =

            if [[ $variable = includefile ]]; then
# Include a single file
                Load_Ini "$include_directory/$value" "$ini_array_name"
                continue
            elif [[ $variable = includedir ]]; then
# Include a directory
# If the value doesn't start with a /, add the calculated include_directory
                if [[ $value != /* ]]; then
                    value="$include_directory/$value"
                fi
# go thru each file
                for file in $(ls $value/*.ini 2>/dev/null); do
                    if [[ $file != *.ini ]]; then continue; fi
# Load a single file
                    Load_Ini "$file" "$ini_array_name"
                done
                continue
            elif [[ $variable = includesection ]]; then
# Copy an existing section into the current section
                eval "all_index=\"\${!$ini_array_name[@]}\""
# It's not necessarily fast. Need to go thru all the array
                for index in $all_index; do
# Only if it is the requested section
                    if [[ $index = $value.* ]]; then
# Evaluate the subsection [section.subsection] --> subsection
                        subsection=${index#*.}
# Get the current value (source section)
                        eval "value_array=\"\${$ini_array_name[$index]}\""
# Assign the value to the current section
# The $value_array must be resolved on the second pass of the eval, so make sure the
# first pass doesn't resolve it (\$value_array instead of $value_array).
# It must be evaluated on the second pass in case there is special character like $1,
# or ' or " in it (code).
                        eval "$ini_array_name[$section.$subsection]=\"\$value_array\""
                        Show_Debug "$ini_array_name[$section.$subsection]=\"$value_array\""
                    fi
                done
            fi

# Add the value to the array
            eval "current_value=\"\${$ini_array_name[$section.$variable]}\""
# If there's already something for this field, add it with the current
# content separated by a LF (line_feed)
            new_value="$current_value${current_value:+$LF}$value"
# Assign the content
# The $new_value must be resolved on the second pass of the eval, so make sure the
# first pass doesn't resolve it (\$new_value instead of $new_value).
# It must be evaluated on the second pass in case there is special character like $1,
# or ' or " in it (code).
            eval "$ini_array_name[$section.$variable]=\"\$new_value\""
            Show_Debug "$ini_array_name[$section.$variable]=\"$new_value\""
        fi
    done  <<< "$pre_parse"
    Show_Debug "exit $0($@)\n"
    }

0

การใช้งานนี้ใช้awkและมีข้อดีดังต่อไปนี้:

  1. จะคืนค่ารายการที่ตรงกันรายการแรกเท่านั้น
  2. ละเว้นบรรทัดที่ขึ้นต้นด้วย ;
  3. ตัดแต่งช่องว่างที่นำหน้าและต่อท้าย แต่ไม่ใช่ช่องว่างภายใน

เวอร์ชันที่จัดรูปแบบ :

awk -F '=' '/^\s*database_version\s*=/ {
            sub(/^ +/, "", $2);
            sub(/ +$/, "", $2);
            print $2;
            exit;
          }' parameters.ini

ซับเดียว :

awk -F '=' '/^\s*database_version\s*=/ { sub(/^ +/, "", $2); sub(/ +$/, "", $2); print $2; exit; }' parameters.ini

0

เมื่อฉันใช้รหัสผ่านใน base64 ฉันใส่ตัวคั่น ":" เพราะสตริง base64 อาจมี "=" ตัวอย่างเช่น (ฉันใช้ksh):

> echo "Abc123" | base64
QWJjMTIzCg==

ในการparameters.iniวางบรรทัดpass:QWJjMTIzCg==และสุดท้าย:

> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | base64 --decode`
> echo "$PASS"
Abc123

หากบรรทัดมีช่องว่างเช่น"pass : QWJjMTIzCg== "เพิ่ม| tr -d ' 'เพื่อตัดแต่ง:

> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | tr -d ' ' | base64 --decode`
> echo "[$PASS]"
[Abc123]


0

คำตอบของ "Karen Gabrielyan" เป็นคำตอบอื่นที่ดีที่สุด แต่ในบางสภาพแวดล้อมเราไม่มี awk เช่น busybox ทั่วไปฉันเปลี่ยนคำตอบตามรหัสด้านล่าง

trim()
{
    local trimmed="$1"

    # Strip leading space.
    trimmed="${trimmed## }"
    # Strip trailing space.
    trimmed="${trimmed%% }"

    echo "$trimmed"
}


  function parseIniFile() { #accepts the name of the file to parse as argument ($1)
        #declare syntax below (-gA) only works with bash 4.2 and higher
        unset g_iniProperties
        declare -gA g_iniProperties
        currentSection=""
        while read -r line
        do
            if [[ $line = [*  ]] ; then
                if [[ $line = [* ]] ; then 
                    currentSection=$(echo $line | sed -e 's/\r//g' | tr -d "[]")  
                fi
            else
                if [[ $line = *=*  ]] ; then
                    cleanLine=$(echo $line | sed -e 's/\r//g')
                    key=$(trim $currentSection.$(echo $cleanLine | cut -d'=' -f1'))
                    value=$(trim $(echo $cleanLine | cut -d'=' -f2))
                    g_iniProperties[$key]=$value
                fi
            fi;
        done < $1
    }

ฉันไม่แน่ใจว่า awk หายไปมากแค่ไหน แต่มี sed, cut และ bash ที่ค่อนข้างสูงกว่าเช่น syntax
Ondrej K.

ระบบไฟล์รูทเริ่มต้นส่วนใหญ่ใช้ / linuxrc หรือ / init เป็นเชลล์สคริปต์ดังนั้นจึงมีเชลล์ขั้นต่ำ (ปกติ / bin / ash) พร้อมกับยูทิลิตี้พื้นที่ผู้ใช้ที่จำเป็น
Ehsan Ahmadi

แน่นอน ฉันแปลกใจเล็กน้อยที่คุณสร้าง busybox ของคุณโดยไม่ต้องตื่น แต่ยังคงมีความใจเย็นตัดและสนับสนุน "bashisms" ต่างๆ ไม่ใช่ว่าจะเป็นไปไม่ได้แค่ทำให้ฉันสงสัย ;)
Ondrej K.

เครื่องมืออื่น ๆ lightwight กว่า awk หากคุณเขียนสคริปต์ลงใน initramfs ด้วย initramfs-tools ใน ubuntu distro คุณจะพบว่าคุณไม่มี awk และเครื่องมืออื่น ๆ เช่น sed, grep ...
Ehsan Ahmadi

แน่นอนว่าฉันไม่ได้พูดถึง GNU awk หรือ full blow awk อื่น ๆ เพียงแค่สงสัยว่าจะประหยัดได้มากแค่ไหนโดยการกำหนดค่า busybox ให้ไม่รวมการรองรับ awk (โดยเฉพาะเมื่อพิจารณาถึงบิตอื่น ๆ ที่กล่าวถึงจะไม่หลุดออกจาก config นั้น) อาจเป็นไปได้ว่า * buntu initrd มีหนึ่งแบบนั้น แค่สงสัยเกี่ยวกับคอมโบ / ตัวเลือกนั่นคือทั้งหมด
Ondrej K.

0

หาก Python พร้อมใช้งานสิ่งต่อไปนี้จะอ่านส่วนคีย์และค่าทั้งหมดและบันทึกไว้ในตัวแปรโดยมีชื่อตามรูปแบบ "[section] _ [key]" Python สามารถอ่านไฟล์. ini ได้อย่างถูกต้องดังนั้นเราจึงใช้ประโยชน์จากมัน

#!/bin/bash

eval $(python3 << EOP
from configparser import SafeConfigParser

config = SafeConfigParser()
config.read("config.ini"))

for section in config.sections():
    for (key, val) in config.items(section):
        print(section + "_" + key + "=\"" + val + "\"")
EOP
)

echo "Environment_type:  ${Environment_type}"
echo "Environment_name:  ${Environment_name}"

config.ini

[Environment]
  type                = DEV
  name                = D01

0

คุณสามารถใช้ตัวแยกวิเคราะห์ CSV xsvเพื่อแยกวิเคราะห์ข้อมูล INI

cargo install xsv
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
$ xsv select -d "=" - <<< "$( cat /etc/*release )" | xsv search --no-headers --select 1 "DISTRIB_CODENAME" | xsv select 2
xenial

หรือจากไฟล์

$ xsv select -d "=" - file.ini | xsv search --no-headers --select 1 "DISTRIB_CODENAME" | xsv select 2

0

หากใช้ส่วนนี้จะทำงาน:

ตัวอย่างผลลัพธ์ดิบ:

$ ./settings
[section]
SETTING_ONE=this is setting one
SETTING_TWO=This is the second setting
ANOTHER_SETTING=This is another setting

การแยกวิเคราะห์ Regexp:

$ ./settings | sed -n -E "/^\[.*\]/{s/\[(.*)\]/\1/;h;n;};/^[a-zA-Z]/{s/#.*//;G;s/([^ ]*) *= *(.*)\n(.*)/\3_\1='\2'/;p;}"
section_SETTING_ONE='this is setting one'
section_SETTING_TWO='This is the second setting'
section_ANOTHER_SETTING='This is another setting'

ตอนนี้ทั้งหมดเข้าด้วยกัน:

$ eval "$(./settings | sed -n -E "/^\[.*\]/{s/\[(.*)\]/\1/;h;n;};/^[a-zA-Z]/{s/#.*//;G;s/([^ ]*) *= *(.*)\n(.*)/\3_\1='\2'/;p;}")"
$ echo $section_SETTING_TWO
This is the second setting

0

ฉันมีซับเดียวที่ดี (คุณมีphpและjqติดตั้ง):

cat file.ini | php -r "echo json_encode(parse_ini_string(file_get_contents('php://stdin'), true, INI_SCANNER_RAW));" | jq '.section.key'

0

คำอธิบายสำหรับคำตอบสำหรับหนึ่งซับ sed

[section1]
param1=123
param2=345
param3=678

[section2]
param1=abc
param2=def
param3=ghi

[section3]
param1=000
param2=111
param3=222
sed -nr "/^\[section2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" ./file.ini

เพื่อให้เข้าใจง่ายขึ้นในการจัดรูปแบบเส้นดังนี้:

sed -nr "
      # start processing when we found the word \"section2\"
      /^\[section2\]/  { #the set of commands inside { } will be executed
          #create a label \"l\"  (https://www.grymoire.com/Unix/Sed.html#uh-58)
          :l /^\s*[^#].*/ p; 
          # move on to the next line. For the first run it the \"param1=abc\"
          n; 
          # check if this line is beginning of new section. If yes - then exit.
          /^\[/ q
          #otherwise jump to the label \"l\"
          b l
          }

" file.ini

0

เธรดนี้มีโซลูชันไม่เพียงพอให้เลือกดังนั้นที่นี่วิธีแก้ปัญหาของฉันจึงไม่ต้องใช้เครื่องมือเช่นsedหรือawk:

grep '^\[section\]' -A 999 config.ini | tail -n +2  | grep -B 999 '^\[' | head -n -1 | grep '^key' | cut -d '=' -f 2 

หากคุณคาดหวังส่วนต่างๆที่มีมากกว่า 999 บรรทัดอย่าลังเลที่จะปรับตัวอย่างข้างต้น โปรดทราบว่าคุณอาจต้องการตัดทอนค่าผลลัพธ์เพื่อลบช่องว่างหรือสตริงข้อคิดเห็นหลังค่า ลบไฟล์^ถ้าคุณต้องการจับคู่คีย์ที่ไม่ได้เริ่มต้นที่จุดเริ่มต้นของบรรทัดดังตัวอย่างคำถาม ดีกว่าจับคู่อย่างชัดเจนสำหรับพื้นที่สีขาวและแท็บในกรณีเช่นนี้

หากคุณมีหลายค่าในส่วนหนึ่ง ๆ ที่คุณต้องการอ่าน แต่ต้องการหลีกเลี่ยงการอ่านไฟล์หลาย ๆ ครั้ง:

CONFIG_SECTION=$(grep '^\[section\]' -A 999 config.ini | tail -n +2  | grep -B 999 '^\[' | head -n -1)

KEY1=$(echo ${CONFIG_SECTION} | tr ' ' '\n' | grep key1 | cut -d '=' -f 2)
echo "KEY1=${KEY1}"
KEY2=$(echo ${CONFIG_SECTION} | tr ' ' '\n' | grep key2 | cut -d '=' -f 2)
echo "KEY2=${KEY2}"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.