私は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 }'
また、もう1つの問題は、=
の近くのスペースが考慮されないことです。この仕事にはsed
のほうが適していると思いますが、sed
にセクション名の一時変数を保持して格納する方法がわかりません。
これを行う方法はありますか?
Gawkはフィールド区切り文字として正規表現を受け入れます。次の例では、等号の前後のスペースを削除していますが、残りの行ではスペースを保持しています。引用符が値の周りに追加されるため、Bash割り当てが実行されたときに、それらのスペースがあれば保持されます。セクション名は数値変数であると想定していますが、Bash 4を使用している場合は、これを適応させて、セクション名自体をインデックスとする連想配列を使用するのは簡単です。
awk -F ' *= *' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" "\"" $2 "\"" }'
Bashの変数名にはスペースを含めることができないため、Khaledが表示するスペースの削除($ 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
ここでも、連想配列はかなり簡単にサポートできます。
組み込みのpython parser :なので、このジョブには単純なINIスクリプトを使用します。
#!/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にはより短い実装がありますが、これはより読みやすく、保守しやすいと思います。
余分なスペースを削除したい場合は、組み込み関数gsub
を使用できます。たとえば、以下を追加できます。
gsub(/ /, "", $1);
これにより、すべてのスペースが削除されます。トークンの最初または最後のスペースを削除したい場合は、
gsub(/^ /, "", $1);
gsub(/ $/, "", $1);
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
という名前の環境変数を作成します。もちろん、これは、値にピリオドや空白が含まれないことが信頼できる場合にのみ機能します(より堅牢なソリューションについては、以下を参照してください)。
Shell関数を使用して入力を保存することにより、任意の値をフェッチします。
function myini() { git config -f mytool.ini; }
ここでもエイリアスは問題ありませんが、通常はシェルスクリプトで展開されません[ 1 ]。とにかく、エイリアスはシェル関数によって置き換えられますほとんどすべての目的、 "[ 2 ]、Bashによると man page 。
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
として覚えて、\2
として覚えて、\3
として検索します置換文字列内の\U
および\E
シーケンス(置換文字列の一部が大文字)は、GNU sed
拡張です。とBSDでは、複数の-e
式を使用するだけで同じ効果が得られます。
セクションの名前に埋め込まれた引用符と空白の処理(これはgit config
で許可されています)は読者のための演習として残されています。 :)
与えられた:
; 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" )
常に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
お役に立てれば!
これが純粋な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スクリプトに加えたいくつかのさらなる改善があります...
Windowsの行末とUnixで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
}