Linux コマンドラインのコンセプト: テキスト指向性、出力リダイレクト、引数
この記事は、シリーズ 機械学習エンジニアのためのLinux の第5回です。
Linux コマンドラインのコンセプト
前回の記事の最後に、次のような(想像上の)読者の声を紹介した。
フォルダを開いたり移動したりするだけなら、エクスプローラ/Finderでもできるじゃん。 これのどこがすごいんだよ!
確かに、エクスプローラや Finder などの GUI ツールでも、ls
や cd
と同じような作業はできる。
だが、CLI の真の魅力は、そうした単純な操作を組み合わせて、複雑な作業を効率的に実行できる、というところにある。
これ以降の数章では、Linux コマンドラインの強力な機能を支える特徴を、実例をみながら解説していこうと思う。 いろいろなコマンドが登場するが、コマンド自体の説明は最小限にしている。 各コマンドの詳しい使い方は、別の記事で再度取り上げる予定なので、今回はざっと読み流しながら「こういう使い方もできるんだな」という感覚をつかんでもらいたい。
CLI は「テキスト指向」環境
CLI のさまざまな機能や特徴の基礎となるのが、テキスト指向性だ。
GUI 環境と CLI 環境の大きな違いは、GUI ではマウス操作や視覚的な画面出力ができるのに対して、CLI では基本的に文字(テキスト)しか入力・表示できない、という点にある。 このため、コマンドラインで利用できるコマンドの多くは、実行結果をテキスト形式で出力する。
これは「コマンドラインでは画像や映像などのデータを扱えない」という意味ではない。 画像や映像を扱えるCLIツールはあるが、そうしたツールの操作もテキストで行う、ということだ。
例えば ls
の出力は、画面上にテキストとして表示される。
$ ls
snap テンプレート ドキュメント ピクチャ 公開
ダウンロード デスクトップ ビデオ ミュージック
pwd
コマンドについても同じだ。
$ pwd
/home/yumemio
出力リダイレクト: コマンドの実行結果をファイルに保存
このように出力がテキストであるメリットの1つとして、コマンドの結果を簡単に保存できる、という点が挙げられる。
コマンドの実行結果は画面上に出力されるが、出力リダイレクトという機能を使えば、出力先としてファイルを指定することもできる。
出力リダイレクトの使い方は、コマンドの最後に >
と出力先ファイルパスを指定する、というものだ。
コマンド > 出力先のファイルパス
例えば、次のようなシチュエーションを考えてみよう。
❓ 課題
データセットに含まれるファイル一覧を、テキスト形式で取得したい。
あるディレクトリの直下にあるすべてのファイル名を、テキストとして書き出すには、どうすればいいか。
この場合は、ls
コマンドと出力リダイレクトを使えばよい。
次のようなコマンドを実行してみよう。
$ ls > files.txt
すると、次のように、なにも画面に出力されずに次のプロンプトが表示されると思う。
$ ls > files.txt
$
ls
の出力はどこに行ってしまったのかというと、カレントディレクトリ内の files.txt
に書き出されている。
テキストエディタで files.txt
を開いてみると、ls
の実行結果が保存されているのがわかる。
この機能は、機械学習において大いに役立つ。
たとえば、データセットを構成するファイルの数や種類を集計するとき、出力リダイレクトを利用してファイル一覧をテキストファイルに書き出し、その後で表計算ソフトやプログラムを使って集計を行うことができる。
また、学習プログラムなどを実行するときに、実行ログを出力リダイレクトしてファイルに保存しておけば、後から詳細な分析やデバッグができる。 特に長時間にわたる学習プロセスでは、ログの保存が重要となる場合が多い。
find
コマンド: ファイルを検索する
ls
と出力リダイレクトの組み合わせを使えば、1つのディレクトリに含まれるファイルの名前を書き出せる。
しかし、複数のディレクトリにまたがってファイルを検索する場合や、ディレクトリが入れ子になっている場合はどうすればよいだろう。
例として、次のような場面を考えてみよう。
❓ 課題
次に示すディレクトリ構造をもつデータセットがあるとしよう。
(このように
train
とvalidation
ディレクトリをもち、サブディレクトリごとにカテゴリを分けたデータセットは、よく見かけるタイプのデータ構造だ)/home/me/dataset |-train/ | |-speaker_a/ | | |-speech_0001.wav | | |-speech_0002.wav | | ... | |-speaker_b/ | |-speaker_c/ | | ... | `-speaker_z/ `-validation/ |-speaker_a/ | |-speech_1001.wav | |-speech_1002.wav | ... |-speaker_b/ |-speaker_c/ | ... `-speaker_z/
このとき、データセット内に含まれるすべての
wav
ファイルのパスをテキストとして書き出すには、どうすればいいか。
このような場合は、ファイル検索コマンドである find
が役に立つ。
find
コマンドには、ファイルやディレクトリの検索条件を指定する。
すると、指定した条件に合致するファイルやディレクトリが検索される。
検索結果は、ファイルパスとして表示される。
find
の基本形は、検索するディレクトリだけを指定して実行する形だ。
この場合、指定したディレクトリ以下のすべてのファイルのパスが表示される。
課題に示した dataset
ディレクトリで find
コマンドを実行すると、こうなる。
$ cd /home/me/dataset
$ find .
.
./train
./train/speaker_a
./train/speaker_a/speech_0001.wav
./train/speaker_a/speech_0002.wav
(以下略)
ls
と異なり、カレントディレクトリの下にある train
や、その下の speaker_a
ディレクトリの内容も表示されていることがわかると思う。
このように、find
はディレクトリを再帰的に——子や孫のディレクトリも含めて——検索できるのが特徴だ。
find
コマンドの後ろに検索条件を指定することで、特定のファイルだけを表示することができる。
検索条件には、ファイルの種類やファイル名、更新日時などさまざまな要素を指定できるが、ここでは -name
という条件を使おう。
find . -name '*.wav'
で「カレントディレクトリ以下にあり、名前が'.wav'
で終わるファイルやディレクトリを検索する」という意味になる。
課題に示した dataset
ディレクトリでこのコマンドを実行すると、次のようになる。
$ cd /home/me/dataset
$ find . -name '*.wav'
./train/speaker_a/speech_0001.wav
./train/speaker_a/speech_0002.wav
...
./validation/speaker_a/speech_1001.wav
./validation/speaker_a/speech_1002.wav
...
wav ファイルのパスだけが出力されるようになった。
📔 ノート
このコマンドでは、名前が
.wav
で終わるディレクトリがあればそれも出力されてしまう、という問題がある。(そのようなディレクトリ名を手動でつけることはあまり無いが、プログラムを組んでデータを処理した場合などに、
0001.wav
のような名前のディレクトリを作ってしまうことは意外と多い。)
-type f
という検索条件を追加すれば、出力からディレクトリパスを除外し、ファイルのパスだけを出力できる。find . -type f -name '*.wav'
これで、「名前が
'.wav'
で終わる ファイルだけ」が表示されるようになる。
出力リダイレクトを使えば、この結果をファイルとして保存することができる。
$ cd /home/me/dataset
$ find . -name '*.wav' > files.txt
files.txt
に、データセット内のすべてのファイルパスが格納されているはずだ。
コマンドライン引数
find
コマンドでは、find
の後ろに検索条件を指定することができた。
$ find . -name '*.wav'
また、前回学んだ cd
でも、コマンドの後ろに移動先のディレクトリを指定した。
$ cd /home/yumemio/dataset
このように、コマンドの後ろには操作するファイル(ディレクトリ)のパスや出力方法などを指定することができる。 これらは「コマンドライン引数」とよばれ、それぞれのコマンドにとって異なる役割を果たす。
ls
コマンドについて考えてみよう。
以前の記事で見たように、引数なしの ls
はカレントディレクトリ内のファイルやディレクトリを一覧表示できる。
$ ls
snap テンプレート ドキュメント ピクチャ 公開
ダウンロード デスクトップ ビデオ ミュージック
一方、ls
にディレクトリパスを引数として指定すると、そのディレクトリの内容を表示できる。
$ ls ドキュメント
sonnet.txt
$ ls /home
chiyurik yumemio
オプション や フラグ とよばれるタイプの引数もある。 これらは、コマンドの機能を有効化・無効化したり、処理の内容を変化させたりすることができる。
例えば、ls -F
と指定すると、ファイルとディレクトリを見分ける記号を出力につけてくれる。
$ ls -F
snap/ デスクトップ/ ピクチャ/
ダウンロード/ ドキュメント/ ミュージック/
テンプレート/ ビデオ/ 公開/
-F
は「短い」オプション名だが、--classify
という「長い」オプション名でも、同じ機能をオンにできる。
$ ls --classify
snap/ デスクトップ/ ピクチャ/
ダウンロード/ ドキュメント/ ミュージック/
テンプレート/ ビデオ/ 公開/
ls -l
はファイルとディレクトリの詳細な情報(ファイルサイズや更新日付など)を表示できる。
$ ls -l
合計 40
drwx------ 4 yumemio yumemio 4096 4月 29 12:35 snap
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ダウンロード
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 テンプレート
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 デスクトップ
drwxr-xr-x 2 yumemio yumemio 4096 5月 14 10:11 ドキュメント
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ビデオ
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ピクチャ
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ミュージック
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 公開
このように、オプションやフラグはハイフン (-
) かダブルハイフン (--
) で始まり、文字や単語が続くことが多い。
複数の引数やオプションを同時に指定したい場合は、どうすればいいだろうか? たいていのコマンドでは、複数の引数やオプションを並べて書くことで、引数をまとめて渡せる。
$ ls /home /home/yumemio
/home:
yumemio
/home/yumemio:
snap デスクトップ ピクチャ
ダウンロード ドキュメント ミュージック
テンプレート ビデオ 公開
$ ls -F -l /home/yumemio
合計 36
drwx------ 4 yumemio yumemio 4096 4月 29 12:35 snap/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ダウンロード/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 テンプレート/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 デスクトップ/
drwxr-xr-x 3 yumemio yumemio 4096 5月 21 14:45 ドキュメント/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ビデオ/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ピクチャ/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 ミュージック/
drwxr-xr-x 2 yumemio yumemio 4096 4月 23 13:48 公開/
ls -F -l
は、 ls -Fl
とまとめて書いてもよい。
ちなみに、多くのコマンドでは、-h
または --help
というオプションを指定するとコマンドの使い方を表示してくれる。
利用できるオプションの一覧なども表示されるので、ぜひ試してみてほしい。
$ ls --help
使用法: ls [オプション]... [ファイル]...
FILE (デフォルトは現在のディレクトリ) に関する情報を一覧表示します。
-cftuvSUX のいずれも指定されず、 --sort も指定されていない場合、
要素はアルファベット順でソートされます。
長いオプションで必須となっている引数は短いオプションでも必須です。
-a, --all . で始まる要素を無視しない
-A, --almost-all . および .. を一覧表示しない
...
おわりに
今回は、CLI 環境の鍵となる特性であるテキスト指向性について掘り下げた。 その実例として、出力リダイレクトという機能を詳しく紹介した。 また、コマンドライン引数やオプションなどの要素を使って、コマンドの挙動をカスタマイズできることも学んだ。
CLI のテキスト指向性のおかげで、コマンドの出力結果をテキスト形式で取得でき、ファイルに保存したり、Excel などの表計算ソフトでデータを後処理したりできる。
しかし、CLIの真価は「パイプ」機能を使うことで発揮される。 この機能を使うことで、コマンドを組み合わせた複雑な処理が可能になる。 これこそが、Linux コマンドラインが備える最強の機能といっても過言ではない。
次回以降の数回は、このパイプ機能について紹介しよう。 次回の記事をお楽しみに!
🎯 まとめ
- テキスト指向: コマンドの入出力が基本的にテキストである、という CLI の特性
- 出力リダイレクト: コマンドの出力結果をファイルに保存する機能
command > file
:command
の実行結果を、カレントディレクトリのfile
に保存するcommand > /path/to/file
:command
の実行結果を/path/to/file
に保存する- 再帰検索: 入れ子になっているディレクトリの内容も含めて検索すること
- find: 再帰的にファイルやディレクトリを検索し、パスを出力するコマンド
find . -name '*.wav'
: カレントディレクトリ以下にある、名前が.wav
で終わるファイルとディレクトリを検索する- コマンドライン引数: コマンドの後ろにつけて、その挙動をカスタマイズする文字列や記号
- オプション: コマンドの機能や処理内容を変化させるのに使う引数
ls -F
: ファイルとディレクトリを見分ける記号をつける。ls --classify
も同じ処理。ls -l
: ファイルやディレクトリの詳細な情報を表示する
❓ 練習課題
ls --help
を実行してみよう。表示されたヘルプを読んで、気になるオプションがあれば使ってみよう。- ホームディレクトリ内のファイルとディレクトリを、
~/ドキュメント/home.txt
に保存するコマンドを作成し、実行しよう。- ホームディレクトリ内から、拡張子が
.txt
で終わるファイルを再帰的に検索し、その結果を~/ドキュメント/text_files.txt
という名前で保存するコマンドを作成し、実行しよう。