3

次のように、いくつかのブロックを含むファイルがあります。

<start> test var=3333
<g>test=000000000000 tst <s>
<end>
...
<start> var=564735628
<title>somethink<\title>
<end>
...

そして、ループ内のセクションとセクションの間にブロックを取得する必要があります。次に、現在のブロックでいくつかのシンボルを取得する必要があります。私はこのようにしようとします:

for block in $(cat $file | sed -n '/<start>/,/<end>/p;'); do
         echo $block 
done

結果は次のとおりです。

<start>

代わりは

<start> test 1
<g>test=000000000000 tst <s>
<end>

さらに処理するためにブロック全体を取得するにはどうすればよいですか?


わかりました、ソースを説明しようとします

<start> test var=3333
<g>test=000000000000 tst <s>
<end>

あなたのコードの結果はブロックではありません。それはただの一刺しです。文字列は<end>t> test var=3333tst <s> 、ご覧のとおり、ブロックの文字列が互いに重なり合っています。

4

5 に答える 5

1

次のようなことができます:

block=""
cat $file | sed -n '/<start>/,/<end>/p;' | while read -r line; do
     if [ -z "$block" ]; then
         block="$line"
     else
         block=$(printf "%s\\n%s" "$block" "$line")
     fi

     if printf "%s\\n" "$line" | grep "<end>" > /dev/null; then
         echo "$block"
         block=""
     fi
done

chorobaが回答で述べたように、 for ループは IFS 変数を使用して sed の出力を個別のフィールドに分割し、ブロック変数には単一のフィールドのみが含まれます。(つまり、ブロックには<start>、次にtest、そしてなどが含まれますvar=3333)。

解決策は、sed の出力を loop コマンドにパイプすることで行ごとに強制的に読み取り、readコマンドを使用して行を読み取ることです。read コマンドの-rフラグは、バックスラッシュをエスケープ文字として解釈しないように強制します。これで、行には変数があります$lineが、ブロックにはありません。ブロックを取得するには、文字列が見つかるまで行を連結するだけ<end>です。

$block変数が空の場合、単純に を代入でき$lineます。それ以外の場合は、コマンドを使用して、改行文字と連結されたprintfの以前の値と の内容を含む新しい文字列を生成します。この改行文字は、ブロックが単一行になるのを防ぎます。$block$line

最後の行が見つかったかどうかをテストするには、ブロックの現在の値を出力して、grep がそれを見つけるかどうかを確認します。printf を使用したのは、表示したい文字列が変数で始まる場合は echo よりも安全だからです (変数がハイフンで始まっていないことを保証することはできません。これは echo がオプションとして解釈する可能性があります)。また、実際にブロックを見つけたら、次のブロックに備えてブロック変数をクリアすることを忘れないでください。

于 2012-10-02T12:13:50.613 に答える
1

ここでは使用しないsedでください。やを解析するためのモジュールを提供するperlやのような言語を使用してください。pythonHTMLXML

于 2012-10-02T12:12:00.317 に答える
0

これはあなたのために働くかもしれません(GNU sedとbash):

OIFS=$IFS; IFS=$'\n'; block=($(sed '/<start>/,/<end>/!d' file)); IFS=$OIFS
for x in "${!block[@]}"; do echo "${block[x]}"; done

sedコマンドの出力を配列にスラップし、配列blockをループします。

于 2012-10-02T15:56:13.777 に答える
0

IFS を変更し、ブロック間に区切り文字を挿入することで、各ブロックを反復処理できます。

たとえば:、区切り記号として使用します

OLDIFS=$IFS; IFS=':'
blocks=$(sed -n '/start/,/end/ {/start/ s/^/:/; p}' file)
for block in ${blocks#:}; do
  echo "This is block $((count++))"
  echo "$block"
done
IFS=$OLDIFS

ノート:

  1. ブロックは、:前に挿入してに<start>設定IFSすることで「分離」されます:
  2. ${blocks#:}最初の を削除します。それ以外の:場合:block1:block2...は として解釈されemptyblock:block1:block2...ます。つまり、ループは、存在しない最初のブロック (空であり、:が配置されているために存在します)を反復します。
  3. または、:後ろに配置することもできます<end>が、ブロックの最後の行になる<end>:\nため、次のブロックの開始前に余分な改行があります。
于 2012-11-03T18:43:49.640 に答える
0

コマンドの出力に単語分割が適用されますsed。IFS を空の値に設定して、sed出力で単語が分割されるのを防ぐことができますが、出力全体がsed1 つの「ブロック」になります。Perl のようなより強力な言語に切り替えることをお勧めします。

于 2012-10-02T09:28:22.010 に答える