Làm cách nào để lấy giá trị INI trong tập lệnh shell?


97

Tôi có một tệp tham số.ini, chẳng hạn như:

[parameters.ini]
    database_user    = user
    database_version = 20110611142248

Tôi muốn đọc và sử dụng phiên bản cơ sở dữ liệu được chỉ định trong tệp tham số.ini từ bên trong tập lệnh bash shell để tôi có thể xử lý nó.

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

Tôi sẽ làm điều này như thế nào?


2
Có câu trả lời nào trong số này tôn trọng các phần không?
ManuelSchneid3r

Câu trả lời:


83

Làm thế nào về việc gửi grepping cho dòng đó sau đó sử dụng awk

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

6
Điều này sẽ bao gồm dấu cách sau '='.

10
Để cắt bớt khoảng trắng, hãy thêm | tr -d ' 'vào cuối.
kenorb

22
Đây thực sự không phải là một giải pháp tốt. Hãy nghĩ đến việc có 2 phần [tham số.ini] với mỗi phần có một biến 'database_version'. Bạn nhận được giá trị hai lần sau đó.
nerdoc

4
vâng xin vui lòng xem xét một phân tích cú pháp ini chuyên như crudini, vì có nhiều trường hợp cạnh không được xử lý bằng cách trên
pixelbeat

3
Vẫn hữu ích và nhanh hơn cho các tệp ini cơ bản.
Cyril N.

51

Bạn có thể sử dụng trình phân tích cú pháp gốc bash để diễn giải các giá trị ini, bằng cách:

$ source <(grep = file.ini)

Tệp mẫu:

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

Để biến truy cập, bạn chỉ đơn giản là in chúng: echo $var1. Bạn cũng có thể sử dụng các mảng như được hiển thị ở trên ( echo ${IPS[@]}).

Nếu bạn chỉ muốn một giá trị duy nhất, chỉ cần grep cho nó:

source <(grep var1 file.ini)

Đối với bản demo, hãy kiểm tra bản ghi này tại asciinema .

Nó đơn giản là bạn không cần bất kỳ thư viện bên ngoài nào để phân tích dữ liệu, nhưng nó đi kèm với một số nhược điểm. Ví dụ:

  • Nếu bạn có khoảng trắng giữa =(tên biến và giá trị), thì bạn phải cắt bỏ khoảng trắng trước, ví dụ:

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

    Hoặc nếu bạn không quan tâm đến các khoảng trắng (bao gồm cả ở giữa), hãy sử dụng:

      $ source <(grep = file.ini | tr -d ' ')
  • Để hỗ trợ ;nhận xét, hãy thay thế chúng bằng #:

      $ sed "s/;/#/g" foo.ini | source /dev/stdin
  • Các phần không được hỗ trợ (ví dụ: nếu bạn [section-name], thì bạn phải lọc nó ra như được hiển thị ở trên, chẳng hạn grep =), tương tự đối với các lỗi không mong muốn khác.

    Nếu bạn cần phải đọc giá trị cụ thể theo mục cụ thể, sử dụng grep -A, sed, awkhoặc ex).

    Ví dụ

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

    Lưu ý: -A5Số hàng để đọc trong phần nằm ở đâu. Thay thế sourcebằng catđể gỡ lỗi.

  • Nếu bạn có bất kỳ lỗi phân tích cú pháp nào, hãy bỏ qua chúng bằng cách thêm: 2>/dev/null

Xem thêm:


1
nhưng ... source <(grep = <(grep -A5 '\[section-b\]' file.ini))điều này sẽ không hoạt động đối với nó: [sec a] a = 1 b = 2 c = 3 [sec b] a = 2 b = 3 [sec c] a = 0. nơi không có quy tắc nhất định với các dòng
Psychozoic

Tôi đã cố gắng sử dụng mã nguồn, nhưng khi tôi lặp lại $ var1, nó không trả lại gì. Tại sao?
A. Gh

@ A.Gh Tôi không chắc, phù hợp với tôi. Đảm bảo rằng bạn đang sử dụng Bash shell. Xem: asciinema.org/a/306481
kenorb

Điều này sẽ rất thanh lịch, nhưng không thể làm cho nó hoạt động trong OS X (Catalina). Nó hoạt động từ dấu nhắc lệnh trong zsh (trình bao mặc định hiện tại), nhưng khi tôi đặt nó vào tập lệnh, tôi gặp lỗi syntax error near unexpected token '('. Với bash, nó âm thầm không thành công cả từ lời nhắc và tập lệnh.
MiRin

29

Bash không cung cấp trình phân tích cú pháp cho các tệp này. Rõ ràng là bạn có thể sử dụng một lệnh awk hoặc một vài lệnh gọi sed, nhưng nếu bạn là bash-Priest và không muốn sử dụng bất kỳ trình bao nào khác, thì bạn có thể thử mã khó hiểu sau:

#!/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
}

Sử dụng:

# 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"

Bash ini-parser có thể được tìm thấy tại trang blog The Old School DevOps .


3
Mặc dù liên kết này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo. Các câu trả lời chỉ có liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
alecxe

8
Tôi thường là người đưa ra những bình luận như thế này; tất cả những gì tôi có thể nói là tôi còn trẻ và ngu ngốc :-)
Fredrik Pihl,

1
Nếu bạn thích đoạn mã này, có một bản cải tiến trên github.com/albfan/bash-ini-parser
albfan

3
Để hoạt động chính xác, cần sử dụng cfg_parser thay vì cfg.parser
Wes

1
TYPO: "cfg.parser" phải là "cfg_parser".
Setop

26

Sed một lớp lót, có tính đến các phần. Tệp ví dụ:

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

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

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

Giả sử bạn muốn param2 từ section2. Chạy như sau:

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

sẽ cho bạn

def

3
sed -nr "/ ^ \ [SECTION2 \] / {: l /^\s*[^#].*/ p; n; / ^ \ [/ q; bl;}" file.conf # để xem toàn bộ phần không có nhận xét cho tệp kiểu .conf với [SECTION2] và # dòng nhận xét kiểu băm. Sau đó, grep cho paramname nếu bạn chỉ muốn một tham số.
gaoithe

sử dụng các địa chỉ phạm vi sed tốt hơn là đọc các dòng tiếp theo:"/^\[section2\]/,/^\[/{...}"
lưu vực

1
nếu trên mac: brew install gnu-sedvà sau đó sử dụng gsed(nếu không sed: illegal option -- r:)
frnhr

Bất cứ ai có thể vui lòng giải thích cách sed -nr "/^\[SECTION2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" hoạt động của biểu thức? cảm ơn bạn
foo_l

22

Chỉ cần bao gồm tệp .ini của bạn vào cơ thể bash:

Tệp example.ini :

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

Tệp example.sh

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

2
Đây sẽ là câu trả lời được chọn. Nó hoạt động với file.properties và có khả năng chịu lỗi (tệp có dòng trống bên trong). Cảm ơn
Anthony

17
không xử lý phần [section] của các tệp INI.
Setop

đây là câu trả lời tốt nhất!
JavaSheriff

17
Hy vọng rằng không ai thêm "rm -rf /" vào tệp ini :(
HeyMan 4/1218

1
An toàn hơn nhiều trong sub-shell: $ (. Example.ini; echo $ DBNAME)
Rich Remer

13

Tất cả các giải pháp mà tôi đã thấy cho đến nay cũng đều dựa trên các dòng bình luận. Cái này thì không, nếu mã nhận xét là ;:

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

2
Đây phải là câu trả lời được chấp nhận vì a) Nó xử lý các dòng được nhận xét b) đơn giản :)
Sudar

1
Thật tuyệt vời, ty @PenguinLust! Cách sử dụng: 1. Cho phép nhận xét toàn dòng với tiền tố dấu chấm phẩy (không cho phép nhận xét cuối dòng nội tuyến); 2. Khoảng trống không được giải thích khỏi kết quả (vì vậy nếu tệp ini có 'a = 1', thì tìm kiếm của tập lệnh cho 'a' sẽ đánh giá là '1').
AnneTheAgile

1
Để cắt bớt khoảng trắng, hãy thêm | tr -d ' 'vào cuối.
kenorb

Điều này có cùng một vấn đề với câu trả lời gợi ý; nó tìm kiếm mọi trường hợp của "database_version"
Nubcake

12

một trong những giải pháp khả thi hơn

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

8

Hiển thị giá trị của my_key trong my_file kiểu ini :

sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
  • -n - không in bất cứ thứ gì theo mặc định
  • -e - thực hiện biểu thức
  • s/PATTERN//p - hiển thị bất cứ thứ gì theo sau mẫu này Trong mẫu:
  • ^ - mẫu bắt đầu ở đầu dòng
  • \s - ký tự khoảng trắng
  • * - không hoặc nhiều (ký tự khoảng trắng)

Thí dụ:

$ 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

Vì thế:

Tìm một mẫu trong đó dòng bắt đầu bằng 0 hoặc nhiều ký tự khoảng trắng, theo sau là chuỗi my_key , tiếp theo là 0 hoặc nhiều ký tự khoảng trắng, một dấu bằng, sau đó lại là 0 hoặc nhiều ký tự khoảng trắng. Hiển thị phần còn lại của nội dung trên dòng đó theo mẫu đó.


Ví dụ của bạn không hoạt động (không barđược in ra), ít nhất là trên Unix / OSX.
kenorb

7

quyến rũ

Bạn có thể sử dụng sedđể phân tích cú pháp tệp cấu hình ini, đặc biệt khi bạn đặt tên phần như:

# 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

vì vậy bạn có thể sử dụng sedtập lệnh sau để phân tích cú pháp dữ liệu trên:

# 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
}

điều này sẽ chuyển đổi dữ liệu ini thành định dạng phẳng này:

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

do đó nó sẽ được dễ dàng hơn để phân tích sử dụng sed, awkhoặc readbằng cách có tên phần trong mỗi dòng.

Tín dụng & nguồn: Tệp cấu hình cho tập lệnh shell , Michael Grünewald


Ngoài ra, bạn có thể sử dụng dự án này:, chilladx/config-parsermột trình phân tích cú pháp cấu hình bằng cách sử dụng sed.


Điều đó thật tuyệt! Tôi đã nghĩ đến việc san bằng nó như vậy nhưng điều này vượt xa những gì tôi định hack cùng nhau!
Grinch

6

Bạn có thể sử dụng crudinicông cụ để lấy giá trị ini, ví dụ:

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

Lưu ý rằng nó dựa trên Python, vì vậy có thể không phù hợp với các ứng dụng Linux nhúng.
Craig McQueen,

Đây là một phần của kho lưu trữ Fedora tiêu chuẩn (được thử nghiệm với 31). yum install crudini
shrewmouse

5

Đối với những người (như tôi) đang tìm cách đọc các tệp INI từ các tập lệnh shell (đọc shell, không phải bash) - Tôi đã gõ vào thư viện trợ giúp nhỏ cố gắng thực hiện chính xác điều đó:

https://github.com/wallyhall/shini (Giấy phép MIT, làm với nó tùy ý bạn. Tôi đã liên kết ở trên bao gồm nó nội tuyến vì mã khá dài.)

Nó có phần "phức tạp" hơn những seddòng đơn giản được đề xuất ở trên - nhưng hoạt động trên cơ sở rất giống nhau.

Hàm đọc từng dòng trong tệp - tìm kiếm các dấu phần ( [section]) và khai báo khóa / giá trị ( key=value).

Cuối cùng, bạn nhận được một lệnh gọi lại cho chức năng của riêng bạn - phần, khóa và giá trị.


@CraigMcQueen - Tôi đã thêm một số hỗ trợ viết rất chất lượng alpha tối nay. Nó không "hoàn chỉnh" bởi bất kỳ sự căng thẳng nào của trí tưởng tượng!
wally

Xuất sắc! :-) Thiếu tá
Jonathan

5

Tương tự như các câu trả lời Python khác, bạn có thể thực hiện việc này bằng cách sử dụng -ccờ để thực thi một chuỗi các câu lệnh Python được đưa ra trên dòng lệnh:

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

Điều này có lợi thế là chỉ yêu cầu thư viện chuẩn Python và lợi thế là không phải viết một tệp kịch bản riêng biệt.

Hoặc sử dụng tài liệu tại đây để dễ đọc hơn, do đó:

#!/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

Điều gì sẽ xảy ra nếu tôi muốn lấy toàn bộ phần dưới dạng Mảng bằng lệnh này?
Debopam Parua

2

Một số câu trả lời không tôn trọng ý kiến. Một số không tôn trọng các phần. Một số chỉ nhận ra một cú pháp (chỉ ":" hoặc chỉ "="). Một số câu trả lời Python không thành công trên máy của tôi do có sự chú thích hóa khác nhau hoặc không thể nhập mô-đun sys. Tất cả đều hơi quá ngắn đối với tôi.

Vì vậy, tôi đã viết của riêng tôi, và nếu bạn có Python hiện đại, bạn có thể gọi nó từ Bash shell của mình. Nó có lợi thế là tuân thủ một số quy ước viết mã Python phổ biến và thậm chí còn cung cấp các thông báo lỗi và trợ giúp hợp lý. Để sử dụng nó, hãy đặt tên nó như myconfig.py (KHÔNG gọi nó là configparser.py hoặc nó có thể cố gắng nhập chính nó,) làm cho nó có thể thực thi và gọi nó như

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

Đây là mã của tôi cho Python 3.5 trên 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

đơn giản phức tạp

tập tin ini

test.ini

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

tập lệnh bash với đọc và thực thi

/ 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

thì trong một tập lệnh khác, tôi lấy nguồn kết quả của lệnh và có thể sử dụng bất kỳ biến nào trong

test.sh

#!/bin/bash

source parseini test.ini

echo $section2_name2

cuối cùng từ dòng lệnh, kết quả đầu ra là

# ./test.sh 
value_2

Giải pháp tuyệt vời! Cảm ơn!
Michael

2

Đây là phiên bản của tôi, phân tích cú pháp các phần và điền vào một mảng liên kết chung g_iniProperties với nó. Lưu ý rằng điều này chỉ hoạt động với bash v4.2 trở lên.

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
}

Và đây là mã mẫu sử dụng hàm trên:

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

1

Tập lệnh này sẽ nhận các tham số như sau:

nghĩa là nếu ini của bạn có:

pars_ini.ksh <đường dẫn đến tệp ini> <tên của Khu vực trong tệp Ini> <tên trong tên = giá trị để trả về>

ví dụ. làm thế nào để gọi nó:


[ Môi trường ]

a = x

[DataBase_Sector]

DSN = cái gì đó


Sau đó gọi:

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

điều này sẽ truy xuất "cái gì đó" sau

tập lệnh "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

Tôi đã viết một tập lệnh python nhanh chóng và dễ dàng để đưa vào tập lệnh bash của mình.

Ví dụ: tệp ini của bạn được gọi food.ini và trong tệp bạn có thể có một số phần và một số dòng:

[FRUIT]
Oranges = 14
Apples = 6

Sao chép tập lệnh Python 6 dòng nhỏ này và lưu nó dưới dạng 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])

Bây giờ, trong tập lệnh bash của bạn, bạn có thể làm điều này chẳng hạn.

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

hoặc là

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

Điều này giả định trước:

  1. bạn đã cài đặt Python
  2. bạn đã cài đặt thư viện configparser (điều này sẽ đi kèm với cài đặt python std)

Hy vọng nó sẽ giúp : ¬)


Tôi đang tìm kiếm thứ gì đó làm được điều này nên tôi đã làm theo ví dụ và nó hoạt động tốt. Tôi quên mất tôi đã viết cái này !!!! Tôi đã cố gắng bỏ phiếu cho chính mình nhưng than ôi, tôi không thể bỏ phiếu cho chính mình !!! ha ha
joe_evans

0

Phiên bản một lớp lót của tôi

#!/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

Vừa mới viết xong trình phân tích cú pháp của riêng tôi. Tôi đã cố gắng sử dụng nhiều trình phân tích cú pháp khác nhau được tìm thấy ở đây, dường như không có trình phân tích nào hoạt động với cả ksh93 (AIX) và bash (Linux).

Đó là phong cách lập trình cũ - phân tích cú pháp từng dòng. Khá nhanh vì nó sử dụng ít lệnh bên ngoài. Chậm hơn một chút vì tất cả các đánh giá cần thiết cho tên động của mảng.

Ini hỗ trợ 3 cú pháp đặc biệt:

  • tệp includefile = ini -> Tải tệp ini bổ sung. Hữu ích để chia ini thành nhiều tệp hoặc sử dụng lại một số phần cấu hình
  • includeir = directory -> Tương tự như includefile, nhưng bao gồm một thư mục hoàn chỉnh
  • includeection = section -> Sao chép một phần hiện có vào phần hiện tại.

Tôi đã sử dụng tất cả các cú pháp thoses để có tệp ini khá phức tạp, có thể tái sử dụng. Hữu ích để cài đặt sản phẩm khi cài đặt một hệ điều hành mới - chúng tôi làm điều đó rất nhiều.

Các giá trị có thể được truy cập bằng $ {ini [$ section. $ Item]}. Mảng PHẢI được xác định trước khi gọi nó.

Chúc vui vẻ. Hy vọng nó hữu ích cho người khác!

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

Việc triển khai này sử dụng awkvà có những ưu điểm sau:

  1. Sẽ chỉ trả lại mục nhập phù hợp đầu tiên
  2. Bỏ qua các dòng bắt đầu bằng ;
  3. Cắt bỏ khoảng trắng đầu và cuối, nhưng không cắt bỏ khoảng trắng bên trong

Phiên bản được định dạng :

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

Một lớp lót :

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

0

Khi tôi sử dụng mật khẩu trong base64, tôi đặt dấu phân tách ":" vì chuỗi base64 có thể có "=". Ví dụ (tôi sử dụng ksh):

> echo "Abc123" | base64
QWJjMTIzCg==

Tóm parameters.inilại pass:QWJjMTIzCg==, và cuối cùng:

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

Nếu dòng có khoảng trắng như "pass : QWJjMTIzCg== "thêm | tr -d ' 'để cắt chúng:

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

0

Điều này sử dụng hệ thống perl và biểu thức chính quy sạch:

cat parameters.ini | perl -0777ne 'print "$1" if /\[\s*parameters\.ini\s*\][\s\S]*?\sdatabase_version\s*=\s*(.*)/'

0

Câu trả lời của "Karen Gabrielyan" trong số các câu trả lời khác là tốt nhất nhưng trong một số môi trường chúng tôi không có awk, như hộp bận thông thường, tôi đã thay đổi câu trả lời bằng mã bên dưới.

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
    }

Tôi không hoàn toàn chắc chắn về khả năng thiếu awk như thế nào, nhưng có sẵn cú pháp sed, cut và tương đối nâng cao hơn bash like.
Ondrej K.

Hầu hết các hệ thống tập tin gốc ban đầu thực hiện / linuxrc hoặc / init như một kịch bản shell và do đó bao gồm một vỏ tối thiểu (thường là / bin / tro) cùng với một số tiện ích với người sử dụng không gian cần thiết
Ehsan Ahmadi

Chắc chắn rồi. Tôi chỉ hơi ngạc nhiên khi bạn xây dựng busybox của mình mà không cần awk, nhưng vẫn có sed, cut và hỗ trợ cho các "bashisms" khác nhau. Không phải là không thể, chỉ làm tôi băn khoăn. ;)
Ondrej K.

Các công cụ khác là lightwight hơn awk. Nếu bạn viết script vào initramfs bằng initramfs-tools trong bản phân phối ubuntu, bạn sẽ thấy rằng bạn không có awk và các công cụ khác như sed, grep ... đều hoạt động tối thiểu.
Ehsan Ahmadi

Chắc chắn, tôi không nói về GNU awk hoặc awk hoàn toàn khác, chỉ tự hỏi người ta tiết kiệm được bao nhiêu bằng cách cấu hình busybox để không bao gồm hỗ trợ awk (đặc biệt là các bit khác được đề cập không bị loại bỏ khỏi cấu hình đó). Có thể là * buntu initrd có một cái giống như vậy. Chỉ băn khoăn về combo / lựa chọn vậy thôi.
Ondrej K.

0

Nếu Python có sẵn, phần sau sẽ đọc tất cả các phần, khóa và giá trị và lưu chúng trong các biến với tên của chúng theo định dạng "[section] _ [key]". Python có thể đọc các tệp .ini đúng cách, vì vậy chúng tôi tận dụng nó.

#!/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

Bạn có thể sử dụng trình phân tích cú pháp CSV xsv làm phân tích cú pháp dữ liệu 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

hoặc từ một tệp.

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

0

Nếu sử dụng các phần, điều này sẽ thực hiện công việc:

Ví dụ đầu ra thô:

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

Phân tích cú pháp 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'

Bây giờ tất cả cùng nhau:

$ 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

Tôi có một lớp lót đẹp (bạn đã có phpjqcài đặt):

cat file.ini | php -r "echo json_encode(parse_ini_string(file_get_contents('php://stdin'), true, INI_SCANNER_RAW));" | jq '.section.key'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.