Jump to content











Photo
- - - - -

command line: one-liner to copy specific files to more than one destination?


  • Please log in to reply
7 replies to this topic

#1 Zharif

Zharif

    Frequent Member

  • .script developer
  • 168 posts
  • Location:Germany
  •  
    Germany

Posted 07 November 2020 - 02:10 PM

Dos a one-liner command  exist to copy a file to several directories, preferrably as part of a windows shell script? No external tools (such as robocopy) must be used.

The copy command allows use of  different source files but one destination directory only.

Is there any possibility (or maybe a command line quirk) to overcome this restriction?

Or maybe a better command I should use?

Environment: WinXP, Win7 or Win10.

 

Scenario:

A file list (.txt file) that contains hundreds of file names and their destination folders.

Something like this (snip):

"*genotypisierung*.sav"   "Folder1"

"*_strahlentherap*.sav"   "Folder2"
"*tumorkonf*.sav"            "Folder3\Subfolder1"
"*tumorkonf*.sav"            "Folder3\Subfolder2"

...

 

This was my first approach to walk through this list (destination folders has been created before via MD command):

FOR /F "tokens=1-2 delims=[space] [tab][space]" %%a in ('TYPE "FileList.txt"') DO (
COPY /N /Y %%a %%b >NUL 2>&1 || ECHO.  Error copying  %%~a to %%b && SET _Err=1)

Have a look at example entries 3 and 4: It's quite annoying for me to repeat the folder names for one specific file if it has to be copied to different places.

 

Any suggestions to improve this approach?

 

 

Much thanks in advance for any response

Zharif

 



#2 Zharif

Zharif

    Frequent Member

  • .script developer
  • 168 posts
  • Location:Germany
  •  
    Germany

Posted 07 November 2020 - 09:12 PM

Sorry, previous code was not correct.

 

In the meanwhile I solved this problem in case of an underlying filelist:

In this filelist, each entry per line must be delimited by a tab and should be surrounded with double quotes.

 

zCopyList.txt:

"FileNm1"	"Folder1"
"FileNm2"	"Folder2"
"FileNm3"	"Folder3\Subfolder1"	"Folder3\Subfolder2"
:: each entry per line in corresponding file list must be
:: delimited by a tab and should be surrounded by double quotes
:: tabs are not allowed in path names

:: CHCP 1252: necessary if folder names in filelist contain special chars such as german umlauts or accents
::            replace codepage value [ACP] if needed
:: Outer loop: ensure a [tab][space][tab] is entered after 'delims='
::                the 2nd delimiter [space] allows output of entries/paths that may contain spaces
:: Inner loop: each line containing more than two entries is processed separately
CHCP 1252 1>NUL
SET "_ERR=0"
SET "_Counts=0"
FOR /F "tokens=1-2* delims=	 	" %%a IN ('TYPE "zCopyList.txt"') DO (
  SET /A _COUNTS+=1
  MD %%b >NUL 2>&1
  COPY /N /Y %%a %%b >NUL 2>&1
  
  IF ERRORLEVEL 1 (
    ECHO.Error copying %%a to %%b && SET /A _ERR+=1
  
  ) ELSE (
    IF [%%c] NEQ [] (
      FOR %%A IN (%%c) DO (
        MD %%A >NUL 2>&1
        COPY /N /Y %%a %%A >NUL 2>&1 || ECHO.Error copying %%a to %%A
        IF ERRORLEVEL 1 SET /A _ERR+=1
      )
    )
  )
)
IF %_ERR% NEQ 0 (ECHO.failed to copy %_ERR% of %_COUNTS% file[s]) ELSE (ECHO.%_COUNTS% copied successfully.)
ENDLOCAL & PAUSE >NUL & EXIT /B %_ERR%

However, much work here.

 

So, my question/request still persist.

Is there a better way to copy several files if some of them must be copied more than once to different locations?



#3 Zharif

Zharif

    Frequent Member

  • .script developer
  • 168 posts
  • Location:Germany
  •  
    Germany

Posted 07 November 2020 - 10:21 PM

Code in Post #2 edited/improved



#4 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 15577 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 08 November 2020 - 09:11 AM

As a side note, set aside the "complication" with the CHCP and special characters (and error control), and JFYI, usually a TAB delimited list can be parsed with a loop.

 

There may be limits to overall length of the parameters*line, though (nothing usually an issue, it is, if I recall correctly, more than 2000 characters).

 

Basically:

FOR /F "tokens=* delims=" %%A IN (TYPE "zCopyList.txt"') DO (

would get the whole line, BUT the contents are pre-parsed/rendered by TYPE (which may or may not be a good idea), so you can use

FOR /F "tokens=* delims=" %%A IN (zCopyList.txt) DO (

this will get the whole line "as is".

Then, the TAB is a valid separator for batch CALLs, so you can do something *like*:

@ECHO OFF
FOR /F "tokens=* delims=" %%A IN (ZcopyList.txt) DO (
CALL :parse_loop 1 %%A
)

GOTO :EOF

:do_copy
::this does the actual copy
::add here any check/error control needed
ECHO COPY %1 %2
GOTO :EOF

:parse_loop
::this sets the source from first item in list
::then calls the copy sub once for each target in list

IF "%1"=="1" SET Source=%2&&SHIFT
SHIFT
IF "%1"=="" GOTO :EOF
CALL :do_copy %Source% %1
GOTO :parse_loop

Maybe it is simpler.

 

:duff:

Wonko



#5 Zharif

Zharif

    Frequent Member

  • .script developer
  • 168 posts
  • Location:Germany
  •  
    Germany

Posted 08 November 2020 - 10:47 AM

Thanks much Wonko,

 

indeed, a good alternate way you posted here.

I already did some tests by avoiding the TYPE command before (post #2).

 

However, due to possible unicode filepath prefixes for the corresponding copylist location and maybe spaces in this filepath

I observed some concerns when putting the filename into doublequotes. For whatever reason the for loop doesn't work as expected then.

Something like this...

FOR /F "tokens=* delims=" %%A IN ("UnicodeFilePathWithSpaces\ZcopyList.txt") DO (

...leads to some weird behaviour while looping.

But, I still have to investigate this.

 

Am I right that you post implies that there's no better way

(or a better build-in command/command sequence) to copy a file (or several files) to more than one destination.?

 

 



#6 Zharif

Zharif

    Frequent Member

  • .script developer
  • 168 posts
  • Location:Germany
  •  
    Germany

Posted 08 November 2020 - 02:25 PM

Wonko, I think I found a solution.

The "trick" for your code is the "usebackq" parameter.

This enables us to put the filename into doublequotes and to replace it with a fully qualified path

(in case the CopyList is not in the same folder as the executing shell script).

 

Basically, it should look like this:

FOR /F "usebackq tokens=* delims=" %%A IN ("UnicodeFilePathWithOrWithoutSpaces\zCopyList.txt") DO (do something)

?



#7 Zharif

Zharif

    Frequent Member

  • .script developer
  • 168 posts
  • Location:Germany
  •  
    Germany

Posted 08 November 2020 - 07:46 PM

Wonko,
I'am in the need to store/count the number of files in the filelist,
but also the number of FILES for which any
copy process (regarding the parameters per line) failed.

Following your advice to put the error handling into the "DoCopy" sub leads to wrong
results because each copy process regarding the number of parameters per line will be counted.
Placement for the ErrCount variable becomes some more difficult there.

Finally, code (based on yours) is looking like this:
 

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
SET _Count=0
SET _Err=0
	
FOR /F "usebackq tokens=* delims=" %%A IN ("%CD%\zCopyList.txt") DO (
  SET /A _COUNT+=1
  CALL :_PARSE_LOOP 1 %%A
  IF ERRORLEVEL 1 SET /A _Err+=1
)

IF %_ERR% NEQ 0 (
  ECHO.failed to copy %_ERR% of %_COUNT% file[s]
) ELSE (
  ECHO.%_COUNT% copied successfully.
)
PAUSE>NUL & GOTO:EOF

:_DO_COPY
:: create directory first
:: copy file to destination
   MD %2 >NUL 2>&1
   COPY %1 %2 >NUL 2>&1 || ECHO.Error copying file %1 to %2
GOTO :EOF

:_PARSE_LOOP
  IF "%1"=="1" SET "Source=%2" && SHIFT
  SHIFT
  IF "%1"=="" GOTO:EOF
  CALL :_DO_COPY %Source% %1
  GOTO :_PARSE_LOOP

Off-Topic:

since opening this topic I always get an "500 Server Error" when trying to post a new reply.

However, reply is created afterwards.



#8 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 15577 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 09 November 2020 - 11:28 AM

Yep  :) , usebackq is the solution in case of the need of filenames/paths with spaces, I thought that the zCopyList.txt file was always in the same directory of the batch.

For counting the copy operation, you are right, you can count them in the main FOR loop as you did :) or add it in the Parse_loop, i.e. you can "divide" the subs, *like*:

@ECHO OFF
SET /A Count=0
FOR /F "tokens=* delims=" %%A IN (ZcopyList.txt) DO (
CALL :parse %%A
)
SET Count
GOTO :EOF

:do_copy
ECHO COPY %1 %2
GOTO :EOF

:parse
SET Source=%1
SHIFT
SET /A Count+=1

:parse_loop
IF "%1"=="" GOTO :EOF
CALL :do_copy %Source% %1
SHIFT
GOTO :parse_loop

This gets rid of the "arbitrarily" prepended "1" identifier.

 

:duff:

Wonko


  • wimb likes this




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users