ファイルを行ごとに比較するシェルスクリプトをご教授ください。

例えば(master.txt)(a.txt)(b.txt)(c.txt)(ok.txt)(ng.txt)と4つのファイルがあり、
(master.txt)と(a.txt)の行を比較し(a.txt)の中のある行が(master.txt)の行と一致した場合(ok.txt)に
(master.txt)の行と一致しなかった場合(ng.txt)に入れる。
その後(master.txt)と(b.txt)の比較し(a.txt)が使用したものと同じ(ok.txt)or(ng.txt)に入れる・・・
といったように繰り返していくシェルスクリプトはどのようにして作成するのでしょうか?

txtファイルの中の行はランダムに入っており、x.txtの一行一行ををmaster.txtの全行と比較する必要があります。

自分で作ってみたものは、while文を2重で使い一行ずつ取り出しcase文で行が一致した物を(ok.txt)に入れる
所までは成功しているのですが、複数回繰り返す時にどのようなロジックで不一致行を(ng.txt)に入れる
ことができるのかが考え付きませんでした。

よろしくお願いいたします。

このQ&Aに関連する最新のQ&A

A 回答 (5件)

#1です。


うーむ。やりたいことがいまひとつわからないのですが・・・。
case文で比較したいってことがネックなのかな?

※以下はアルゴリズムとして捕らえてください。
シェルの種類とかバージョンで異なると思うし、検証した訳でないので・・・。
-----------------------------------------------------------------
cat /dev/null > ok.txt
cat /dev/null > ng.txt
for fn in a.txt b.txt c.txt ; do
while read line ; do
flg=0
while read line2 ; do
if [ $line = $line2 ]; then
echo $line >> ok.txt
flg=1
break
fi
done < $fn
if [ $flg -eq 1 ]; then
echo $line >> ng.txt
fi
done < master.txt
cp ng.txt master.txt
cat /dev/null > ng.txt
done
-----------------------------------------------------------------
    • good
    • 0

#です。

失礼しました。Solarisのカテゴリだったんですね。

fgrep -s -x "$data" master.txt

に訂正。
    • good
    • 0

Linux等gnuのgrepだとして、



for f in a.txt b.txt c.txt
do while read data
do if fgrep -s -q -x "$data" master.txt
then echo "$data" >> ok.txt
else echo "$data" >> ng.txt
fi
done < $f
done
    • good
    • 0

テンポラリファイルを使うとか, 予めソートしておくとか, 考えることはいろいろありそう.


その気になれば diff とかを駆使してできるのかもしれん.
    • good
    • 0

> 不一致行を(ng.txt)に入れる


最後までループして一致しなければ(ng.txt)にいれるじゃ駄目なのかな?

単純に1ファイルを引数とする子シェルを作って引数とmaster.txtを比較する。
その親シェルが(a.txt)(b.txt)(c.txt)をループで渡す。
とすれば、そんなに複雑な話じゃないと思うけど。

この回答への補足

すいません、書き間違えていました。
(a.txt)を(ok.txt)や(ng.txt)に入れるのではなく、(master.txt)を(ok.txt)や(ng.txt)に入れて、
2回目より(ng.txt)を(master.txt)にリネームして回そうと考えていたのですが、
(master.txt)を(ng.txt)に入れるロジックが分からないのです。

while文を2重で使いその内側のcase文にて一致を(ok.txt)に入れそれ以外を(ng.txt)にいれると
内側が回転する毎に(master.txt)の一致しない全ての行が(ng.txt)に入ってしまうので、
全て不一致だと(master.txt)の行×(a.txt)の行分(ng.txt)に入ってしまうことになり手詰まりになりました・・・

補足日時:2009/05/12 21:00
    • good
    • 0

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qsedの置換文字に変数を使用したいのですが・・・

あるファイルの特定の文字を変換し、上書きをする処理を行いたいのですが、sedの置換文字に変数が渡せなくて困っています。

例:
X="a"
Y="b"
echo test.txt | sed 's/${X}/${Y/g}' >test.txt

sedでは置換文字に${X}といった変数を使用することはできないのでしょうか?

Aベストアンサー

' ・・・' で囲まれた中の$はそのままドルマークです。変数展開をするなら、'・・・'で囲んではいけません。

何も囲まないか、"・・・"で囲むかです。

Qgrepで検索文字列が完全一致した行だけ取り出す方法

grepの文字列検索で検索文字列が単語として、完全一致した行だけ取り出す方法はないでしょうか?

通常は
grep hoge hoge.txt

と打つと、hogeが含まれる行が出力されますが、今回は含まれる行ではなくて完全に文字列が一致した行だけ取り出したいのです。

例えばhoge.txtの中に
cc ghoge
kkl hogem
jjll hoge
という3行があったとしたら最後の行でhogeという文字が空白で区切られた行だけ取り出したいのです。

何かよい方法があれば教えてください

Aベストアンサー

-w オプションじゃだめですか?

参考URL:http://www.linux.or.jp/JM/html/GNU_grep/man1/grep.1.html

Qsedなどで、特定の文字列の後の文字列を抽出したい

sedなどで、特定の文字列の後の文字列を抽出したい

シェルスクリプト内で、sedなどを使って特定の文字列の後の文字列を抽出したいのですが、どうすればいいでしょうか?

たとえば、abcXYZ123defghiのなかから、XYZの後の「123」を抜き出したいです。

echo abcXYZ123defghi | sed ...

のようにして実行させたいです。

Aベストアンサー

日本語対応sedだと日本語数字混じりでもできますね。

echo abcXYZ12357defghi | sed -e 's/^.*XYZ\([0-90-9]*\).*$/\1/' -e 'y/0123456789/0123456789/'
12357

※ 使っている日本語コードの指定は必要かも(例えば、 --ctype=EUC)

echo abcXYZ12357defghi | sed -e 's/^.*XYZ\([0-90-9]*\).*$/\1/'
12357

※ 入力フォームに書いている時はASCIIと日本語の判別がし易いけど回答見るとわかり難いですね。後ろの例での結果57とy/0123456789/の数字部分が日本語です。

Qシェルスクリプトでファイル内の数値文字列を数値として扱うには

失礼します。
シェルスクリプトでファイル内のテキスト(数値文字列)を取得して、それを使って計算するにはどうすれば良いでしょうか?

str:ファイル内のテキスト(数値文字列)

res=$(( $str + 1 ))

・エラー
")syntax error: invalid arithmetic operator (error token is "


よろしくお願いします。

Aベストアンサー

bashをご使用と判断して

res=$(( $str + 1 ))
ではなく、
res=$(( str + 1 ))
だと思います。

> exprもやってみたのですが、処理が遅くなるので使いません。
興味があって以下のシェルで検証してみました。

#!/bin/bash

str=1
i=0

echo 'Using $((str + 1))'
date '+%H:%M:%S.%N'

while [ $i -lt 10000 ]
do
str=$(( str + 1))
i=`expr $i + 1`
done

date '+%H:%M:%S.%N'

exit

$ ./test.sh
Using $((str + 1))
09:18:46.290418000
09:18:56.929345000
これをexprに書き換えたところ
$ ./test2.sh
Using expr
09:19:00.302748000
09:19:19.259990000

exprだと19秒ですが、$(( 演算 )) だと10秒程度なので、演算が多くなれば確かにexprは不利ですね。

bashをご使用と判断して

res=$(( $str + 1 ))
ではなく、
res=$(( str + 1 ))
だと思います。

> exprもやってみたのですが、処理が遅くなるので使いません。
興味があって以下のシェルで検証してみました。

#!/bin/bash

str=1
i=0

echo 'Using $((str + 1))'
date '+%H:%M:%S.%N'

while [ $i -lt 10000 ]
do
str=$(( str + 1))
i=`expr $i + 1`
done

date '+%H:%M:%S.%N'

exit

$ ./test.sh
Using $((str + 1))
09:18:46.290418000
09:18:56.929345000
これをe...続きを読む

QUNIXで、ディレクトリを比較して、ファイル・ディレクトリの存在を比較したい

やりたいこと自体は単純なことなのですが・・

UNIXのコマンドで、
2つのディレクトリA、Bを比較して、
Aだけにあるディレクトリ・ファイルのみを取得できるようなものを教えてください。
(ファイルが同一かどうかは問題ではなくて、あくまでも存在不存在の比較です。)

コマンドを組み合わせたり、オプションを使ってもかまいません。

diffコマンドってこういうとき使えないのでしょうか。

Aベストアンサー

( cd (ディレクトリA) ; find . -print |sort ) > A.txt
( cd (ディレクトリB) ; find . -print |sort ) > B.txt
としてディレクトリリストを作っておいて
diff A.txt B.txt | grep '^<' (Aだけにあるファイル)
diff A.txt B.txt | grep '^>' (Bだけにあるファイル)
でどうでしょうか?

QAWKで1項目以外を簡単に出力したい

awkのprint出力で$1$2$3$4$5$6と書くのではなく、$1から$6までを簡潔に指定できる方法はありませんか? (たとえば$1-$6のような)
すべて出力したい場合は$0でいいのですが、1項目だけ除外してあとはすべて出力したい時に項目が多い大変ですから。

Aベストアンサー

最後のフィールドだけ消せばいいのなら、組み込み変数NFをデクリメント
してやればいいです。$7=""だと中身が空のフィールドになるだけなので
余計なセパレータが出力されるように見えているのです。

ひょっとしたらNFをデクリメントしたあとで$0の再構築を
強制的にさせる必要があるかもしれません($0=$0 とか $1=$1で
やってくれるはず)。

Q2つのファイル(.log)を比較し、条件が満たしているレコード(行)を抽出する方法

初心者です。

2つのファイル(.log)を比較し、条件が満たしているレコード(行)を抽出する方法を教えてください。



Aファイル

11.111.111
22.222.222
33.333.333
44.444.444


Bファイル

11.111.111.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.333.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.222.222.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
33.333.355.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
33.333.555.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
44.444.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200




Cファイル
11.111.111.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.222.222.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
44.444.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200


このように、AファイルとBファイルを比較し、結果をCファイルに抽出したいです。
エクセルでVlookupの関数を使う方法もありますが、Bファイルは容量が重いのでエクセルデータでは全部読み
取ることができません。
よって、UNIXコマンドでなんとか作業をしたいものです。
commを使う作業もありますが、手作業で不要な部分を削除していくのも大変なのでできれば自動化が希望です。

パソコンのOSはWin2000です。

どなたかご知恵をお借りください。
よろしくお願いします。

初心者です。

2つのファイル(.log)を比較し、条件が満たしているレコード(行)を抽出する方法を教えてください。



Aファイル

11.111.111
22.222.222
33.333.333
44.444.444


Bファイル

11.111.111.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.333.444.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
22.222.222.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
33.333.355.1111 [01/Jun/2007:00:00:00 +0000] "GET / test.html 200
33.333.5...続きを読む

Aベストアンサー

> しかし、ひとつだけ今日トラブルがありました…
> 私も考えていなかったので、質問には書かなかったのですが、
> ユーザIDと同じ名前が対象URLの中に紛れこむときもあるということです。

ああ、あえてその可能性には言及してなかったのですがそういうデータもありですか。

awkでやるなら話は簡単で、検索対象のフィールドを限定すればいいです。
$0 を対象にすると行全体からマッチするものを探していしまいますから、
期待するIDフィールドのところ(提示されたデータで判断すると$3)だけ
注目すればいいです。

ただこうすると、せっかくIDで探すときもIPアドレスで探すときも
共通のスクリプトが使えていたのに分離しなくてはならなくなるので、
何かの形で簡単なコマンドをAファイルの一行目に書けるとかしとくと
いいかもしれません。スクリプトに対するコマンドラインオプションでも
いいですけど。

NR==FNR {
chkitems[$0]=1
next
}

↑ここまでは共通で、

IPアドレスで検索するなら
{
for (idx in chkitems) {
if (index($1, idx) > 0)
print
}
}
こうで、
ユーザーIDで探すなら

{
for (idx in chkitems) {
if (index($3, idx) > 0)
print
}
}

> しかし、ひとつだけ今日トラブルがありました…
> 私も考えていなかったので、質問には書かなかったのですが、
> ユーザIDと同じ名前が対象URLの中に紛れこむときもあるということです。

ああ、あえてその可能性には言及してなかったのですがそういうデータもありですか。

awkでやるなら話は簡単で、検索対象のフィールドを限定すればいいです。
$0 を対象にすると行全体からマッチするものを探していしまいますから、
期待するIDフィールドのところ(提示されたデータで判断すると$3)だけ
注目すれば...続きを読む

Q1行単位で差分を出力するテキスト比較ソフト

1行単位で差分を出力するテキスト比較ソフトを探してます。
2つのファイルで差異を比較するDIFFソフトは多いですが、
比較対象行の上下の順番が違うとうまく差分を検出してくれません。
処理速度は遅くなると思いますが、2つのファイル間で1行ごとに
差分を表示し、保存できるソフトがあれば紹介してください。
よろしくお願いします。

Aベストアンサー

・フリーソフトを使わなくても標準で『FC.EXE』がありませんか?
★『FC.EXE』で出来ると思う。
 このプログラムで2つのファイルを比較してその結果をリダイレクション出力で
 コマンドラインから実行するプログラムです。
 ファイルに保存すれば良いでしょう。なお、1行単位という事なのでオプションで
 『連続する最大不一致行』を1行に指定すれば良いと思います。
・下にその手順を記述します。

手順:
・『Windows』+『R』キーを押す。→『ファイル名を指定して実行』ダイアログ
・『cmd』と入力して『Enter』キーを押す。→コマンドプロンプトが起動
・『FC /L /LB1 /N /T ファイル1 ファイル2』と入力して『Enter』キーを押す。
 これで相違点が1行単位で画面に表示されます。その結果をファイルに保存
 するにはファイルにリダイレクトすれば良い。つまり
 『FC /L /LB1 /N /T ファイル1 ファイル2 >diff.txt』と入力して『Enter』キーを押す。
 ↑
 これで『diff.txt』に相違点が記録されます。
・あと『/LB1』オプションで上手くいかない場合は『/nnnnn』オプションにして下さい。
 つまり
 変更前⇒『FC /L /LB1 /N /T ファイル1 ファイル2』
 変更後⇒『FC /L /1 /N /T ファイル1 ファイル2』
 とします。

その他:
・注意事項としては実行するときのカレント・ディレクトリの位置に気をつけて下さい。
 コマンドプロンプトが起動した直後では『C:\Documents and Settings\ユーザ名』の
 フォルダに位置します。また、比較するファイルにパスを付けて比較します。
 ファイルはドラッグ&ドロップでコマンドライン入力に挿入できます。
・パスについて分からない場合はまたアドバイスしますので補足して下さい。
 とにかく特別なソフトは必要ないです。
 既にインストールされているはずのコマンドを利用してできます。
・以上。

『FC.EXE』のヘルプより:
2 つのファイルまたはファイル セットを比較し、相違点を表示します。

FC [/A] [/C] [/L] [/LBn] [/N] [/OFF[LINE]] [/T] [/U] [/W] [/nnnn]
[ドライブ1:][パス1]ファイル名1 [ドライブ2:][パス2]ファイル名2
FC /B [ドライブ1:][パス1]ファイル名1 [ドライブ2:][パス2]ファイル名2

 /A     相違する各部分の 1 行目と最後の行だけを表示します。
 /B     バイナリの比較を実行します。
 /C     英字の大文字と小文字を区別しません。
 /L     ファイルを ASCII テキストとして比較します。
 /LBn    連続する最大不一致行を指定行数に設定します。
 /N     ASCII の比較で行番号を表示します。
 /OFF[LINE] オフライン属性が設定されたファイルをスキップしません。
 /T     タブをスペースに変換しません。
 /U     Unicode テキスト ファイルとしてファイルを比較します。
 /W     連続した空白 (タブとスペース) を 1 つのスペースに圧縮して比較
       します。
 /nnnn   不一致発見後に確認する、一致すべき連続行数を指定します。
      
 [ドライブ1:][パス1]ファイル名1
       比較する最初のファイルまたはファイル セットを指定します。
 [ドライブ2:][パス2]ファイル名2
       比較する 2 番目のファイルまたはファイル セットを指定します。

・フリーソフトを使わなくても標準で『FC.EXE』がありませんか?
★『FC.EXE』で出来ると思う。
 このプログラムで2つのファイルを比較してその結果をリダイレクション出力で
 コマンドラインから実行するプログラムです。
 ファイルに保存すれば良いでしょう。なお、1行単位という事なのでオプションで
 『連続する最大不一致行』を1行に指定すれば良いと思います。
・下にその手順を記述します。

手順:
・『Windows』+『R』キーを押す。→『ファイル名を指定して実行』ダイアログ
・『cmd』と...続きを読む

Qバッチファイルで昨日の日付を取得

すみません、どなたか教えて下さい。

バッチファイルの記述で、昨日の日付を取得する方法を教えて下さい。
今日の日付は下記のように取得しています。

rem 日時変数の取得
for /f "tokens=1-3 delims=/" %%a in ('echo %date:~-10%') do (set YYYYMMDD=%%a%%b%%c
)

Aベストアンサー

:: ----- prevdate.bat はじめ -----
@echo off
::今日の日付を取得
set yy=%date:~0,4%
set mm=%date:~5,2%
set dd=%date:~8,2%
echo 今日は、%yy%年%mm%月%dd%日です。

::1日前の日付を計算する
set /a dd=%dd%-1
set dd=00%dd%
set dd=%dd:~-2%
set /a ymod=%yy% %% 4
if %dd%==00 (
if %mm%==01 (set mm=12&& set dd=31&& set /a yy=%yy%-1)
if %mm%==02 (set mm=01&& set dd=31)
if %mm%==03 (set mm=02&& set dd=28&& if %ymod%==0 (set dd=29))
if %mm%==04 (set mm=03&& set dd=31)
if %mm%==05 (set mm=04&& set dd=30)
if %mm%==06 (set mm=05&& set dd=31)
if %mm%==07 (set mm=06&& set dd=30)
if %mm%==08 (set mm=07&& set dd=31)
if %mm%==09 (set mm=08&& set dd=31)
if %mm%==10 (set mm=09&& set dd=30)
if %mm%==11 (set mm=10&& set dd=31)
if %mm%==12 (set mm=11&& set dd=30)
)
echo 昨日は、%yy%年%mm%月%dd%日です。

echo.
pause
:: ----- prevdate.bat おわり -----

参考URL:http://www.atmarkit.co.jp/fwin2k/win2ktips/419batchdate/batchdate.html

:: ----- prevdate.bat はじめ -----
@echo off
::今日の日付を取得
set yy=%date:~0,4%
set mm=%date:~5,2%
set dd=%date:~8,2%
echo 今日は、%yy%年%mm%月%dd%日です。

::1日前の日付を計算する
set /a dd=%dd%-1
set dd=00%dd%
set dd=%dd:~-2%
set /a ymod=%yy% %% 4
if %dd%==00 (
if %mm%==01 (set mm=12&& set dd=31&& set /a yy=%yy%-1)
if %mm%==02 (set mm=01&& set dd=31)
if %mm%==03 (set mm=02&& set dd=28&& if %ymod%==0 (set dd=29))
if %mm%==04 (set mm=03&& set dd=31)
if...続きを読む

Q文字列の一部を取り除きたい(awk?)

こんにちわ。

下記の文字列操作を行いたいのですが上手くいきません。
どなたか教えて下さい。
環境OSはsolarisです。
awkにはこだわりません。

・最初の2文字を取り除く
・最後の文字が"2"の場合取り除く


yoroshikune → roshikune
onegai2 → egai

Aベストアンサー

どうせなら、両方ともsedで、

sed -e 's/^..//' -e 's/2$//'

awkだと、
awk '{X=substr($0,3);sub(/2$/,"",X);print X}'


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング

おすすめ情報