OSコマンドのテキスト処理メモ

個人的なメモです。今も昔も最も見返す回数が多いですが超古いの多いし中途半端です。
更新しやすくなるかと思うので一応のせてみます。ネタ元やもっと詳しいページリンクを追記したりとかそのうち気が向いたら。

※ぱっと見て分かりやすいサンプルと備忘録的な簡単説明のみ掲載。
　詳しくはmanみるかぐぐるか実際に使ってみたらいいとおもいます。（他に影響の少ない環境で。)
　載ってない便利なやつの情報あれば大歓迎です。

▽他の便利なやつご紹介
・1行プログラミング
http://www.commandlinefu.com/commands/browse

目次
<a href="#1">★awk</a>
<a href="#2">★cut</a>
<a href="#3">★sed</a>
<a href="#4">★grep</a>
<a href="#5">★sort</a>
<a href="#6">★join</a>
<a href="#7">★paste</a>
<a href="#8">★perl</a>
<a href="#9">★scp</a>
<a href="#10">★tar</a>
<a href="#11">★date</a>
<a href="#12">★find</a>
<a href="#19">★zip</a>
<a href="#20">★split</a>
<a href="#21">★xaugs</a>
<a href="#13">★文字化ける時</a>
<a href="#14">★shell</a>
<a href="#15">・演算オプション</a>
<a href="#17">・ループ処理(for)</a>
<a href="#16">・特殊な変数</a>
<a href="#18">・正規表現</a>


### <a name="1"><b>★awk</b></a>

`nawk -F: '{FS=","; print $2}' ./user3.txt `
カンマで区切られたフィールドの2番目のカラムをファイルから抽出する

`grep keyword file | awk '{FS=",";total+=$20}; END {print total}'`
ファイルの中から検索文字列が含まれる行の20カラム目を全部足して出力。

`cat file | awk '{FS=",";print $2 + $3 + $4}' | sed -e 's/^/,/g' > res.file`
ファイル内容を渡して、２,３,４カラム目を足して行頭をカンマに置換して結果ファイルに出力。

`cat ../rev/2ｘ2.xx.ｘｘ.rev | awk '{print $4,$2,"A","2ｘ2.ｘｘ.ｘｘ."$1}' | sed 's/.domein.net.//g' | perl -pe 's/ /\t/g' | tail -255 >> domain.zone`
ダブルクォートで追加したい文字列を入れてるのと、空白をタブにしてる。
大量のレコードを逆引きのゾーンファイルから正引きのゾーンファイルに追記したい時用。

```
cat access_log.20100421|egrep -E -o "HTTP/1.1\" [0-9]* [0-9]*">aaa
PAGE_NUM=`egrep -a '.php |.html ' access_log.20100421|wc -l`
cat aaa |sed -e 's/-//g' -e '/^$/d'|awk '{TOTAL+=$3/$PAGE_NUM/1024};END{print TOTAL}'
```

Webページ1枚あたりの平均ファイルサイズ (KB)を出す。※1日分。

参考URL:
http://uguisu.skr.jp/Windows/awk.html

### <a name="2"><b><a href="http://www.linux.or.jp/JM/html/gnumaniak/man1/cut.1.html" target="_blank">★cut</a></b></a>

`cut -d: -f1 file`
fileのコロンで区切られたフィールドの1番目のカラムを標準出力に表示する

`cat SUMI.MT.20070401.001|grep '^2.* 44017317 '|cut -c64-70|awk '{sum+=$1} END{print sum}'`
MTファイルから、接続料のデータレコードを出して、64文字目から70文字目を切り出してawkで列を足し算。

### <a name="3"><b>★sed</b></a>

`sed -e 's/^ //g' -e 's/ \{1,100\}/,/g' file-a > file-b`

file-aの先頭のスペースを全行削除して1～100までの連続したスペースをカンマひとつに変換しfile-bに出力する。
sの前に行数を指定することもできる。
※同じこと(スペースをまとめる)がtr -s ' 'でできます。

`sed -e '5,10d' file`
ファイルの5行目から10行目を削除する

`sed  '/^$/d'  sample.txt `
空白行を消す

`sed -e '/error/i ##check-line###' log.file`
ログファイルにerrorがあったらチェックラインを追加

`sed -i 's/enabled = 1/enabled = 0/g' /etc/yum.repos.d/rpmforge.repo`
直接中身を書き換えている(iオプションの後ろに.bkなどのサフィックスを指定するとバックアップファイルが作られます）

```
head -1 log |sed 's/ [^ ]*@/ XXX@/g'
2010/04/01 00:00:24 sent  30000 XXX@softbank.ne.jp 123.123.60.252 XXX@hoge.net
```
メールログっぽいもののアカウントをマスクしてる（調査依頼時用）
[^ ]はスペース以外のすべての文字列と記号をあらわす正規表現。

```
ec2-describe-instances -K ./pk-oscaws.pem -C ./cert-oscaws.pem --region ap-southeast-1 --show-empty-fields |sed -e 's/, \{0,10\}/，/g' -e 's/\t\{1,100\}/,/g' -e 's/^TAG,[^ ]\{1,100\}/&\n-------------------------------/g'
```
カンマとスペースを全角スペースに変えてタブをカンマにしてTAGから始まる次の行に区切り線を入れてる。
sedの中で使う&(アンパサンド)は一致した文字列の引用を表す。

ある文字列を特定行に挿入する

```
sed -e "2i hoge" test.txt 
sed -e "2a hoge" test.txt
```
"line2"の行前に挿入

`sed -e "/^line2$/i hoge" test.txt`
http://d.hatena.ne.jp/rx7/20110310/p1


・viでも置換ができる(編集モードで)
`:%s/aaa/bbb/g`
aaaに当てはまるものすべてをbbbに置換

`:1,3s/bbb/ccc/g`
1～3行目のbbbをcccに置換

sedでのアドレス指定

```
　3　3行目
　20,$　20行目から最終行まで
　10,5　10行目(2番目の数字が小さい場合)
　/^[0-9]/　先頭が数字の行全て
　15,/Z$/　15行目から最終文字がZで終わる行まで
　5,10!　5～10行目以外の行(1～4行目と11～最終行）
```

行番号を出すには、、
`:set nu`
行番号を消すには、、
`:set nonu`

[sed一行野郎 - ボクノス](http://d.hatena.ne.jp/tanakaBox/20070308/1173368230)

### <a name="4"><b>★grep</b></a>

`grep '検索文字列' file`

fileから検索文字列を含む行を標準出力に表示する。
-vで含まれない行の意味となる
-nで行番号が出る。

`fgrep -f list file`

listに含まれる行をfileから標準画面に出力

`egrep 'a|b' file`
アンド検索。

`openssl x509 -noout -text -in cert1.pem | grep -A 2 Subject:`
証明書の情報を確認するコマンドの処理結果をパイプでgrepに渡して必要な箇所だけ表示。
-A 2 と指定すると当てはまった行の下の2行も出せる。-B 2 だと上の2行がでる。

`# egrep -A 1 "^zone" named.conf|perl -pe 's/{\n//g'|awk '{print $2","$4}'|sed -e '/^,$/d' -e 's/"//g' -e 's/;$//g'|sort`
bindの設定ファイルからzone名とtypeをカンマ区切りで抽出

```
perl -MIO::Socket -e 'my $w = IO::Socket::INET->new(PeerAddr => "whois.jp", PeerPort => 43, Proto => "tcp") or die $!; printf $w "%s\n", shift; print <$w>;' aaaaa.jp/e | grep -i -E '(state|expire)' | grep -i -E -o '([0-9]{4}/[0-9]{2}/[0-9]{2})'
2010/07/31
```
期限をwhoisで調べる。


### <a name="5"><b>★sort</b></a>

`sort -u file`
重複する文字列をひとつにして昇順に並び替える


`sort -t : -k 2.2n,2.10n file1 > file2`

区切り文字を指定して、キーは第２フィールドの２番目の文字から１０番目の文字として並べかえる。nで数値として並べかえる。

`grep "26/Feb/2007:02" access_log.2007-02-26|awk '{print $2}'|sort|uniq -c|sort -r|more`

アクセスログの特定の時間をgrepしてIPをawkで出してuniq -cでカウントしてsort -r で多い順表示


### <a name="6"><b>★join</b></a>

`join -a 1 -j1 1 -j2 2 -t : -o 1.1,1.2,1.3,2.2,2.3 txt1 txt2 > file`

１番目のファイルは一致しない部分も全て出力、キーのフィールドは１、２番目のファイルのキーのフィールドは２。
区切り文字を指定して、出力するフィールドを指定、引数ファイルを並べて出力ファイルにリダイレクトする。
sortしてからjoinしないと大体失敗する。


### <a name="7"><b>★paste</b></a>

`paste -d "," File1 File2`

ファイル1とファイル２を区切り文字をカンマとして行を横に結合する。
select結果など完全に同じキーの列がそろうと分かってるときに使うことが多い。


### <a name="8"><b>★perl</b></a>

```
EID=`nawk -F: '{FS=","; print $11}' $FDIR/DL_OK_$DATE.csv | perl
 -pe 's/\n/,/g' | perl -pe 's/,$//g'`
```

複合的にコマンド処理結果を変数に代入。でもperlでしているのは改行をとって、行末のカンマも取っているだけ。
http://infosys.gsid.nagoya-u.ac.jp/~ohna/perl_lesson/intro2perl/text.html

```
cut -f1 id_tmp.txt | perl -e 'while(<>) {$word = $_;chomp($word) ;print("^",$word,"\n" ;}' > id.txt
```
http://www.tohoho-web.com/wwwperl2.htm
http://www.rfs.jp/sb/perl/02/01.html

```
perl -i -pe 's/\/usr/\#\/usr/g' /etc/cron.daily/slocate.cron
perl -i -pe 's/renice/\#renice/g' /etc/cron.daily/slocate.cron
```

これは明け方の負荷となるupdatedbをとめたいということ。直接編集せずにperlでsedっている。
サーバの台数が多い場合、直にviしないことで工数削減とオペミス防止に。

perlモジュールのインストール方法は以下のとおりパッケージから入れる方法とcpanからの2種ある。

```
# yum install --enablerepo=epel perl-DBD-SQLite perl-Time-Piece
# perl -MCPAN -e shell
cpan> install HTML::Template::Expr
```
つかえるかの確認方法は以下の通りでエラーが出なければよい。

```
perl -e 'use Time::Piece';
perl -e 'use DBD::SQLite';
perl -e 'use HTML::Template::Expr';
perl -MDBD::mysql -e -1
```

### <a name="9"><b>★scp</b></a>

・put
`scp -Cpr -P [port-num] local-file user@server:/dir`

・get
`scp -Cpr -P [port-num] user@server:/remote-dir/file /local-dir/`

-C = Compress (圧縮)
-p = preserve (ファイルの属性などをそのまま保持)
-r = recursive (再帰的にサブディレクトリのデータも収集する)

### <a name="10"><b>★tar</b></a>

`cd taisyou-dir`

・圧縮
`tar cvf kekka.tar ./taisyou-dir/`
-z　：gzipを通して処理
-Z　：compressを通して処理
-j　：bzip2を通して処理

・解凍
`tar xvf aaa.tar.gz`

・追加
`tar rf aaa.tar add.file`

・内容表示
`tar tf aaa.tar`

・1ファイル指定してアーカイブから削除
`tar vf aaa.tar --delete file-name`

・1ファイル指定して抽出(ディレクトリとファイル名指定する)
`tar zxf aaa.tar.gz dir/file`

・リストを指定(-T listfile)して固める
`time tar -czvf spool_11a.tar.gz -T spool_11a >debug.sp11a.txt`

・除外するリストを指定してアーカイブする。

```
MIRROR_DIR=/data1
EXFL=/opt/bin/exclude.txt
cd $MIRROR_DIR
tar --exclude-from $EXFL -czf $DAILY_BK ./doc/.
```
・アーカイブしてパイプで渡して圧縮
`tar cf - ./mysql/ |bzip2 -c > $BACKUPFILE.bz2`


### <a name="11"><b>★date</b></a>

```
# CDATE=`date '+%Y%m%d.%H%M'`
# DATE=`date '+%Y%m%d'`

# gnu_date -d '1 month ago' +%Y/%m
2007/02
# gnu_date -d '1 month' +%Y/%m
2007/04
gnu_date
Tue Mar  6 20:56:46 JST 2007
```
スクリプトの中で結果ファイルやログファイルのファイル名を作業日の日付で出したかったりするときによく使う。
solarisだとdateだと前月とか出せません。なのでgnu_dateが入ってればそれを使う。

・UNIX時間を見やすく表示する

`# date -d "1970-1-1  GMT +1236038400 second" +%Y%m%d.%H:%M:%S`
20090303.09:00:00

`# date -d "1970-1-1  JST +1236038400 second" +%Y%m%d.%H:%M:%S`
20090303.00:00:00

・指定時刻のUNIX時間(1970/1/1から経過した秒数)を取得する

`# date -d "2009-5-24 9:00 JST" +%s`
1243123200

・タイムスタンプからローカル時間への変更

```
# date +%s
1256265645
# awk 'BEGIN{print strftime("%c",1256265645);exit}'
2009年10月23日 11時40分45秒
```
### <a name="12"><b>★find</b></a>

```
find / -name aaa.txt -atime 1 -exec  rm {} ;
find /export/home -perm -u+x -type f -atime -100 -size +100000k
```
ファイルを名前で探してアクセスした時間も指定して引数渡して消してしまう。
消すのは検証しまくってからが良さそう。いらないログやバックアップをディスク溢れ防止にcronに組み込んで消すのに便利。
http://www.sixnine.net/roadside/find.html
http://x68000.q-e-d.net/~68user/unix/pickup?find

`$ find /hoge/path |tr A-Z a-z |sort |uniq -d`

findしたファイルを小文字に統一しuniqで重複ファイルを検索する

```
# DIRLIST=`grep source /etc/lsyncd.conf.xml|awk -F \" '{print $2}'`
# for i in $DIRLIST;do find $i -type f -o -type d |wc -l;done|awk '{TOTAL+=$1}; END {print TOTAL}'
```
lsyncdの管理下のファイルとディレクトリ数の合計値を知る(fs.inotify.max_user_watchesと比べる)

`find /var/log -name "*log" -type f |xargs ls -l |awk '{total +=$5}; END {print total}'`
832128

指定した名称を含むファイルの合計サイズを知る

`find /home -size +10000k -exec ls -lh {} \;`
指定したサイズ以上のファイルを検索する

`# find ./ -name "*.rb" | xargs grep -n node.roles`
特定の文字列を含むファイルを検索する（行番号も出す）


### <a name="19"><b>★zip</b></a>

`# /usr/local/bin/zip -P hoge -e -j auth_time_left_20090217.zip auth_time_left.20090217.105040.csv`

-P ****	# password設定
-j	# 指定した名前のフォルダに入れる
-e	# zipパスワードでEncryptする

zipパスワードをかけてファイルを自動送信するなど。
solarisにもともとついてるzipはパスワードかけれないことが多いので、わざわざ入れる必要がある。

`/bin/nice -n 19 /bin/gzip -9 ${LOG_DIR}/access_log.${YESTERDAY}`
サイズが大きすぎて負荷が高くなりそうなときに。
nice値で優先度を最低にして、gzipで時間がかかっても圧縮効率を高くする。


### <a name="20"><b>★split</b></a>

`split -a 1 -b 1m logfile.log prefix-`

ログファイルが肥大化した場合などに使う。
上記の例では、1MB毎に分割(-b 1m)して、プレフィックス文字列の後にアルファベット順にサフィックスを１文字(-a 1)付けて出力する。

### <a name="21"><b>★xargs</b></a>

`ls | xargs -i ksh -c 'grep {} data_file | wc -l'`

xargsがデータを引き渡すコマンドの中で処理ごとに随時パイプを使いたいが、普通に使うとxargsが全部処理を終了してからパイプに引き渡される。
そこで登場するのがksh。上記のような感じで使えば毎処理ごとにパイプに渡される。中かっこの中身はlsの結果。

### <a name="13"><b>★文字化ける時</b></a>
※Solarisの場合
・Bシェル系なら(プロンプトが$)

```
LANG=ja
export LANG
```
・Cシェル系なら(プロンプトが%)
`setenv LANG ja`

もちろんリモートログインツール(puttyやTeraterm)の文字コードが違ったりフォントが日本語サポートしてなくてもだめなので、設定を調整する。
2007年ころは大体EUC-JPだったが今時のLinuxの文字コードは大体UTF-8。
RedHat系だと`cat /etc/sysconfig/i18n`とかすると文字コードが分かる。
または`env | grep LANG`とか。

bashでつかえるやつ↓
`# stty cs8 -istrip -parenb`
  sttyの引数説明(文字化け対応)
    ･cs8 偶数パリティを選択
    ･-istrip 入力文字を7ビットにストリップしない
    ･-parend パリティの生成と検出を無効にする


### <a name="14"><b><a href="http://muffin.cias.osakafu-u.ac.jp/~matumoto/cgi-bin/xt.cgi?prog/sh" target="_blank">★shell</a></b></a>
　カーネルとコマンド(命令文)の橋渡しをしてくれるもの。シェルから命令文を渡されたカーネルがCPUなどのデバイスに対して処理をさせて結果を返す。
　シェルスクリプトは基本的に移植性を考慮してどんな環境でも動くようにBシェルで書くのがいいとされる。移植することを想定しなくてよい場合はbashが便利。
　shという一番シンプルなやつ。ボーンシェル。　
　スクリプトの最初の行に、以下のように書くとそのスクリプトはBシェルで実行可能に。(スクリプトファイルに実行権限があれば。)
`#!/bin/sh`

### <b>☆bash</b>
　Cシェルより処理が早くメモリを食う量が比較的少ない。Bシェルの仲間たちのひとつ。
　そのためマルチユーザ環境のバッチサーバなどでは推奨される。

http://members.jcom.home.ne.jp/j-klein/shell/shellscript-basic.html

<a name="15"><b>・testコマンド演算オプション</b></a>

```
-eq　等しい (equal) 
-ne　等しくない (not equal) 
-lt　小さい (less than) 
-le　小さいまたは等しい (less than or equal) 
-gt　大きい (greater than) 
-ge　大きいまたは等しい (greater than or equal) 
-a　 アンドの意味
-o 　オアの意味
```
この他は`man test`で確認
移植性を考慮して計算したいなら`expr`を使う。

### <a name="16">・<b>特殊な変数</b></a>
シェルスクリプトは引数を利用することができ、bashに初めから用意された特殊な変数を使用し参照することができる。
これらの変数は参照するもので、値を代入することはできない。

| 変数 | 説明 |
|:---|:--------|
|`$n`|nは数字であり、`$0`はシェルスクリプト名、以降`$1`、`$2`…は第1引数、第2引数…である。第10引数以降は`${10}`、`${11}`…で参照する。"${0##*/}"はディレクトリを取り除いたスクリプト名。|
| `$#` |与えられた引数の個数|
|`$@`|`$0`以外の全ての引数（`"$@"`のようにダブルクォーテーションで囲んだ場合`"$1" "$2" …"`のように個別に展開される。）|
|`$*`|`$0`以外の全ての引数（`"$@"`のようにダブルクォーテーションで囲んだ場合`"$1 $2 …"`のように展開される。）|
|`$?`|最後に実行したコマンドの終了ステータス|
|`$!`|最後に実行したバックグラウンドコマンドのPID|
|`$$`|シェルのPID|
|`$-`|現在のオプションフラグ|

http://cyberam.dip.jp/linux_command/shellscript/shellscript_main.html

参考：

```
if [ $# -lt 2 ]
  then
    echo "引数が足りません。"
    echo "usage: $0 user_id enduser_id"
    exit 1
  else
    : 
fi
```
この場合は、引数が2個未満なら`echo`でエラーと使い方表示出して終了、そうでなければ続ける。
`if`で条件分岐に使う`[]`は`test`コマンドとイコール。
他にも色々な条件指定が可能。
http://www.linux.or.jp/JM/html/gnumaniak/man1/test.1.html


### <a name="17"><b>・ループ処理</b></a>
・while loop 参考

```
DATE=20080809
D=09
T1=00
T2=01
T3=02
Y=2008
M=Aug
HOST=`uname -n`
cd /usr/local/apache/logs
i='0'
while [ $i -le 9 ]
do
zgrep -c "${D}/${M}/${Y}:${T1}:0${i}" ${HOST}.access_log.${DATE}.gz >> ${HOST}.${T1}min${DATE}.log
zgrep -c "${D}/${M}/${Y}:${T2}:0${i}" ${HOST}.access_log.${DATE}.gz >> ${HOST}.${T2}min${DATE}.log
zgrep -c "${D}/${M}/${Y}:${T3}:0${i}" ${HOST}.access_log.${DATE}.gz >> ${HOST}.${T3}min${DATE}.log
i=`expr $i + 1`
done
i='10'
while [ $i -le 59 ]
do
zgrep -c "${D}/${M}/${Y}:${T1}:${i}" ${HOST}.access_log.${DATE}.gz >> ${HOST}.${T1}min${DATE}.log
zgrep -c "${D}/${M}/${Y}:${T2}:${i}" ${HOST}.access_log.${DATE}.gz >> ${HOST}.${T2}min${DATE}.log
zgrep -c "${D}/${M}/${Y}:${T3}:${i}" ${HOST}.access_log.${DATE}.gz >> ${HOST}.${T3}min${DATE}.log
i=`expr $i + 1`
done

```
※分毎にアクセスログをカウントして時間毎のファイルに出力している

・for loop 参考 

```
list="a b c"
for i in $list
do
echo ${i}
done
```

・for文を入れ子(nest)にしてみる

```
# for i in aaa bbb ccc; do echo "$i"; for a in 1 2 3;do echo "$a" ;done; done
aaa
1
2
3
bbb
1
2
3
ccc
1
2
3
```

例：heartbeatのパスをとおす(様々なタイプと権限のファイルが混在するリンク元から一気にシンボリックリンク貼りたい場合)

```
LIST=(
`find /usr/lib64/heartbeat -maxdepth 1 -type f -perm +1`
)
for i in ${LIST[@]}
do
ln -s ${i} /usr/local/bin/
done
```
一行で何かしたい場合はセミコロンで区切るなど。

```
for i in `seq 2 9`;do cp -p ifcfg-eth0:1 ifcfg-eth0:${i};sed -i "s/0:1/0:${i}/g" ifcfg-eth0:${i};done
```
仮想NICをseqで渡して一気につくりDEVICEを置換してる。


### <b>・コントロールキー</b>


|キー| stty名| 機能 |
|:--|:--|:--|
|CTRL-C| intr| 現在のコマンドを中止 |
|CTRL-D| eof| 入力を終了 |
|CTRL-\| quit| CTRL-Cが未成功時現在のコマンドを中止 |
|CTRL-S| stop| 画面への出力を停止 |
|CTRL-Q| --| 画面への出力を開始 |
|DELorCTRL|-?| erase 最後の文字を削除 |
|CTRL-U| kill| コマンドライン全体を削除 |
|CTRL-Z| susp| 現在のコマンドを一時停止|

### <b>・シェルのリダイレクト関連参考頁</b>
http://sonic64.com/2004-03-28.html
http://e-words.jp/w/E38395E382A1E382A4E383ABE38387E382A3E382B9E382AFE383AAE38397E382BF.html
http://cai.cs.shinshu-u.ac.jp/sugsi/Lecture/HowToUnix/2-1.html

### <a name="18"><b>・正規表現</b></a>
よく使うメタ文字のメモ。

```
. 改行を除く任意の1文字 
* 0回以上の繰り返し 
+ 1回以上の繰り返し 
? 0回または1回 
^ 先頭 
$ 末尾 
```

正規表現関連参考頁
http://www.sixnine.net/regexp/regexp3.html
http://www.kt.rim.or.jp/~kbk/regex/regex.html

### <b>・その他</b>

うっかり実行したあとのコマンドをバックグラウンドに渡す、、`ctrl+z`のち`bg`と打つ。