简体   繁体   中英

WIndows batch script to find a correct parent folder

I am having a simple problem that I cannot figure out: I want to find a parent folder that contains a certain file, and generate an absolute path of that parent folder - and this needs to be done inside a batch script - no powershell, no perl, no python...

Example, say this is my directory tree:

  root_parent_path
    dir0
    dir1
      file1
      dir2
        dir21
        dir22
      dir3
        dir31
        dir32

In example above, root_parent_path is in form c:\\folder1\\folder2\\ .

The result should be stored in an environment variable, say RESULT_PATH.

So if I call any of the following:

script.batch file1 root_parent_path\dir1\dir2
script.batch file1 root_parent_path\dir1\dir2\dir21
script.batch file1 root_parent_path\dir1\dir3\dir31

the variable RESULT_PATH should be set to root_parent_path\\dir1 , because that is a parent that contains that file1 . (I don't care if there is more then one or no parent folder - I will make sure there is only one.)

Help is much appreciated, so much time has been wasted on this...

Note: I would appreciate if code is also explained! If two answers offer working solution, I will pick up the one with better explanation.

Excuse me. There are a couple points in your question that are not clear to me.

  • If root_parent_path is a folder placed inside the root folder of the disk, as you indicated in your directory tree, then it must include a backslash before its name this way: \\root_parent_path , right? If this is true, then the result root_parent_path\\dir1 is not a relative path, but an absolute one that start from the root folder of the disk this way: \\root_parent_path\\dir1 , right? Please note that the Batch file below assume this point and insert a backslash before the second parameter.

  • As I understand it, you want the first folder in the path given by second parameter that contain the file given in first parameter. This Batch file do that:

EDIT: This Batch file have been modified to accept a fully qualified path in the second parameter

@echo off
setlocal EnableDelayedExpansion
rem Get drive from second parameter, given or implied
for %%a in (%2) do set drive=%%~Da
rem Extract each partial path from second parameter and search first parameter into it
set return_path=
set param2=%2
rem Delete drive from second parameter, if any
set param2=%param2:*:=%
for %%a in (%param2:\= %) do (
   set return_path=!return_path!\%%a
   if exist %drive%!return_path!\%1 goto continue
)
set return_path=PATH NOT FOUND
:continue
echo %drive%%return_path%

Remember that this result is an absolute path. A relative path result in your examples above would be these values:

script.batch file1 root_parent_path\dir1\dir2           ->      ..
script.batch file1 root_parent_path\dir1\dir2\dir21     ->      ..\..
script.batch file1 root_parent_path\dir1\dir3\dir31     ->      ..\..

Please note that each folder in the path can not contain spaces. This may be fixed, if required.

Test the program and report the result...

Antonio

PD - In your ORIGINAL question you said you want a relative path as output and put an example with a relative path as input. I noted you that your answer was not relative , but absolute with no drive , and that my program assume this situation. If you would answer in a comment that you want absolute path as both input and output, I would do that immediately, but you don't answer anymore...

You must note that relative and absolute path management is entirely different and also that if the disk drive is given or implied . If your first question would included the second parameter as it really is: c:\\folder1\\folder2\\ , this point wouldn't be a problem.

EDIT : New version that accept spaces in the second parameter.

@echo off
setlocal EnableDelayedExpansion
rem Get drive from second parameter, given or implied
for %%a in (%2) do set drive=%%~Da
rem Get second parameter removing enclosing quotes, if any
set param2=%~2
rem Delete drive from second parameter (from beggining of path until first colon)
set param2=%param2:*:=%
rem Change possible spaces in the path by another character (I used dollar sign)
rem to avoid separate names with spaces at that point
set param2=%param2: =$%
rem ... of course, dollars must be returned back to spaces later
rem Extract each partial path from second parameter and search first parameter into it
set return_path=
for %%a in (%param2:\= %) do (
   set return_path=!return_path!\%%a
   rem Get back spaces converted into dollars
   set return_path=!return_path:$= !
   rem Enclose file name in quotes (required if path contain spaces)
   if exist "%drive%!return_path!\%1" goto continue
)
set return_path=PATH NOT FOUND
:continue
echo %drive%%return_path%

In this case, use quotes to enclose the path if it contain spaces:

script.bat file1 "c:\\first dir\\second dir\\dir1\\dir2"

Getting the absolute path of the parent is trivial.

@echo off
setlocal
set result_path=%~dp1

Getting the relative path of the parent is a bit tricky. There is no built in way to manipulate relative paths in batch. Here is the simplest solution I could come up with.

@echo off
setlocal
for %%F in (":.:%~1\..") do set "result_path=%%~fF"
set "result_path=%result_path:*:.:=%"

Note that the above code does not validate the existence of the result. It simply strips off the last folder name from the path provided in the 1st argument.

EDIT

OK, I think I understand your requirements. Given a relative path: "rel_path_root\\child1\\child2\\child3" , look for "file1" within each step of the path.

So need to test if any of the following exist:

  • "rel_path_root\\file1"
  • "rel_path_root\\child1\\file1"
  • "rel_path_root\\child1\\child2\\file1"
  • "rel_path_root\\child1\\child2\\child3\\file1"

The following simple script should do the trick.

@echo off
setlocal enableDelayedExpansion

:: Get parameters
set "file=%~1"
set "myPath=%~2"

:: Break myPath into component folders by splitting at \
:: For example: "root dir\dir 1\dir 2" --> "root dir" "dir 1" "dir 2"
set "myPath= "!myPath:\=" "!" "

:: Eliminate empty folders that result from consecutive or trailing \
set "myPath=!myPath:"" =!"

:: Loop through the folders, building the path back up again.
:: At each iteration, check if file exists and break if found.
set "testPath="
set "resultPath="
for %%F in (!myPath!) do (
  if defined testPath (set "testPath=!testPath!\%%~F") else set "testPath=%%~F"
  if exist "!testPath!\!file!" (
    set "resultPath=!testPath!"
    goto :break
  )
)
:break

:: print the result
set resultPath

This will not work with a UNC path like \\\\server\\folder1\\folder2 , nor will it work with a relative path consisting of nothing but a drive letter C: . But other than that it should work with any absolute or relative path.

Something like this?

@echo off
set file=%1
set dir=%2
:LOOP
if exist %dir%\%file% (
setx RESULT_PATH %CD%
) else (
cd..
goto :LOOP
)

This could also be a solution:

@echo off
rem save current dir
pushd "%cd%"
rem go to specified folder
cd "%2"
:loop
IF EXIST "%1" (
  rem found existing file
  call :relative_path return_path "%cd%"
  goto :end_loop
)
rem trying to go to upper dir. if it succedes, cd_prec != cd
set cd_prec=%cd%
cd ..
IF NOT "%cd%" == "%cd_prec%" goto :loop 
set return_path=NOT FOUND
goto :end_loop

rem function to extract \folder1\folder2\etc from c:\folder1\folder2\etc
:relative_path <resultVar> <pathVar>
set "%~1=%~p2"
exit /b
)

:end_loop
popd

echo %return_path%

The path you specify can be absolute or relative, and it returns both absolute path where the file is found and (if needed) also the path relative to the directory specified, and it supports files and names with spaces. I'm just using basic commands, but I think this is what you are looking for.

C:\Users\user\code\cmd>mkdir c:\folder1\folder2\dir1
C:\Users\user\code\cmd>copy nul c:\folder1\folder2\dir1\file1
C:\Users\user\code\cmd>mkdir c:\folder1\folder2\dir1\dir2\dir21
C:\Users\user\code\cmd>mkdir c:\folder1\folder2\dir1\dir2\dir31

C:\Users\user\code\cmd>script.cmd file1 c:\folder1\folder2\dir1\dir2\
\folder1\folder2\dir1

C:\Users\user\code\cmd>script.cmd file1 c:\folder1\folder2\dir1\dir2\dir21
\folder1\folder2\dir1

C:\Users\user\code\cmd>script.cmd file1 c:\folder1\folder2\dir1\dir2\dir31
\folder1\folder2\dir1

C:\Users\user\code\cmd>cd c:\folder1\folder2\dir1\dir2\dir21
C:\folder1\folder2\dir1\dir2\dir21>script.cmd file1 .
\folder1\folder2\dir1

etc...

they all return the same relative path, without disk label, i am not sure but i think it's what you are looking for. My post before this edit would have returned the absoulte path (like c:\\folder1\\folder2 ) and the relative path ( ..\\..\\. ) starting from root_parent_path .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM