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

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

新規登録して質問してみよう
ただいま回答率
85.35%
シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

3回答

1386閲覧

シェルスクリプト格納されてるファイルの指定の行に重複してる文字列があるかないかの結果を出したい

rookieman

総合スコア2

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

0クリップ

投稿2020/11/01 11:08

編集2020/11/03 07:54

fooファイルで行いたい処理フロー

手順1.対処のフォルダにhogeファイルがあるかfooで確認する(以降はあった場合の手順となる)
.IDの初回登録の場合 受け取ったhogeファイルのの内容は以下になります。

1行目から20行目まではID以外の内容が記載されている。 IDがある行は21-23行に存在 # aaaaa = txxxxx,txxxxx # bbbbb = txxxxx,txxxxx # ccccc = txxxxx,txxxxx 24行以下にはID以外の内容が記載されている

手順2.fooで初回のID登録か判断する(先頭に#があるとき初回登録扱い)
・先頭に#があればtrue

手順3.追加するIDがあるか確認する(手順2がfalseのときの処理)
・hogeファイルのグループとIDの行を取得
* IDを追加する場合 追加するIDがあるかの確認
・IDがリストにあればtrue

* IDを削除する場合 削除するIDがあるかの確認

   ・IDがリストになければtrue

手順4.手順2.3でファイルの内容を確認して内容を変更していくか判断する
・手順2がtrueもしくは 手順3がfalseの場合以降の手順を進める
・内容の変更がない場合はwarningを表示させて終了

手順5.hogeファイルの内容を変更する前に hoge.yyyyMMddHHmmSS.sssの形で同じ階層にバックアップを作成

手順6.手順2でtrueだった場合初回登録とみなす
・IDを追加する前に先頭にある#と半角スペース =後ろにある txxxxx,txxxxxを削除し aaaaa=の状態にする

手順7.ここでIDリストの追加と重複削除処理を行う
* 手順3で受け取ったIDリストを配列に変更(カンマを半角スペースに変える)
* 変更した配列で削除追加処理を行う
* 追加削除した配列をカンマ区切りの文字列に戻す。
* 追加削除し終えた行をhogeファイルの元の行に置き換える

以上のフローを実行できればと思っています。

ーーーーーーーーーーーーーーーーーーーー

※初回登録かそうではないかのtrue falseは確認できる状態です※

hogeが更新されるごとにグループが持つのIDが追加される
稀に重複したIDが追加されるため重複してるかしていないかの確認処理をfooで行いたい。

・使用するファイルは2つあります。
①テキストファイルhoge(グループとIDが追加されていく)
②テキストファイルfoo(重複があるかないかを確認、あった場合hogeのバックアップを作成し、その後重複の処理を行う)

・テキストファイルhogeの内容は以下(初回登録ではない場合)になります。
(例)グループ名 = ID,ID,ID,ID

aaaaa = t11111,t22222,t33333,t44444,t55555 bbbbb = t11111,t22222,t33333,t44444,t55555 ccccc = t11111,t22222,t33333,t44444,t11111

以下は修正前と修正後のhogeファイルの内容となります。
IDリスト記載の行のみ抜粋(修復するIDを増やしています)

修正前hogeファイル(重複がある場合)

aaaaa = t11111,t22222,t33333,t44444,t55555 bbbbb = t11111,t22222,t33333,t44444,t55555 ccccc = t11111,t22222,t33333,t44444,t11111,t22222,t99999

修正後hogeファイル

aaaaa = t11111,t22222,t33333,t44444,t55555 bbbbb = t11111,t22222,t33333,t44444,t55555 ccccc = t11111,t22222,t33333,t44444,t99999

経験が浅く見当違いな質問でしたら申し訳ございません。
説明不足かとは思いますが、、、
ご意見、ご回答教えていただきますととても助かります。
宜しくお願い致します。

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

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

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

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

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

hidezzz

2020/11/01 11:19 編集

hogeは入力ファイルになると思うのですが、fooは出力ファイルですか?それとも最終的な出力を行うために作る中間ファイルのようなものですか? fooは具体的にはどのような書式を想定していますか?fooとは別に最終的な出力があるとしたらそれはどのような書式を想定していますか? > 重複チェックには正規表現を用意てマッチできればと思います。 重複チェックであればID同士を完全一致でマッチさせれば良いような気がしますが、正規表現でマッチとはどのようなマッチング処理を想定していますか?
rookieman

2020/11/01 13:37

hogeが更新されるとグループごとにID追加されていくのですが、稀に同じIDが追加されることがあるので、hogeが更新されるごとに、IDの重複があるかの確認処理fooでを行い、hogeの内容に変更があればfooでhogeの加工をする流れを想定しています。 ですのでfooは中間ファイルのようなものとなります。 hoge受け取る→fooでhogeの指定した行に重複文字列があるか確認する 重複がなければ、true  のような形にしたいと思っています。 また、ID同士を完全一致させる考えがありませんでしたのでそちらで作成してみます。 正規表現は訂正いたします。 お忙しいところ、お時間をさいてくださり有り難うございます!
rookieman

2020/11/04 01:08

hidezzzさんの下さったアドバイス大変助かりました。 有難うございます。 また、大変申し訳ありませんが、ベストアンサーにはKojiDoiさんの回答を選ばせていただきたいと思います。
guest

回答3

0

ベストアンサー

補足コメントを見ても、どのようにfooを使いたいのかが全く理解できませんが、要は、hogeにIDの重複がある場合はそれを解消する方向に修正したいということでしょうか。

cp hoge hoge.bak awk -F " *[=,] *" '{delete n; printf "%s=%s", $1,$2; n[$2]=1; for(i=3;i<=NF;i++){n[$i]++; if(n[$i]<=1) printf ",%s",$i;} printf "\n"}' hoge.bak > hoge

追記

だいぶん話が違っていたことがわかってきたので、改めて。

hoge.txt

# aaaaa = t11111,t22222,t33333,t44444,t55555 aaaaa = t11111,t22222,t33333,t44444,t55555 bbbbb = t11111,t22222,t33333,t44444,t55555 ccccc = t11111,t22222,t33333,t44444,t11111,t22222,t99999

test.awk

BEGIN{ FS = " *[=,] *"; } /^#/{ sub(/^# */, "", $1); printf "%s=\n", $1; next; } { delete n; printf "%s=%s", $1, $2; n[$2]=1; for(i=3; i<=NF; i++){ n[$i]++; if(n[$i]<=1) printf ",%s", $i; } printf "\n"; }

実行

$ awk -f test.awk hoge.txt aaaaa= aaaaa=t11111,t22222,t33333,t44444,t55555 bbbbb=t11111,t22222,t33333,t44444,t55555 ccccc=t11111,t22222,t33333,t44444,t99999

投稿2020/11/01 15:00

編集2020/11/03 20:16
KojiDoi

総合スコア13692

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

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

rookieman

2020/11/02 00:56

説明力のなさ知識の少なさをを痛感しております、、 申し訳ございません。 おっしゃる通りでございます! 参考にさせていただきます。 有り難うございます。
guest

0

hogeとfooの関係が質問文ではゴチャゴチャして不明です。

とりあえず、IDが英数字からなる前提で、重複のある行を抜き出すのは、

sh

1egrep '=.*\b(\w+)\b.*\b\1\b' hoge

です。

投稿2020/11/01 11:37

otn

総合スコア85901

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

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

rookieman

2020/11/01 13:13

ご指摘ありがとうございます。 内容をまとめて編集したします。 また、egrep のご提案ありがとうございます。 試してみます!
guest

0

やろうとしている処理がよく分からなくて一部こちらで勝手に問題設定している感じではありますが、
以下のようなスクリプトを使用すればhogeからID重複しないようにしたファイルを出力することは出来ます。

↓「idfilter.awk」というファイル名で保存してください。

awk

1#! /usr/bin/awk -f 2BEGIN { 3 # これはデフォルト値と同じだが、この値でないと正常動作しないため明示的に指定する。 4 FS = " " 5} 6{ 7 # この時点で $1=(グループ) $2="=" $3=ID,ID,ID,... 8 9 # IDをカンマで分割する。 10 split( $3, in_ids, "," ) 11 12 # in_ids から重複を取り除いた ids を取り出す。 13 delete dupid 14 delete ids 15 j = 0 16 for ( i in in_ids ) { 17 id = in_ids[ i ] 18 19 # まだ登場していないID? 20 if ( dupid[ id ] == "" ) { 21 ++dupid[ id ] 22 23 # まだ登場していないIDの場合 idsへコピーする。 24 ids[ ++j ] = id; 25 } 26 else { 27 # 重複を検出したら標準エラー出力へ出力する。 28 print "重複検出: グループ名=" $1 " ID=" id > "/dev/stderr" 29 } 30 } 31 32 # 処理した結果を出力 33 printf $1 " " $2 " " 34 f = 0 35 for ( id in ids ) { 36 37 if ( f == 0 ) { 38 f = 1 39 } 40 else { 41 printf "," 42 } 43 44 printf ids[ id ] 45 46 } 47 print "" 48}
$ cat hoge aaaaa = y11111,y22222,y33333,y44444,y55555 bbbbb = u11111,u22222,u33333,u44444,u55555 ccccc = t11111,t22222,t33333,t44444,t11111 $ awk -f idfilter.awk hoge >hoge.new 2>hoge.err $ cat hoge.new aaaaa = y11111,y22222,y33333,y44444,y55555 bbbbb = u11111,u22222,u33333,u44444,u55555 ccccc = t11111,t22222,t33333,t44444 $ cat hoge.err 重複検出: グループ名=ccccc ID=t11111

投稿2020/11/01 15:28

編集2020/11/01 15:44
hidezzz

総合スコア1248

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

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

rookieman

2020/11/02 01:05

煩わしさを感じさせる内容で申し訳ありません、、 また、改善策のご提案してくださり有り難うございます!! 重複しないようにしたファイルの出力をするというよりも、 「重複しているか確認」ができればと思っています。 重複があれば、hogeバックアップ作成→ 既存のhogeにある重複ID削除 重複がなければ、処理は終了 ができればと思っています。
hidezzz

2020/11/02 01:38

であれば、回答の実行例で言うhoge.errを参照してhogeを手で修正すれば良いんではないでしょうか? その修正結果はhoge.newと同じものになるとは思いますけど。
hidezzz

2020/11/02 02:09 編集

前のコメントを言い換えると、 > 「重複しているか確認」ができればと思っています。 > 重複があれば、hogeバックアップ作成→ 既存のhogeにある重複ID削除 > 重複がなければ、処理は終了 ということであれば、回答の実行例の後に、 (1) hoge.errを見て重複を確認 (2) もし重複があったら、hoge.newの変更内容が妥当であることを確認した上で以下のコマンドを実行 $ mv hoge hoge.bak $ mv hoge.new hoge という手順を行えば良いと思ったのですが、認識は合ってますか?
rookieman

2020/11/02 02:39

お忙しい中有り難うございます。 手作業での修正ではなく、確認し削除処理までをフロー化したいと思っております。 質問させていただきたい部分としましては、 削除までの実行処理ではなく、重複してるかしていないかの確認処理になります。 実行するのではなく処理として作成していです。
hidezzz

2020/11/02 03:35

なんか話が噛み合っていないような気がします。 回答で提示した実行例を見て、awkスクリプトがどのような処理をしているかを理解されていますか? awkスクリプトには重複の検出をする処理は入っていてスクリプトを改造すれば重複検出処理だけを取り出すことは可能です。 awkスクリプトをどのように改造すれば良いかという方向でやり取りを進めていくことは可能ですか? (それともawkスクリプトが理解不能などの理由で、別の方向での解決方法の提示を求めていますか?)
rookieman

2020/11/02 05:46

awkの知識があまりなく、違う手順で進めようとしていました。 foo内で if ["` echo $c | egrep `"] echo '重複ある' else echo '重複ない' fi のような形を想定しており、egrepの条件式さえうまくかければ、判定できるものだと 思っていたのですが、それが見当違いな考えでしょうか?
KojiDoi

2020/11/02 06:38

既に回答があるように「重複かあるか否か」をegrepで判定することはたしかに可能でしょうが、結局重複処理に回すのであれば、「どれが重複していているのか。」「重複しておらず温存すべきはどれか」をそこでもう一度判別せざるを得ません。つまり、わざわざfooなる中間ファイルを作る方式は全くの二度手間=無駄だと思います。
KojiDoi

2020/11/02 06:42

ちなみに私の回答とhidezzzの回答は、awkの中で行内の文字列を分割して連想配列を使って重複チェックするという発想をしている点で全く同じです。hidezzzさんはものすごく丁寧に書いていて、私は一切のムダを省こうとしているので、一見全く違う処理をしているように見えるかもしれませんが。
rookieman

2020/11/02 07:03

お忙しい中アドバイスくださり有り難うございます! fooではIDの重複チェックの他にも、hogeが新たに格納されたかのチェック、初回のID登録なのか、初回登録ではないのであれば 新たにIDが追加されたのかetc..などの他の確認処理も入れているので、 中間ファイルは必須と考えています。。 hogeファイルにコマンドで実行するのは避け、 1.中間ファイルで確認処理 2.変更があれば バックアップの作成 3.foo内でhogeの内容を加工 4.加工した行をhogeに置き換え の順を想定しています。 hidezzzさんKojiDoiさん含め皆さんの意見とても助かっています。 私の技量のなさで、何度も説明させてしまい、申し訳ございません。。
hidezzz

2020/11/02 11:10

とりあえず、「中間ファイル」の中身がどのようなものにしようとしているのかが明確になれば話がもう少し進むような気がします。
rookieman

2020/11/03 06:41

返信が遅くなり申し訳ございません。 内容を編集してみたので、ご確認いただければと思います。 一連のフローを記載してみました。 何度もご解説、問題の改善へむけてご協力してくださり本当に有り難うございます!
KojiDoi

2020/11/03 07:06

とりあえず一つ質問します。あなたがfooなる中間ファイルが必須だと信じているのは、過去の行の内容が現在注目中の行のIDの新規非新規の判断に影響するはずだからと考えているから、ということで合ってますか?
KojiDoi

2020/11/03 07:08

それと、できれば、修正前・修正後のhogeファイルの例を示してもらえると話が早いと思います。
rookieman

2020/11/03 08:00

質問に対して... 中間ファイルとしての役割は、その通りです。記載の手順で作成願いを出されていたのでそれを中心として考えていました。。。 fooは内容確認、内容変更、の処理を行うには必要ないファイルになりますでしょうか? 修正前・修正後のhoge ファイル記載しました! 宜しくお願いいたいます。
KojiDoi

2020/11/03 08:07

肝心なところがわからないのですが。初回のIDとかの判断とその処理をどうするのかです。 そして、現時点で追記された内容に関する限りは、私が示したawkスクリプトで完全に修正できています。つまりfooなるファイルは不要で、余計な手間をかけようとしていると言わざるを得ません。
rookieman

2020/11/03 11:24

初回のID の場合は#が先頭にあるかないかの判断をし手順4以降の処理に進みます。 なるほど、、、 awkに関してもう少し調べてみます!! お教え下さった内容を参考にさせていただきます。
hidezzz

2020/11/03 12:43

更新された質問を見ましたが、fooファイルの中身に関する具体的な記述が見られないためどのような使われ方をするのかがよく分かりません。 fooファイルの初期状態がどのような中身で、処理の過程でどのような状況でどのように書き換えていくのかについても記述を追加してみてください。
rookieman

2020/11/03 15:18

fooファイルの中身は質問に追加した手順を実行していく処理のみの内容になります。 fooファイルの中身を書き換えることはないです。 ↑はhidezzzさんの質問への回答になっていないでしょうか。。。? fooファイルで実行していく処理を追加してくださいとのことですか?
KojiDoi

2020/11/03 15:26

我々は初手から重大な誤解をしていたわけなんでしょうか。fooファイルは中間データを格納するための一時ファイルと考えていましたが(中間ファイルだと明言されていましたし)、シェルスクリプトか何かを考えていたということでしょうか?
KojiDoi

2020/11/03 15:33

あと、手順2と、手順6についてですが、hogeファイルには始めから行頭に#がついている行とついてない行が混在しているということですか? そして、行頭に#がついていた場合は、その行の内容のいかんを問わず=以降の文字列を削除する処理をするということですか?
rookieman

2020/11/03 15:42

大変申し訳ございません。fooファイルには中間データは格納せずに、hogeファイルの内容を加工していく処理のみになります。。
rookieman

2020/11/03 15:53

IDの登録が一度も行われていない場合は、全てのグループ名の行頭に#がついている状態になります。 ですので最初のID登録の際、グループaaaaaとcccccにはIDの追加があったとき、加工後の状態は aaaaa = t11111,t22222 # bbbbb = txxxxx,txxxxx ccccc = t11111,t22222 のような形になります。 (グループbbbbbまだ初回登録はしていない状態) また、行頭に#がついていた場合は内容問わず、 (グループ名 = )の形にします。
KojiDoi

2020/11/03 20:25

> fooファイルには中間データは格納せずに、hogeファイルの内容を加工していく処理のみになります。。 私が知る限り、そのようなファイルのことを「中間ファイル」と呼ぶことはありません。中間ファイルといえば、処理途中の中間加工品を一時的にためておくファイルのことを普通は指します。道理で話が噛み合わないはずです。 私やrookiemanさんが示したawkファイルがfooに相当するということですね。 始めは無かった「行頭に#」云々の話も考慮して、回答を追加しておきました。
rookieman

2020/11/04 00:38

話を変な方向に向かわせてしまい申し訳ありません。 ご指摘くださり、有難うございます! また、こちらでもawkファイル作成し、実行結果確認することができました。 有難うございます!! awkにはまだ疎く、22行から24行のみに回答してくださった処理を実行させる場合はどうすればいいのでしょうか。少し調べてみたのですが、、、
rookieman

2020/11/04 01:02

また、KojiDoiさんの回答をベストアンサーにさせていただきます! 他皆様も大変助かりましたが! 間違って別の方を選択してしまってのでKojiDoiさんに変更したします。
rookieman

2020/11/04 01:30

もう一点質問させてください、実行後の結果が現在 aaaaa= aaaaa=t11111,t22222,t33333,t44444,t55555 の状態となりますが、 aaaaa = aaaaa = t11111,t22222,t33333 とaaaaa半角スペース=半角スペースt11111,t22222,t33333 の状態にするにはどうすればいいでしょうか。 重ねて申し訳ございません。
KojiDoi

2020/11/04 04:36

>半角スペース=半角スペース printf "%s = %s", $1, $2; のように修正すればよいです。 >22行から24行のみに NR>=22 && NR<=24{やりたい処理} あとはawkのマニュアルを見てください。
rookieman

2020/11/04 04:52

KojiDoiさん ご回答くださり本当に有難うございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問