自分用のLinuxコマンド・シェルコマンド備忘録

細かいけどメモしておきたいものが出てきたので、gitとvimに続いてLinuxコマンド・シェルコマンドの備忘録です。追記したいものが出てきたら随時編集します。

ファイル名がハイフンで始まるファイルを消したり、リネームしたりする

$ rm ./-foo.txt

この様に./から書き始めます。

$ rm -foo.txt

こう書くとオプション扱いになってダメです。

ひとつ前のパスに移動する

$ cd ~
$ cd ../
$ cd -

この場合、cd -で自分のホームディレクトリに移動できます。仕組みはよくわかりませんが、ハイフンで一つ前のディレクトリに移動出来るようです。ハイフンは$OLDPWDと同じだそう。id:calpoさんありがとうございます。

$ ls --

lsの場合はls --とハイフン2つで、ひとつ前のディレクトリ内を見れました。

とあるユーザーのホームディレクトリ

$ cd ~foo
$ cd /home/foo

この2つは同じ動きをします。~が自分のホームディレクトリなのは知ってましたが、この書き方は知りませんでした。

実行中のコマンドをバックグラウンドに移行する

Ctrl+z
^Z
[1]+  Stopped                 sudo port selfupdate
$ bg 1
[1]+ sudo port selfupdate &
$ jobs
[1]+  Running                 sudo port selfupdate &

この例ではMacPorts*1のselfupdateが、久しぶりの実行で割と長かったのでバックグラウンドに持っていってみました。
bg 1の1は省略すると、直近で止めたジョブを操作することになります。

$ fg 1
sudo port selfupdate

バックグラウンドからフォアグラウンドに持ってくる。処理中は標準出力にあまり何も出さないコマンドを実行中だと、フォアグラウンドに移行してからしばらく何も出ないのでちょっと心配になる。

ちなみに、selfupdateは何かを解凍するのが長いらしくbzip2がCPUを使ってました。Ctrl+zで止めるとCPU使用率が下がり、bgfgで再開させると、またCPUリソースを食い始めるので、Ctrl+zはほんとに止めてるみたい。

バックグラウンドのジョブをkillする

$ jobs
[1]+  Running                 sudo port selfupdate &
$ kill -15 %1

ジョブ番号は%を付けて指定できます。

$ %1
$ fg 1

ついでに%1でfg 1と同様に、バックグラウンドからフォアグラウンドに移行出来ます。

終了ステータス*2

コマンド終了時には「終了ステータス(exit-status)」と呼ばれる、コマンドの成否を表す数値が特殊変数 $? に自動で設定される。 各コマンドにより異なるが、一般的にはコマンド成功時には「0」が、失敗時には「1」が設定される。

command
echo $?

→ 直前に実行したコマンドの成否は、特殊変数 $? に設定されている値で確認する。

終了ステータス | UNIX & Linux コマンド・シェルスクリプト リファレンス

終了ステータスってのがあるみたいだけど、phpやperlと意味が逆でちょっと馴染めなさそう。

ハイフンは標準出力、を表すことが多い

特に決まりというわけではないそうですが、多くのコマンドが、ハイフン(-)ひとつを標準入出力のことと読み替えて処理をしてくれます。

【linux】コマンドの結果を標準出力に出す at softelメモ

UNIX文化の習慣でハイフンは標準出力を表すことが多いらしい。でもあくまでコマンドそれぞれの実装しだいなんだとか。

ではwgetで試してみる。

wget -O index.html http://www.softel.co.jp/

wgetの結果はindex.htmlに保存される。では標準出力に出力して、パイプでwcに渡して行数を数えるには、

wget -O - http://www.softel.co.jp/ | wc
  • Oオプションの保存先にハイフンを指定。
【linux】コマンドの結果を標準出力に出す at softelメモ

lnコマンドでシンボリックリンクを作る際のパス

lnコマンドの引数に相対パスを使うとややこしい。ln -s シンボリックリンクを基準にした実ファイルへのパス 作るシンボリックリンクのパスという事になり、一応書いてみたけど多分余計混乱する。

$ cd ~
$ ls usr/local/bin/
foo
$ ls
usr bar

この様に、~/usr/local/bin/fooと~/barというファイルがあるとして、それぞれにシンボリックリンクを作る場合の例。

$ ln -s usr/local/bin/foo foo
実ファイル ~/usr/local/bin/foo
シンボリックリンク ~/foo
$ ln -s ../../../bar usr/local/bin/bar
実ファイル ~/bar
シンボリックリンク ~/usr/local/bin/bar

curlでHTTP Headerを確認する

$ curl -I GET http://example.com/

HTTP Headerが標準出力される。ただし、HTTP HEADメソッド。

$ curl -I -X GET http://example.com/

HTTP GETメソッドを指定する事もできる。
httpサーバーは、HEADに対しGETと同じHTTP Headerを返さないといけないルールになっているけど、何か事情があってGETとHEADで帰ってくるHeaderが違うなんて事があるならこれで。

$ curl -i -X POST http://example.com/

-Iオプションだと、HTTP HeaderだけでBodyが見られないので-iオプションの方が便利かも。

sedコマンドの使い方

-eは省略可能
$ sed -e 's/foo/bar/'
$ sed 's/foo/bar/'
デリミタは変更可能
$ sed 's|http://|https://|'
正規表現である
$ sed 's| HTTP/1\.[0-1]||'
デリミタ色々
$ sed 's|foo|bar|' # |
$ sed 's=foo=bar=' # =
$ sed 'sAfooAbarA' # A
$ sed 's(foo(bar(' # (

使う機会はほぼ無いと思うけど、やってみたら*3英字とかも出来た。

$ sed 's(foo)(bar)' # ダメ

残念ながらPerlのように括弧を括弧的なデリミタとして使う事は出来ないみたい。

行を狭める

例)
1行目だけを置換したい場合
$ sed '1s/linux/リナックス/g' ファイル名 > 出力先ファイル名

1〜10行目だけを置換したい場合
$ sed '1,10s/linux/リナックス/g' ファイル名 > 出力先ファイル名

2行目から最終行までを置換したい場合
$ sed '2,$s/linux/リナックス/g' ファイル名 > 出力先ファイル名

1行目から先頭がlではじまる行までを置換したい場合
$ sed '1,/^l/s/linux/リナックス/g' ファイル名 > 出力先ファイル名

先頭がlではじまる行だけを置換したい場合
$ sed '/^l/s/linux/リナックス/g' ファイル名 > 出力先ファイル名

先頭がlではじまる行以外を置換したい場合
$ sed '^t/!s/リナックス/g' ファイル名 > 出力先ファイル名

http://www.nurs.or.jp/~sugi/sed.htm

こんな事も出来るらしい。

一括置換

$ find . -type f -print0 | xargs -0 sed -i -e 's/foo/bar/g'
$ find . -type f -print0 | xargs -0 perl -pi -e 's/foo/bar/g'

# $ find . -type f | xargs sed -i -e 's/foo/bar/g'
# $ find . -type f | xargs perl -pi -e 's/foo/bar/g'

-print0、-0についてはMacでfindとxargsをパイプで繋いで使うなら -print0 と -0 を必ず使うメモ - kanonjiの日記を参照。

findで除外

$ find . ! -name "*.svn*" -a -name "*.html"

.svnフォルダを除外 and *.html。ただし、厳密に.svnフォルダに絞れているかは疑問。foo.svn.htmlなんてファイルがあったら混ざるかも。

$ find . ! -path "*.svn*" -a -name "*.html"

案の定漏れてたので改めて調べて-pathを見つけました。

cutコマンドで空白をデリミタにする

$ cut -d' ' -f1,3 foo.log

GNU Screen中に画面が固まったら復帰できる場合がある

C-a C-q

これは、元々C-a C-sで画面を固定できるが、知らないうちにやってしまった場合に、復帰するためのC-a C-qを試してみるというだけの事。

ASCII制御コード 信号 GNU Screenで入力 Terminalから入力
DC1 XON C-a C-q C-q
DC3 XOFF C-a C-s C-s

別にGNU Screenの機能という訳ではなく、ASCII制御コードのDC1, DC3に対してよくある動きらしい。例えばtopコマンドを動かしてる際にC-sを入力すると、画面が固定される。あらゆる状況でそうなるという訳じゃないと思うけど。この辺まだちゃんと把握できてない。

C-aはGNU Screenのコマンド文字。

コマンドをグルーピングする

$ { cat 20120601.log; cat 20120602.log; } | grep foo

波括弧でコマンドのグルーピングが出来る。

{ cat 20120601.log; cat 20120602.log; } #OK
{ cat 20120601.log;cat 20120602.log;}   #OK
{cat 20120601.log; cat 20120602.log; }  #NG

何故か左の波括弧とコマンドの間のスペースは、無くすとエラーになります。あと最後のコマンドの後ろの;*4も必須。

make -n installでdry run

$ man make
[snip]
-n, --just-print, --dry-run, --recon
    Print the commands that would be executed, but do not execute them.
[snip]

dry runでinstall時にどこにファイルが配置されるか、インストール前に確認できる。

curlでリジューム

$ curl -C - -o foo.tar.gz http://www.example.com/foo.tar.gz
** Resuming transfer from byte position 772428440
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 77.4M  100 77.4M    0     0   326k      0  0:04:03  0:04:03 --:--:--  163k

こんな感じでリジュームできます。不安定な回線でちょっとでかいファイルをダウンロードしないとならない時なんかに活躍。

なお-C 10だと、ファイルの頭から11byteからリジュームらしい。

curlでPOST, PUT, DELETE

# GET
$ curl http://localhost:3000/users.xml
$ curl http://localhost:3000/users/3.xml

# POST
$ curl http://localhost:3000/users -X POST -d "user[name]=postman" -d "user[age]=19"

# PUT
$ curl http://localhost:3000/users/4 -X PUT -d "user[name]=putman" -d "user[age]=20" 

# DELETE
$ curl http://localhost:3000/users/5 -X DELETE
curlコマンドからのRESTなリクエストを送り方 - ちくわプログラマにっき

curlでCookie

$ curl -c foo.txt http://www.example.com/ #Cookie保存
$ curl -b foo.txt http://www.example.com/ #Cookie送信
$ curl -b "foo=bar; baz=qux;" http://www.example.com/ #Cookie送信

大体こんな感じ。

$ curl --cookie-jar foo.txt http://www.example.com/ #Cookie保存
$ curl --cookie foo.txt http://www.example.com/ #Cookie送信

一応、オプションをフルネームで書くとこうなる。

git grepの様にフォルダ内をgrepする

$ grep foo -r .
$ find . -type f -print0 | xargs -0 grep foo

find xargs grepでやってたけどgrepの-rオプションがすごい便利です。find xargs grepの方はMacでfindとxargsをパイプで繋いで使うなら -print0 と -0 を必ず使うメモ - kanonjiの日記という事もあって、コマンドが長くなって割とめんどくさい。

マッチしたファイル名のリストを取得
$ grep foo -r . -l

単にgrepの-lオプションってだけだけど、忘れがちなのでメモしておきます。

一部除外する
$ grep --exclude=.svn foo -r .

psコマンドでとにかく全情報を出力する

$ ps aux
$ ps auxf #ツリー構造で表示。Macだと使えない。
a 全ユーザーのプロセスを表示
u ユーザー名やその他詳細な情報を表示
x 制御端末の無いプロセスを表示

grepのハイライトをパイプしたlessでもハイライトする

$ grep foo --color=always | less -R

--color=autoでも行けるっぽい情報も有ったけど、自分の環境では--color=alwaysじゃないとだめだった。

curlでajaxアクセスを装う

$ curl -H "X-Requested-With: XMLHttpRequest;" http://example.com

単にヘッダーに追加しているだけなんだけど、jQuery等を使うと、この拡張ヘッダーがつくらしいので、手動でつける事でajaxアクセスを装う。ヘッダーに追加する以外は普通のアクセスなのでただ付けるだけなんだけど。

Macでポートを使っているプロセスを調べる

$ lsof -i :3306
COMMAND     PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
mysqld    16099 myuser   12u  IPv4 0xffffff8026416c00      0t0  TCP *:mysql (LISTEN)
MySQLWork 40355 myuser   22u  IPv4 0xffffff80189f3160      0t0  TCP localhost:51146->localhost:mysql (CLOSED)

多分Mac専用。

環境変数とシェル変数を一覧

$ printenv #環境変数の一覧
$ set         #シェル変数の一覧
$ printenv TERM #TERMの値だけ表示

HDD全域からファイルをfindする

$ find / -name foo.log 2>/dev/null

Permission denied が鬱陶しいので、標準エラーを捨てる。

大体どのフォルダが容量食ってるのか確認する

$ du -hxd 1

もしかしたらMac OS Xのオプション体系かも?

ファイルのmd5 sha1ハッシュ値を求める

$ md5sum foo.txt
$ sha1sum foo.txt

$ sha1sum foo.txt bar.txt #複数指定
$ sha1sum *.txt                #ワイルドカード

# Mac OS X
$ md5 foo.txt
$ openssl sha1 foo.txt

MacはLinuxと使えるコマンドが違う様子。
複数指定やワイルドカードは、たまたま調べたところに書いてあったのでsha1sum以外でも使えるかしらない。

2007年現在、SHAは生成するビット長が異なるSHA-1 (160ビット)、SHA-224、SHA-256、SHA-384、SHA-512の5種類が存在している。

Secure Hash Algorithm - Wikipedia

グループのメンバーで1993年にはじめに発表されたものは、公式にはSHAと呼ばれている。しかしその後のものと区別するためにしばしばSHA-0と呼ばれている。2年後、SHAに初めての後継となるSHA-1が発表された。さらにSHA-224、SHA-256、SHA-384、SHA-512と4つの変形が、増加する出力の範囲とわずかなデザインの違いで発行されている。それらはしばしばまとめてSHA-2といわれている。

Secure Hash Algorithm - Wikipedia

shaって名前紛らわしいので、メモ。

標準入力をコマンドに渡す

$ echo 'foo' | example

$ example <<_EOT_
foo
bar
_EOT_

example foo.txtの様に、ファイルをオプションに取るコマンドが有るとして、ファイルを作らずに内容だけコマンドに渡すには、標準入力が使えたりします。echoをパイプするのは直ぐ思いついたけど、改行を含むなら、ヒアドキュメントをリダイレクトする方法が使える。

リダイレクト (CLI) - Wikipedia
まさかこういう情報をWikipediaで見つけるとは思ってなかった。

ヒアドキュメント内のシェル変数を展開しない様にする

#「$」を表示したいときは「\$」のようにエスケープする
cat <<_EOT_
  ヒアドキュメント中では変数も使用できます。
  \$1 は $1 です。

_EOT_

# 終了文字をエスケープするとヒアドキュメント中の変数は展開されない
cat <<'_EOT_'
   シングルクオートで終了文字を囲むと変数は無視されます。
  \$1 は $1 です。
  `echo "コマンド置換も無視されます。"`

_EOT_

cat <<\_EOT_
  バックスラッシュでも同様です。
  \$1 は $1 です。
  `echo "コマンド置換も無視されます。"`

_EOT_
入力と出力 | UNIX & Linux コマンド・シェルスクリプト リファレンス

ヒアドキュメント内では、シェル変数が使えるため$をそのまま文字として扱いたい場合、上記2つの方法で出来るとの事。

gccでよく使うオプション、らしい

知っていると便利な gcc のオプション

標準入力と標準出力を同時にリダイレクトする

wc -l < foo.log > /tmp/log-line-count

foo.logを標準入力にリダイレクトしてwcに渡し、その結果の標準出力を/tmp/log-line-countにリダイレクトして書き込んでます。

文字列内でコマンド実行

$ echo "foo`echo .`bar"
$ echo "foo$(echo .)bar"

どちらもfoo.barと出力。

$ echo 'foo`echo .`bar' # foo`echo .`bar
$ echo 'foo$(echo .)bar' # foo$(echo .)bar

シングルクォートだと実行されない。

kshやbashの場合、「$(コマンド)」($+単一の丸括弧)はバッククォート指定「`コマンド`」と同じ。
$()が使えるシェルなら、バッククォートよりも$()を使うべき。(バッククォートは古い形式であり、ネストできないし、開始と終了もまぎらわしい)

http://www.ne.jp/asahi/hishidama/home/tech/unix/sh.html#backquote

バッククォートよりも$()が推奨される。

echo $(cd $(dirname $0);pwd)

http://unoh.github.io/2008/09/02/bashtips.html

$()でのネストの例。

関数の有無で分岐

$ type __git_ps1 > /dev/null 2>&1 && echo ok  # okが出力
$ type __git_ps1_ > /dev/null 2>&1 && echo ok # 出力されない

Bashでコマンドの存在チェックはwhichよりhashの方が良いかも→いやtypeが最強 - Qiita

シェルスクリプトでif文の場合
if type __git_ps1 > /dev/null 2>&1 ; then
    # Do something.
fi

標準出力、標準エラーは捨てて、終了ステータス(exit-status)で判別するんだと、思う。

関数の有無じゃないけど、ちょっと似てる事という事でtestコマンド使ってcron
 0 0 * * * /usr/bin/test $( date -d '+1 day' +\%d ) -eq 1 && /home/hoge/cron/foo.sh

↑で、毎月月末日の零時にfoo.shが実行されるようになります。

http://qiita.com/yadok/items/5c99cecdb17a436351f2

wcの結果から空白を取り除く

$ cat file | wc -l | td -d ' '

wcは行数の前に幾つかの空白が入るので、場合によっては邪魔になります。その空白をtdで取り除きます。

変数に入れた改行を出力する

$ FOO=$(ls -1)
$ echo $FOO
foo bar baz

$ echo "$FOO"  #ダブルクォートで囲むと改行が出力される
foo
bar
baz

findしたファイルをパイプで別コマンドに1個ずつ渡したい

$ find . -type f -name *.csv | while read f; do some_command $f; done

パイプ出力を現在のシェル上のwhileに喰わせる上手いやり方 - Qiita

ちなみに、別コマンドに渡すんじゃなくて変数に入れるような場合は、whileがサブシェルで実行されてしまうらしく、whileの中で変数に入れても、消えてしまうらしいです。

vmstatに日時を付けてファイルに出力する

$ vmstat 1 | gawk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0 } { fflush() }' >> vmstat.log
$ vmstat 1 | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0 } { system(":") }' >> vmstat.log

なぜかはわからないのだが、ファイルへの書き込みが、パイプ後ろのプロセス(上記なら awk や grep)が終了してから行われているようだ。なので、3秒おきに10回出力した結果は、awk が正常終了した後に、ファイルへ書き込まれる。途中で vmstat を強制終了した場合には、ファイルへは何も書き込まれない。

[snip]

gawk で、fflusu() 関数を使えばパイプがリフレッシュされるとのことで、次のようにしたらうまくいった。

# vmstat -n 3 | gawk '{ print strftime("%Y/%m/%d %H:%M:%S"), $0 } { fflush() }' >> vmstat.log

awk でも、system() 関数を使えばパイプがリフレッシュされるとのことで、(ちょっときたないが)次のようにしたらうまくいった。

# vmstat -n 3 | awk '{ print strftime("%Y/%m/%d %H:%M:%S"), $0 } { system(":") }' >> vmstat.log
(今さら) vmstat の結果に時間をつけてファイルに出力する - あしのあしあと

*1:このエントリー、Linuxコマンドの備忘録だったけど、まぁいっか

*2:exit-status

*3:ざっとしか確認してないけど

*4:セミコロン