Tag: linux Tag: shell Tag: perl Tag: apache Tag: bash Tag: フィルタコマンド Tag: gnuplot Tag: awk

Linuxのフィルタコマンド

公開するシステムの場合は、SQLインジェクション、OSコマンドインジェクションに十分な注意と対策をしてください。

  • 2009.11.27 strings、xargs 追加
  • 2010.03.10 sedのサンプル2つ追加
  • 2010.11.16 awkを追加
  • 2011.02.28 pasteを追加
  • 2011.07.08 trとsedとサンプルを追加
  • 2011.07.28 awkのサンプル1つ追加
  • 2011.08.05 awkの出力時にシングルコーテーションを使う場合とサンプル追加
  • 2011.08.21 書籍紹介を追加
  • 2011.08.26 sedのサンプル追加
  • 2011.08.26 nkfを追加
  • 2011.09.01 head,cat,wc,ddを追加
  • 2011.09.11 splitを追加
  • 2012.06.13 uniqを追加
  • 2012-06-20 「grepとsedで手作業」を追加
  • 2013-10-10 「複数のキーを利用したソート」について追加
  • 2013-10-10 「ファイル一覧からサイズと名前を取り出す」を追加
  • 2015-07-26 sedで1行のみ表示する例を二つ追加。awkページリンクを追加
  • 2016-05-04 ed,exコマンドの説明を追加
  • 2016-05-06 ssconvertの説明を追加
  • 2017-02-28 「ファイル一覧の最初と最後を取得」を追加
  • 2020-02-03 http://dbweb.0258.net/wiki.cgi より

シェルはbashを想定しています

Linuxで使えるフィルタコマンド色々。

 Linuxではテキストファイルを加工する便利なコマンドがあります。これらのコマンドをパイプ記号「|」で繋げることで、ログの解析をはじめとして様々な処理が出来る(はず)。以前にも別のページで紹介してかもしれませんが、一部を紹介します。コマンドのオプションなど詳細は、「linux コマンド名」でネットを検索したり、Linuxのmanコマンド、コマンド自体の説明(コマンド --help)で確認して下さい。

 apacheのログファイルでエラーのある行を検索するなどして使い方を練習し、CSVファイルで活用出来るようになれば、あとはPerlなどでCGIと組み合わせると、社内で便利に活用できるようになると思います。

 フィルタで使えると言う事は、標準入力、標準出力に対応しているコマンドと言う事になりますね。ファイルの分割や圧縮なども標準入力、標準出力で活用すると便利そうなものも書き留めておこうと思っています。


head

head ファイル名

 テキストフィルの先頭から指定行数を表示する。行数は「-n 行数」と指定するが、「head -n -行数 ファイル名」とすると、先頭から最後の指定行数を除いて表示する。

head -n -2 ファイル名

なら、最後の2行以外全部となります。


tail

tail ファイル名

ログの最後を手早く見るときに便利。また、-fオプションでログの簡単な監視に便利。

tail -n +2 ファイル名

とすると、2行目から最後までと指定可能


sort

テキストファイルをソートする

-t 区切り文字の指定
-k ソートするキーの番号(列)の指定
-n 文字を数値として並べ替える
  • 複数のキーを利用したソート
    • 次のようなデータをソートする場合、ですが、1-1,1-2,1-3,1-11,1-12,2-2,2-3,2-11とハイフンをキー(フィールド)の区切りとして数値として並び替える場合は、オプションの指定は、「-n -t'-' -k1,1 -k2,2」となります。「-n -t'-' -k1,2 」だと思ったように並びません。複数のキーをソートに使う場合は注意が必要です。
1-1
1-11
1-12
1-2
1-3
2-11
2-2
2-3


grep

テキストファイルを検索

  • grep -v "^ *#" /usr/local/samba/lib/smb.conf | grep -v "^ *$" ・・・ Sambaの設定ファイルより空白行とコメント行と思われる内容を取り除いて標準出力へ出す。


egrep

grepの拡張版。検索パターンのバリエーションが正規表現


tac

テキストファイルを改行を単位に逆順にする。
ログファイルで利用すると、新しいログが最初に表示される。


cut

区切り文字を指定してフィールド単位にテキストファイルから情報を抜き出す。

  • cut -d, -f1,3,4,12 targetfile.csv ・・・ targetfuke,csv(カンマ区切り)で1,3,4,12列のみ表示する。
  • cut -d, -f3- targetfile.csv ・・・ targetfuke,csv(カンマ区切り)で左2列をカットして表示する。

※切り出す列を指定出来ますが、順序を入れ替えることは出来ません。入れ替えがある場合は、awkなどを使って下さい。


rev

テキストファイルにて行で文字を逆順に並べる。
何に使うのかな。


tr

文字列の置換を行なう。改行コードの変換に便利。

  • tr ',' '\t' <inputfile.csv >outputfile.txt ・・・ inputfile.csvのカンマをタブ文字に変換する。
  • tr -d '\"' <inputfile.csv >outputfile.txt ・・・ inputfile.csvのダブルコーテーションをカットする。
  • tr "hoge" "HOGE"
    は、標準入力のデータの全てのhをHに、oをOに、gをGにeをEに変換するもので、hogeをHOGEにするのと違う。


sed

テキストファイルの様々な加工が可能。

  • sed -n '10,20p' ファイル名 ・・・ テキストファイルの10~20行を表示。
  • sed -n '50000p' ファイル名 ・・・ 5万行目の1行のみ表示。
  • sed '50000!d' ファイル名 ・・・ 5万行目の1行のみ削除しないで表示。
  • sed '1,10s/FFDDEE/FFFFDD/' test.html ・・・ test.htmlファイルの1から10行目までに出てくる'FFDDEE'を'FFFFDD'に変換した内容を標準出力に出す。
  • sed -n '2,$p' inputfile.txt ・・・ inputfile.txtの2行目から最後までを表示。これは、 sed -e '1d' inputfile.txt ど同様。
  • sed -e '1,2d' -e '$d' テキストファイルの名前・・・ファイルから1行目、2行目、最終行をカットする。sed '1,2d' テキストファイルの名前 | sed '$d' としてパイプを使っても結果は同じになる。なお、1,2行目を飛ばして3行目から表示するのであれば、その部分は、「tail -n +3 ファイル名」としても良い。
  • sed '/^$/d' ファイル名・・・空の行を削除
  • sed '/^#/d' ファイル名・・・行頭が「#」の行を削除
  • sedをパイプで3つ繋げて
    (123:221)(124:234)(126:233)(127:227)・・・
    のようなファイル a.txt を
    sed 's/)(/\n/g' a.txt | sed 's/:/ /g' | sed 's/[()]//g' >b.txt
    とすれば、
    123 221
    124 234
    126 233
    127 227
     :
     :
    といった変換が可能。・・・Gnuplotで使うデータに加工した。
    gnuplotでは、
    $ gnuplot
    で対話形式のコマンドを起動し、
    gnuplot> plot "b.txt" using 1:2
    といった感じになる。
    gnuplotについては、リンク程度しかありませんが、→GNUPLOT?
  • テキストファイル中の「public.」をカットする
    sed 's/public\.//g' pg_dump_sample.dat > henkou.dat
    PostgreSQLの8.3.xのデータで比較的単純なものを、7.2.3のスキーマの無いシステムへ持っていくときに利用。なお、pg_dumpではオプションで「--inserts」を付加し、COPY でなく、INSERT INTO でデータを追加するようにした。処理はCOPYの時より遅くなる。


  • 任意の複数の行を行指定で大きめのファイルから取り出す

例えば、hogehoge.sed の名前で以下の内容を作成し、

100p
2002p
3001p
1000p
99p
  • -f オプションで以下の様にこのファイルを指定することで、5行を一度に表示することが出来る。一つずつ実行するより処理時間が大きく短縮可能。
sed -n -f hogehoge.sed 処理したいテキストファイル

ただし、処理するテキストファイルの順になります。
簡単な例では、

sort hogehoge.txt | sed -n -e 1p -e \$p -e 2p

結果は1行目、2行目、最終行となります。これを1行目,最終行,2行目と出来ないのかな


  • テキストファイルの指定行を置換
    • 2017-02-28
sed 100d hogehoge.txt | sed '100i sample0123456'

hogehoge.txtの100行目を「sample0123456」にする


  • 行頭の日付などで該当した行を置換
    • 2017-09-22

 日付の情報を行頭にあるテキストファイルで指定した日付の行を更新するシェルスクリプトの一部分。Ubuntu14.04のbashを想定。

fname=datafile.csv
deleteHeader=2017-09-22
newData=2017-09-22:hogehoge
echo sed -i.bak -e "s/^${deleteHeader}.*$/${newData}/" $fname
sed -i.bak -e "s/^${deleteHeader}.*$/${newData}/" $fname

※ -i.bak とすると、元のファイルを新しい内容で上書きする前に、元のファイル名の最後に「.bak」と付加して保存します。




ed

エディタのコマンド版といったらよいのかな。

ed テキストファイル

として読み込んでから、コマンドモードと編集モードを切り替えながらファイルを編集します。最初に大文字のP[Enter]とするとコマンドモードで先頭に「*」が付加されて作業がしやすくなるようです。

1234,tanaka,12,112345
1235,yamada,22,223344
2222,ando,31,34567
3333,endo,3909876

の様なCSVファイル(最左列はユニーク)を読み込んだ場合なら、

P
*1,$g/^2222,/p
2222,ando,31,34567
*d
*i

とすると「2222,ando,31,34567」の行をこれから入力する内容に置き換えることが出来る。

9999,ando,33,99999
.

で変更

*w
*q

で保存して終了となります。

 通常行を指定してコマンドを指定するそうですが、直前に検索などをすると最後の行が対象となります。
 *d : 対象行を削除、*i:その場所に入力内容を挿入、*w:保存、*q:ed終了
保存せずに終了する場合は、

*w
*q

*Q

とすると良いようです。

参考外部URL: http://www.uetyi.com/server-const/command/entry-271.html


ex

 viのコマンド版のようなもの。edコマンドど似たような感じでの使用ができるが、viと連携が容易になっている。

参考URL


strings

標準出力で表示できるデータを表示する。「strings sample.gif | head」 とすると、先頭行に「GIF89a」などが表示され、データの判別に利用できる事がある。


xargs

指定した行毎に1行にまとめる。「xargs ファイル名 -n 10」・・・バージョンによってはこのファイル指定は不可。「cat ファイル名 | xargs -n 10」だと良いみたい。
Ubuntuの12.04では、デフォルト指定だと空白を区切り文字として認識するので、改行を区切りとしてbashで使う場合は、次のような感じでデリミタを指定します。

 cat sample.txt | xargs -d"\\n" -n3


od

ファイルを16進数などで表示できる。バイナリファイルの内容確認に利用可能。「dd if=/dev/hda bs=512 count=1 | od -a」とすれば、ハードディスクの/dev/hdaのブートレコードの情報をテキスト文字等で表示します。ブートローダーに「LILO」を使っているディスクだと最初の方に「LILO」などの文字が見えたりします。

# dd if=/dev/hda bs=512 count=1 | od -a
0000000   z   h   B nul soh soh   L   I   L   O soh nul syn stx nul nul
0000020 nul nul nul nul  gs   k   v   J del del  ht nul   R eot  fs   b
0000040   ` dle nul can   b   ` dle nul  em   b   ` dle nul sub   b   `


次の様な例もあった。こちらは、ブートローダーがGRUBです。

# dd if=/dev/sda bs=512 count=1 | od -v -t x1z -Ax
1+0 レコード入力
1+0 レコード出力
512 バイト (512 B) コピーされました、 5.7605e-05 秒、 8.9 MB/秒
000000 eb 63 90 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0  >.c..............<
000010 fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00  >...|.........!..<
000020 00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75  >....8.u........u<
000030 f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 01 8b  >.........|...t..<
 
----- 中略 -----
 
000170 26 5a 7c be 8e 7d eb 03 be 9d 7d e8 34 00 be a2  >&Z|..}....}.4...<
000180 7d e8 2e 00 cd 18 eb fe 47 52 55 42 20 00 47 65  >}.......GRUB .Ge<
000190 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 65 61  >om.Hard Disk.Rea<
0001a0 64 00 20 45 72 72 6f 72 0d 0a 00 bb 01 00 b4 0e  >d. Error........<
0001b0 cd 10 ac 3c 00 75 f4 c3 39 eb 02 00 00 00 80 20  >...<.u..9...... <
0001c0 21 00 83 fe ff ff 00 08 00 00 00 98 cb 24 00 fe  >!............$..<
0001d0 ff ff 05 fe ff ff fe a7 cb 24 02 40 77 00 00 00  >.........$.@w...<
0001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
0001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  >..............U.<
000200


awk

  • テキストファイルの処理コマンド(スクリプト言語)。様々な条件指定でテキストデータを処理できる。
  • awk -F ':' '{print $1}' /etc/passwd ・・・ 区切り文字を":"にして、フィールドの1個目のみ表示する
  • awk -F, '{print $2","$3","$1","$4","$5}' data.csv ・・・ カンマ区切りデータのフィールドの順番を変える
  • シングルコーテーションを出力時に使いたいときは、\047とする。
  • 例) awk -F, '{print "INSERT INTO TABLE VALUES(\047"$1"\047,\047"$2"\047);"}' sample.csv
  • CSVファイルの列数を表示
    awk -F, "NR==1{print NF"} sample.csv
  • CSVファイルの1~100行目までを表示、その際頭に行番号を付ける
    awk -F, 'NR==1,NR==100 {print NR "," $0}' sample.csv
NR:行番号の指定。範囲を指定する場合は上記の様に指定をカンマでつなげる
NR:は出力で指定する異も可能。
$0:1行全体。$1,$2・・・とするとカンマ区切りで1列目、2列目となる。
  • CSVファイルの1~100行目までをそのまま表示
     行番号を追加したり列を入れ替えたりしなのであれば、printの部分は省略可能。
    awk -F, 'NR==1,NR==100' sample.csv
  • 正規表現、ファイルへの出力
     カンマ区切りで入力。1列目に数字の1が含まれる場合に、その行をファイル「f1.csv」の末尾に追加保存する。
awk -F, '$1 ~ /1/ {print >> "f1.csv"}' sample1.csv

「$1 ~ /1/」が$1に1が含まれる「$1 !~ /1/」とすると含まれないとなる。

awk -F, '$1 ~ /1/ {print >> "f1.csv"}; $1 ~/2/ {print >>"f2.csv"}' sample1.csv

とすると、1が含まれればf1.csvへ、2が含まれればf2.csvへ追加出力する。


参考:awk


paste

テキストファイルの各行をタブ記号で区切って横に連結する。


nkf

文字コードの変換を行う。よく利用したのは、Shift-JISとEUC-JPの変換。最近気付いたのですが、-dで改行コードをLFにしてくれる。CRLFにする-cもあるようでです。

ポイント
 バイナリファイルを表示して、画面が文字化けしたらbashの雑記?の、「端末が文字化けしたら・・・」にあるエスケープシーケンスの送信で直ることがあります。


cat

 標準入力の内容を表示する。-nのオプションを指定すると各行の先頭に行番号を追加して表示します。
 awkやsedで行の指定方法と結果を確認する前にダミーデータに行番号をつけると動作が状況の確認がしやすくなります。

$ cat myondo.csv | head
2014-06-02 19:05,Physicalid0,50.0,Core0,47.0,Core1,50.0,
2014-06-02 19:10,Physicalid0,51.0,Core0,48.0,Core1,51.0,
2014-06-02 19:15,Physicalid0,50.0,Core0,46.0,Core1,50.0,
2014-06-02 19:20,Physicalid0,53.0,Core0,48.0,Core1,53.0,
2014-06-02 19:25,Physicalid0,49.0,Core0,46.0,Core1,50.0,
2014-06-02 19:30,Physicalid0,49.0,Core0,45.0,Core1,49.0,
2014-06-02 19:35,Physicalid0,50.0,Core0,45.0,Core1,50.0,
2014-06-02 19:40,Physicalid0,50.0,Core0,45.0,Core1,50.0,
2014-06-02 19:45,Physicalid0,50.0,Core0,47.0,Core1,50.0,
2014-06-02 19:50,Physicalid0,52.0,Core0,50.0,Core1,52.0,
$ cat -n myondo.csv | head
     1  2014-06-02 19:05,Physicalid0,50.0,Core0,47.0,Core1,50.0,
     2  2014-06-02 19:10,Physicalid0,51.0,Core0,48.0,Core1,51.0,
     3  2014-06-02 19:15,Physicalid0,50.0,Core0,46.0,Core1,50.0,
     4  2014-06-02 19:20,Physicalid0,53.0,Core0,48.0,Core1,53.0,
     5  2014-06-02 19:25,Physicalid0,49.0,Core0,46.0,Core1,50.0,
     6  2014-06-02 19:30,Physicalid0,49.0,Core0,45.0,Core1,49.0,
     7  2014-06-02 19:35,Physicalid0,50.0,Core0,45.0,Core1,50.0,
     8  2014-06-02 19:40,Physicalid0,50.0,Core0,45.0,Core1,50.0,
     9  2014-06-02 19:45,Physicalid0,50.0,Core0,47.0,Core1,50.0,
    10  2014-06-02 19:50,Physicalid0,52.0,Core0,50.0,Core1,52.0,


wc

テキストファイルなどの行数、単語数、文字数などを表示します。


dd

標準入力やファイルの内容を標準出力やファイルに出力する。パーティションやハードディスクのユニットを指定できるので、ディスクのバックアップなどに使える。参照→dd?


split

ファイルを指定したサイズ以下に分割する。

以下は「split -b 4M KEN_ALL.CSV ken_all.」書けばいいのですが、標準入力をわざわざ使うサンプルにしてみました。(2011.09.11)

$ cat KEN_ALL.CSV  | split  -b  4M  -  ken_all.

とすると、

-rwxr--r--  1 isao isao 12156972 2011-08-25 16:17 KEN_ALL.CSV

のファイルが、

-rw-r--r--  1 isao isao  4194304 2011-09-11 10:57 ken_all.aa
-rw-r--r--  1 isao isao  4194304 2011-09-11 10:57 ken_all.ab
-rw-r--r--  1 isao isao  3768364 2011-09-11 10:57 ken_all.ac

となります。以下は分割したファイルを一つにまとめ、diff で違いを比較する例とmd5sumの結果をそれぞれ表示してみた結果。

$ cat  ken_all.aa  ken_all.ab  ken_all.ac  >  ken_all.all
$ diff  ken_all.all  KEN_ALL.CSV
$ md5sum KEN_ALL.CSV
6a22bf176680d6917862f681ffb78656  KEN_ALL.CSV
$ md5sum ken_all.all
6a22bf176680d6917862f681ffb78656  ken_all.all


uniq

行の重複と取り除く

uniq --help
Usage: uniq [OPTION]... [INPUT [OUTPUT]]
Discard all but one of successive identical lines from INPUT (or
standard input), writing to OUTPUT (or standard output).
 
Mandatory arguments to long options are mandatory for short options too.
  -c, --count           prefix lines by the number of occurrences
  -d, --repeated        only print duplicate lines
  -D, --all-repeated[=delimit-method] print all duplicate lines
                        delimit-method={none(default),prepend,separate}
                        Delimiting is done with blank lines.
  -f, --skip-fields=N   avoid comparing the first N fields
  -i, --ignore-case     ignore differences in case when comparing
  -s, --skip-chars=N    avoid comparing the first N characters
  -u, --unique          only print unique lines
  -w, --check-chars=N   compare no more than N characters in lines
      --help     display this help and exit
      --version  output version information and exit
 
A field is a run of whitespace, then non-whitespace characters.
Fields are skipped before chars.
 
Report bugs to <bug-textutils@gnu.org>.



複数のコマンドを使いながら作業

ファイル一覧の最初と最後を取得

  • 2017-02-28
    ファイル名をプログラムなどで日付から作成している場合に、格納されたフォルダで先頭一致で条件を指定しその日付が最古と最新のファイルを取得
ls hogehoge* | sort | sed -n -e \$p -e 1p
\$p:$pだと変数を展開するため、$をエスケープ
「-e \$p -e 1p」は、「 -e 1p -e \$p」としてもsedは、ファイルの中の順に結果を表示するので同じです。

上記の例では、結果として例えば次の様に表示されます。

hogehoge_2015110100_01.png
hogehoge_2017022706_01.png


grepとsedで手作業

  • 2012-06-20
    ・・・grepである文字が何行目になるか調べておいて、そこだけをsedで置換する

integerというキーワードをファイルのある部分のみ置換したい。まず、何行目にあるかgrepで調べる。

isao@np11oi21:~$ grep -n integer meisai.pg_dump
27:    denpyou_id integer,
31:    packnum integer,
33:    tanka integer,

・31行目のintegerだけを置換したい

isao@np11oi21:~$ sed '31s/integer/double precision/' meisai.pg_dump > meisai.pg_dump2

確認

isao@np11oi21:~$ grep -n integer meisai.pg_dump2
27:    denpyou_id integer,
33:    tanka integer,


ファイル一覧からサイズと名前を取り出す

  • 2013-10-10
    $ ls -k -l /home/isao/data | tr -s " " | cut -d" " -f5,9-

ls ・・・ -k:KBでのファイルサイズ表示。-l:詳細情報
tr ・・・ -s:指定した文字の連続を1個にする
cut・・・ -d:デリミタの指定。-f取り出すフィールド指定


温度情報の温度部分を1行にまとめる

  • 2013-10-27
$ sensors
coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +39.0°C  (high = +76.0°C, crit = +95.0°C)
Core 0:         +35.0°C  (high = +76.0°C, crit = +95.0°C)
Core 1:         +39.0°C  (high = +76.0°C, crit = +95.0°C)
$ sensors | grep "°C" | cut -d"(" -f1 | \
  xargs -d"\n" -n3 | sed -e s/°C/\\t/g | \
   sed -e s/:/\\t/g | tr -d " " | tr -d "+"

Physicalid0	40.0	Core0	36.0	Core1	40.0	


Perlのコマンドオプションの活用

フィルタコマンドとは違いますが、Perlでコマンドオプションを利用すると、テキストファイルの加工をする事が出来ます。

例1・・・perl_script.plの「usr/local/bin」を「usr/bin」に置換する。変更前の内容は、perl_script.bakに保存される。

perl -p -i.bak -e "s/usr\/local\/bin/usr\/bin/" perl_script.pl

例2・・・mysite.htmlの「FF7700」を「FFCC77」に変更する。

perl -p -i.bak -e "s/FF7700/FFCC77/i" mysite.html

例3・・・192.168.1.2xxとなる文字のある行を表示。-neが指定したファイルを1行づつ-eで指定したスクリプトで処理する。

perl -ne 'print if m/192\.168\.1\.1.2../' /etc/rc.d/rc.iptables


その他のコマンド

ssconvert

例えば、

ssconvert -S hogehoge.xlsx hoge.csv

などとすると、シート1は、hoge.csv.0、シート2はhoge.csv.1・・・と変換が可能。


参考: 大きなExcelファイルとUbuntuコマンド?




!!書籍紹介

 テキストファイルでのデータ格納。Linuxでフィルタ機能を使ってみようと思うかも知れない本。


 sed、awkなどの基本的なコマンドを利用したデータの処理などを解説しています。中古だと1000円以下で購入出来るようです。