如何解析其值可能包含某些字元的 ini 檔案?

如何解析其值可能包含某些字元的 ini 檔案?

我看過幾個 bash ini 解析腳本,我看到了一個在這裡用過幾次,所以我想看看它是否適合我。看起來它會多次逐行讀取 ini 文件,並且每次傳遞都會逐步建構一個最終被評估的函數。它適用於某些特殊字符,但不適用於其他字符。如果檔案中的值包含單引號或大於/小於符號,則腳本傳回語法錯誤。其他符號也會產生意想不到的結果。遇到這些字元時我該如何處理?

這是解析ini的函數。

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

ini 文件

[Section1]
value1=abc`def # unexpected EOF while looking for matching ``'
value2=ghi>jkl # syntax error near unexpected token `>'
value3=mno$pqr # executes ok but outputs "mnoqr"
value4=stu;vwx # executes ok but outputs "stu"

答案1

事實是你做某事bash並不意味著你應該

shbash等)腳本最適合作為相對簡單的包裝器來啟動程式或圍繞文字處理命令。對於更複雜的任務,包括解析 ini 檔案並對其進行操作,其他語言更合適。您是否考慮過用perlor編寫腳本python?兩者都有很好的 .ini 檔案解析器 -Config::INI當我需要解析 ini 檔案時,我曾多次使用過 perl 的模組。

但如果您堅持在 bash 中執行此操作,則應該使用關聯數組而不是設定單個變數。

從這樣的事情開始:

#! /bin/bash

inifile='user1074170.ini' 

# declare $config to be an associative array
declare -A config

while IFS='=' read -r key val ; do 
    config["$key"]="$val"
done <  <(sed -E -e '/^\[/d
                     s/#.*//
                     s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )

# now print out the config array
set | grep '^config='

sed腳本刪除該[Section1]行(實際上,所有以左方括號開頭的行[- 您將需要在具有多個部分的 ini 文件中以不同的方式處理此問題[1]),並刪除註釋以及前導和尾隨空格。這個while循環讀取每一行,用作=字段分隔符,並將內容分配給變數 $key 和 $val,然後將其添加到 $config 數組中。

輸出:

config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )

您可以稍後在腳本中使用陣列條目,如下所示:

$ echo value1 is "${config[value1]}"
value1 is abc`def

$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true

[1]awkperl有以「段落」模式方便地閱讀文件的簡單方法。段落被定義為由一個或多個空白行與其他文字區塊分隔開的文字區塊。

例如,要僅使用[Section1],請在將腳本輸入到上面的循環awk之前插入下面的腳本:sedwhile

awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...

"$inifile"(當然,並從命令列末尾刪除sed- 在您費盡心思從中提取文件之後,您不想再次輸入該文件[Section1])。

如果您只從 ini 檔案中提取一個部分,則設定ORS並不是絕對必要的 - 但如果您要提取兩個或多個部分,則對於保持段落分隔很有用。

答案2

我知道這是一個不完整的答案,但MySQL.lnsin augeas 似乎能夠解析其中的大部分內容。在augtool

augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> set /augeas/load/testini/lens "MySQL.lns"
augtool> load
augtool> ls /files/root/
.ssh/      test.ini/
augtool> ls /files/root/test.ini
target/ = Section1
augtool> ls /files/root/test.ini/target/
value1/ = abc`def
value2/ = ghi>jkl
value3/ = mno$pqr
value4/ = stu

唯一搞砸的是最後一個,說實話,我不認為這是一個錯誤。在.ini文件中,分號標記註釋的開始。我還想問一下你的數據其實是這樣的嗎?

如果是這樣,您可以sed在它之前做一些設置,設置;一些未使用的字元值,然後將其轉換回後處理。但最終,您將需要一些標準,以便文件能夠具有任何可辨識的結構。

編輯:

我用 PHP 鏡頭對其進行了測試,只要引用這些值就可以得到整個結果:

[root@vlzoreman ~]# augtool
augtool> set /augeas/load/testini/lens "PHP.lns"
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> load
augtool>  ls /files/root/test.ini/Section1/
value1 = abc`def
value2 = ghi>jkl
value3 = mno$pqr
value4 = stu;vwx

否則它就跟 MySQL 鏡頭一樣遠了。

編輯#2:

我確信有一種更簡潔的方法可以編寫此內容,但這是範例用法:

[root@vlp-foreman ~]# bash bash.sh
Values for: Section1:
        :: value1 is abc`def
        :: value2 is ghi>jkl
        :: value3 is mno$pqr
        :: value4 is stu;vwx
Values for: Section2:
        :: value1 is abc`def

腳本是:

#!/bin/bash

sections=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini | cut -f1 -d/)

for currentSection in $sections; do

  echo "Values for: $currentSection:"

  fields=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini/$currentSection | awk '{print $1}')

  for currentField in $fields; do

    currentValue=$(augtool -A --transform "PHP.lns incl /root/test.ini" print /files/root/test.ini/$currentSection/$currentField | cut -f2 -d=)
    currentValue=$(echo $currentValue | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed -e 's/^"//' -e 's/"$//')

    echo -e "\t:: $currentField is $currentValue"

  done

done

答案3

看一眼crudinihttps://www.pixelbeat.org/programs/crudini/
在 Ubuntu 上,您可以安裝它以sudo apt install crudini
從 ini 檔案中讀取值,運行:

$ value1=$(crudini --get "crudini.ini" "Section1" "value1")

crudini 支援多種 ini 檔案格式並處理特殊字元:

克魯迪尼.ini

[Section1]
value1=abc`def
value2=ghi>jkl
value3=mno$pqr
value4=stu;vwx

讀數值

$ for i in {1..4}; do crudini --get "crudini.ini" "Section1" "value$i"; done
abc`def
ghi>jkl
mno$pqr
stu;vwx
$

相關內容