iniファイルを解析してbash配列変数に変換する方法は?


12

私はiniファイルをbash配列変数に変換しようとしています。サンプルのiniは次のとおりです。

[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

これらは次のようになります。

session[foobar]=foo
path[foobar]=/some/path
session[barfoo]=bar

等々。

今、私はこのコマンドだけを思い付くことができました

awk -F'=' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" $2 }'

また、別の問題は、スペース=を考慮に入れていないことです。sedおそらくこの仕事に適していると思いますが、セクション名の一時変数を保持して保存する方法がわかりませんsed

これを行う方法はありますか?


これを行う他の効率的な方法がある場合は、ソリューションも投稿してください:)
フリント


簡単な解決策については、次を確認してください。シェルスクリプト内でINI値を取得する方法を教えてくださいstackoverflow SEで。
ケノーブ

回答:


10

Gawkは、フィールド区切り文字として正規表現を受け入れます。次の例では、等号の前後のスペースを削除しますが、残りの行ではそれらを保持します。値の周りに引用符が追加されるため、Bashの割り当てが実行されたときにスペースが保持されます(存在する場合)。セクション名は数値変数になると想定していますが、Bash 4を使用している場合は、セクション名自体をインデックスとして使用する連想配列を使用するように簡単に調整できます。

awk -F ' *= *' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" "\"" $2 "\"" }'

Bash変数名にはスペースを含めることができないため、Khaledが表示するスペースの削除($ 1とセクションのみ)も行う必要があることに注意してください。

また、値に等号が含まれている場合、このメソッドは機能しません。

もう1つの手法は、Bash while readループを使用し、ファイルの読み取り時に割り当てを実行することdeclareです。これを使用すると、ほとんどの悪意のあるコンテンツから安全です。

foobar=1
barfoo=2  # or you could increment an index variable each time a section is found
while IFS='= ' read var val
do
    if [[ $var == \[*] ]]
    then
        section=$var
    elif [[ $val ]]
    then
        declare "$var$section=$val"
    fi
done < filename

繰り返しますが、連想配列はかなり簡単にサポートできます。


1
非常に良い情報であり、外部コマンドに依存する代わりにbash組み込み関数を使用するため、2番目の手法が特に気に入っています。
フリント

@TonyBarganski:1つを別のAWK呼び出しにパイプする代わりに変更できます。
追って通知があるまで一時停止します。

10

このジョブにはINI パーサーが組み込まれているため、単純なPythonスクリプトを使用します。

#!/usr/bin/env python

import sys, ConfigParser

config = ConfigParser.ConfigParser()
config.readfp(sys.stdin)

for sec in config.sections():
    print "declare -A %s" % (sec)
    for key, val in config.items(sec):
        print '%s[%s]="%s"' % (sec, key, val)

そして、bashで:

#!/bin/bash

# load the in.ini INI file to current BASH - quoted to preserve line breaks
eval "$(cat in.ini  | ./ini2arr.py)"

# test it:
echo ${barfoo[session]}

確かに、awkにはより短い実装がありますが、これはより読みやすく、保守しやすいと思います。


3
bashのより前のバージョン4.2にそれを充填する前に関連付ける配列を宣言する必要がある、例えば、内print "declare -A %s" % (sec)
フェリックスイブ

2
代わりにevalsource <(cat in.ini | ./ini2arr.py)
さらに通知があるまで一時停止します。

3

余分なスペースを削除する場合は、組み込み関数を使用できます gsub。たとえば、次を追加できます。

gsub(/ /, "", $1);

これにより、すべてのスペースが削除されます。トークンの先頭または末尾のスペースを削除する場合は、使用できます

gsub(/^ /, "", $1);
gsub(/ $/, "", $1);

クールなトリック。そのような組み込み関数があることを知りませんでした:)
フリント

0

これが純粋なbashソリューションです。

これは、chilladxが投稿したものの新しく改善されたバージョンです。

https://github.com/albfan/bash-ini-parser

本当に簡単な初期の例に従うために:あなたはこれをダウンロードした後、単にファイルをコピーbash-ini-parserし、scripts/file.ini同じディレクトリに、そして私もその同じディレクトリに下に設けられてきた例を使用して、クライアントのテストスクリプトを作成します。

source ./bash-ini-parser
cfg_parser "./file.ini"
cfg_section_sec2
echo "var2=$var2"
echo "var5[*]=${var5[*]}"
echo "var5[1]=${var5[1]}"

bash-ini-parserスクリプトに加えたいくつかの改善点を以下に示します...

Unixと同様にWindowsの行末でiniファイルを読みたい場合は、ファイルを読み込む行の直後にあるcfg_parser関数にこの行を追加します。

ini=$(echo "$ini"|tr -d '\r') # remove carriage returns

制限付きアクセス許可を持つファイルを読み取りたい場合は、次のオプション機能を追加します。

# Enable the cfg_parser to read "locked" files
function sudo_cfg_parser {

    # Get the file argument
    file=$1

    # If not "root", enable the "sudo" prefix
    sudoPrefix=
    if [[ $EUID -ne 0 ]]; then sudoPrefix=sudo; fi

    # Save the file permissions, then "unlock" the file
    saved_permissions=$($sudoPrefix stat -c %a $file)
    $sudoPrefix chmod 777 $file

    # Call the standard cfg_parser function
    cfg_parser $file

    # Restore the original permissions
    $sudoPrefix chmod $saved_permissions $file  
}

のためにダウン票を投じなければならなかったchmod 777。せいぜい日陰の練習ですが、iniファイルを実行可能にする必要はありません。より良い方法はsudo、アクセス許可を台無しにするのではなく、ファイルの読み取りに使用することです。
-Richlv

@Richlv OK。賛成票の説明に感謝します。しかし、それはこの小さな部分であり、全体として質問に答える限り、最小限の意味しかありません。「答えは」リンクです:github.com/albfan/bash-ini-parser。既にすべてを否定するのではなく、既にオプションのラッパー関数とラベル付けされているものについては、編集を提案することもできます。
-BuvinJ

0

常にPythonのConfigParserがあると仮定して、次のようなシェルヘルパー関数を作成できます。

get_network_value()
{
    cat <<EOF | python
import ConfigParser
config = ConfigParser.ConfigParser()
config.read('network.ini')
print (config.get('$IFACE','$param'))
EOF
}

$IFACEそして $param、それぞれのセクションのパラメータです。

このヘルパーは、次のような呼び出しを許可します。

address=`param=address get_network_value` || exit 1
netmask=`param=netmask get_network_value` || exit 1
gateway=`param=gateway get_network_value` || exit 1

お役に立てれば!


0

Gitが利用可能であり、キー名にアンダースコアを使用できないという制約がある場合git configは、汎用INIパーサー/エディターとして使用できます。

キーと値のペアを周囲から解析し、重要で=ない空白を破棄し、さらにコメント(;およびの両方#)を取得し、基本的に無料で型強制を入力します。OPの入力.iniと目的の出力(Bash連想配列)の完全な動作例を以下に示します。

ただし、次のような構成ファイルを指定します

; mytool.ini
[section1]
    inputdir = ~/some/dir
    enablesomefeature = true
    enablesomeotherfeature = yes
    greeting = Bonjour, Monde!

[section2]
    anothersetting = 42

…迅速で汚い解決策が必要なだけで、Bash連想配列の設定を持つという考えと結婚していないのであれば、次のようなもので十分です。

eval $(git config -f mytool.ini --list | tr . _)

# or if 'eval' skeeves you out excessively
source <(git config -f mytool.ini --list | tr . _)

という名前の環境変数を作成します sectionname_variablename現在の環境でた。もちろん、これは、値にピリオドや空白が含まれないことを信頼できる場合にのみ機能します(より堅牢なソリューションについては以下を参照してください)。

その他の簡単な例

入力を保存するシェル関数を使用して、任意の値を取得します。

function myini() { git config -f mytool.ini; }

ここでもエイリアスは問題ありませんが、これらは通常シェルスクリプト[ 1 ]では展開されず、とにかくエイリアスは「ほとんどすべての目的のために」シェル関数によって置き換えられます[ 2 ]、Bashのマニュアルページによる

myini --list
# result:
# section1.inputdir=~/some/dir
# section1.enablesomefeature=true
# section1.enablesomeotherfeature=yes
# section2.anothersetting=42

myini --get section1.inputdir
# result:
# ~/some/dir

この--typeオプションを使用すると、特定の設定を整数、ブール値、またはパスとして「正規化」できます(自動的に展開します~)。

myini --get --type=path section1.inputdir  # value '~/some/dir'
# result:
# /home/myuser/some/dir

myini --get --type=bool section1.enablesomeotherfeature  # value 'yes'
# result:
# true

少し堅牢なクイックアンドダーティの例

現在の環境とmytool.ini同様SECTIONNAME_VARIABLENAMEに、すべての変数を使用可能にし、キー値の内部空白を保持します。

source <(
    git config -f mytool.ini --list \
      | sed 's/\([^.]*\)\.\(.*\)=\(.*\)/\U\1_\2\E="\3"/'
)

英語でsed式がしていることは

  1. ピリオドまでの非ピリオド文字の束を見つけ\1、それを覚えて、それから
  2. 等号までの文字の束を見つけ、それを覚えて\2、そして
  3. 等号の後のすべての文字を見つける \3
  4. 最後に、置換文字列で
    • セクション名+変数名は大文字であり、
    • 値部分は二重引用符で囲まれます。引用符で囲まれていない場合(シェルのように)シェルにとって特別な意味を持つ文字が含まれている場合

置換文字列の\Uandおよび\Eシーケンス(置換文字列のその部分を大文字にする)はGNU sed拡張です。macOSおよびBSDでは、複数の-e式を同じ効果が得られます。

セクション名に埋め込まれた引用符と空白の処理(これgit configが許可されます)は、読者の課題として残されています。:)

セクション名をBash連想配列へのキーとして使用する

与えられた:

; foo.ini
[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

これは、単にsed置換式のキャプチャの一部を再配置することにより、OPが要求する結果を生成し、GNU sedなしで正常に動作します。

source <(
    git config -f foo.ini --list \
      | sed 's/\([^.]*\)\.\(.*\)=\(.*\)/declare -A \2["\1"]="\3"/'
)

実際の.iniファイルの引用にはいくつかの課題があるかもしれないと予測していますが、提供された例ではうまくいきます。結果:

declare -p {session,path}
# result:
# declare -A session=([barfoo]="bar" [foobar]="foo" )
# declare -A path=([barfoo]="/some/path" [foobar]="/some/path" )
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.