この記事は村松研 Unix チュートリアルのコンテンツです.
このページでは皆さんが普段使っているであろう Windows や Mac の通常操作とは異なる,Unix 系 OS におけるコマンド操作について学びます.
また,その過程で Unix 系 OS を扱う上で知っておく必要のあるいくつかの概念に触れていきます.
Unix 系 OS では,ユーザはターミナル(ターミナルエミュレータ)と呼ばれるソフトウェアにコマンド(UNIX コマンド)を入力することで様々な操作を行うことができます.
皆さんが普段使うグラフィック(アイコン)ベースのインターフェース GUI: Graphical User Interface に対して,このような文字ベースのインターフェースを CLI: Command Line Interface(または CUI: Character User Interface)といいます.
それでは,UNIX コマンドによる CUI 操作の練習を始めてみましょう.
とりあえず触ってみる
前回インストールしたローカルの Unix 環境を操作してみましょう.
Ubuntu on WSL (or VirtualBox),Cygwin,Mac のいずれを使っていても,ここで紹介するコマンドの多くは共通して動くはずです.
ターミナルを起動して前回セットアップした環境に入れていれば,下のようにドルマーク $(プロンプト)が現れ,文字が入力可能な状態になっているでしょう.
プロンプトの左側の表示は環境によって異なります.
[username@hostname: ~]$
ファイル・ディレクトリの一覧表示
本格的な説明に入る前に,試しにいくつかのコマンドを入力してみましょう.
まずは ls と入力して,エンターキーを押してみてください.
下のように,コマンドの次の行に何らかの文字列が表示されたでしょうか?
環境によっては,最初は何も表示されないかもしれません.
[username@hostname: ~]$ ls
Desktop Documents Downloads Music Pictures Videos
ls コマンドはその語源 "list" の通り,指定した場所にあるファイルやディレクトリ(フォルダのことだと思ってください)の一覧を返します.
今回は場所の指定を省略したため,現在あなたがいるディレクトリ(ワーキングディレクトリ)の情報が返ってきています.
ディレクトリの作成と移動
次はこちらです.
[username@hostname: ~]$ mkdir work
[username@hostname: ~]$ ls
Desktop Documents Downloads Music Pictures Videos work
mkdir コマンド ("make directory") は指定した名前で新たなディレクトリを作成します.
今回は "work" という名前を指定しました.
この場合の "work" のように,コマンドに渡す情報をコマンドの「引数(ひきすう)」といいます.
続けて打ったlsコマンドで work ディレクトリが増えていることが確認できたでしょうか?
[username@hostname: ~]$ pwd
/home/username
[username@hostname: ~]$ cd work
[username@hostname: ~/work]$ pwd
/home/username/work
pwd コマンド ("print working directory") でワーキングディレクトリの確認が,cd コマンド ("change directory") で指定したディレクトリへの移動ができます.
pwd コマンドでディレクトリの移動を確認できましたか?
多くの環境ではプロンプトの左側にワーキングディレクトリが表示されます.
[username@hostname: ~/work]$ cd ..
[username@hostname: ~]$
一つ上のディレクトリへ移動するには .. を引数に与えます.
このようなファイルやディレクトリの場所を表す文字列のことを「パス (path)」といいます.
パス指定方法の詳細は後ほど説明します.
[username@hostname: ~]$ echo $HOME
/home/username
[username@hostname: ~]$ cd work
[username@hostname: ~/work]$ cd /home/username
[username@hostname: ~]$ cd work
[username@hostname: ~/work]$ cd ~
[username@hostname: ~]$ cd work
[username@hostname: ~/work]$ cd
[username@hostname: ~]$
ターミナルを起動したあなたが最初にいることになるディレクトリのことをホームディレクトリといい,echo $HOME で確認ができます(echo コマンド及び $HOME については後ほど説明します).
たとえば username アカウントのホームディレクトリは /home/username となることが多いです.
以下,username はご自身のユーザ名に置き換えて考えてください.
ホームディレクトリはチルダマーク ~ で短く表すことができます.
また,cd コマンドの引数を省略した場合はホームディレクトリに移動します.
ファイルの作成と内容表示
ディレクトリだけでなくファイルも作成してみましょう.
[username@hostname: ~]$ echo "hoge"
hoge
[username@hostname: ~]$ echo hoge
hoge
[username@hostname: ~]$ echo "hoge)"
hoge)
[username@hostname: ~]$ echo hoge)
bash: syntax error near unexpected token ')'
[username@hostname: ~]$ cd work
[username@hostname: ~/work]$ echo "hoge" > piyo.txt
[username@hostname: ~/work]$ ls
piyo.txt
[username@hostname: ~/work]$ cat piyo.txt
hoge
echo コマンドは基本的に引数の文字列をやまびこのように標準出力(ターミナルの画面のことだと思ってください.後ほど詳しく説明.)に返します.
入力文字列はダブルクォーテーション "" で囲っておくのが無難です.
大なり記号 > は echo コマンドなどによる標準出力の内容を,次に指定したファイルに書き込みます(出力リダイレクト).
ls コマンドで新たなファイルができていることを確認できましたか?
cat ("concatenate") コマンドでファイルの中身を標準出力に表示することができます.
ちなみに,空のファイルを作成するには touch コマンドが使えます.
[username@hostname: ~/work]$ ls > fuga.txt
[username@hostname: ~/work]$ cat fuga.txt
fuga.txt
piyo.txt
ls コマンドなどの結果も標準出力に出ているため,出力リダイレクトでコマンドの結果をファイルに書き込むこともできます.
だんだんターミナルとの対話に慣れてきましたか?
ここからは,先ほど後回しにしたいくつかの重要な概念について説明していきます.
パスの書き方
ファイルやディレクトリの位置を表す文字列をパス (path) といいます.
ここでパスの二つの表し方,絶対パスと相対パスについて説明します.
絶対パス
Unix 系 OS のファイル・ディレクトリ階層構造のトップをルートディレクトリといい,/ で表します.
ファイル・ディレクトリ階層構造を逆さの木に見立てたとき,根っこの部分にあたるため「ルート (root)」です.
Windows では多くの場合 C:\ がこれに対応しますね.
ある特定のディレクトリ・ファイルの場所を記述するためにトップのルートディレクトリを基準とし,そこからたどって考えるパスのことを絶対パスといいます.
パス内でファイル・ディレクトリは / で区切って表します.
たとえば,先ほど作成した hoge.txt ファイルの絶対パスは /home/username/work/hoge.txt,work ディレクトリの絶対パスは /home/username/work となります.
一番左の / がルートディレクトリを表し,それ以外の / がディレクトリ同士(あるいはディレクトリとファイル)の区切りを表しています.
少し実験をしてみましょう.
[username@hostname: ~/work]$ mkdir hoge
[username@hostname: ~/work]$ cd /home/username/work/hoge
[username@hostname: ~/work/hoge]$ pwd
/home/username/work/hoge
work ディレクトリの中に作成した hoge ディレクトリへ絶対パスで移動できましたか?
全部入力するのはちょっと長くて大変ですね.
コマンドやパスの入力途中で Tab キーを押すと,入力文字列を自動補完してくれます.
たとえば,cd /h の状態で Tab キーを押せば cd /home/ と入れてくれます.
今回は / で区切られた各要素が短いためあまり恩恵がありませんが,コマンド名やパスの要素が長い場合は活用していきましょう.
補完候補が複数存在する場合は,Tab キーを 2 回押すと候補を一覧表示してくれます.
[username@hostname: ~/work/hoge]$ cd /
[username@hostname: /]$ pwd
/
[username@hostname: /]$ ls
bin dev home lib32 media opt
boot etc lib lost+found mnt proc
cd / でルートディレクトリに移動できますね.
出力結果を省略してしまいましたが,ls してみるとルートディレクトリの中に様々なディレクトリがある様子が見れます.
皆さんのホームディレクトリ /home/username のてっぺん /home も見えています.
相対パス
相対パスは注目するディレクトリ(多くの場合ワーキングディレクトリ)から「相対的に」考えたときのパスの記述方法です.
建物の位置で例えるならば,絶対パスは(日本をルートとして)「日本国神奈川県横浜市港北区日吉 3 丁目 14-1」という住所,相対パスは「現在の地点から 3 軒東の家」といった表現でしょうか.
[username@hostname: /]$ cd home/username
[username@hostname: ~]$
cd コマンドの引数として与えたパス home/username の頭に,ルート / を付けませんでした.
この場合,ワーキングディレクトリ(ここではルート /)にいることを前提に,見えているディレクトリ以下を相対的に指定したことになります.
少し脇道に逸れて,次のコマンドを試してみます.
[username@hostname: ~]$ ls --all
. .bashrc Documents Music .profile work
.. Desktop Downloads Pictures Videos
以前 ls コマンドを試したときには見られなかったディレクトリ・ファイルの表示が増えたかと思います.
表示が増えたアイテムには,共通して頭にドット(ピリオド). がついています.
これら .bashrc や .profile などは主にツールの設定に関わる隠しファイル・ディレクトリで,ドットファイルと呼ばれます.
また,ls コマンドに与えた --all はオプションといい,コマンドの挙動に関して指示を与える文字列です.
この場合は,「隠しファイルを含めたすべてのファイル・ディレクトリを列挙する」オプションとなります.
[username@hostname: ~]$ ls -a
. .bashrc Documents Music .profile work
.. Desktop Downloads Pictures Videos
--all オプションの短縮形として,-a が使えます.
こちらの方がよく見ます.
フルスペルオプションはハイフン 2 つ --,短縮オプションはハイフン 1 つ - を頭につけて表すことが多いです(例外多数あり).
話を戻します.
ここで注目してほしいのは,ls -a の結果に出てきた . と .. です.
前述した cd コマンドの説明に,.. というパスが出てきたことを思い出してください.
-a オプションを与えるまで隠れていましたが,. はそのディレクトリ自身を,.. は一つ上のディレクトリを表します.
ここまでの知識を使うと,次のようなパス指定ができます.
[username@hostname: ~]$ pwd
/home/username
[username@hostname: ~]$ cd ../../usr/bin
[username@hostname: /usr/bin]$ pwd
/usr/bin
わざわざややこしくする意味はありませんが,次のようなこともできますね.
[username@hostname: /usr/bin]$ pwd
/usr/bin
[username@hostname: /usr/bin]$ cd ././.././../usr/../home/username
[username@hostname: ~]$ pwd
/home/username
以上のように,ファイル・ディレクトリ間の相対的な位置を考えるパスが相対パスです.
シェルスクリプト
次のコマンドを実行して first_shellscript.sh というファイルを作ってみます.
最後の5行の行頭にある > は勝手に表示されるので,自分で入力する必要はありません(> に続く #!/bin/bash などの部分だけを入力してください).
[username@hostname: ~]$ mkdir ~/work/shellscript
[username@hostname: ~]$ cd ~/work/shellscript
[username@hostname: ~/work/shellscript]$ cat << EOS > first_shellscript.sh
> #!/bin/bash
> echo "hello"
> sleep 3
> echo $PWD
> EOS
cat コマンドはここまでにファイル内容の表示のために使っていました.
ここではファイルの代わりに標準入力(後ほど説明します)を用いて目的の文字列(#!/bin/bash 以降)を cat コマンドに渡し,出力リダイレクトを用いて表示の代わりに first_shellscript.sh ファイルへの書き込みを行っています.
また,その時にヒアドキュメントという形式(<< EOS ~ EOS)を用いて複数行のデータを渡しています.
ヒアドキュメントについては詳しく解説しないので,興味がある人は自分で検索して調べてみてください.
次のコマンドを実行して,同じ出力が確認できればファイルの作成はOKです.
[username@hostname: ~/work/shellscript]$ cat first_shellscript.sh
#!/bin/bash
echo "hello"
sleep 3
echo $PWD
ここで作成したファイルはシェルスクリプトと言い,UNIX コマンドをまとめて実行するためのファイル(スクリプトファイル)です.
今まで操作してきたターミナルの中でコマンドを受け付けているプログラムをシェルといい,UNIX コマンドのことをシェルコマンドとも言います.
シェルコマンドをまとめて実行するのでシェルスクリプトです.
一般的なテキストファイルには .txt という拡張子を付けることが多いですが,シェルスクリプトには .sh という拡張子を付けるのが通例となっています(なくてもちゃんと動きます).
Unix 系 OS は Windows ほど拡張子にうるさくなく,一部のプログラムがファイルの種類判別に拡張子を見ることもありますが,主に人間にファイル種類を判別する程度のためだけに付けられます.
同じ役割のテキストファイルに対して,人によって様々な拡張子を付けていることがありますが,あまり混乱しないで大丈夫です.
ファイル1行目の #!/bin/bash はシバン (shebang) といい,このファイルの実行を担うべきインタプリタ(スクリプトを解釈・実行するプログラム)をシェルに伝えるためのコメントです.
シェルは zsh, csh, fish など様々な種類があり自分で好きなものを選んで使うことができるのですが,Ubuntu (on WSL) のデフォルトシェルは bash というものになっています.
ここで書いたシバンは,スクリプトを実行するプログラムとして bash を指定するという意味なわけですね.
ちなみに macOS でも以前は bash がデフォルトシェルだったのですが,Catalina バージョンからデフォルトが zsh に変更されています.
作成したシェルスクリプトを,まずは次のように bash コマンドにファイルを渡す形で実行してみましょう.
[username@hostname: ~/work/shellscript]$ bash first_shellscript.sh
hello
/home/username/work/shellscript
"hello" が表示されてから3秒経った後に "/home/username/work/shellscript" が表示されるはずです.
これは echo "hello" と echo $PWD の間に sleep 3 (3秒待つ)を入れたためですね.
出力二行目のパス表示は echo $PWD ( echo $HOME のワーキングディレクトリ版)によるものです.
このように,シェルスクリプトを用いて3つのコマンドを順番にまとめて実行することができました.
パーミッション
今度は次のコマンドを実行してみてください.
[username@hostname: ~/work/shellscript]$ ls -l
total 0
-rw-r--r-- 1 username username 67 Feb 17 15:36 first_shellscript.sh
[username@hostname: ~/work/shellscript]$ chmod +x first_shellscript.sh
[username@hostname: ~/work/shellscript]$ ls -l
total 0
-rwxr-xr-x 1 username username 67 Feb 17 15:36 first_shellscript.sh
[username@hostname: ~/work/shellscript]$ ./first_shellscript.sh
hello
/home/username/work/shellscript
一つ目の ls コマンドの -l オプションは長い詳細情報 (long listing) を表示するためのものです.
ファイル名のみならず,左からパーミッション(これから説明)・ハードリンク数・所有者・所有グループ・ファイルサイズ (bytes)・タイムスタンプ(ファイルの最終更新日時)が表示されます.
ハードリンク数についてはここでは解説しません.
パーミッション情報 -rw-r--r-- 10文字のうち,左から1文字目がディレクトリ d か否 - か,2~4文字目 rw- が所有者のパーミッション,5~7文字目 r-- がグループのパーミッション,8~10文字目が他人のパーミッションを表しています.
r は読み出し許可 (read), w は書き込み許可 (write),ここではすべて - となっていて登場していませんが x が実行許可 (execute) を意味します.
すなわち -rw-r--r-- 1 username username は,所有者 username さんは読み書きが許可されているが実行は許可されていない,グループ username に属するユーザ(グループの説明は略)は読み出しのみ許可,その他のユーザ(所有者 username 本人でもなく username グループにも属してない)も読み出しのみ許可という意味になっています.
ディレクトリの場合は rwx の意味合いが若干異なってきます.
パーミッションの詳細についてはこちらの記事がわかりやすいです.
二つ目の chmod +x (change mode) により,first_shellscript.sh ファイルに実行権限を付与しています.
三つ目の ls -l により,パーミッション情報が -rwxr-xr-x となって,所有者・グループ・他人が実行可能な状態に変化していることが確認できます.
四つ目の ./first_shellscript.sh では,ファイルパスのみの指定によりシェルスクリプトを実行しています.
ひとつ前のコマンド実行例では, bash first_shellscript.sh のように bash コマンドの引数に first_shellscript.sh ファイルを指定することでスクリプトを実行していましたが,ファイルに実行権限を付与しておくことでファイルパスの指定によるスクリプト実行が可能となります.
PATH 環境変数
ファイルパスを指定したスクリプト実行と,今まで用いてきた UNIX コマンドの実行の違いは何でしょうか?
実は今まで登場したコマンドのうち多くは,実はスクリプト実行と同じように裏でファイルパスが指定されて実行されています.
[username@hostname: ~/work/shellscript]$ which ls
/bin/ls
[username@hostname: ~/work/shellscript]$ /bin/ls
first_shellscript.sh
[username@hostname: ~/work/shellscript]$ which cd
which コマンドにより,コマンドを打った際に実行されるファイルのパスを知ることができます.
which ls で /bin/ls と返ってきているのは, ls コマンドを担う実行ファイルが /bin/ls パスに存在することを示しています.
二つ目の実行例では, ls とコマンド名を打つ代わりに, /bin/ls とファイルパスを記述することでコマンドを実行しています.
これはすなわち,前の実行例で ./first_shellscript.sh としてシェルスクリプトを実行していたのと同じことが起こっているわけです.
一方, which cd の実行ではファイルパスが表示されておらず,実行ファイルが見つからないことを示しています.
これは,bash では cd コマンドは実行ファイルとして存在するのではなく,bash 内部にプログラムされた組み込みコマンドであるためです.
一部のコマンドではそういうこともあります.
先ほど作成したスクリプトファイルも, ls のようにコマンド名(ファイル名)だけで実行できるのでしょうか?
試してみましょう.
[username@hostname: ~/work/shellscript]$ first_shellscript.sh
first_shellscript.sh: command not found
[username@hostname: ~/work/shellscript]$ cd ~
[username@hostname: ~]$ first_shellscript.sh
first_shellscript.sh: command not found
[username@hostname: ~]$ ~/work/shellscript/first_shellscript.sh
hello
/home/username/work/shellscript
一つ目のコマンドは環境によっては実行成功するかもしれませんが,三つ目のコマンド(ホームディレクトリからの first_shellscript.sh 実行)は確実に command not found で失敗するでしょう.
四つ目のコマンドのようにちゃんとファイルパスを指定すれば,ホームディレクトリにいても first_shellscript.sh が実行できますね.
ファイル名のみで実行可能な ls とパスの指定が必要な first_shellscript.sh の違いはなんでしょうか?
その違いは PATH 環境変数にあります.
[username@hostname: ~]$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
上のコマンドを試すと,コロンで区切られた複数のパスが表示されます.
実はこのパスに配置されたファイルは,パスを指定せずともファイル名(コマンド名)のみでファイル(コマンド)の実行ができるのです.
今までに echo $HOME echo $PWD echo $PATH の形で登場した HOME, PWD, PATH などは環境変数といって,中にデータ(ここではパス)が格納されており,シェルコマンドのみならず様々なプログラムから利用することができます.
環境変数の「環境」の意味が気になる方は,"環境変数 シェル変数" を検索して意味を調べてみてください.
first_shellscript.sh をファイル名のみで実行したければ,次のようにシェルスクリプトが置かれたディレクトリのパスを PATH 環境変数へ追加すれば(パスを通すといいます)良いです.
[username@hostname: ~]$ export PATH="$PATH:$HOME/work/shellscript"
[username@hostname: ~]$ first_shellscript.sh
hello
/home/username/work/shellscript
一行目では,ダブルクォーテーションの中で変数 PATH を展開し,その末尾に :$HOME/work/shellscript ( HOME も展開される)を追加したものを PATH へ上書きする,といったことが起こっています.
パス区切り文字であるコロンの追加をお忘れなく.
ちなみに PATH 内のコロンで区切られたパスのリストは,前方ほど実行(検索)の優先度が高いです.
すなわち, PATH が /usr/bin:/bin となっていて /usr/bin と /bin の両方に piyo という同一名のファイルが存在した場合, piyo コマンドでは /usr/bin/piyo が優先的に実行されます.
基本的に,ちょっとした処理を自動化したくてシェルスクリプトを書いたときは,普通にパスを指定して実行すれば十分な場合が多いです.
ただし,汎用的に多くの場面で使える便利なシェルスクリプトが書けたときは,そのディレクトリを PATH に追加してファイル名だけで実行できるようにしておくと便利です.
様々なディレクトリに散らばったシェルスクリプトの実行のために,たくさんのディレクトリを節操なく PATH に追加しすぎるのは望ましくないので,次のように便利スクリプトをまとめて配置するディレクトリを用意しておくと良いです.
[username@hostname: ~]$ mkdir -p ~/.local/bin
私の手元の Ubuntu on WSL では,bash の設定ファイルのひとつ ~/.profile に「 ~/.local/bin ディレクトリが作成済みの場合はそのパスを PATH に追加する」旨の次のような設定が書かれていました.
(if, then, fi や -d についてはここでは説明しませんが,興味がある方はシェルスクリプトについてさらに勉強してみてください.)
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi
そのため ~/.local/bin ディレクトリ作成後にターミナルを再起動すれば,勝手にパスが通ります.
実はひとつ前の実行例で試した export PATH="$PATH:$HOME/work/shellscript" による PATH 変数の書き換えは,bash が再起動すると書き換え前の状態へ戻ってしまうのです.
そのため,日頃からパスを通しっぱなしにしておきたいディレクトリパスに関しては,bash を立ち上げる度にわざわざ export コマンドを手打ちせず済むように,bash の設定ファイルに export コマンドを記述しておくと良いです.
私の環境では ~/.local/bin にデフォルトでパスが通っていたと書きましたが,同じように設定されておらずうまくいかない方は,次のコマンドを実行しておきましょう.
環境によっては設定の反映のために PC の再ログインが必要になると思います.
[username@hostname: ~]$ echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.profile
Bash の設定ファイルには .profile (あるいは .bash_profile , .bash_login )の他に有名なものとして .bashrc があります.
.profile と .bashrc の反映タイミング(役割の違い)はこの記事に詳しいです.
今後 .bashrc を編集する機会はそれなりにあると思いますので,一度調べてみるといいでしょう.
標準入出力とリダイレクト・パイプ(以下執筆途中)
説明を始める前に,次のコマンドを実行して print_stdouterr.sh というファイルを作っておきましょう.
[username@hostname: ~]$ cd ~/work/shellscript
[username@hostname: ~/work/shellscript]$ cat << EOS > print_stdouterr.sh
#!/bin/bash
echo "stdout" >&1
echo "stderr" >&2
EOS
[username@hostname: ~/work/shellscript]$ chmod +x print_stdouterr.sh
バックグラウンド実行(途中)
ssh 越しにリモートで計算を回す時に,コマンドを通常実行すると ssh 抜けた時に計算も止まってしまうため,nohup コマンド & が必要みたいなことを頭の片隅に置いておく必要がありそう.
とりあえず,プログラミングとシミュレーション実行に必要な最低限の知識は説明したので,C 言語/Fortran の演習資料に移りましょう.