前置き
このテキストは「本格的にターミナルにこもって作業をはじめる前にとりあえず知っておいた方が良いこと」をまとめたものです.情報系の研究室に配属されたばかりの新4年生,というかなり絞られた対象を想定読者にしているため,一部偏った記述があるかもしれません.レベル感としては「ls とか cd が何をするコマンドなのかは分かるし,C のプログラムをターミナルから emacs を叩いて書いたこともある.標準入力とかパイプとか言われれば使えるような気がする.が,複雑なコマンドを書かれるとよく分からないし,ターミナルの中で生活しているわけではない」…くらいを想定しています.
参考文献の列挙など,今後加筆・修正をおこなう可能性は高いです.誤りがあればコメント欄などでご指摘いただけると助かります.
はじめに
コマンド とは,いわゆる黒い画面で打ち込まれるアレのことです.たとえば学生実験などで触ってきた ls や cd は典型的なコマンドです.コマンドを利用して計算機とやりとりをする CLI(command line interface)に慣れると,GUI(graphical user interface)を介して計算機とやりとりをするよりも,ずっと正確に,高速に,大量の処理を実行できるようになります.研究に使う各種ツールも大抵は CLI のみを備えていますし,これから自分で作成する研究のためのスクリプトも入出力のインタフェースは CLI を用意することが多いでしょう.慣れるまでは大変かと思いますが,手元の Mac をいじるときもできるだけマウスを使わず,ターミナルからコマンドを入力する習慣をつけておくと,後々お得です.
黒い画面を実現するソフトウェアのことは ターミナルエミュレータ(あるいは簡単に ターミナル) と呼びます.OS X であれば,とくにこだわりがなければ,iTerm2 が高機能でカスタマイズもしやすくおすすめです.
この資料では,黒い画面に引きこもる前に知っておいた方が良い基礎知識をごく簡単にまとめておきました.
- コマンドの option や argument の指定の仕方がわかる
- 自分で作った python のスクリプトファイルを実行可能ファイルにできる
- リダイレクトやパイプを迷いなく利用できる
くらいが目的地です.
ここで扱っているようなシェル・ターミナル周りの知識をチュートリアルの形式でじっくり学びたい人は書籍にあたるのが良いでしょう.類書の中でも,最近出版された『新しい Linux の教科書』(三宅,大角.SB Creative,2015)は特におすすめです.1〜2週間で十分読みこなせる分量ですし,今後ずっと使える知識となるので,時間がある人は是非取り組んでみてください.
語句はできるだけ英語表記と日本語表記を併記しました.プログラミングに関する知識は,ほとんどの場合,英語で検索をした方が正確な情報が得られます.「アレを引用してはいけない」と耳にタコができるほど聞かされてきたであろう Wikipedia も,英語版であれば,計算機科学に関する項目はかなり正確です.これを機に英語で検索することに少しずつ慣れていきましょう.
キーワード
このテキストで取り上げた主な語句は以下の通りです(相当軽く触れているものも含みます).すべての語句について意味や使い方が分かるようでしたら,これ以上読み進める必要はないでしょう.
- CLI,GUI,ターミナルエミュレータ
- コマンドの option(オプション)と argument(引数),short option,long option,
--helpオプション,manコマンド,pager(ページャ),lessコマンド -
permission(パーミッション),
chmodコマンド,shebang(シバン),サーチパス,whichコマンド,「パスを通す」 -
stdin(標準入力),stdout(標準出力),stderr(標準エラー出力),file descriptor(ファイル・ディスクリプタ),redirect(リダイレクト),pipe(パイプ),process substitution(プロセス置換),command substitution(コマンド置換),here string(ヒア・ストリング),here document(ヒア・ドキュメント)
1. ひとつのコマンドを使う
まずは,CLI のコマンドの基本形を理解しましょう.
- コマンドには option や argument がつけられること
- option には short option と long option があること
- flag(引数が必要な option)の使い方
を理解することが目標です.
CLI コマンドの基本形
option
$ ls -a のように ls に -a を加えて実行すると,不可視ファイル(ファイル名が . ではじまるファイル)も確認できるようになります.$ cd でホームディレクトリに移動したあと $ ls -a を実行すると大量にファイルが隠れていることがわかるでしょう.この -a のようにコマンドの機能を一部変更・拡張するために加える命令を option (オプション)と言います.
$ ls -a の代わりに $ ls --all と打っても全く同じことが起きます.ハイフン(-)がふたつ付いていることに注意してください.多くの場合,オプションは短いもの(-a)と長いもの(--all)のふたつが用意されています.それぞれ short option,long option と言います.短いオプションは打鍵が楽で,長いオプションは可読性に優れています.慣れたものやよく使うものは short option を使い,慣れないもの(見直したときに混乱しそうなもの)や他の人も利用するシェルスクリプトに用いるオプションは long option を利用すると良いでしょう.
Mac を利用していて,かつ GNU coreutils を導入していなければ,$ ls --all は動かなかったかもしれません.**同じ名前のコマンドでも,環境によって仕様が異なる可能性があります.**Web サイトや書籍のコマンドを写経して動かなかった場合は,自分がいま使っているコマンドの仕様を調べましょう.調べ方については後述します(--help オプションおよび man コマンド).
※このドキュメントを含め,多くの Web サイトや書籍が GNU/Linux 環境を想定しています.Mac を使っている場合でも Linux 系のコマンドを導入しておけば混乱なく学習を進めていけるでしょう.OS X 環境に Linux コマンドを導入する手順についてはこちらにまとめておきました.
次の例に移りましょう.$ cut -f 1 foo.txt と入力すると,タブ区切りテキスト foo.txt の一列目だけを取り出すことができます.ここでは -f 1 の部分がオプションです.-f1 としても同じ効果が得られます.先の -a と違って,-f だけでは効果が得らず,引数 1 が必要です.
このように引数が必要なオプションのことを特に flag(フラグ),先の $ ls -a の -a のように引数をとらないオプションのことを特に switch(スイッチ)と言い表して区別する場合もあります.
引数が必要なオプションであるフラグにも,多くの場合ロングオプションが用意されています.たとえば cut の -f のロングオプションは --fields です.ロングオプションへの引数は = で指定します.つまり,$ cut -f1 foo.txt と同じことをロングオプションを用いて書きたければ,$ cut --fields=1 foo.txt となります.
補足として,短い形式のスイッチは続けて入力することができます.たとえば $ ls -l -h -G -A -F は $ ls -lhGAF と入力できます.これは -lhGAF というロングオプション ではありません ので注意してください.(ハイフンもひとつですね.)
この節のまとめです.次の表を見て「はいはい」と思えればOKです.
| option | short | long | 連続入力 |
|---|---|---|---|
| switch | -a |
--all |
-lhGAF(= -l -h -G -A -F) |
| flag |
-f 1, -f1
|
--fields=1 |
argument
コマンドには,オプション以外に argument(引数)を与えることができます.$ head foo.txt と入力すると,foo.txt の冒頭の一部だけが出力されます.ここでの foo.txt のようにコマンド自体に与える引数を argument(引数)と言います.argument としてはファイルへのパスや文字列を与えることが多いでしょう.
たとえば次の例では,sed コマンドの一番目の引数に文字列,二番目の引数にファイルへのパスを与えています:$ sed 's/foo/baa/g' baz.txt.引数を与える順番はコマンドにより指定されています.
コマンドの使い方を調べる
オプションや引数の与え方をはどのように調べれば良いのでしょうか.ここでは --help オプションや man コマンドを紹介します.
※何かを新しく学ぶとき,まずはじめに「困ったときにどのように調べれば良いか」を調べるのは良い習慣です.ヘルプのためのコマンドの他には,公式のドキュメントのURL,定番の書籍,身近な人のうち誰がその対象に詳しいかという情報(これは重要です),有益な情報が集まっている blog,掲示板,メーリングリスト,などがあるでしょう.
--help を使う
多くのコマンドには次のロングオプションが標準的に用意されています.
-
--help: ヘルプを表示します. -
--version: コマンドのバージョンを表示します.
コマンドの仕様について不明点がある場合は,まずは $ [コマンド名] --help で調べましょう.よく使われるオプションなどはここですぐに調べることができます.長いヘルプが出てくる場合は $ [コマンド名] --help | less としてページャ(後述します)に渡すと良いでしょう.
man を使う
--help オプションよりも詳しい情報が必要な場合は man(manual)コマンドを利用しましょう.$ man [コマンド名] を入力するとページャが開き,詳細なマニュアルが表示されます.ページャについては後述します.
コマンドラインツールで不明点があれば,まず --help や man にあたるというのは良い習慣です.一般に,**プログラミングのスキルと公式のドキュメントを読む力の間には強い相関があります.**多くのプログラミング言語(たとえば Python 2.x)やツール(たとえば Bash)が充実したドキュメントを公開しています.
もちろんググる力(ちから)も重要です.たとえば,典型的で便利な利用例,分かりやすい解説などは,網羅的解説をを目的とした --help や man では得られませんし,ネット上の掲示板や blog 記事から有益な情報を得られることもしばしばです.例えば Stack Overflow で多くの like を集めている回答は,それなりに信用できる(ないし分りやすい)と捉えて良いと思います.
less の使い方
pager(ページャ)というのはテキストファイルの中身を閲覧するためのソフトで,デフォルトであれば less が設定されているかと思います.(ほかによく使われるページャとしては,日本語の取り扱いにすぐれた lv などがあります.)ファイルの編集の必要がないときは,エディタ(vim や emacs)ではなくページャを使うのがおすすめです.特にファイルが巨大なときは,ページャの軽快な動作に助けられるでしょう.
ここでは,広く使われるページャである less の使い方を簡単にまとめておきます.
まずはファイル内検索をできるようになりましょう.vim ライクなコマンドで検索することができます./ と打ったあと検索ワードを入れるて Enter で検索開始,n を押すと次の候補に移動します.
-
/: search forward for a pattern -
?: search backward for a pattern -
n: next (repeat previous search) -
N: previous (repeat previous search, but in the reverse direction)
画面上で動くために,vim ライクなコマンドと emacs ライクなコマンドが用意されています.とりあえず f と b で1画面分ずつ前後に動けることと,g と G でファイルの冒頭および末尾に移動できることを覚えておけば困らないでしょう.
- line
-
j,Ctrl+n,enter -
k,Ctrl+p
-
- window
-
f,Ctrl+v,space -
b,Alt+v
-
- half window
Ctrl+dCtrl+u
- file
g-
G(Shift+g)
他にも以下のような機能があります.ここではとりあえず q で元の画面に戻れることだけ覚えておいてください.(もちろん,$ man less とすればもっと潤沢な情報が出てきます.)
-
ma: mark the current position with the letter 'a' -
'a: go to the marked position 'a' -
q,ZZ: exit -
v: open the file with the default editor
2. 実行権限とサーチパス
ここでは,自分で作った python スクリプト foo.py を ls などと同様に直接実行可能にする方法を説明します.
- ファイルに実行権限を付与する
- シバンを書く
- パスを通す
あたりの意味がわかり,自分でも実行できるようになることが目標です.
パーミッションとシバン
C のソースをコンパイルして作った実行可能ファイル(たとえば a.out)は $ ./a.out と直接呼び出すことができました.一方,python のスクリプトファイル(たとえば foo.py)は $ python2 foo.py のようにまず python2 を呼び出す必要があります.(違いを強調させるために後者を書き直せば $ python2 ./foo.py となります.)よく使うスクリプトであれば,a.out の場合のように直接ファイルを叩ける方が便利です.この節では,スクリプトファイルを直接呼び出すための方法を説明します.
最初にすべきなのは permission(パーミッション)の変更です.$ ls -l /bin | head と入力してみましょう.各実行可能ファイルの一番左側にパーミッション(-rwxr-xr-x) と表示されていることが確認できます.ここでは詳細な説明は省きますが,x の部分が実行可能(e“x”cuable)であることを示しています.一方,手元の python スクリプトのパーミッションを $ ls -l foo.py のように確認すると,一番左側に -rw-r--r-- と表示されます.x がありません.実行可能ファイルではない,ということです.手元のスクリプトを実行可能にするには(パーミッションを変更するには)chmod というコマンドを使います.今回の場合,$ chmod +x foo.py とすれば foo.py を実行可能ファイルにできます.これで $ ./foo.py のように直接呼び出すための準備が整いました.
もうひとつすべきなのは,シバンの設定です.スクリプトファイルを直接呼び出す場合は,ファイルの1行目に shebang(シバン,シェバン)を書きましょう.python スクリプトの場合は
# !/usr/bin/env python2
です.1行目にこのコメントがあると,シェルが「このファイルは python2 で実行すべきファイルなのだな」と理解してくれます.シェルスクリプトの場合はファイルの1行目に
# !/usr/bin/env bash
と書いておけばOKです.
サーチパス
ところで,ls は単に $ ls と打てば実行できますが,自分で作った実行ファイル foo.py は $ ./foo.py と(相対)パスを指定しないと実行できません.これは,ls という名前の実行ファイル(ls も foo.py と同様に「ファイル」です)が格納されている ディレクトリ が サーチパス(あるいは単に パス)と呼ばれる ディレクトリ一覧表 に登録されているためです.
まず ls というコマンドの所在を which コマンドで確認してみましょう.$ which ls と打てば /bin/ls などと表示されますね.これは, $ ls と入力した場合に呼び出されているのは実際には /bin/ls という実行可能ファイルである ことを意味します.実際,この場合,$ ls と打っても $ /bin/ls と打っても全く同じ結果が得られます.
次に登録済のサーチパス(くどいですがディレクトリの一覧表です)を確認しましょう.$ echo $PATH と打てば確認できます./usr/local/bin:/usr/local/sbin:... といった文字列が表示されますね.「:」は区切り文字で,全体でディレクトリのリストを表しています.リスト中に /bin が見つかるはずです.先の例で /bin/ls を ls で呼び出せたことを思い出してください.サーチパスに /bin が登録されていたので,/bin に入っている ls はファイル名だけで実行可能になっていた ということです.コマンドが入力されると,シェルはサーチパスを順に検索し,同名の実行可能ファイルが見つかればそのファイルを実行する,という仕組みです.
サーチパスに新たにディレクトリを追加することを パスを通す などと言います.自作のよく使うシェルスクリプトなどは $HOME/bin 以下に登録し,$HOME/bin にパスを通しておくと便利です.(場所は $HOME/bin でなくても構いませんが,慣例上ここがよく使われます.)$HOME はホームディレクトリのことで,手元の Mac であれば /Users/[USER_NAME],Linux サーバであれば /home/[USER_NAME] のことです.$HOME と ~ は同じ意味です(と,シェルが解釈します).パスの通し方ですが,bash の場合は ~/.bash_profile,zsh の場合は ~/.zshenv に
export PATH=${HOME}/bin:${PATH}
と1行追加し,$ source ~/.bash_profile または source ~/.zshenv とすれば $HOME/bin にパスが通ります.上の命令は,PATH という環境変数を,「${HOME}/bin + : + 現在の PATH の中身」に書きかえよ,という意味です.(環境変数を参照する際に {} が付いているのは一旦気にしないでもらって=真似するだけで大丈夫です.)こうすれば,いかなるディレクトリに居ても,$ foo.py と打つだけで,$HOME/bin に置かれた foo.py を呼び出せるようになります.
3. 複数のコマンドを連携させる
シンプルで単機能なコマンドを複数個上手に組み合わせることで望む処理を実現するのは実に UNIX 的なやりかたです.はじめに標準入力・標準出力・標準エラー出力の復習をして,そのあとパイプなどを使って複数のコマンドを連携させる方法を見てみましょう.
ファイルディスクリプタ
「stdin(標準入力),stdour(標準出力),stderr(標準エラー出力)とは何か」ですが,まずは次のような理解でOKです:「どのプログラムにも3本のホースがささっていて,1本はデータをプログラムに流し込むためのホース(stdin),残り2本はプログラムからデータを吐き出すためのホース(stdout,stderr)」 .なお,こちらからプログラムに特別に指示を与えなければ,stdin はキーボードに,stdout と stderr はターミナルの画面に繋がっています.
![]()
(ファイル記述子 - Wikipedia より)
C言語でプログラムを書く際,scanf() などでプログラムに入力を与えたかと思いますが,あれはプログラムの stdin から流れ込んできたデータを処理するための関数です.同じく,printf() などでプログラムからの出力を指示したかと思いますが,あれはプログラムの stdout から何を出力するかを指示する関数です.
この後説明する「リダイレクト」や「パイプ」は,このホースを繋ぎ替えるための機能です.
※「ホース」のくだりをもう少し正確に説明してみます.コマンドを実行するとプロセスが立ち上がり,プロセスは file descriptor(FD,ファイルディスクリプタ,ファイル記述子) を通してファイルにアクセスできるようになります.(キーボードも画面も「ファイル」です.)0,1,2 は OS により stdin,stdout,stderr に自動的に割り当てられます.
リダイレクト
stdin は < で redirect(リダイレクト)できます.つまり,stdin というホースがささっている先を,キーボード以外のファイルに変更できます.C言語の実験では $ ./a.out < input.txt などと書いたでしょうか.これは「a.out というプログラムの標準入力に input.txt を割り当てよ」という意味です.同様に,stdout は > でリダクレクトできます.$ ./a.out < input.txt > output.txt と書けば,a.out の stdin と stout をそれぞれ input.txt,output.txt に繋ぎ替えることができます.stderr は 2> でリダイレクトできます.
パイプ
この記号「|」は pipe(パイプ)と呼ばれます.パイプを使うと,前段のコマンドの stdout と後段のコマンドの stdin を接続することができます.前段のコマンドの標準出力のホースと後段のコマンドの標準入力のホースを直接接続する ということです.たとえば $ sort foo.txt | uniq と入力すると,$ sort foo.txt というコマンドの出力が,そのまま uniq コマンドの入力になります.$ sort foo.txt > sorted.txt,$ uniq sorted.txt のようにいちいち中間生成ファイルを用意する必要はない,ということです.
今後研究のためにプログラムを書く際も,できるだけひとつひとつのプログラムは単機能にし,プログラム同士をパイプで接続して実行できるようにすると,あとあと見通しがよくなります.
![]()
(Pipeline (Unix) - Wikipedia より)
ほかの「繋ぎ方」
※この節は多少 advanced です.
我々がコマンドに渡したいものとしては,
- ほかのコマンドの出力
- ファイル
- 文字列
があります.
また,渡し方として,
- 標準入力から渡す
- 「ファイルを受け取ることになっている argument」に与える
- 「文字列を受け取ることになっている argument」に与える
があります.
「あるコマンドの実行結果を次のコマンドに渡したいのだけど,後段のコマンドは argument でファイルを受け取ることしかできないんだよな」…なんて場面がそのうち出てくるかと思いますが,実は,どんな組み合わせでも対応可能です.必要がでてきたら,あるいはWebや書籍で見かけた謎の構文の意味が分からなければ,この節を参考にしてみてください.
-
コマンドの標準出力 を,次のコマンドの…
- 標準入力に渡す:
command1 | command2- pipe(パイプ)
- 「ファイルを受け取ることになっている argument」に渡す:
<(command1)- process substitution(プロセス置換)
- 「文字列を受け取ることになっている argument」に渡す:
-
"$(command1)"または"`command1`" - command substitution(コマンド置換)
-
- 標準入力に渡す:
-
ファイル を,コマンドの…
- 標準入力に渡す:
<file- (標準入力のリダイレクト)
- 「ファイルを受け取ることになっている argument」に渡す:
file- (path を書くだけ,そのまま)
- 「文字列を受け取ることになっている argument」に渡す:
-
"$(< file)"(equivalent to"$(cat file)") - ファイル置換入力
-
- 標準入力に渡す:
-
文字列 を,コマンドの…
-
標準入力に渡す
-
here string(ヒア・ストリング)
<<<"s"
-
here document(ヒア・ドキュメント)
<< EOF foo baa EOF
-
-
「ファイルを受け取ることになっている argument」 に渡す:
<(echo "s")- (
echoを使ってプロセス置換)
-
「文字列を受け取ることになっている argument」に渡す:
"s"- (文字列を書くだけ)
-
-
「繋ぎ方」のまとめ
| to stdin | to argument as a file | to argument as a string | |
|---|---|---|---|
| from stdout | pipe | <(command) |
"$(command)" |
| from file | <file |
file |
"$(< file)" |
| from string | <<<"s" |
<(echo "s") |
"s" |