値に特定の文字を含めることができるiniファイルを解析する方法は?

値に特定の文字を含めることができるiniファイルを解析する方法は?

私はいくつかのbash ini解析スクリプトを見て、次のようなものを見ました。これここで何度使ったことがあり、それが私に効果があるかどうか調べたかったです。 iniファイルを1行ずつ複数回読み取るように見え、各パスは最終的に評価される関数を徐々に構成します。一部の特殊文字では機能しますが、他の文字では機能しません。ファイルの値に一重引用符または記号より大きいまたは小さい記号が含まれている場合、スクリプトは構文エラーを返します。他のシンボルも予期しない結果を招く可能性があります。このようなキャラクターに会ったらどうすればいいですか?

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ファイルの解析や操作など、より複雑な操作の場合は、他の言語がより適しています。perlまたはでスクリプトを書くことを検討しましたか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]awkそれとも、perl「短絡」モードでファイルを便利に読み取る簡単な方法はありますか?段落は、1つ以上の空行で他のテキストブロックと区切られたテキストブロックとして定義されます。

たとえば、使用するには、[Section1]上記のループに供給するawk前に以下のスクリプトを挿入してください。sedwhile

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

"$inifile"(もちろん、コマンドラインの最後からファイルを削除してください。sedファイルを抽出するためにすべての手間をかけた後、ファイルを再入力したくありません。[Section1]

この設定は、iniファイルから1つのセクションのみを抽出する場合は必ずしも必要ではORSありませんが、2つ以上のセクションを抽出する場合は段落を分離したままにするのに役立ちます。

答え2

私はこれが不完全な答えであることを知っていますが、augeasはMySQL.lnsほとんどの答えを解析できるようです。存在する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ファイル形式をサポートし、特殊文字を処理します。

crudini.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
$

関連情報