Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to safely append a file name to a Windows folder path argument?

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.

like image 884
dbenham Avatar asked Nov 02 '14 17:11

dbenham


People also ask

What does \\ mean in Windows path?

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.

How do you add a file path to a file?

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.


3 Answers

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.

like image 50
dbenham Avatar answered Oct 10 '22 07:10

dbenham


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.

like image 2
MC ND Avatar answered Oct 10 '22 08:10

MC ND


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
like image 2
Harry Johnston Avatar answered Oct 10 '22 07:10

Harry Johnston