【WSL入門】第3回 WSL活用の落とし穴:LinuxからWindowsフォルダへのアクセス完全マスターITの教室

WSLを活用するには、bashのヒストリー機能をマスターするのが近道。またWSLとのWin32相互運用性を理解するのが重要だ。今回はこの2つについて解説する。

» 2019年04月19日 05時00分 公開
[塩田紳二]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

ITの教室 WSL入門」では、WSLを活用するための基礎をインストールから解説していく。



 WSLのシェルである「bash」の最大の魅力は、コマンドラインで指定する引数に対してさまざまな表現方法が用意されている点にある。例えば、Windows OSでは、制御文字(改行やタブなど)を引数として指定することはできない。しかし、bashでは8進数や16進数を表現する方法が用意されているため、Linuxのコマンドには引数として制御文字を受け付けられるものがある。

 また、複数のファイルを正規表現や数式、範囲指定などで表現できるため、複数の、それも大量のファイルを引数として受け付けるコマンドも多い。bashでは、こうした機能を「展開」といった用語を使い「履歴展開(ヒストリー展開)」「パス名展開」「変数展開」「算術式展開」などの機能がある。

bashを使ってフォルダ内の特定拡張子のファイル名を変更する bashを使ってフォルダ内の特定拡張子のファイル名を変更する
このように特定の拡張子のファイル名のみ(これはWindows OSでも可能だが)、正規表現を使って名前を変更するといったことが簡単に行える。

 Linuxのコマンドラインが便利なのは、コマンドが多数用意されているというだけでなく、bashが提供する強力な「展開」機能により、引数の指定が容易かつ強力というのも理由だ。まずは、簡単に履歴展開を見てみよう。

bashの便利なコマンドライン

 Windows OSのコマンドラインにはヒストリー機能があり、[↑]キーで前に実行したコマンドを呼び出し、編集した上で再度実行することができる。この機能はもともとMS-DOS 5.0(1991年)から搭載されたDOSKEYと呼ばれる常駐プログラムが管理していた関係で、現在でもdoskey.exeコマンドが使われている(詳細はdoskey.exe /?を参照のこと)。

 これに対してWSLのbashにも、カーソルキーでヒストリー(履歴)を呼び出す機能があるが、さらに「!」文字を使った「履歴展開」機能により、似たようなコマンドや似たような引数を繰り返し利用することができる。

 そもそも、UNIXの時代、コンピュータは「ターミナル」(端末)と呼ばれるハードウェアを介して利用が行われていた。端末とは、キーボードとディスプレイを備えた装置で、表示は文字のみである。コンピュータから文字列を受け取るとそれをディスプレイに表示し、キーボードが打鍵されると、その文字をコンピュータに送り返す、という機能を持つ(Windows OSのコンソールウィンドウは、この端末をソフトウェア化したものといえる)。

 このため、UNIXでは、作業効率を上げるため打鍵数をなるべく少なくすることが考えられた。頻度の高いコマンドを2文字にしたり、ヒストリーや編集機能を使って、一回入力したコマンドを再利用したりできるようにするといった工夫が行われた。

 Windows OSのコマンドラインでは、dirコマンドを使って、フォルダの様子を見て、必要ならそこにcdで移動するといったことはよくやる作業だ。これは、Win32側では、以下のような感じになる。

>dir \this\is\very\deep\folders
>cd \this\is\very\deep\folders


カレントフォルダを移動するコマンド(Windows OS標準)

 ここで2つ目のcdコマンドは、ヒストリー機能で、1つ目のdirコマンドを呼び出し、カーソルを先頭に持っていって、「dir」を「cd」に書き換えるといった方法でも実行できる。

 もちろん、同じような操作はbashでもできるが、もっと打鍵数が少なくなる、以下のコマンドが使える。

$ ls /this/is/very/deep/folders
$ cd !*


bash上でのカレントディレクトリ(フォルダ)を移動するコマンド

 ここの「!*」は、「直前のコマンドの引数部分全体(=コマンド名を除いた部分)」という意味だ。WSLのbashのコマンドラインでは、「!」は、「履歴展開」のための記号で、さまざまな記号と組み合わせて以前に実行したコマンド(bashではこれをイベントと呼ぶ)から情報を取り出すことができる。

 簡単なものだけでも、下表のようなものがある。こうした「ヒストリー置換」を使いこなすと、コマンドラインの入力を少ない打鍵数で行えるようになる。

パターン 意味
!n ヒストリーのn番目のコマンド(nは1以上の整数。ヒストリー番号はhistoryコマンドで確認できる)
!-n ヒストリーでn個前に実行したコマンド(nは1以上の整数)
!! 直前に実行したコマンド(!-1と同じ)
!str 文字列strで「始まる」、最後に実行したコマンド
!?str? 文字列strを「含む」、最後に実行したコマンド
^str1^str2^ 文字列str1をstr2に置換して直前のコマンドを再度実行(!が付かないことに注意)
history ヒストリーの一覧リスト表示
bashのヒストリー置換(主なもの)

 WSLのbashにはこれ以外にもいろいろな表記方法があるので「man bash」や「info history」コマンドなどで調べてほしい。ヒストリー機能に慣れると、同種のコマンドや再度同じコマンドを実行するのが簡単になる。実際やってみると多くの作業で同じようなコマンドを繰り返し使っていることがある。lsやechoなどの比較的無害なコマンドを使って実際に試してみるといいだろう。

bashのヒストリー機能の優れた点

 もちろんWSLでも、Win32のdoskey.exeと同じくカーソルの[↑]キーでヒストリーからコマンドを呼び出せる。しかし、ヒストリー置換に慣れるとより少ないキーストロークでコマンドを再実行でき、しかも後述するようにパラメーターの置き換えなども可能で、カーソルキーでヒストリーを呼び出して再編集するよりも簡単だ。ある意味、最小打鍵数でコマンドラインが利用できる。

 例えば、過去に「echo」で始まるコマンドを実行していたとしたら、「!echo」で、そのコマンド行を再度実行させることができる。

bashのヒストリー機能の例 bashのヒストリー機能の例
bashのヒストリー機能を使えば、過去に実行したコマンドを指定して呼び出し、さらにその一部を変更して簡単に再実行できる。例えば!echoだと、echoで始まるコマンドのうち、最も新しいものを呼び出して再実行する。^を使って直前のコマンドの一部を置換することもできる。

 直前のコマンドをもう一回実行するなら「!!」と入力すればよい。このほかに、「^」を使って「^str1^str2^」として直前に実行したコマンドの一部を文字列置換して再度実行することも可能だ。

 ただし、簡単なコマンド名の置き換えだけなら、最初の例にあるように新しいコマンド名を打ち込んだあと、引数として「!*」を指定してもよい。

 さらに、「ヒストリー置換」では、コマンドライン中の単語を指定する「単語指示子」を組み合わせることで、指定した過去のコマンド(イベント)から特定の部分を取り出すこともできる。

 なお、bashでは、コマンドライン先頭のコマンド自体が「0番目」の単語である。「ls -l」なら「ls」が0番目、「-l」が1番目となる。なお、この単語指示子の後にさらに修飾子を付けて、単語の一部を抜き出すという指定もできる。

 詳しくはmanで調べてほしいが、1つだけ解説すると「:p」を最後に付けると履歴展開した結果のみを表示することができ、コマンドは実行されない。履歴展開になれないうちは、これを使うといいだろう。例えば、「ls /temp」のあと「cd !*:p」とすると、cdコマンドの引数として直前に実行したコマンドの引数全部を展開した結果「cd /temp」を表示するがコマンド自体は実行されない。ただし、展開の結果は、ヒストリーの最後に追加されるので、直後に「!!」で実行させることもできるし、後で参照しやすくなる。

パターン 意味
!:1 直前に実行したコマンドの1つ目の単語(最初の引数)
!:n 直前に実行したコマンドのn個目の単語(nは1以上の整数)
!:n-m 直前に実行したコマンドのn個目からm個目までの単語(n、mは1以上の整数)
!:$ 直前に実行したコマンドの最後の単語
!:* 直前に実行したコマンドの引数全て(!*と省略することも可能。!:1-$の省略形)
!:n* 直前に実行したコマンドのn個目から最後までの引数(!:n-$の省略形)
!:n- 直前に実行したコマンドのn個目から最後の1つ前までの単語(最後の単語は含まない)
ヒストリー置換の「単語指示子」(主なもの)

 「!echo:4-$」は、「echoで始まる直前のコマンドの4つ目から最後までの引数」という意味になる。前述の「!*」は、ヒストリー置換の単語指示子の省略表現で、「!:1-$」の省略表現である。この辺りを図解すると次のようになる。

bashのヒストリー置換機能の例 bashのヒストリー置換機能の例
赤字部分が入力したコマンドライン。!を使った記法で、過去に実行したコマンドやその引数の一部を取り出すことができる。

ヒストリーで呼び出してコマンドを編集することも可能

 さらにbashでは、ヒストリーで呼び出したり、ユーザーが入力している現在行の編集をテキストエディタのように行ったりすることもできる。左右カーソルキーで1文字ずつカーソルを移動させる、[Home]/[End]キーで行の先頭、末尾に移動も当然行えるが、bashでは、テキストエディタと同等のカーソル移動コマンドや編集機能がある。

 [Esc][Back Space]キーは1つ前の単語を「キル(削除)」し、[Ctrl]+[Y]キーはキルした単語をカーソル位置に貼り付ける。これを使うと、下図のような編集がコマンドライン行内で行える。こうした編集作業は、UNIX/Linux系では、全て端末の制御コード(エスケープシーケンス)で行われる。

bashの行編集機能の例 bashの行編集機能の例
カーソルをワード単位で動かして、ワードの削除や貼り付けが行える。

 コマンドラインでは、スペースを区切りとしているため、「ワード」の削除コマンドやワード単位の移動コマンドは非常に便利だ。さらにbashの組み込みコマンドのfcを使えば、ヒストリーにあるコマンド行をエディタに読み込み、編集した後で実行させることも可能だ。

 もっともここまで細かく編集することはまれで、実際には、前述のヒストリー呼び出しなどで大半が片付く。これら以外の割り当てについては、以下の記事も参照のこと。

 取りあえず、どのようなキー割り当てになっているのかは、bashの組み込みコマンドであるbindを使って「bind -p」で表示できる。このとき、各行の先頭のダブルクオートで囲まれた部分がキー、コロンの後が動作(bashの組み込み関数)である。「\C-」は[Ctrl]キーを押しながら、「\e」は、[Esc]キーを意味する。例えば、「"\C-a"」は[Ctrl]+[A]キーを意味し、「"\eb”」は「[Esc][B]キー」という意味である。

 このキー割り当ては、デフォルトではemacsエディタのキー割り当てに基づいている。他にvi系エディタに基づいたキー割り当ても利用できる。それぞれのキー割り当てと変更方法については、以下の記事も参照のこと。

 このコマンドの出力をファイルなどに保存してみれば分かるが、ほとんど全ての文字に対して関数が定義されている。「a」には「a」を入力するというコマンド(self-insert)が割り当てられている。逆にいうと、bashでは、全ての入力文字にコマンドを割り当てることが可能である。どうしても、bind -pの出力が見難いという場合にはawkやsedコマンドを使って文字列を置換し、分かりやすい表記に置き換えることもできる(コマンドの意味についてはおいおい解説する予定だ)。

bind -pの出力をawkコマンドで整形した出力 bind -pの出力をawkコマンドで整形した出力
bind -pの出力をawkで文字列置換すれば、見やすいコマンドラインのキー割り当てのリストを作ることができる。

bind -p | awk '{ gsub(/\\C/,"[Ctrl]"); gsub(/\\e/,"[Esc]"); print }' | sort


bind -pの出力を見やすくするコマンド例

 このようにWSLを使うと、コマンドの出力を加工でき、簡単な出力のコマンドを組み合わせて作業を行うことができる。こうした考えは「Software Tools」という書籍(邦訳は「ソフトウェア作法」、共立出版)で世の中に広まったが、著者の一人は、UNIXの開発者でもあるBrian W. Kernighan氏である。この本で述べられる基本的な考え方はいまでも有効なので、未読の方は一度、目を通すことをお勧めする。

WSLとWin32の関係

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。