「CD コマンドの機能拡張 第二版」以来、16年ぶりの大幅修正。機能は変えていないが、リファクタリングで機能追加がシンプルになった。
cdd.bat
という名前で作って、doskey cd=cdd $*
でaliasを定義して使う。
機能は、
- 移動はすべて pushd で行う。
- cd のみのときはディレクトリスタックの表示
- ホーム(%HOME%)への移動は cd ~
- cd - で前のディレクトリに戻る
- cd + で現ディレクトリと一つ前のディレクトリの交換
- cd 存在するディレクトリ だとそこへ移動
- cd ショートカット の場合、ショートカット先がディレクトリならそこへ移動し、ファイルだとそのファイルのあるディレクトリへ移動
- cd 存在するファイル だとそのファイルのあるディレクトリへ移動 (explorerからcmdプロンプトへファイルをドラッグ&ドロップしたとき便利)
- cd それ以外 の場合環境変数CDPATHを探索してオペラランドがそこにディレクトリとして存在すればそこに移動
ほとんどの機能はモジュール化していて、ファイルの最後で定義する。
指定文字列 移動先ディレクトリ
指定文字列 サブルーチン
サブルーチンの1つとして、RubyのGemsディレクトリへの移動を書いてみた。
Firefoxのプロファイルディレクトリも、profiles.ini
を読んで決めるようにしようかと思ったけど、面倒くさいわりにメリットが無いのでやめた。
@echo off
rem 定義済みの移動先ならそこに移動する
call :PreDefined "%~dpnx0" "%~1"
if not ERRORLEVEL 1 exit /b
rem 存在するディレクトリ、ファイル、ショートカットか?
rem ファイルの場合はそのディレクトリに移動する
if exist "%~1" (
dir /b/ad "%~1" >NUL 2>NUL
if not ERRORLEVEL 1 (
call :Pushd "%~1"
) else (
if /i "%~x1"==".lnk" (
call :Shortcut "%~1"
if not ERRORLEVEL 1 exit /b
)
call :Pushd "%~dp1"
)
exit /b
)
rem %CDPATH% に存在するディレクトリなら、そこに移動する
if not "%~$CDPATH:1"=="" dir /b/ad "%~$CDPATH:1" >NUL 2>NUL && (
call :Pushd %~$CDPATH:1
exit /b
)
:Pushd
rem 移動用処理
rem 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
)
)
exit /b
:Shortcut
rem ショートカットファイルのリンク先を調べてそこに移動する
setlocal enabledelayedexpansion
set "SV="
set "FILE="
for /f "delims=" %%A in ('more ^< "%~1"') 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 SV (if defined FILE (set "FILE=!FILE!%%A")else set "FILE=!SV!\%%A")&set "SV="
if "!A:~0,2!"=="\\" set "SV=%%A"
)
if defined FILE endlocal&call :Pushd "%FILE%"&exit /b 0
rem リンク先が見つからない場合
exit /b 1
:PreDefined
rem 定義済み移動先の処理を行う
rem 移動先ディレクトリ名 または 呼び出すサブルーチン名
setlocal enabledelayedexpansion
set "DATA="
for /f "usebackq tokens=1*" %%A in (%1) do (
if /i "%%~A" == "__DATA__" (
set DATA=1
) else if defined DATA (
set "X=%%~B"
if /i "%%~A" == "%~2" if "!X:~0,1!" == ":" (
endlocal
call %%~B %*
exit /b 0
) else (
endlocal
call :Pushd "%%~B"
exit /b 0
)
if /i "%~2" == ":Help" echo.%%A %%B
)
)
if /i "%~2" == ":Help" exit /b 0
rem 定義済み移動先に存在しなかった
exit /b 1
rem ここから個別処理
:Dirs
rem ディレクトリスタックを横に並べて表示する
setlocal enabledelayedexpansion
set TEMPFILE="%TEMP:"=%\cd$$.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
exit /b
:Back
rem 1つ前のディレクトリへ
popd
goto :Dirs
:Swap
rem 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
)))
goto :Dirs
:Help
rem 定義済み移動先の一覧の表示
call :PreDefined %1 :Help
exit /b
:RubyGems
rem RubyGemsのディレクトリへ
for /f "tokens=1-3 delims=." %%A in ('ruby -e "puts RUBY_VERSION"') do (
call :Pushd "D:\Ruby\lib\ruby\gems\%%A.%%B.0\gems"
)
exit /b
rem ここから移動先定義
__DATA__
"" :Dirs
- :Back
+ :Swap
/? :Help
~ %HOME%
/ %TEMP%
// D:\Temp
/e %windir%\system32\drivers\etc
/f %APPDATA%\Mozilla\Firefox\Profiles\xxxxxxxx.20180101
/g :RubyGems