CD コマンドの機能拡張 第二版

CDコマンドの拡張について二回書いたが([1][2])、仕様を一部変更追加削除して、ネットワークパスにも対応した。

  • 移動はすべて pushd で行う。
  • cd のみのときはディレクトリスタックの表示
  • ホーム(%HOME%)への移動は cd ~
  • cd - で前のディレクトリに戻る
  • cd + で現ディレクトリと一つ前のディレクトリの交換
  • cd 存在するディレクトリ だとそこへ移動
  • cd ショートカット の場合、ショートカット先がディレクトリならそこへ移動し、ファイルだとそのファイルのあるディレクトリへ移動
  • cd 存在するファイル だとそのファイルのあるディレクトリへ移動 (explorerからcmdプロンプトへファイルをドラッグ&ドロップしたとき便利)
  • cd それ以外 の場合環境変数CDPATHを探索してオペラランドがそこにディレクトリとして存在すればそこに移動

前版との相違点は以下の通り。

  • ディレクトリ名を値に持つ環境変数名を指定した時の対応はコードの量の割に使い出が無いので削った
  • /s の機能を削った。時間がかかりすぎて実用的でないため。適切にCDPATHを設定すれば十分のはず
  • ディレクトリ表示順を新しい順で現ディレクトリも含めることにした(bashのdirs仕様と合わせた)
  • cd + 機能の追加(cd +n や cd -n は bash だとディレクトリスタックのローテートだがこれくらいのほうがシンプルで使いやすいと思い独自仕様にしてある)
  • ディレクトリ判断方法を変更し、ネットワークパスやネットワークドライブにも対応(したはず)
  • ショートカット先がCドライブ以外や共有フォルダ内の時も対応(したはず)

最後の2点はテストが足りてないかもしれない。

@echo off
if "%~1"=="" ( %引数が無ければ表示のみ%
 call :dirs
 goto :eof
)
if "%~1"=="-" ( % - なら戻って表示%
 popd
 call :dirs
 goto :eof
)
if "%~1"=="+" ( % + なら前回と交換%
 for /f "delims=" %%X in ('echo %%CD%%') do (
  popd
  for /f "delims=" %%Y in ('echo %%CD%%') do (
   popd
   for /f "delims=" %%Z in ('echo %%CD%%') do (
   if not "%%Y"=="%%Z" (pushd %%X) else (cd /d %%X) 
   if not "%%X"=="%%Y" pushd %%Y
 )))
 call :dirs
 goto :eof
)
if "%~1"=="~" if defined HOME ( % ~ ならホームへ%
 call :pushd %HOME%
 goto :eof
)
if exist "%~1" ( %存在するか?%
 dir /b/ad "%~1" >NUL 2>NUL
 if not ERRORLEVEL 1 ( %ディレクトリか?%
  call :pushd "%~1"
 ) else ( %普通のファイルだ%
  if "%~x1"==".lnk" ( %ショートカットだ%
   call :shortcut "%~1" "%~0"
   if not ERRORLEVEL 1 goto :eof %リンク先が見つかった%
  )
  call :pushd "%~dp1"
 )
 goto :eof
)

if not "%~$CDPATH:1"=="" dir /b/ad "%~$CDPATH:1" >NUL 2>NUL && ( %CDPATHに見つかった%
 call :pushd %~$CDPATH:1
 goto :eof
)

call :pushd %*
goto :eof

:pushd %pushdが成功しても元と同じならpopdする(履歴を残さないため)%
for /f "delims=" %%X in ("%CD%") do (
 pushd %*
 if not ERRORLEVEL 1 for /f "delims=" %%Y in ('echo %%CD%%') do (
  if "%%X"=="%%Y" popd
 )
)
goto :eof

:dirs %ディレクトリスタックを横に並べて表示する%
setlocal enabledelayedexpansion
set TEMPFILE="%TEMP:"=%\cd$$%TIME::=.%.tmp"
pushd > %TEMPFILE%
set DIRS=%CD%
for /f "usebackq delims=" %%A in (%TEMPFILE%) do set "DIRS=!DIRS! %%A"
del %TEMPFILE% 2>NUL
set /p=%DIRS%<NUL
endlocal
goto :eof

:shortcut
setlocal enabledelayedexpansion
set F=&set FILE=
for /f "delims=" %%A in ('more "%~1"^|^ %findstrはWin2Kの日本語バグのため必要%
findstr /v /b aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') do (
 set A=%%A
 if "!A:~1,2!"==":\" for %%B in (A B C D E F G H I J K L M N O P Q R S T U V W
   X Y Z) do if "!A:~0,1!"=="%%B" set FILE=%%A
 if defined F (if defined FILE (set FILE=!FILE!%%A) else (set FILE=!F!\%%A))&set F=
 if "!A:~0,2!"=="\\" set F=%%A
)
if defined FILE endlocal&call "%~2" "%FILE%"&exit /b 0
endlocal&exit /b 1

なお、コード中のWin2Kのバグについては以前の記事を参照。