Suppose I have a batch script that expects a folder path in argument %1
. I want to append a file name to the path and use that in a command. Is there a simple way to do this in a way that is reliable in all situations?
I do not want to PUSHD %1
and simply ignore the path afterward. Assume my logic requires the current directory to remain unchanged.
The issue is the %1
argument may or may not have a terminating backslash - i.e. c:\path
or c:\path\
.
Typically I can simply use "%~1\file.ext"
because Windows treats "c:\path\file.ext"
and "c:\path\\file.ext"
identically.
But there is one nasty case - c:\
The following commands produce this error: The filename, directory name, or volume label syntax is incorrect.
dir "c:\\file.ext"
del "c:\\file.ext"
The odd thing is, these commands work just fine:
if exist "c:\\file.ext" echo OK
echo text>"c:\\file.ext"
copy "c:\\file.ext" "somePath"
xcopy "c:\\file.ext" "somePath"
There is another problematic case - c:
, meaning the current directory on drive C:, will give the wrong result if I append a backslash. "c:file.ext"
is not the same as "c:\file.ext"
.
Is there a simple way to safely append a file name to any given folder path argument?
I am not concerned with UNC paths, as I do not use them very often, and I believe there are some commands that do not play nice with UNC paths.
Note: This is a case where I am posting a paired question and answer. This issue has been a thorn in my side for a number of years, and I just recently discovered a simple solution that I thought others may be interested in.
They indicate that the path should be passed to the system with minimal modification, which means that you cannot use forward slashes to represent path separators, or a period to represent the current directory, or double dots to represent the parent directory.
From the "Text" group, click [Quick Parts] > Select "Field..." Under "Field names," select "FileName." In the "Field properties" section, select a format. In the "Field options" section, check "Add path to filename." The file name will now appear in the header or footer.
EDIT - answer changed to address MC ND's comment
The solution is so simple :-) "%~f1.\file.ext"
Windows file names cannot end with .
or <space>
, so all Windows commands simply ignore trailing .
and <space>
in file/folder names.
And a single .
following a backslash represents the current directory at that location.
Assume the current working directory is c:\test
, then the table below show how various values get resolved:
"%~1" | "%~f1.\file.ext" | equivalent to
-------------+------------------------+------------------------
"c:\folder\" | "c:\folder\.\file.ext" | "c:\folder\file.ext"
| |
"c:\folder" | "c:\folder.\file.ext" | "c:\folder\file.ext"
| |
"c:\" | "c:\.\file.ext" | "c:\file.ext"
| |
"c:" | "c:\test.\file.ext" | "c:\test\file.ext"
| |
"." | "c:\test.\file.ext" | "c:\test\file.ext"
| |
".." | "c:\.\file.ext" | "c:\file.ext"
| |
<no value> | "c:\test.\file.ext" | "c:\test\file.ext" I hadn't thought of this case
until I saw MC ND's answer
You can easily reduce the expression into the canonical equivalent form using a FOR loop:
for %%A in ("%~f1.\file.ext") do echo "%%~fA"
As Harry Johnston states in his comment, this solution does not work with UNC root paths (\\server\shareName
) or long paths (\\?\c:\somePath
). See MC ND's answer if you need to support either of those cases. The Harry Johnston answer is simpler, but it does not support the case of no argument given.
I don't see a way to make it work for all the cases. Your solution will handle all the local paths cases, but to handle UNC or long paths it seems that it is necessary to first correct the path and then append the file data
@echo off
setlocal enableextensions disabledelayedexpansion
for /f "delims=" %%a in ("%~f1."
) do for /f "delims=" %%b in ("%%~fa\file.ext"
) do echo "%%~fb"
This "should" handle local, network or long paths (the reason for the for /f
is the ?
), both absolute and relative, and the posibility of no giving a path in the command line.
Since ~f eliminates multiple backslashes, I think this works just as well as the original proposal, and also appears to handle share roots and long paths correctly:
for /F "delims=" %%i in (%~f1\file.ext) do set filename=%%~fi
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With