alias万歳。doskeyは微妙。

シェルのalias機能が好きだ*1

シェルといっても普段はWindows上でbashtcshを使っているのだけど、例えばlsにはaliasでオプションを付けている。特に「--show-control-chars」が無いと8bitキャラクタの情報が無視されてしまい、日本語のファイル名が表示されない。

# .bashrcの場合
alias ls='ls -FG --color=auto --show-control-chars'
# .tcshrcの場合
alias ls ls -FG --color=auto --show-control-chars

ところでlsは素のコマンドプロンプトからも使用できるようにしてあるのだけど、コマンドプロンプトにはaliasそのものは存在しない。単純に考えると「毎回オプションをつけてlsを実行しなさい」なんてことになりそうだけど、非常に面倒。というか解決策ぐらいあるだろう、いくらコマンドプロンプトでも。

doskeyを使う

コマンドプロンプトではaliasに近い機能をdoskeyで実現できる。

doskey ls=ls -FG --color=auto --show-control-chars $*

$*はヘルプによると「コマンド ライン上のマクロ名に続くテキストで置き換えられるシンボルです。」ということで、バッチファイルの%*と似たようなものらしい。

登録したエイリアス(doskeyのヘルプ曰く「マクロ名」)を削除したい場合は、次のようにすればよい。

doskey ls=

これもバッチファイルで環境変数を空にする方法に似た書き方だ。

doskeyの設定を恒久的にする

doskeyで別名を登録する方法は分かったのだけど、doskeyで登録したマクロはプロンプトを閉じると無効になってしまう。マクロを登録するバッチファイルを作成してコマンドプロンプト起動後に毎回実行する、という方法もあるけど、面倒だ。

bashの.bashrcみたいに、コマンドプロンプト起動時に特定のバッチファイルを実行する仕組みがあれば、そこでマクロ登録用のバッチファイルを実行すればよいはずだ。

cmd.exeのヘルプによると、レジストリを弄ればコマンドプロンプト起動時に特定のコマンドを実行できるように設定できる。レジストリにマクロ設定用のバッチファイルを登録しておけば、コマンドプロンプト起動時に自動的にバッチファイルが実行されてマクロが設定されるはずだ。

以下のレジストリ変数の片方ないし両方にREG_SZかREG_EXPAND_SZで実行するコマンドを登録しておけばよいようだ。

HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun

例えば以下の内容をD:\home\cmdrc.batに保存しておいて、

@echo off
doskey ls=ls -FG --color=auto --show-control-chars $*
@echo on

先ほど挙げたレジストリ変数AutoRunに「D:\home\cmdrc.bat」と書き込んでおくと、コマンドプロンプト起動時にこのバッチファイルが実行されてマクロが有効になる。

doskeyの不便なところ

……と、ここまで書いてきてアレだけど、実は普段doskeyは全く使っていない。少なくとも私の環境ではdoskeyを使用する方法に若干の不具合があるのだ。

というのも、パイプで複数コマンドを連結して実行する時、例えば、

doskey foo=tail $*
ls -l | foo -1

という風に実行すると、fooからtailにマクロ置換されないらしく、実行に失敗してしまう。これは仕様なのか、それとも私の環境でのみ発生する問題なのか。未だに謎だ。

バッチファイルで誤魔化す

doskeyの代わりにバッチファイルで誤魔化す方法もある。要するに「使用したいエイリアス名でバッチファイルを作り、実行して欲しい処理を書いておく。で、コマンド実行時に実行ファイル本体じゃなくてバッチファイルを実行させてしまう」という、何もセキュリティ的に微妙な方法だ。

この方法では、環境変数PATHEXTの内容や、コマンド実行時の実行ファイル検索の順番を利用している。

環境変数PATHEXTについて

Windowsでは実行可能ファイルかどうかを拡張子で判断しているようで、実行可能ファイルの拡張子が環境変数PATHEXTに登録されている。コマンド実行時に拡張子を指定しなかった場合、PATHEXT内の拡張子が補完された名前のファイルが検索されるようだ。

PATHEXTには.exeや.batなどが登録されているので、プロンプトで「ls」と実行するとls.exeやls.batなどが検索されることになる。

同一ディレクトリにls.exeとls.batがある場合はどうなるだろうか? PATHEXTには優先順位があって*2、実行可能ファイル検索時には優先順位の高い拡張子から順に補完され、検索される。標準では優先順位は「.exe > .bat」となっているので、先にls.exeが検索され、次にls.batが検索される。なので「ls」と実行するとls.exeが呼ばれることになる。ls.batを実行したい場合は「ls.bat」と拡張子付きで実行する必要がある。

ちなみにPATHEXTの内容を変更することは、セキュリティホールになる可能性があるのでオススメできない。

コマンド実行時の実行ファイル検索順

コマンドプロンプトでコマンドを実行する時、そのコマンドの実行ファイルやバッチファイルなどを環境変数PATHなどから探すのだけど、その順番は大体次のようになっているようだ。

  1. カレントディレクト
  2. システム環境変数PATHに登録されているディレクトリ(先頭側から順に)
  3. ユーザ環境変数PATHに登録されているディレクトリ(先頭側から順に)

複数のディレクトリに同名の実行ファイルがあった場合、上記の順番で検索して最初に見つかったものが実行されるようだ。

ところでPATHEXTの都合により、同一ディレクトリにls.exeとls.batがある場合、「ls」と実行するとls.exeが呼ばれる。PATHEXTを変更せずに「ls」でls.batが呼ばれるようにしたい場合、ls.exeとls.batを別のディレクトリに保存し、環境変数PATHを検索する時にls.batのあるディレクトリが先に見つかるようにすればよい。

バッチファイルで誤魔化す具体例

例えばlsで「ls -FG --color=auto --show-control-chars」を実行するようにしたい場合。

まずls.batを作る。内容は以下の通り。

@echo off
ls.exe -FG --color=auto --show-control-chars %*
@echo on

作成したls.batを、環境変数PATHを検索する時にls.exeのあるディレクトリよりも先に検索される場所に置く。そんな場所が無いのなら作る。

この状態でコマンドプロンプトで「ls」と実行すると、ls.batの内容が実行される。

パイプによる連結は大丈夫か?

doskeyではパイプによる連結でうまく動作しなかったのだけど、バッチファイルを使う方法なら何とかなるようだ。

@echo off
:: foo.bat
tail.exe %*
@echo on
ls -l | foo -2

私の環境では、これで末尾の2行のみ表示された。

doskeyとバッチファイルのどちらの方法が良いか

実行速度は、何となくdoskeyでマクロ登録した場合の方が速い気がする。doskeyの方が速いというよりはバッチファイル化したほうが遅い*3というべきかもしれない。

エイリアスを複数登録したいなら、doskeyでマクロ名を登録するバッチファイルを作ってコマンドプロンプト起動時に自動実行するようにしたほうがよいだろう。

バッチファイルや自作コマンド用のディレクトリをPATHに登録しているような人で、せいぜい2〜3個のエイリアスがあればよいという場合は、バッチファイル化の方が手軽に感じるかもしれない。

私の場合、基本的にbashtcshを使うので、必要なコマンドプロンプト用のエイリアスはlsぐらいだ。なのでバッチファイルで誤魔化している。

*1:軟弱者呼ばわりされるかもしれないけど。

*2:PATHEXTのリストの先頭側に登録されている拡張子が優先される。多分、単純に先頭から検索しているのだと思う。

*3:動作に微妙な引っ掛かりを感じる。