I have encountered several customers where something I thought was well understood wasn’t as clear as I thought, and that is relative paths and working directories. In an effort to help the SCCM community I will try to explain it all here.
When you are writing a batch file to control some kind of software installation you obviously need to reference the files of that install. The trick is how the execution of that batch file finds those files. As an example for this discussion I will keep it simple and create a batch file (I will call it copyfiles.cmd) with the following line in it:
Xcopy /y trace32.exe c:\
For my example I will place copyfiles.cmd and trace32.exe in c:\packages\copyfiles.
When the batch file executes it will go searching for trace32.exe and if it can be found it will be copied to the root of the C drive. How does it get found? Well, the directory that you are in when you execute the batch file is called your working directory, and that is the first thing which the OS looks in to find trace32. If I open a command prompt and I navigate to the root of the C drive, then the root of the C drive is my working directory. From my command prompt I might try this:
This fails to find trace32 to copy. Why? Because when that batch file executes it looks for trace32.exe in the root of the C drive, my working directory, and fails to find it. I could solve this by defining an absolute path, such as:
xcopy /y \\Myserver\c$\packages\copyfiles\trace32.exe c:\
Awesome. Now that I have an absolute path I can run my batch file from any directory on the machine and it will work. But wait..., what happens when I distribute this with SCCM or SMS? If I want trace32.exe in the root of the C drive on all my SMS/SCCM clients and not just this one I would create a package with a package source of \\Myserver\packages\copyfiles and for that package I would create a program that calls copyfiles.cmd. I advertise that out to all my clients and everyone gets trace32 in the root of their C drive, right?
Maybe. There are a few catches. Yes, this would run and yes it would copy trace32 into the root of every C drive IF every computer has permissions to access \\myserver\packages. That is its own catch but what I am discussing here is the fact that it has to go to \\myserver at all. When you setup that SMS/SCCM package you probably set it up to copy all the files from \\myserver\packages\copyfiles out to your distribution points. The clients then probably downloaded all (two) of those files down into the local cache on the hard drive of the SCCM/SMS client. The SCCM/SMS client then looked in that local folder, found the copyfiles.cmd and executed it per the program. The second catch is that even though we went through all the trouble to move trace32.exe from the site server to the distribution point and down into the client cache, the batch file had a hard coded path back to the server. When the batch file ran it ignored the copy of tracew32 sitting next to it and instead reached across your WAN to the server to copy the file from there. That somewhat defeats the usefulness of SMS/SCCM for minimizing bandwidth usage for software deployments.
In this example the first version of the batch file would have been the right one to use. Unlike my manual process outlined above, when SCCM runs a package it sets the working directory to the root of the SCCM/SMS package that was copied into the client cache. Thus it would have found the already local copy of trace32.exe.
Ok, now I told you all that to tell you this. SCCM/SMS sets the working directory (by default, it can be modified) to the top level of the package you setup in the source location. To make this clear, consider these two similar, yet different, package directories and files:
For package1 our original batch file would work fine. That same batch file in package2 would fail because trace32 is in a subdirectory, not the working directory that SMS/SCCM sets up. To solve this we need to give a relative patch to trace32.exe. For package2 the batch file would need to look like:
Xcopy /y .\trace\trace32.exe c:\
This gives us the best answer. We don’t have a hardcoded absolute path. Instead we are telling the computer that runs the batch file to look in the trace directory under the working directory (where ever that may be) to find trace32.exe.
For more information you can do a Bing search on “relative paths” or start with http://en.wikipedia.org/wiki/Path_%28computing%29.
what about in cmd run %~dp0 for relative path regardless which DP it runs from?
%~dp0 is only available within a batch file and expands to the drive letter and path in which that batch file is located (which cannot change). It is obtained from %0 which is the batch file's name.
Good point John. In my example I was avoiding variables to aid in helping people understand things. In practice, %~dp0 is very handy.