Work

Use Relative Paths In Your Batch Files

Reader Paul writes in with an interesting tip for using relative paths in a batch file: you can use a special code to represent the current path—useful for batch files on a Flash drive.

We’ve previously covered a similiar way to create shortcuts that run off a USB stick—but if you need the full path to the batch file regardless of the machine you plugged it into, you can substitute %~dp0 anywhere you want to use the current path of the batch file. Paul explains:

If you use “%~dp0″ (sans quotes) in a batch file, this will point to the batch file’s path. For example :

SET MAC=00:00:00:00:00:00
%~dp0mc-wol.exe %MAC%

MC-WOL.EXE is a wake-on-lan program, and this script wakes up the PC ready for the boss to remotely access his PC. I can get him to wake up his PC remotely without confusing the poor thing too much.

It’s one of those tips that might not be useful for everybody, but it could really come in handy as part of your flash drive toolkit. For more, you can check out how FAST is a geeky command-line database, or how to kill multiple applications from the command line with a batch file. Thanks, Paul!

Got any useful batch-scripting wisdom to share? Tell us in the comments.

Comments (AU Comments | US Comments)

  • Bill Clark
    @Phoshi:

    PowerShell isn't so bad. I haven't delved too deeply into it, but it's much more powerful than cmd.

    However, the setback to PowerShell is that it's not installed by default on existing OS's. When we roll out Windows 7 at my company, things will get much easier in terms of scripting workstations.

  • daddydave

    Slightly off topic, but

    I used to create my personal unattended install disk using a 30+ printed page batch file called HFSLIP.

    And for anyone who is serious about writing batch files, Timo Salmi's Batch File FAQ is a must have.

  • daddydave

    @lostarchitect:

    Then you'd be surprised at all the new features they keep adding.

  • krn

    I think it's also worth noting that %~dpn1 gives the path of te first file passed in as a parameter. I used this to write a batch file to convert video files to iPod-compatible videos.

    @echo off
    cd /d %~dp0
    start "iPod Conversion: Normal Aspect Ratio" /BELOWNORMAL "ffmpeg.exe" -vcodec xvid -b 500 -qmin 3 -qmax 5 -bufsize 4096 -g 300 -acodec aac -ab 96 -threads 2 -i "%~dpnx1" -s 320x240 "%~dpn1.ipod.mp4"

    This way, you can drag and drop any video file supported by ffmpeg onto the batch file, and the converted file will be created in the same folder as the original file, but with a .ipod.mp4 extension.

    There's a great explanation of how to get the drive letter, path, file name and extension from a parameter here.

    krn

  • peanut_butter

    @eagles500: Some people might agree, but your comment is hardly helpful unless you provide some actual code.

    peanut_butter

  • nicoco

    @nicoco: sorry, I missed the first bit:

    In addition, substitution of FOR variable references has been enhanced.
    You can now use the following optional syntax:

    %~I - expands %I removing any surrounding quotes (")
    %~fI - expands %I to a fully qualified path name
    %~dI - expands %I to a drive letter only
    %~pI - expands %I to a path only
    %~nI - expands %I to a file name only
    %~xI - expands %I to a file extension only
    %~sI - expanded path contains short names only
    %~aI - expands %I to file attributes of file
    %~tI - expands %I to date/time of file
    %~zI - expands %I to size of file
    %~$PATH:I - searches the directories listed in the PATH
    environment variable and expands %I to the
    fully qualified name of the first one found.
    If the environment variable name is not
    defined or the file is not found by the
    search, then this modifier expands to the
    empty string

    nicoco

  • nicoco

    There are other interesting %~ things you can do in batch.
    Type "help for" on the command line to find out:
    The modifiers can be combined to get compound results:

    %~dpI - expands %I to a drive letter and path only
    %~nxI - expands %I to a file name and extension only
    %~fsI - expands %I to a full path name with short names only
    %~dp$PATH:i - searches the directories listed in the PATH
    environment variable for %I and expands to the
    drive letter and path of the first one found.
    %~ftzaI - expands %I to a DIR like output line

    In the above examples %I and PATH can be replaced by other valid
    values. The %~ syntax is terminated by a valid FOR variable name.
    Picking upper case variable names like %I makes it more readable and
    avoids confusion with the modifiers, which are not case sensitive.

    nicoco

  • paintbox

    I am afraid. I am afraid because a bunch of people may yet pounce on this thread with Bash scripts! :)

    paintbox

  • paintbox

    @eagles500: Comments like yours are like flies, inevitable yet predictable. Alas.

    paintbox

  • courtarro

    @eagles500: Yeah, 'cause a portable USB drive used on Windows machines won't be rendered useless by the propensity of Windows to come without bash installed!

    I dare you to follow that series of sarcastic negatives!

    Or were you suggesting a portable version of bash with 50MB of Cygwin DLLs attached? Totally worth it!

  • SQLGuru

    @Brandon Smith:
    Why not just run a command like this one (assuming your batch file is named test.cmd)

    @for %%f in (*.*) do @if %%f neq test.cmd @ren %%f %%f.old

    I assume you already have the rename command working, so just sub it with whatever you are using. Single command to do all of the magic (no perl and regex required).

  • SQLGuru

    @lamintak: I think the main difference is that the %~dp0 is the path to the batch file regardless of where the current directory (.) or parent directory (..) point to.

    Imagine this scenario:
    c:> f:\flash-files\some.bat

    contents of some.bat
    ..\someother.bat

    the batch file won't find someother.bat because the parent is still pointing to the c drive.

    if the contents of some.bat were this:
    %~dp0\..\someother.bat

    It should succeed.

  • jimforcy

    In my job we use many batch files to install programs so I've written a program based off of BatchRunner so that I have a nice little gui to run all the batch scripts. I do this because the batch files are written by all sorts of different people who have used different naming conventions and commands for each. Some rely on each other so I cannot rename them either. My program is customized for our work but I'd recommend checking out Batch Runner if you want an easy way to catalog your batch scripts.

    [corz.org]

    jimforcy

  • lostarchitect

    you know, i don't think i've written a batch file in... geeze. 10 years? wow.

  • theformatter

    @Brandon Smith: If you have a limited number of extensions to deal with, you can try something like this:

    ren *.log *.csv

    This will leave your .cmd file alone and you can have it clean up after itself.

    You could use also use a FOR command, it's a bit ugly but works well (this is all on one line in your batch file):

    FOR %%f in (*.*) DO @(IF NOT %%f==%~n0%~x0 REN %%f %%~nf.csv)

    Where %~n0%~x0 expands to the name of the current batch file (you can use just %0 if you ALWAYS use the full name of the batch file when you run it) and %%~nf is the filename up to but not including the extension.

    The FOR command is grossly underutilized in Windows command shell - it actually has 5 or 6 different functions with billions and billions of possibilities.

    Jim

  • Brandon Smith

    @kevin:
    Sorry Kevin, I don't think this works. I got no change after running it. Is there something that I'm missing here? (havent had much exp with batch scripting)

    Brandon Smith

  • LethAL

    This can be used if you want to be able to run batch files with relative paths as administrator easily in Vista/7. Just add "cd /d %~dp0" near the top of the script (below "@echo off" would be good). It can be used in a 'portable' batch file as well, I just thought that I would highlight its seemingly unusual effect.

    It's not unusual at all, in fact. Vista/7/su(do) all reset all or most of the environment (including the working directory) when permissions are elevated.

    LethAL

  • onaclov2000

    @Brandon Smith:
    Why not just write the batch script at one level higher, and tell it to CD to the directory then do it, then your batch file you created won't be touched.

    Just a suggestion!
    Onaclov
    Onaclov Nation

    onaclov2000

  • kevin

    @Brandon Smith: Here's a batch script that will rename every file in its folder from *.* to *.csv, and then deletes itself. All of the %~whatever magic is explained if you type "for /?" (sans quotes).

    for %%i in (*.*) do call :SELECTIVERENAME %%i
    del %~f0
    goto :EOF

    :SELECTIVERENAME
    if %~f1 == %~f0 goto :EOF
    ren %~f1 %~n1.csv

  • MavisGadhero

    PUSHD and POPD have been part of Windows since XP. http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/pushd.mspx?mfr=true

    MavisGadhero

  • TheFu

    @Brandon Smith: Good luck with that. I don't think it works the way you hope it does.

    When it comes to renaming/moving files, you definitely want to create an exact list of files to work on.

    If you are on *nix, here's a trivial perl script to rename files using regex notation:

    #!/usr/bin/perl -w

    $op = shift or die "
    Usage: rename expr [files]
    chomp(@ARGV = ) unless @ARGV;
    for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
    }

    TheFu

  • TheFu

    Be careful about trusting the current PATH when you run scripts. Almost every external program called should be completely, fully specified inside the script (or via an initialization `source`) to limit the probability of running the wrong program in your scripts. That's why root accounts should never have '.' in the PATH. Simply too dangerous.

    Scripting in Windows is painful when compared to almost any other system. Perhaps wsh will change that. BAT/CMD scripts are a joke when compared to the alternatives. Heck, even an IBM mainframe running MVS has had REXX for 15+ years.

    In *nix systems, the `whence` command can be used to determine which directory your script is being run from.

    TheFu

  • Brandon Smith

    So I am currently trying to make a batch file that change the extension of all the files within a folder. This is done fairly easily with the REN function but this causes the actual batch file to get renamed (considering its in the same directory when I run it). I would most prefer that it gets deleted after being used but unfortunately it gets renamed mid process so the deletion doesnt work. Is there a better way to do this by referencing the path but making it so that no batch code would have to be changed each time its used? This is the current code (only one line). Anyone have any thoughts?

    ren "*.*" "*.csv"

    Brandon Smith

  • P_Smith

    @P_Smith:

    Whoops, I neglected to include the URL of the main Garbo archive:

    [garbo.uwasa.fi]

    P_Smith

  • P_Smith

    If you need to run directories in specific places but where you are running them from changes, you can use the PUSHDIR and POPDIR utilities from the "batpower" utility package at the Garbo DOS archive:

    ftp://garbo.uwasa.fi/pc/batchutil/batpower.zip

    The Garbo site is also worth checking for many other classic DOS programs.

    PUSHDIR saves the current directory into a text file, and the directories can be "stacked" if you run PUSHDIR more than once. POPDIR does the reverse, removing the "top" of the stack and changing to that directory. The text file for the stack is put on the root directory of the drive that COMMAND.COM was run from.

    If you wanted to copy a directory listing of DLLs from your windoze directory, you could run this batch file. Its design with use of PUSHDIR and POPDIR guarantees that no matter where you run it from, that is where the "DLL.txt" file will end up, not lost amongst a maze of directories.

    rem ----------------------------------------...-">----------------------------------------...

    rem --- Save the current directory
    PUSHDIR

    rem --- Change to the windoze directory
    rem --- (D: on my dual-boot system)
    C:
    CD\winnt

    rem --- Copy directory listing of DLLs to a text file
    DIR *.dll > dll.txt

    rem --- Pop the stack, return to starting directory
    POPDIR

    rem --- Move the DLL text file to the current directory
    MOVE C:\winnt\dll.txt .

    rem --- The dot makes the current directory
    rem --- (where you started the batch file)
    rem --- into the destination of the move or copy
    rem ----------------------------------------...-">----------------------------------------...

    P_Smith

  • chaws

    Here's a useful trick for cycling through a group of files dragged on to a batch file:

    :CHECKARG
    if "%~f1"=="" (
    echo No more args given. Done.
    exit /b 0
    )
    :: Operations to perform on each file
    shift
    goto CHECKARG

    chaws

  • Phoshi

    @eagles500: or Cygwin, powershell, or a set of personal .exe files. The default cmd is pretty lacking compared the the *nix counterparts, yeah :(

  • lamintak

    @Nosgoroth: Ah yes, I forgot about that. Normally I run my batch files in the directory they're in.

    lamintak

  • Nosgoroth

    @lamintak: From my experience, %dp0 is the path of the batch file, which might not be the same as %cd%, for example if you call the batch file as "somedir/batchfile.bat" or if it is in the path.

  • eagles500

    ah it just so much eaiser with a proper shell like bash

    eagles500

  • lamintak

    Excuse my last comment!

    After some brief testing, it looks like the difference between %~dp0 and %CD% is that %~dp0 has a trailing backslash whereas %CD% does not. One awesome benefit of %~dp0 is that it apparently supports UNC paths! It can take a while to translate, though.

    lamintak

  • lamintak

    After some brief testing, @AmphetamineCrown: Yes, but it's sometimes nice to know the exact path

    lamintak

  • AmphetamineCrown

    Can't most relative scripting be done with "." (= current directory) and ".." (= parent directory)?

    AmphetamineCrown

Post Your Comments

Got something to say? There are two ways to comment:

1. Guests

Click here to comment instantly.

2. Facebook Users

Click below to comment using your Facebook account.

We're looking for comments that are interesting, substantial or highly amusing. If your comments are excessively self-promotional, obnoxious, or even worse, boring, you will be banned from commenting. All comments are moderated.