LoginSignup
15

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-03-25

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

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

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

目次
★awk
★cut
★sed
★grep
★sort
★join
★paste
★perl
★scp
★tar
★date
★find
★zip
★split
★xaugs
★文字化ける時
★shell
・演算オプション
・ループ処理(for)
・特殊な変数
・正規表現

★awk

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
ファイル内容を渡して、2,3,4カラム目を足して行頭をカンマに置換して結果ファイルに出力。

cat ../rev/2x2.xx.xx.rev | awk '{print $4,$2,"A","2x2.xx.xx."$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

★cut

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で列を足し算。

★sed

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一行野郎 - ボクノス

★grep

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で調べる。

★sort

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

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

区切り文字を指定して、キーは第2フィールドの2番目の文字から10番目の文字として並べかえる。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 で多い順表示

★join

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

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

★paste

paste -d "," File1 File2

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

★perl

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

★scp

・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 (再帰的にサブディレクトリのデータも収集する)

★tar

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

★date

# 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秒

★find

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
特定の文字列を含むファイルを検索する(行番号も出す)

★zip

# /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で時間がかかっても圧縮効率を高くする。

★split

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

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

★xargs

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

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

★文字化ける時

※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 パリティの生成と検出を無効にする

★shell

 カーネルとコマンド(命令文)の橋渡しをしてくれるもの。シェルから命令文を渡されたカーネルがCPUなどのデバイスに対して処理をさせて結果を返す。
 シェルスクリプトは基本的に移植性を考慮してどんな環境でも動くようにBシェルで書くのがいいとされる。移植することを想定しなくてよい場合はbashが便利。
 shという一番シンプルなやつ。ボーンシェル。 
 スクリプトの最初の行に、以下のように書くとそのスクリプトはBシェルで実行可能に。(スクリプトファイルに実行権限があれば。)
#!/bin/sh

☆bash

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

・testコマンド演算オプション

-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を使う。

特殊な変数

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

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

参考:

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

・ループ処理

・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を置換してる。

・コントロールキー

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

・シェルのリダイレクト関連参考頁

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

・正規表現

よく使うメタ文字のメモ。

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

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

・その他

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15