linuxなどで使うテキスト編集コマンドの使い方で、これさえ知っていればOKというもののまとめ(vi, sed, grep, sort, uniq, cut, join, tr, nkf, diff)。
後半は特に必要とはいえないため適当。重要なのはvi, sed, grepまでかな。
複数のファイルを扱う場合に使うxargsコマンドについても説明する。
目次
vi
※(数指定)のついているものは、コマンドの前に数を指定することで指定された数だけ該当する操作を行える。
(6h
で6字分左に移動)
コマンドモードと入力モード
- コマンドモード→入力モード
i
: カーソルの直前に挿入(insert)I
: カーソル行の最初に挿入a
: カーソルの直後に挿入(add)A
: カーソル行の最後に挿入o
: カーソルの下の行に挿入O
: カーソルの上の行に挿入
- 入力モード→コマンドモード:
ESC
移動
(ちょうど右手のホームポジション)
h
: 左(数指定)j
: 下(数指定)k
: 上(数指定)l
: 右(数指定)
(ページ移動)
Ctrl-b
: PgUp(back)Ctrl-f
: PgDn(forward)
(行内)
^
: 行頭(正規表現)$
: 行末(正規表現)
(行指定)
Ctrl-g
: 現在の行表示G
: 最終行数字G
: 指定行に移動
コピー、カット、ペースト
x
: 文字削除(数指定)dw
: 単語削除(数指定)dy
: 単語コピー(数)dd
: 行削除(数指定)yy
: 行コピー(数指定)p
: カーソルの下に貼り付けP
: カーソル行に貼り付け)s
: カーソル位置の文字を修正、上書き(数指定)Ctrl-h
: Back SpaceJ
: カーソル行と次行を連結する
undo,redo
u
: 直前の作業の取り消し.
: 直前の作業の繰り返し
検索、置換
:/文字列
: 文字列の検索(/.はエスケープが必要)n
: 下方向への検索N
: 上方向への検索:%s/文字列1/文字列2/g
: ファイル内の文字列1を文字列2に置換(全箇所)
便利なコマンド
:!コマンド
: ローカルコマンド実行
設定
- .swpファイルを生成しない。
:set noswapfile
- .swpファイルを生成する。
:set swapfile
ファイルを開く
e: ../toedit.txt
: 指定したパスのファイルを開く
保存、終了
:w!
: 保存:q!
: 終了:wq!
: 保存して終了:w ファイル名
: ファイル名として保存
sed
行や文字列の置換、削除、抽出を行うコマンド
sed -e コマンド1 -e コマンド2 ... infile > outfile
sed -i -e コマンド1 -e コマンド2 ... infile
置換(s
コマンド)
あるファイル内の文字列1をすべて文字列2に置換し、ファイルに出力する
sed -e 's/文字列1/文字列2/g' 置換するファイル > outfile
find . -name \*.html -exec sed -i -e 's/ onkeypress="[^"]*"//g' '{}' \;
指定行の文字列1を文字列2に置換する
sed -e '5s/文字列1/文字列2/g' infile > outfile
sed -e '1,10s/文字列1/文字列2/g' infile > outfile
sed -e '20,$s/文字列1/文字列2/g' infile > outfile
パターンにマッチした行のみ、文字列1を文字列2に置換する
sed -e '/パターン/s/文字列1/文字列2/g' infile > outfile
これらを組み合わせて、例えば1行目から最初の空行までを置換する
sed -e '1,/^$/s/文字列1/文字列2/g' infile > outfile
#
ではじまる行以外を置換する
sed -e '/^#/!s/文字列1/文字列2/g' infile > outfile
*.pl
ファイルだけ1行目の/usr/local/bin
を/usr/bin
に置換
find ./ -type f -name *.pl | xargs sed -i -e '1s|/usr/local/bin/|/usr/bin/|'
削除(d
コマンド)
文字列1を含むすべての行を削除
sed -e '/文字列1/d' infile > outfile
1行目だけ削除して、すべての行頭に""
囲みのファイル名を付け、まとめて1個のファイルに出力
ls *csv | xargs -I% sed -e '1d' -e 's/^/"%",/g' % >> a.csv
抽出(p
コマンド)
該当する行だけを表示する-n
オプションと、
条件にマッチした行のみ取り出すp
コマンドをセットで使う。
最終行だけ削除して上書き
sed -i -n -e '$!p' a.csv
ヘッダ付きCSVファイルの結合
sed -n -e '1p' a1.csv >> a_merged.csv
find . -name 'a*.csv' | xargs sed -n -e '2,$p' >> a_merged.csv
便利なオプション
拡張正規表現
+ ? { } ( ) |
を使う場合、-e
ではなく-E
を指定する。
以下は同じとなる
sed -i -e 's/^#\(.*NOPASSWD: ALL\)/\1/' /etc/sudoers
sed -i -E 's/^#(.*NOPASSWD: ALL)/\1/' /etc/sudoers
sed のコマンドスクリプトを別ファイルに記述しておいて、それを実行する
sed -f スクリプトファイル infile > outfile
参考
http://hydrocul.github.io/wiki/commands/sed.html
grep
ファイルからパターンを検索する。
grep パターン infile
grepの種類
fgrep
: 正規表現が使えず、固定の文字列のみの検索grep
: 正規表現egrep
: 拡張正規表現(grep -E
と同じ)
速度は
(高速) fgrep > grep > egrep (低速)
基本的な使い方
infileからパターンを検索し、その含まれる行のみをoutfileに出力する
grep パターン infile > outfile
コマンドの出力結果からパターンの存在する行のみ表示する
コマンド | grep パターン
パターンが含まれない行のみを出力する
grep -v パターン infile
複数のパターンを指定する
grep -e パターン1 -e パターン2 infile > outfile
その他代表的なオプションは
-i
: 大文字小文字を区別しない-n
: 行番号を出力する
条件に合致する行の抽出、削除
抽出
sed -n -e '/xxx/p'
grep -e 'xxx'
削除
sed -e '/xxx/d'
grep -v -e 'xxx'
リストの比較
2つのリストの共通行を抽出したり、一方にのみ含まれる行を抽出したりする。
よく使うオプション
-f ファイル
: ファイル内の文字列に含まれているかどうかを判定する-x
: 文字列ではなく行(\n
と\n
の間)単位で比較する。\r
も含まれるため、Windowsで作ったテキストファイルだと比較できないことがあるので事前に除去しておく必要がある
file1とfile2の両方に含まれる行を抽出する(積集合)
fgrep -xf file1 file2
file1に\r
が含まれており、file2には含まれない場合
cat file1 | tr -d "\r" | fgrep -xf - file2
file2の中でfile1に含まれない行を抽出する(差集合)
fgrep -v -xf file1 file2
標準入力を使って、コマンドの出力結果の中からfile1にも含まれる行を抽出する(積集合)
コマンド | fgrep -xf file1 -
ちょっと複雑に
コマンドの出力結果の中からfile1に含まれない行を抽出する(差集合)
コマンド | fgrep -v -xf file1 -
file1の中からコマンド出力結果に含まれない行を抽出する(差集合・逆パターン)
コマンド | fgrep -v -xf - file1
CSV処理
1列目の値が2か5で、3列目の値が999以外場合の行を抽出(区切り文字はタブ\t
)
cat infile | egrep '^[25]' | egrep -v '^([^\t]*\t){2}999' > outfile
.*\t
(最長マッチ)や.*?\t
(最短マッチ)は使えない。
- 最長マッチだと
.*
の中に\t
を含んで、さらにその先に指定回数の\t
まで含んだうえで正規表現全体がマッチングしてしまうため、回数指定の意味がなくなる - 最短マッチだと
\t
まで到達せずにマッチングが終了してしまう
sort
テキストの並べ替えを行う。CSVの指定したフィールドをキーにした並べ替えもできる。
sort オプション ファイル名
コマンド | sort オプション
ソートの仕方に対するオプション
-r
: 降順でソートする(デフォルトで昇順)-b
: フィールド(行)先頭の空白文字を取り除いたうえでソートする。スペースが連続して区切られる固定長フィールドの場合に必要-n
: 数字を文字列ではなく数値として扱う。これを指定しないと文字列扱いになる。-f
: 大文字小文字を区別しない
CSVの場合
-k m(,n)
: フィールド番号m
(からn
)をキーにしてソートする。複数指定することも可(前のほうがより優先される)-t 区切り文字
: フィールドの区切り文字を指定する。デフォルトの区切り文字は空白文字(ホワイトスペースとタブ、日本語ロケールでは全角スペースも区切り文字扱いになる)
-k
のフィールド番号に並べ替えオプションを付けることで、フィールドごとにソートの仕方を指定できる。
たとえば
- 第1ソートキー:2列目を数字(降順)
- 第2ソートキー:5列目を文字列(昇順)
- タブ区切り
sort -t$'\t' -k2nr,2 -k5,5 infile
-k2nr,2
のようにオプションを指定する。
固定長フィールド、たとえば以下のようなテキストをPIDの列でソートする場合(ps aux
の出力の一部)
“`ps.txt
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 19236 516 ? Ss Feb22 0:19 init
root 2 0.0 0.0 0 0 ? S Feb22 0:00 [kthreadd/4433]
root 481 0.0 0.0 4068 88 tty1 Ss+ Feb22 0:00 /sbin/mingetty console
root 482 0.0 0.0 4068 8 tty2 Ss+ Feb22 0:00 /sbin/mingetty tty2
user1 1862 0.0 0.0 100496 4584 ? Ss 00:48 0:00 sshd: user1@pts/0
user1 1864 0.0 0.0 108404 2048 pts/0 Ss 00:48 0:00 -bash
root 5667 0.0 0.0 10648 80 ? S<s Jun03 0:00 /sbin/udevd -d
root 5747 0.0 0.0 66240 240 ? Ss Jun03 0:10 /usr/sbin/sshd
root 13857 0.0 0.0 10428 272 ? Ss Feb29 0:00 /usr/sbin/pptpd
並べ替えオプションなし、`b`、`n`の違いは
```shell-session
# 空白文字がソート対象→ソートの意味をなさない
$ sort -k2,2 ps.txt
root 1 0.0 0.0 19236 516 ? Ss Feb22 0:19 init
root 2 0.0 0.0 0 0 ? S Feb22 0:00 [kthreadd/4433]
root 481 0.0 0.0 4068 88 tty1 Ss+ Feb22 0:00 /sbin/mingetty console
root 482 0.0 0.0 4068 8 tty2 Ss+ Feb22 0:00 /sbin/mingetty tty2
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 5667 0.0 0.0 10648 80 ? S<s Jun03 0:00 /sbin/udevd -d
root 5747 0.0 0.0 66240 240 ? Ss Jun03 0:10 /usr/sbin/sshd
root 13857 0.0 0.0 10428 272 ? Ss Feb29 0:00 /usr/sbin/pptpd
user1 1862 0.0 0.0 100496 4584 ? Ss 00:48 0:00 sshd: user1@pts/0
user1 1864 0.0 0.0 108404 2048 pts/0 Ss 00:48 0:00 -bash
# 文字列としての数字がソート対象
$ sort -k2b,2 ps.txt
root 1 0.0 0.0 19236 516 ? Ss Feb22 0:19 init
root 13857 0.0 0.0 10428 272 ? Ss Feb29 0:00 /usr/sbin/pptpd
user1 1862 0.0 0.0 100496 4584 ? Ss 00:48 0:00 sshd: user1@pts/0
user1 1864 0.0 0.0 108404 2048 pts/0 Ss 00:48 0:00 -bash
root 2 0.0 0.0 0 0 ? S Feb22 0:00 [kthreadd/4433]
root 481 0.0 0.0 4068 88 tty1 Ss+ Feb22 0:00 /sbin/mingetty console
root 482 0.0 0.0 4068 8 tty2 Ss+ Feb22 0:00 /sbin/mingetty tty2
root 5667 0.0 0.0 10648 80 ? S<s Jun03 0:00 /sbin/udevd -d
root 5747 0.0 0.0 66240 240 ? Ss Jun03 0:10 /usr/sbin/sshd
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# 数値がソート対象
$ sort -k2n,2 ps.txt
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 19236 516 ? Ss Feb22 0:19 init
root 2 0.0 0.0 0 0 ? S Feb22 0:00 [kthreadd/4433]
root 481 0.0 0.0 4068 88 tty1 Ss+ Feb22 0:00 /sbin/mingetty console
root 482 0.0 0.0 4068 8 tty2 Ss+ Feb22 0:00 /sbin/mingetty tty2
user1 1862 0.0 0.0 100496 4584 ? Ss 00:48 0:00 sshd: user1@pts/0
user1 1864 0.0 0.0 108404 2048 pts/0 Ss 00:48 0:00 -bash
root 5667 0.0 0.0 10648 80 ? S<s Jun03 0:00 /sbin/udevd -d
root 5747 0.0 0.0 66240 240 ? Ss Jun03 0:10 /usr/sbin/sshd
root 13857 0.0 0.0 10428 272 ? Ss Feb29 0:00 /usr/sbin/pptpd
uniq
ソート済みのテキストからユニークな行(、重複している行)を抽出する。
出現頻度のカウントもできる。
uniq infile
出現頻度のカウント
uniq -c infile
重複行のみ抽出
uniq -d infile
ログファイルの指定列の値の出現頻度を上位から順に表示(定番のイディオム)
cat infile | cut -f5 | sort | uniq -c | sort -k1 -r
cut
CSVファイルの指定列を抽出する。
主なオプション
-d'区切り文字'
: 区切り文字を指定。デフォルトの区切り文字はタブ\t
-fフィールド番号
: 抽出するフィールド番号を指定
カンマ区切りテキストファイルinfileの2列目と5列目を(カンマ区切りで)抽出する
cut -d',' -f2,5 infile
sort
コマンドと同様、スペース区切りの場合と固定長フィールドの相性が悪い。
-bフィールドのバイト数
でバイト数を指定するか、以下のように連続するスペースをトリミングする。
cat infile | sed -E 's/^\s+//g' | sed -E 's/\s+$//g' | sed -E 's/(\s)+/\1/g' | cut -d' ' -f3-5 > outfile
join
2個のCSVを指定フィールドをキーにjoinする
join オプション file1 file2
オプションは
-t区切り文字
: 区切り文字を指定-1 フィールド番号
: file1のキーになるフィールド番号を指定(1列目の場合不要)-2 フィールド番号
: file2のキーになるフィールド番号を指定(1列目の場合不要)-a ファイル番号
: 指定した番号のファイルを全行表示する(left join / right join / full join)-o 出力する列
: 出力する列を指定する
たとえば
file1の3列目とfile2の1列目をキーにinner joinする
join -1 3 -2 1 file1 file2
file1の2列目とfile2の1列目をキーにleft joinし、
ソートのキーとfile1の3-5列目、file2の1-2列目を表示
join -a 1 -1 3 -2 1 -o 0 1.3 1.4 1.5 2.1 2.2 file1 file2
file1の2列目とfile2の1列目をキーにfull joinする
join -a 1 -a 2 -1 3 -2 1 file1 file2
tr
パターン1をパターン2に置換
tr パターン1 パターン2 < infile > outfile
DOS の CR コードを削除
tr -d '\r' < infile > outfile
nkf
文字コード変換
nkf [option] infile > outfile
find path -type -f -name "*.js" | xargs nkf -w --overwrite
--overwrite
: ファイルに上書き
出力する漢字コードの指定
-j
: JIS で出力(デフォルト)-e
: EUC で出力-s
: Shift-JIS で出力-w
: UTF-8 で出力
入力する漢字コードの指定(なくてもよい)
-J
: JIS で入力-E
: EUC で入力-S
: Shift-JIS で入力-W
: UTF-8 で入力
出力する改行コードの指定(デフォルトでは変換なし)
-Lu
:LF
(Unix)-Lw
:CR+LF
(Windows)-Lm
:CR
(Mac)-
-c
: 行末にCR
を追加 -d
: 行末からCR
を削除-m
: MIME を解読する
diff
2つのテキストファイルの違いを出力する
diff file1 file2
xargsとパイプ
前のコマンドの出力をそのまま後続のコマンドの引数にする。
区切り文字
find
からxargs
に渡すときはNULL文字を区切り文字として指定をするのが安全。前のコマンドの出力結果に空白文字が含まれるとアウト。
find . -type d -print0 | xargs -0 ls -l
引数文字
-I区切り文字
で指定する。デフォルトでは{}
find . -name '*.txt' -print0 | xargs -0 cp {} {}.bak
find . -name '*.txt' -print0 | xargs -0 -I% cp % %.bak
その後にリダイレクトが入ると引数文字が使えなくなる
NGな例
find . -name '*.sql' -print0 | xargs -0 sed -e 's/AAA/BBB/g' {} > {}.bak
正解
find . -name '*.sql' -print0 | xargs -0 sh -c "sed -e 's/AAA/BBB/g' {} > {}.bak"
並列処理
-P 最大プロセス数
: ただし-P 0
でできるだけ多くのプロセス数-L 各プロセスに与える引数の数
: たとえば-L 3
では3個ずつ処理する
-P
だけだと必ずしも最大限(のコア数を)使ってくれるわけではない。-L
と併用して強制する。-L 1
だと前のコマンドの出力結果を1個ずつ処理することになる。
このあたり詳細は
http://d.hatena.ne.jp/pasela/20120122/xargs
find . -name '*.sql' -print0 | xargs -0 -L 1 -P 4 sh -c "sed -e 's/AAA/BBB/g' {} > {}.bak"
デバッグ
-t
で実行されるコマンドを(エラー出力で)見ることができる。-L
や-P
でややこしくなった分割の様子を確認するなどデバッグに。
find . -name '*.sql' -print0 | xargs -0 -t -L 1 -P 4 sh -c "sed -e 's/AAA/BBB/g' {} > {}.bak"
データ周辺の技術 の記事一覧