質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.33%

【Linux】txtファイル内容を変数とした繰り返し処理でawkを行いたい

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 742

s-o

score 6

txtファイル内容を変数とした繰り返し処理でawkを行いたい

環境はWindows Ubuntu ver.18.04.2 Linuxです。Linuxに触れて数日の初心者です。
下記の冗長なbashスクリプトを実装しました。
本来実装したいことは、
(1)awk内で繰り返し処理をするとき、別途ファイルを参照すること。
(2)awkで繰り返し処理をするとき、出力を再度入力にすること。です。

具体的に処理した下記コード欄を参考に概要は、
入力はタブ区切りtxtで、あるフィールド(f2)に情報が詰め込まれています。
ループ処理で別途切り出し用txtの列内容(1列目)を変数にしてawkのmatchで文字列の抜き取り、組み込み関数 (RSTART, RLENGTH)でf2の単語を入力ファイルの最後列(f101)に付け加え、タブ区切りtxtで出力(out.txt)する。
出力したtxtをmvでinput.txtに変更後awkの再入力とし、別途切り出し用txtの内容(2列目)でループ処理を続ける、というものです。
以下に例として、入力ファイル、別途切り出し用txtファイル、bashコードを示します。

使用するファイル(例)

#入力ファイルinput.txt(タブ区切りtxt)
f1 f2 ... f100
1 aaa=1.0;bbb=0.500;ccc=0.200 ... def
2 aaa=0.800;bbb=0.750;ccc=0.800 ... ghi
3  ... jkl
… 
100 aaa=0.004;bbb=0.000;ccc=0.008 ... xxx

#別途切り出し用ファイルregex.txt (スペース区切りtxt)
r1 regex1 val1 val2
r2 regex2 val3 val4
r3 regex3 val5 val6
…
r10 regex10 valX valY

実装済の冗長ソースコード

awkの外側の外部ファイル参照によるwhile read文で繰り返し処理を行っている。

while read key regex val1 val2 ; do
    cat ./input.txt | awk '
        BEGIN{
            FS = "\t"
            OFS = "\t"
        }
        NR==1{$0,'"$key"'}
        NR>1{
            if(match($2, /'"$regex"'/)){
                print $0,substr($2, RSTART + '"$val1"', RLENGTH - '"$val2"')
            }else{print $0,"0"}
        }
    '>./out.txt #同名の./input.txtだと処理がうまくいかない(空白のテキストができる)
    mv ./out.txt ./input.txt #処理に時間がかかるがout.txtとして1度出力し、mvで名前を変更した。
done < ./regex.txt

実装したいこと

awkの中で別途切り出し用txtを変数に入れ繰り返し処理をする。そうすることで、下記のawk再入力問題は解決できる?気がするため。
もしくはawkの外側のwhile leadで繰り返しながら、awkの出力をそのまま再入力にする。
awkでできないなら、触れたことがありませんがperl等でも構いません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • takasima20

    2020/02/13 20:27

    mv を無くして awk 内で完結したいってこと? あと、いまのままだとループごとに1行づつ減るよね?

    キャンセル

  • s-o

    2020/02/13 20:41

    >mv を無くして awk 内で完結したいってこと?
    その通りです。省きましたが入力ファイルは複数あるため、無駄な処理(mv)をなくしたいです。
    >いまのままだとループごとに1行づつ減るよね?
    NR>1の部分でしょうか?省きましたが、NR==1の処理も行っております。申し訳ございません。軽く修正します。

    キャンセル

  • takasima20

    2020/02/14 00:16

    ついでに… match しなかった場合もなんか入れとかないと、項目数的に整合性とれなさそげ。

    キャンセル

  • s-o

    2020/02/14 09:20

    ifのelse処理もあえて省いていました。申し訳ございません。
    本来の質問内容に該当する部分を抽出して示していました。
    念のため修正します。

    キャンセル

回答 4

checkベストアンサー

+2

私だったらこんな感じにします。
ちゃんとテストしてないので細かいミスがあるかもしれません。あくまで雰囲気を示します。

# test.awk

BEGIN{
    n=0
    while((getline < "regex.txt")>0){
        regexp[n]=$2; val1[n]=$3; val2[n]=$4;
        n++
    }

    FS = "\t"
    OFS = "\t"
}

NR==1{$0,'"$key"'}
NR>1{
    if(match($8, regexp[i])){
        for(i=0; i<n; i++){
            $0 = $0 substr($2, RSTART + val1[i], RLENGTH - val2[i])
        }
        print
    }
}
$ awk -f test.awk input.txt

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/13 21:47

    別解書こうと思ってたら先に書かれてしまった

    キャンセル

  • 2020/02/13 23:10 編集

    迅速なご返答ありがとうございます。明日、挑戦してみます。
    getlineで別途切り出し用ファイルを配列として渡すのですね。
    さらに配列の最終行取得のためwhileとnを使用するということでしょうか。とても勉強になります。
    その後、match以降は配列を使用して抜き出しを行い、行単位に対してfor文で配列を回しているという解釈でしょうか。
    ありがとうございます。とてもすっきりしました。
    現行のスクリプトとご提示いただいたスクリプトで実行速度がどう変化するか確認してみます。
    また、当スクリプトですが、入力ファイルが同一ディレクトリ内に複数ある場合の処理も考えています。
    ls *.txtでパイプで渡せられるかどうか、明日いろいろ試してみます。

    キャンセル

  • 2020/02/13 23:42

    KojiDoi解が良いと思います。
    私の方法は、二つのfileのどちらが最終結果か返還回数で決まるのでが厄介です。
    KojiDoi解を
    for f in inputfiles inutfile2 ..... ; do awk -f test.awk $f > $f.out ;done

    regex.txt が巨大だと問題がでるかも、ですが

    キャンセル

+1

手抜き解

mv を無くすだけでよければ

f0=./input.txt
f1=./work.txt
while read key regex val1 val2 ; do
    cat $f0 | awk ...
        :
        }
    '> $f1
    f=$f0
    f0=$f1
    f1=$f
done < ./regex.txt

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/13 23:13

    このような回し方もあるのですね。納得しました。
    mvをなくせそうですね。明日試してみます。

    キャンセル

+1

テスト可能なデータが書かれてないので、テストしてませんが、こんな感じで。
input.txtの方を全部変数に読み込んで、regex.txtの方を読みながら書き換えていきます。

awk '
FS=="\t" && NR==1 {head=$0;next}
FS=="\t" {all[NR]=$0;a[NR]=$2;N=NR;next}

{ key=$1; regex=$2; val1=$3; val2=$4
  head=head "\t" key
  for(i=2;i<=N;i++){
   if(match(a[i],regex)){
     all[i]=all[i] "\t" substr(a[i],RSTART+val1,RLENGTH-val2)
   }
  }
}
END{
  print head
  for(i=2;i<=N;i++){
    print all[i]
  }
}
' FS="\t" input.txt FS=" " regex.txt

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

regex.txt の内容が変わらないのなら、それから awk スクリプトを生成しておく方法も

awk1.tpl

BEGIN{
    FS = "\t"
    OFS = "\t"
    getline
    print $0,"key"
}


awk2.tpl

    if(match($2, /regex/)) {
        line = line,substr($2, RSTART + val1, RLENGTH - val2)
    }


awk スクリプト生成

sed 's/key/`cut -f 1 regex.txt | xargs | tr " " "\t"`/' awk1.tpl >hoge.awk
echo '{' >>hoge.awk
echo '    line = $0' >>hoge.awk
cat regex.txt | \
while read key regex val1 val2
do
    sed 's/regex/'$regex'/;s/val1/'$val1'/;s/val2/'$val2'/' awk2.tpl >>hoge.awk
done
echo '    print line' >>hoge.awk
echo '}' >>hoge.awk


実行はこんな感じで

cat input.txt | awk -f hoge.awk >result.txt


※動かしてないので考え方だけ参考にしてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る