開始単語は含まれていますが、終了単語は含まず、2つの単語の間にある複数のテキストインスタンスを抽出します。

開始単語は含まれていますが、終了単語は含まず、2つの単語の間にある複数のテキストインスタンスを抽出します。

PDBファイルには、タンパク質構造に関する多くの詩が含まれています。

各形式はキーワードで始まります。原子キーワードで終わる終わり

ATOMからENDまでのすべての行を読むことができるように、bashからファイルを読み取ろうとしますが、ENDという単語を読みたくありません。

各フォーム(段落)に対してこれを行い、各段落を配列に保存したいと思います。

ファイルは次のようになります。

ATOM line 1...

ATOM line 2...

ATOM line 3...

# More lines....

END

ATOM line 1...

ATOM line 2...

ATOM line 3...

# more lines...

END

一つ原子到着終わり形態である。

各型を配列で読み取ることができるようにしたい。ATOMは含むがENDは含まない

2つのキーワードの間のテキスト(2つの単語を除く)を読み取ることができますが、開始単語は含まれ、終了単語は除外する方法がわかりません。

conf[0]また、各形式を=最初の形式、=2番目の形式などの配列に読み込むことはconf[1]機能しません。

パスワード:

#!/bin/bash

filename='coor.pdb'
echo Start
i=0
while read line; do
    conf[$i]=$(sed -n '/ATOM/,/END/{//!p}') 
    i=i+1           
done < $filename
echo $conf[0] > first_frame.data

答え1

#!/bin/bash

filename='coor.pdb'
echo Start
i=1
input=false
while read -r line
do
    if [ "${line%% *}" == "ATOM" ]
    then
        input=true
    elif [ "${line%% *}" == "END" ]
    then
        ((i++))                 # increase variable i by 1 == (i+1)
        rm -f "${i}_frame.data" # remove ${i}_frame.data" if exist
        input=false             # stop output lines until next ATOM
    fi
    if $input                   # if var INPUT is true add line to ${i}_frame.data file
    then
        echo "$line" >> "${i}_frame.data"
    fi
done < "$filename"

未来のためにsedヒント:

sed '/ATOM/,/END/!d;/END/d'
sed -n '/ATOM/{:;N;s/\nEND//;T;p}'

したがって、次の操作を実行できます。

nl -s'.frame.data' -b p"^END" coor.pdb | 
sed -n '/ATOM/{s/^/echo \"/;:;s/ \{6,\}//;N;s/END//;T;s/\n  */\">/p}' |
bash

答え2

Bashのテキスト処理が遅い。純粋なbash文字列操作は、すでに変数にあるテキストまたは非常に小さなファイルを読み取るのに役立ちます。コンピュータ生物学ファイルは一般にそれほど小さくないため、このようなツールを使用するための開始コストはawk最小限に抑えられますが、テキスト処理はbashよりはるかに高速です。

実際にファイルを分割したいと仮定すると、次のようになりますpdb

awk -v RS='\nEND\n' '{ fn="frame" NR ".pdb"; print > fn; close(fn) }' "$filename"

awkに\nEND\n改行の代わりに入力レコード区切り文字を使用させ、レコードカウンタを使用することもできます。出力レコード区切り文字はまだデフォルトですORS="\n"。 (Costasが非常に良い提案をしました。行の先頭になるようにEND修正し、フォームのclose多い入力にファイル記述子をあまり使用しないように追加しました。)

私の最初の考えは次のとおりです。

awk 'BEGIN{i=0; fn="frame0.pdb"}
     !/^END/ { print > fn; }
     /^END/{ close(fn); fn="frame" ++i ".pdb"; }' \
     "$filename"

awkはファイルハンドルをキャッシュするので、print > fnファイルを何度も閉じてもファイルは再開されません。 (close(fn)そうします。効率のためにのみ存在するので、awkは最終的に多くのファイルを開きません。)

ロジックは次のとおりです。各行全体を現在のファイル名で印刷します。行が表示されたら、END次のファイル名に移動します。最後の行の後に他の行がない場合、新しいENDファイル名は記録されず、残りの最後のファイルも生成されません。

OTOH、メモリ内の行ブロック配列で操作を実行したい場合:

# add a `!/^END/` condition to the concat block if you want to avoid a stray newline after each END
awk 'BEGIN{i=0}  
     !/^END/ { arr[i] = arr[i] $0 "\n"; }  # concat onto this array element
     /^END/ { i++; }
     END{for (k in arr) { print arr[k]; > ("frame" k ".pdb") } }' \
"$filename"

その後、ブロックのawk行配列を使用して必要な操作をすべて実行できますEND。正規表現機能に優れています。


sedbashを使用してsedを駆動しようとしましたが失敗しました(nvm、shellなど、一度に1バイトを読み取らないため失敗しましたread):

i=0
while true; do
    outf="frame${i}.data";
    ##### DON'T USE THIS, sed READS TOO MUCH #####
    strace -o sed.tr sed '/^END/q42' > "$outf";  # strace to see that the 2nd sed invocation finds the file empty
    ret=$?;
    ((i++));
    if [[ $ret == 0 ]];then  # sed didn't see END before EOF
        [[ -s $outf ]] || rm -f "$outf";  # clean up empty last file
        break;
    elif [[ $ret != 42 ]]; then
        echo some other sed error;
        break;
    fi;
done < "$filename"

関連情報