2012-04-16 11 views
2

私のシェルスクリプトは少し錆びているようです。私の望みは、bashのarraylist構成変数をループし、このループの中で得られた必要なパラメーターをすべて呼び出して、すべてをフォークせずに呼び出すことです。bashパラメータの改行による置換

CONFIG=" 
    0, 0x00, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' % 
    1, 0x01, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' % 
    51, 0x10, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' % 
    63, 0xd4, 'Power up and unmute DAC' % 
    64, 0x00, 'Power up and unmute DAC' % 
" 

Iだろう、そのパラメータをループするようなので、のように:

基本的に、私は次のように内部のスクリプトの人間が解析可能なコンマ区切りの設定変数と呼ばれなければならないものを作成しました

while read reg val expl; do 
    printf "%s %s\n" "Calling i2c_write() with reg=${reg//,/}" \ 
      "val=${val//,/} expl=$expl __EOL__"; 
    # i2c_write() call 
done <<< "${CONFIG//\%/$'\n'}" 

電流出力である:

Calling i2c_write() with reg= val= expl= __EOL__ 
Calling i2c_write() with reg=0 val=0x00 expl='Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' __EOL__ 
Calling i2c_write() with reg= val= expl= __EOL__ 
Calling i2c_write() with reg=1 val=0x01 expl='Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' __EOL__ 
Calling i2c_write() with reg= val= expl= __EOL__ 
Calling i2c_write() with reg=51 val=0x10 expl='Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' __EOL__ 
Calling i2c_write() with reg= val= expl= __EOL__ 
Calling i2c_write() with reg=63 val=0xd4 expl='Power up and unmute DAC' __EOL__ 
Calling i2c_write() with reg= val= expl= __EOL__ 
Calling i2c_write() with reg=64 val=0x00 expl='Power up and unmute DAC' __EOL__ 
Calling i2c_write() with reg= val= expl= __EOL__ 
Calling i2c_write() with reg= val= expl= __EOL__ 

所望の出力は次のようになります

Calling i2c_write() with reg=0 val=0x00 expl='Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' __EOL__ 
Calling i2c_write() with reg=1 val=0x01 expl='Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' __EOL__ 
Calling i2c_write() with reg=51 val=0x10 expl='Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' __EOL__ 
Calling i2c_write() with reg=63 val=0xd4 expl='Power up and unmute DAC' __EOL__ 
Calling i2c_write() with reg=64 val=0x00 expl='Power up and unmute DAC' __EOL__ 

私は、より適した構造とCONFIG変数を置き換えることが嬉しい限り:

  • a)はフォークが変数のエントリをループに必要ありません、と
  • b)人間がこの変数の項目を解析して編集することは比較的簡単です。
  • c)bash 3.2.x以上で動作します。
+0

すべての作業ソリューションを読んでたので、私はいつも私の試みはかなり複雑だった感じがしました。私はちょうど適切な解決策を見つけることができませんでした。私はPeter.O、Sorpigal、ormaajに感銘を受けるコメントと美しく作られた自明のコードスニペットに感謝したいと思います。毎日何か新しいものを手に入れることは素晴らしいことです。 – Moreaki

答えて

2

この複雑なCONFIG形式を維持する必要がありますか?

これは動作するはずです:

#!/bin/bash 
CONFIG=" 
    0, 0x00, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    1, 0x01, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    51, 0x10, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    63, 0xd4, 'Power up and unmute DAC' 
    64, 0x00, 'Power up and unmute DAC' 
" 

while IFS=,$'\n ' read -r reg val expl; do 
    [ -z "$reg" ] && continue 
    printf "%s %s\n" "Calling i2c_write() with reg=${reg}" \ 
      "val=${val} expl=$expl __EOL__"; 
    # i2c_write() call 
done <<< "${CONFIG}" 

私は単にあなたがその後でとにかくやっているので、リテラル改行で%を置き換え、そしてまた,区切り文字を作りました。また、先頭と末尾の空白行をスキップするテストを投げました。

%を改行に変換し、残りの部分をそのまま使用できます。

EDIT:

あなたはさらに少ないあなたの複雑CONFIGフォーマットを保つことによってバインドされている場合は、私は以下の選択肢を提案することができますか?

#!/bin/bash 

config+=(0 0x00 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338') 
config+=(1 0x01 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338') 
config+=(51 0x10 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338') 
config+=(63 0xd4 'Power up and unmute DAC') 
config+=(64 0x00 'Power up and unmute DAC') 

for((i=0;i<${#config[@]};i+=3)) ; do 
    reg=${config[$i]} 
    val=${config[$i+1]} 
    expl="${config[$i+2]}" 
    printf "Calling i2c_write() with reg=%d val=%s expl='%s' __EOL__\n" \ 
      $reg $val "$expl" 
    # i2c_write() call 
done 

これが本当表示される、あなたは常に3つのパラメータのセットを持っていることを前提とし、厄介な解析ロジックを回避することができます。やや効率的です。私はまた、printfをより簡単に変更しました。 EDIT 2

:ので、私はそれがbash 3.1で実行されます推測これが実行されます

#!/bin/sh 
CONFIG=" 
    0, 0x00, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    1, 0x01, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    51, 0x10, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    63, 0xd4, 'Power up and unmute DAC' 
    64, 0x00, 'Power up and unmute DAC' 
" 

echo "$CONFIG" | while IFS=' ', read -r reg val expl; do 
    [ -z "$reg" ] && continue 
    printf "Calling i2c_write() with reg=%d val=%s expl=%s __EOL__\n" \ 
      $reg $val "$expl" 
    # i2c_write() call 
done 
+0

興味深い解決策:私は確かに "IFS =、$ '\ n'"部分については知らなかった。しかし、CONFIGはコードの可読性を妨げるものではありません。なぜなら、最終的に許容可能な尺度を超えて成長し、別々の設定ファイルにまとめなければならないからです。私はあなたの2番目のバージョンを堪能しました。誤解がなければ、3番目のオプションでサブシェルを入力します。 – Moreaki

+0

@Moreaki:第3版のサブシェルはパイプのみのためです。あなたはピーターとしてここに刺すことができます。実際、 '$ CONFIG'が拡張されたここの文字列は、この問題を引き起こさずに私が望むもの(その内容の処理からCONFIGの宣言を分離してください)を達成することを考えるようになりました。 – Sorpigal

2

shです:Peter.Oと競合 は、ここshで動作するバージョンです。 regvarのパラメータ置換(//,/)が嫌いでしたので、カンマを切り捨てて変更しました。入力方法をhere-docと変更しましたhere-string

なぜ入力ラインの末尾に%があるのか​​わかりません。そこに改行を入れるだけでしたか? (しかし、そこに改行がすでにあると%置換は、単に空白行を追加します)...

ここで変更したスクリプト

while read -r reg val expl; do 
    printf "%s %s %s\n" \ 
     "Calling i2c_write() with reg=${reg%,}" \ 
           "val=${val%,}" \ 
           "expl=$expl __EOL__"; 
done <<EOF 
    0, 0x00, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    1, 0x01, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    51, 0x10, 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
    63, 0xd4, 'Power up and unmute DAC' 
    64, 0x00, 'Power up and unmute DAC' 
EOF 

は、ここで(ちょうど比較のためにawkバージョンがあるさ)。同じデータを読み込みます。良い測定のための

awk -vFS=\' '{split($1,f," ") 
       split(f[1]f[2],f,",") 
       print "Calling i2c_write() with" \ 
        " reg=" f[1] \ 
        " val=" f[2] \ 
        " expl="FS $2 FS" __EOL__" 
}' <<EOF 
:: data :: 
END 

sed ...

sed -nr "s/^ +([0-9]+), +([0-9a-fx]+), +('.*')$/\ 
Calling i2c_write() with reg=\1 val=\2 expl=\3 __EOL__/p 
" <<EOF 
:: data :: 
END 
+0

私はhere-documentアプローチについて完全に忘れていました;)。 awkとsed版もありがとう。当初は絶望から選択された '%'区切り文字は、塊茎を舐めるような豚ほど醜いものでした。コメント部分を一重引用符から二重引用符に少し変更して、より穏やかなCSV仕様に準拠させました。 – Moreaki

+0

@Moreaki:二重引用符で注意してください。単一引用符は、シェルが内容を全く処理しないことを保証するので安全です。二重引用符では、そのような保証はありません。 – Sorpigal

1
main() { 
    local a n 
    while read -r a; do 
     local -a 'args=('"$a"')' 
     printf "%s reg=%s val=%s expl='%s' __EOL__\n" 'Calling i2c_write() with' "${args[@]}" 
     # i2c_write {reg,val,expl}"=${args[n++%3]}" 
    done <<"EOF" 
0 0x00 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
1 0x01 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
51 0x10 'Reset the chip, enable PLL with P=4, R=1, J=9, D=6338' 
63 0xd4 'Power up and unmute DAC' 
64 0x00 'Power up and unmute DAC' 
EOF 
} 

main 

私はあなたがここに3つのフィールドの "構造体" を記憶し、3つの個別の引数として渡すしようとしていると仮定します。コメントされた行は、あなたがおそらくコマンドを呼び出すと思います。あなたのprintfがちょうど1つを通過しているので、引数が渡されているものについての質問はあまり明確ではありません。その場合、この問題ははるかに簡単です。

あなたが配列を持つシェルを使用していると仮定すると、絶対に配列が必要です。残念なことに、Bashには多次元配列がありません.bash < 4.0には連想配列が欠けています。これは本質的に間接指定と文字列の構文解析のどちらかを選択することになります。どちらも醜いです。個人的には、とにかく配列を使用しようとします。

上記の文書化されていない、ポータブルではなく、将来の安全性が保証されていないハッキングは、現在のバージョンでBash 3の「安全」であり、 bash 3の制限は、printf -vを使用できないため、非常に苦痛であり、フォークはコマンド置換を意味しません。つまり、printf '%q'を意味しません。基本的には、heredocの各行は、通常は複合割り当て(適切に引用され、エスケープされた)の有効な内容でなければなりません。

http://mywiki.wooledge.org/BashFAQ/050

+0

あなたの解決策は、bashシェルの文脈を通して配列を読み込んで提供する将来の問題に取り組むための興味深い洞察を提供しました。どうもありがとうございました。 サイドノードでは、連想配列の欠如に関してあなたのコメントに触発されており、この問題の半分の解決策を考え出しています。 – Moreaki

+0

私は渡し/返す配列を一般化するためのライブラリを作成していますシンプルで高次の機能を備えていますが、Githubではまだまだです。この特定の動作は* [不明](http://lists.gnu.org/archive/html/bug-bash/2004-09/msg00135.html)*です。必要に応じて、もっと複雑な例があります:http://wiki.bash-hackers.org/syntax/arrays#indirection。より良いデータ構造が必要で、非シェル言語を使用できず、パフォーマンスが重要な場合は、代わりに現在のksh93バージョンを参照することをお勧めします。 – ormaaj

+0

世界をコンパイルするためにビットバック/ openembeddedを待つ間に、ハッシュを使用して何かを素早くハッキングしました:http://pastebin.com/THFkY7BE これは本当に遅く、不幸なパラメータ拡張のバグですが、 。私は時にはzshやksh93を使用することがありましたが、解決すべき問題は既存のバージョンよりも数桁速く実装する必要がありました。これはほとんどの場合、 'declare'(とリンク!)の非常に面白い使用のために、PerlまたはCのいずれかの書き換えをもたらしました。 – Moreaki