値に特定の文字が含まれる可能性がある 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ということは、すべき

sh(およびbashその他) スクリプトは、プログラムを起動したり、テキスト処理コマンドをラップする比較的単純なラッパーとして最適です。ini ファイルの解析やそれに基づく操作など、より複雑なタスクには、他の言語の方が適しています。 または でスクリプトを記述することを検討しましたperlpython? どちらも優れた .ini ファイル パーサーを備えています。ini ファイルを解析する必要があるときに、Perl のConfig::INIモジュールを何度か使用しました。

しかし、どうしても 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

不完全な回答であることは承知していますが、MySQL.lnsin augeas ではそのほとんどを解析できるようです。 In 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 ファイルをサポートし、特殊文字を処理します。

クルディニ

[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
$

関連情報