私はいくつかのbashini解析スクリプトを見て、 this ここで数回使用されているのを見たので、それが私のために機能するかどうかを確認しようとしています。 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"
あなたができるbash
で何かをするという事実は、あなたがすべきであることを意味しません。
sh
(およびbash
など)スクリプトは、プログラムを起動したり、テキスト処理コマンドを囲んだりするための比較的単純なラッパーに最適です。 iniファイルの解析や処理など、より複雑なタスクには、他の言語の方が適しています。スクリプトをPerl
またはpython
で書くことを検討しましたか?どちらにも優れた.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]
行を削除します(実際には、角括弧が開いた[
で始まるすべての行-これを別の方法で処理する必要があります[1] 複数のセクションがあるiniファイル内)、コメントと先頭および末尾の空白を削除します。 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
ループにフィードするsed
スクリプトの直前にwhile
スクリプトを挿入します。
awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...
(もちろん、sed
コマンドラインの最後から"$inifile"
を削除します。ファイルから[Section1]
だけを抽出するという問題が発生した後は、ファイルを再度フィードする必要はありません)。
ORS
の設定は、iniファイルから1つのセクションのみを抽出する場合は厳密には必要ありませんが、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
それが台無しにした唯一のものは最後のものであり、TBH私はそれがエラーだとは思わない。.ini
ファイルではセミコロンがコメントの始まりを示している。Iまた、データが実際にそのように見えるかどうかも尋ねたいと思います。
その場合は、その前に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