Navigate to a command file's directory in Windows CMD Shell

I use Ruby a lot on Windows and it’s quite common to use a simple Command file to launch the script, especially when scheduling it using something like Windows Scheduler or PyCron. When launching a Command script in Windows, it will usually have an empty environment and will launch in something like c:\windows\system32 instead of the directory where the command scripts is. Often, it’s quite useful to move to the directory of the script so that everything works with relative paths.

This post is just a note for me to be able to remember the details of how to do this.

The simple way to do this is to edit the command file directly. Let’s say that the command file is in d:\work and you’d like to move to this path before running the rest of the script, the easy way is to add this to the top of the file:

cd \work

Note that the second line could be cd \work or cd d:\work but it’s important that this have either the full path on D:\ or at least start with a \ since that will ensure that the path changes correctly. If you just have it as D:\ and cd work it could fail for a few reasons. Try it on command prompt to get a feel of the things that could go wrong.

However, this expample is helpful and illustrative since it tells us what we need to do:

  • Identify the drive of the file and change to that drive
  • Identify the directory of the file and change to that directory
  • Run the rest of it

Short Version

This is basically it!

:: Shift directories to the correct path
cd %~dp0

Longer version – the explanation

If you want to know what this does, here are the details. What you need to know is that when a command is called, there are parameters passed to it. The actual arguments passed to the script start from 1 but the script itself is parameter 0.

Now, to try out what each of the parts does, create a file called t.cmd in some folder and add this to it:

echo off
echo Param0 is %0
echo Drive0 is %~d0
echo Path0  is %~dp0

Run it as t.cmd and see the output you get – here is what I get (the script is in e:\blog)

E:\blog>echo off
Param0 is t.cmd
Drive0 is E:
Path0  is E:\blog\

So, what we see is:

  • %0 gives us the name with which the command is called
  • ~d0 gives us the drive of the command script
  • ~dp0 gives us the full directory path of the script (without the script name)

This then gives you a clear idea of why the commands in the previous section work:

  • Change drive
  • Change to the full path

Bonus – returning to the Launch Directory

My main need is met by what’s written above. However, some times, it is quite annoying that the directory has changed (especially when run interactively). If you want to allow the script to come back to the directory from where it was launched, you need to use a pair of other commands: pushd and popd.

The final command file will look something like this then:

:: Save the directory where we are
pushd %CD%

:: Shift directories to the correct path
cd %~dp0

:: do what we need to do
:: do what else we need to do

:: Restore the directory where we started from

For this, we need to know 3 things:

  • %CD% at the start of the command file holds the directory in which the command file is launched
  • pushd pushes a directory path to the stack – this is the parameter passed to pushd. It also moves to that directory.
  • popd pops the directory path off the stack. It also moves to that directory.

So, if you pushd a directory, it will be saved on the top of the stack; when you do a popd, it will be taken off the stack and passed back to your script. So, the first bit saves the path on the stack and the second bit retrieves it so that we can use it. (This assumes that any other things pushed on to the stack have been properly retrieved before the popd is called).

As you see, we are doing this:

  • pushd %CD% saves the launch directory
  • At the end we do popd to retrieve and move to the launch directory

Further Reading

Most of this information is based on information found from the following:

  • Command line arguments are explained at (read the d0 and dp1 bits)
  • Details of pushd –
  • Details of popd –
  • A helpful discussion –
comments powered by Disqus