0

I'm trying to pass a set of variables to robocopy, all variables contain spaces and are potentially very long. I need to be able to wrap arguments in a manner that enables robocopy capturing full path.

:: Define array of input files to copy
set "MASTER_SOURCE=\\path\with\some file\and messy. folder name\bla bla --- bla\stuff"
set "SOURCE_FILES[0]=%MASTER_SOURCE%\this file with a pesky name.csv"
set "SOURCE_FILES[1]=%MASTER_SOURCE%\this other file with a pesky name.csv"

:: Define output file path
SET "OUTPUT_ROOT=C:\Users\me\stuff and things\save here"

:: Create dated folder within the OUTPUT_ROOT location
:: Obtain date/time timestamps
:: Source: https://stackoverflow.com/a/19706067/1655567
for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"
set "datestamp=%YYYY%%MM%%DD%" & set "timestamp=%HH%%Min%%Sec%"
set "fullstamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"

:: Define output path
set "OUTPUT_DIR=%OUTPUT_ROOT%\%fullstamp%"
:: Create output path
mkdir "%OUTPUT_DIR%"
echo.
echo Progressing messy file copying
echo Created output: %OUTPUT_DIR%.

:: Define robocopy log file
set "ROBO_LOG=%OUTPUT_DIR%\robocopy.log"

:: Iterate over array elements and copy files
:: Set counter
set "x=0"

:SymLoop
if defined SOURCE_FILES[%x%] (
    call set "FILE_FULL_PATH=%%SOURCE_FILES[%x%]%%"
    echo Copying: %FILE_FULL_PATH%
    for /F "delims=" %%I IN ("%FILE_FULL_PATH%") do (
        call set "FILE_NAME=%%~nxI"
        echo File name: %FILE_NAME%
        call set "FILE_PATH=%%~dpI"
        echo File path: %FILE_PATH%
    ) 
    robocopy \"%FILE_PATH%\" \"%OUTPUT_DIR%" "%FILE_NAME%" /R:5 /W:2 /V /LOG+:"%ROBO_LOG%"
    call echo Finished copying: %FILE_NAME%
    set /a "x+=1"
    GOTO :SymLoop

)

Problem

The problem is with line:

robocopy \"%FILE_PATH%\" \"%OUTPUT_DIR%" "%FILE_NAME%" /R:5 /W:2 /V /LOG+:"%ROBO_LOG%"

Error

The returned error is:

ERROR : Invalid Parameter #3 : "Data\ "C:\Users\me\stuff and things\

I've tried looking at other answers1, 2 and modifying what is passed to robocopy, adding \ before and after ", but I wasn't able to find / figure out solution that would enable me to properly pass the following:

  • FILE_PATH - Path to the folder where the file is located with spaces and other undesired character
  • OUTPUT_DIR - Path to the directory where the file is to be copied to, including spaces and other undesired characters
  • FILE_NAME - name of the file is to be copied

Other points

  • I need SOURCE_FILES as an array as I've to iterate over file path to source single file located across numerous LAN shares, etc. so I can't just pass the path the folder with something like *.csv. The idea is to expand SOURCE_FILES to a number of items.

1 In this discussion, suggestions concerned with manual path editing are expressed. This is not helpful as I'm intending to pass a number of files with potentially difficult names.

2 Similar advice was expressed here.

Konrad
  • 17,740
  • 16
  • 106
  • 167
  • 1
    You cannot have a trailing backward slash on your source or destination directories. You should also understand that the entire paths should be enclosed within doublequotes, not just individual parts of them, i.e incorrect: `C:\"Program Files"\"Internet Explorer"`, correct: `"C:\Program Files\Internat Explorer"`. Also ```\``` is a location, it means the root of the current directory, so if your current working directory was `D:\MyPath\MyCurrentDirectory`, ```\``` would mean ```D:\```. I'd assume therefore that `"%FILE_PATH%"`, and `"%OUTPUT_DIR%"` should not be proceeded with a ```\```. – Compo Dec 13 '21 at 11:32
  • Thanks, chaning the command to `robocopy "%FILE_PATH%\" "%OUTPUT_DIR%" \"%FILE_NAME%\" /R:5 /W:2 /V /LOG+:"%ROBO_LOG%\"` moves this forward a little. The problem is now with the `LOG` parameter and with `%FILE_NAME%` that has spaces inside. – Konrad Dec 13 '21 at 11:49
  • 1
    Why are you still including backward slashes? I told you that you cannot do that! _(they are actually escaping the doublequotes which they precede)_. Without reading any of your code, I'd expect it to look more like this: `%SystemRoot%\System32\Robocopy.exe "%FILE_PATH%" "%OUTPUT_DIR%" "%FILE_NAME%" /R:5 /W:2 /V /LOG+:"%ROBO_LOG%"` – Compo Dec 13 '21 at 11:51
  • 1
    When you use ```%%~dpI```, the resiiulting path will have a trailing backward slash. Becase that trailing backward slash would effectively escape the doublequote, you'd need to prevent that. The simplest way to do that is to suffix a period to it, ```%%~dpI.``` – Compo Dec 13 '21 at 12:53

1 Answers1

2

Here's a quick rewrite, correctling a few things, removing unnecessary things, and changing your iteration of the 'array' items to something a little bit simpler:

Rem Define array of input files to copy
Set "MASTER_SOURCE=\\path\with\some file\and messy. folder name\bla bla --- bla\stuff"
Set "SOURCE_FILES[0]=%MASTER_SOURCE%\this file with a pesky name.csv"
Set "SOURCE_FILES[1]=%MASTER_SOURCE%\this other file with a pesky name.csv"

Rem Define output file path
Set "OUTPUT_ROOT=C:\Users\me\stuff and things\save here"

Rem Create dated folder within the OUTPUT_ROOT location
Rem Obtain date/time timestamps
Set "dt="
Rem Source: https://stackoverflow.com/a/19706067/1655567
For /F "Tokens=2 Delims==" %%G In ('%SystemRoot%\System32\wbem\WMIC.exe OS Get
 LocalDateTime /Value 2^>NUL') Do Set "dt=%%~nG"

If Not Defined dt GoTo :EOF

Rem Define output path
Set "OUTPUT_DIR=%OUTPUT_ROOT%\%dt:~0,4%-%dt:~4,2%-%dt:~6,2%_%dt:~8,2%-%dt:~10,2%-%dt:~12,2%"

Rem Define robocopy log file
Set "ROBO_LOG=%OUTPUT_DIR%\robocopy.log"

Echo(
Echo Progressing messy file copying

Rem Iterate over array elements and copy files
For /F "Tokens=1,* Delims==" %%G In ('"(Set SOURCE_FILES[) 2>NUL"') Do (
    Echo Copying: %%H
    %SystemRoot%\System32\Robocopy.exe "%%~dpH." "%OUTPUT_DIR%" "%%~nxH" /R:5 /W:2 /V /LOG+:"%ROBO_LOG%"
    Echo Finished copying: %%H
)
Compo
  • 36,585
  • 5
  • 27
  • 39
  • Thanks very much for the useful answer. What are the benefits of using `For /F "Tokens=1,* Delims==" %%G In ('"(Set SOURCE_FILES[) 2>NUL"')` over the solution I've? Second question, is there any benefit associated with using `Rem` instead of `::` for comments? – Konrad Dec 13 '21 at 14:19
  • 1
    The benefit, it that you do not need to use delayed expansion, _(or in your case try to use workaround hacks with `call`, and `set`ting interim variables)_. `Rem` is used for remarks, it is the correct and official method of providing inline comnments. `::` is a hack, it is essentially a broken label, and as such is simply a command line which usually does nothing. However, if you try to use broken lables within a parenthesized block, it could in some circumstances error your code. My advice is to ever use `::` instead of the proper `Rem` command. – Compo Dec 13 '21 at 14:30
  • Apologies for my typos above. In the penultimate sentence `lables` should read as `labels`, and in the closing sentence, `ever` should read a `never`. – Compo Dec 13 '21 at 14:42