プログラマのための IT 教科書
第1部 開発者の道具箱

コマンドラインで生きる

読了目安 40

この章を読むとできるようになること
  • パイプとリダイレクトを組み合わせて目的の出力を作れる
  • ログから必要な行だけを抜き出せる
  • パーミッションエラーの原因を自分で特定できる

前の章で、シェルは「プログラムを起動する係」だと分かりました。 この章では、そのプログラムたち(コマンド)を実際に使えるようにします。

覚えるコマンドは20個もありません。大事なのは個々のコマンドではなく、 小さな道具をつなげて、その場で必要な道具を組み立てるという考え方です。 これが分かると、ログ調査やデータ確認が劇的に速くなります。

ファイルとディレクトリを移動する

まず基本の5つです。

コマンド意味
pwd今いる場所(print working directory)
ls中身の一覧(list)
cd移動(change directory)
mkdirディレクトリを作る
touch空ファイルを作る

パスの書き方

書き方意味
/etc/hosts絶対パス。ルート(/)からの完全な道順
config/app.yml相対パス。今いる場所からの道順
.今いるディレクトリ
..1つ上のディレクトリ
~ホームディレクトリ

cd .. で1つ上に、cd ../.. で2つ上に上がれます。 cd を引数なしで打つとホームに戻ります。迷子になったらこれです。

ls はオプションで表示が変わります。

ls        # 名前だけ
ls -a     # 隠しファイル(.で始まるもの)も表示
ls -l     # 権限・サイズ・名前を1行ずつ
ls -la    # 両方

.zshrc のようなドットで始まるファイルは ls では見えません-a が必要です。

中身を見る

コマンド用途
cat file全部表示する
head -5 file先頭5行
tail -5 file末尾5行
tail -f file末尾を追い続ける(ログの監視)

cat は短いファイル向けです。数万行のログに cat を打つと画面が流れ切って何も読めません。 まず head で形を見る、というのが実務の順番です。

演習

app.log から ERROR が含まれる行だけを表示してください。

コマンドを入力して Enter を押してください。

~/work/api%
  • 未達最後の出力が指定どおりになっている
  • 未達grep で絞り込んだ
help で使えるコマンドが見られます

標準入出力とパイプ

ここがこの章の中心であり、コマンドラインの本質です。

Unix のコマンドは、ほとんどが次の形をしています。

入力(標準入力) → コマンド → 出力(標準出力)
                            → エラー(標準エラー出力)

出力先が3つに分かれているのがポイントです。 そして、あるコマンドの出力を、次のコマンドの入力に繋げられます。これがパイプ|)です。

grep ERROR app.log | wc -l

これは「app.log から ERROR の行を抜き出し、その結果の行数を数える」という意味です。 grep は行を絞り込む道具、wc -l は行を数える道具で、 それぞれは何も知らないまま繋がっています。

演習

app.log の ERROR の行数を数えてください。出力は数字だけになるようにします。

コマンドを入力して Enter を押してください。

~/work/api%
  • 未達最後の出力が指定どおりになっている
  • 未達パイプ(|)でコマンドをつないだ
help で使えるコマンドが見られます

リダイレクト — ファイルに書き出す

画面ではなくファイルに出力したい時は > を使います。

grep ERROR app.log > errors.txt    # 上書き
grep ERROR app.log >> errors.txt   # 追記
演習

app.log の ERROR 行を errors.txt というファイルに保存してください(画面に出すのではなくファイルに書きます)。

コマンドを入力して Enter を押してください。

~/work/api%
  • 未達errors.txt の内容が指定どおりになっている
help で使えるコマンドが見られます
> は容赦なく上書きする

> は既存のファイルを確認なしで空にしてから書き込みます。 >>(追記)のつもりで > を打って、それまでのログを全部消す事故がよくあります。

大事なファイルに書き出す時は、まずリダイレクトなしで実行して結果を確認し、 それから > を付けるのが安全です。

標準エラー出力

エラーは標準出力とは別の経路で出ます。だから次のようなことが起きます。

ls /nonexistent > out.txt      # エラーは画面に出て、out.txt は空
ls /nonexistent > out.txt 2>&1 # エラーも out.txt に入る

2>&1 は「標準エラー出力(2番)を、標準出力(1番)と同じ場所に流す」という意味です。 CI のログ設定などでよく見かけます。

テキストを操る

パイプで繋ぐための道具たちです。ここでは実務で本当に使うものだけ挙げます。

grep — 行を絞り込む

grep ERROR app.log        # ERROR を含む行
grep -i error app.log     # 大文字小文字を無視
grep -v INFO app.log      # INFO を含まない行(反転)
grep -n ERROR app.log     # 行番号つき
grep -c ERROR app.log     # 件数だけ
grep -r "TODO" .          # ディレクトリを再帰的に検索

grep -r はリポジトリ内の文字列検索に毎日使います。

sort / uniq — 並べて数える

sort file            # 並べ替え
sort -r file         # 逆順
sort -n file         # 数値として並べ替え
uniq file            # 連続する重複をまとめる
uniq -c file         # 重複の個数をつける
uniq の前には必ず sort

uniq連続している同じ行しか見ません。 バラバラに散らばった重複はまとめられないので、先に sort して並べておく必要があります。 sort | uniq -c は定型句として覚えてしまってください。

awk / cut — 列を取り出す

行の中から特定の列だけ欲しい時に使います。

awk '{print $1}' access.log      # スペース区切りの1列目
cut -d',' -f2 data.csv           # カンマ区切りの2列目

awk は本来もっと高機能なプログラミング言語ですが、 実務では9割が {print $1} のような列の取り出しです。

sed — 置換する

sed 's/old/new/' file      # 各行の最初の1つを置換
sed 's/old/new/g' file     # 各行の全部を置換

ここまでの道具を組み合わせると、こういうことができます。

演習

access.log から、アクセス数の多い IP アドレスの上位5件を「回数つき」で出してください。1行のパイプで書けます。

コマンドを入力して Enter を押してください。

~/work/api%
  • 未達出力に「203.0.113.10」が含まれている
  • 未達uniq -c で件数を数えた
  • 未達head で上位だけに絞った
help で使えるコマンドが見られます

この5コマンドの組み合わせは、アクセスログから攻撃元 IP を特定したり、 エラーの種類ごとの件数を出したりする時に、そのままの形で使えます。

ファイルを探す

find . -name "database.yml"    # 名前で探す
find . -name "*.log"           # パターンで探す
find . -type d -name "config"  # ディレクトリだけ
演習

このプロジェクトのどこかに database.yml があります。場所を find で探してください。

コマンドを入力して Enter を押してください。

~/work/api%
  • 未達出力に「config/database.yml」が含まれている
  • 未達find で探した
help で使えるコマンドが見られます

中身で探すなら grep -r名前で探すなら find です。

権限

ls -l の左端に出ている文字列が権限です。

-rwxr-xr-x  deploy.sh

最初の1文字がファイル種別(- はファイル、d はディレクトリ)で、 残り9文字が3文字ずつ「所有者・グループ・その他」の権限を表します。

文字意味数値
r読める4
w書ける2
x実行できる1

rwx = 4+2+1 = 7、r-x = 4+1 = 5 なので、rwxr-xr-x755 です。

よく使う値意味
644所有者は読み書き、他は読むだけ(普通のファイル)
755上に加えて全員が実行できる(スクリプト・ディレクトリ)
600所有者だけが読み書き(秘密鍵など)
演習

scripts/deploy.sh を実行しようとすると Permission denied になります。実行できる権限(755)に変えてください。

コマンドを入力して Enter を押してください。

~/work/api%
  • 未達scripts/deploy.sh の権限が 755 になっている
help で使えるコマンドが見られます
chmod 777 を打ちたくなったら止まる

権限エラーが出た時に chmod 777(誰でも何でもできる)で解決しようとするのは、 鍵が開かないからドアを外すのと同じです。

まず ls -l で今の権限を見て、 「誰が」「何を」できないのかを確認してください。たいていは 755 か 644 で足ります。 SSH の秘密鍵は 600 でないと逆にエラーになります

プロセス

動いているプログラムの一覧を見たり、止めたりします。

ps aux              # 動いているプロセスの一覧
ps aux | grep node  # node に絞る
kill 12345          # プロセス番号を指定して終了を要求
kill -9 12345       # 強制終了(最後の手段)
lsof -i :8080       # ポート 8080 を使っているものを調べる

lsof -i :8080 は、 「ポート 8080 は既に使われています」というエラーが出た時に犯人を特定するコマンドです。 実務で頻繁に使います。

この節はシミュレーションでは試せません

プロセス系のコマンドは実際に動いているプログラムが必要なので、 この教科書のターミナルでは動きません。実機で ps aux | head を試してみてください。

実務の落とし穴まとめ

  1. rm に取り消しはない — ゴミ箱に入らず、その場で消えます。 rm -rf は特に、打つ前に ls で対象を確認する習慣をつけてください
  2. > は上書き — 追記のつもりで打つとデータが消えます
  3. uniq の前に sort — 忘れると集計結果が壊れます(エラーにならないので気づきにくい)
  4. スペースを含むファイル名rm my file.txt は2つのファイルを消そうとします。 クォートで囲むか、Tab 補完に任せてください
  5. chmod 777 で解決しない — 権限は「誰が何をできるか」の設計です

まとめ

  • コマンドは小さな道具。パイプで繋いでその場の道具を組み立てるのが本質
  • grep で絞り、sort | uniq -c で数え、head で上位を見る
  • > は上書き、>> は追記。エラー出力は別経路(2>&1
  • 名前で探すなら find、中身で探すなら grep -r
  • 権限は r=4 w=2 x=1 の足し算。755644 を覚えれば大体足りる
  • rm は取り消せない

章末問題

access.log の中で、最もアクセスの多い IP を調べたい。sort を2回使う理由は何ですか。

ログを1つのファイルに集め続けたいので、毎日 grep ERROR app.log > errors.txt を実行しています。何が問題ですか。

デプロイスクリプトが Permission denied で動きません。最初にやるべきことは?

次の章は vim です。サーバー上で設定ファイルを直す時、git commit でエディタが開いた時、 kubectl edit を打った時 —— 逃げられない場面が必ず来ます。

読み終わったら記録しておくと、目次で進み具合が分かります。