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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Bash on Ubuntu on Windows

Bash on Ubuntu on Windowsは、Windows Subsystem for Linux(WSL)の非推奨の名称。Windows10およびWindows Server上でLinuxのバイナリ実行ファイルをネイティブ実行するための互換レイヤーです。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

AWK

AWKは、UNIX 上で開発されたプログラミング言語で、CSVファイルなどのテキストファイルの処理を目的にデザインされています。

Q&A

解決済

4回答

1559閲覧

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

s-o

総合スコア6

Bash on Ubuntu on Windows

Bash on Ubuntu on Windowsは、Windows Subsystem for Linux(WSL)の非推奨の名称。Windows10およびWindows Server上でLinuxのバイナリ実行ファイルをネイティブ実行するための互換レイヤーです。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

AWK

AWKは、UNIX 上で開発されたプログラミング言語で、CSVファイルなどのテキストファイルの処理を目的にデザインされています。

0グッド

1クリップ

投稿2020/02/13 10:58

編集2020/02/14 00:23

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文で繰り返し処理を行っている。

bash

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

実装したいこと

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takasima20

2020/02/13 11:27

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

2020/02/13 11:41

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

2020/02/13 15:16

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

2020/02/14 00:20

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

回答4

0

ベストアンサー

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

awk

1# test.awk 2 3BEGIN{ 4 n=0 5 while((getline < "regex.txt")>0){ 6 regexp[n]=$2; val1[n]=$3; val2[n]=$4; 7 n++ 8 } 9 10 FS = "\t" 11 OFS = "\t" 12} 13 14NR==1{$0,'"$key"'} 15NR>1{ 16 if(match($8, regexp[i])){ 17 for(i=0; i<n; i++){ 18 $0 = $0 substr($2, RSTART + val1[i], RLENGTH - val2[i]) 19 } 20 print 21 } 22} 23
$ awk -f test.awk input.txt

投稿2020/02/13 12:34

KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2020/02/13 12:47

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

2020/02/13 14:20 編集

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

2020/02/13 14:42

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

0

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

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

投稿2020/02/13 16:18

編集2020/02/13 16:34
takasima20

総合スコア7458

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

sh

1awk ' 2FS=="\t" && NR==1 {head=$0;next} 3FS=="\t" {all[NR]=$0;a[NR]=$2;N=NR;next} 4 5{ key=$1; regex=$2; val1=$3; val2=$4 6 head=head "\t" key 7 for(i=2;i<=N;i++){ 8 if(match(a[i],regex)){ 9 all[i]=all[i] "\t" substr(a[i],RSTART+val1,RLENGTH-val2) 10 } 11 } 12} 13END{ 14 print head 15 for(i=2;i<=N;i++){ 16 print all[i] 17 } 18} 19' FS="\t" input.txt FS=" " regex.txt

投稿2020/02/13 14:25

otn

総合スコア84499

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

手抜き解

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 12:39

winterboum

総合スコア23329

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

s-o

2020/02/13 14:13

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問