Beginner Questions (Formatting, "Filepath was unexpected at this time") - file

I am new to batch files and I only need a very simple thing done.
I would like a batch file to take a text file which is a list of filepaths --
Filelist.txt
Begin File>>
O:\X\Y\Z\Board BOM Rev 4.xls >>Files
O:\X\Y\U >>Entire filepaths
< End File
and then copy the files (not the names of the files, to clarify) to a given location.
Say that the batch file is in O:\X\Y\Z (And so is the text file), and I would like to copy all of those files to that folder. I have tried to use this code
#echo off
set input="O:\X\Y\Z\Filelist.txt"
set dest=%cd%
for /f %%i in "input" do xcopy "%%i" %dest%\ /S
To do what I need to do, but I get the aforementioned error. I have done very little with batch files, so corrections with explanations would be much appreciated!
Thanks!

First, to use your variable "input" write %input%
Second, for the correct format of for you have to put parantheses around it:
for /f %%i in ("%input%") do xcopy "%%i" %dest%\ /S

1st. (input)
In the first set you have double-quoted wrong the value, that formatting is for extraordinary cases, not this time.
2nd. (dest)
You need to ensure to double-quote every "variable=value" (at least when they are string variables and when can contains spaces)
but anyways you can simplify the code a little to don't depend on that dest variable,
3rd. (For)
The dest variable inside for has the same quote problem, also you are typing incorrectly the input variable, and for syntax can be improved to prevent future errors by agroupping the commands using the agroupation operators ().
Here is the code:
#echo off
set "input=O:\X\Y\Z\Filelist.txt"
REM set "dest=%cd%"
for /f "Usebackq Delims=" %%i in ("%input%") do (
xcopy "%%i" ".\" /S
)
PS: Forgive my English.

Related

How do I write a batch programe to copy a set of folders with same suffix and their files from a server to a client?

I want to write a batch program to copy some deeply nested folders with the same suffix in this case 100,
It only copied all the folders but only one file in the top most folder (they are alphabetically arranged in the server) *100.bat was copied.
I want to copy all files in all folders with name_of_folder100.
Thanks for your Time.
This is my attempt:
#echo off
:: variables
set hour=%time:~0,2%
if "%hour:~0,1%"==" " set hour=0%time:~1,1%
set
set drive= E:\PWD_BACKUP_%date:~10,4%_%date:~4,2%_%date:~7,2%_%hour%_%time:~3,2%
set PWD_drive_100=E:\PWD_BACKUP_\PWD_100_%date:~10,4%_%date:~4,2%_%date:~7,2%_%hour%_%time:~3,2%
set backupcmd=xcopy /s /c /d /e /h /i /r /y
%backupcmd% "\\xx.xx.xx.xx\live_projects\PWD\*100" %PWD_drive_100%
It's normal to follow the #echo off line with setlocal. This makes sure that any variables altered or created in the batch file are not maintained in the cmd session's environment when the batch ends.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.
The set drive=... statement will include the space following the = in the value assigned to drive. This is probably of no consequence in the current situation, but may be significant for other batches.
Other than the last 2 lines of code, there is a great deal of date/time-manipulation going on. We have to rely on your local knowledge here as the date/time format is user-dependent.
The overall problem is that * cannot be used for directorynames. Logically, what you appear to want to copy from your description is ...e_projects\PWD\*100\*, but that structure is not supported by xcopy.
So I'll restate the problem as "copy all directories that have a name ending 100"
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the directories and filenames are names
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
FOR /f "delims=" %%e IN (
'dir /b /s /ad "%sourcedir%\PWD\*" ^|findstr /e "100"'
) DO (
SET "dirname=%%e"
SET "dirname=!dirname:*\PWD\=!"
ECHO XCOPY "%%e\*" "%destdir%\!dirname!\"
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The setlocal enabledelayedexpansion invokes delayedexpansion mode, where within a code block (parenthesised sequence of lines) !var! is the run-time value of var (as var is changed) and %var% is the value that var contained when the code block was parsed.
Documentation: Stephan's DELAYEDEXPANSION link
We then establish the source and destination directories. I've posted my test directories, you would need to edit them to suit your particular circumstances.
The dir command lists all directories (/ad) in basic form (/b) that is, name only - no size, date, etc. The /s extends the scan to subdirectories also.
The dir output is piped to findstr (the caret escapes the pipe to show that it is part of the command to be executed, not of the for) which filters the dir list for (directorynames) which /e end "100".
The for/f assigns each line of the result to %%e. The delims=" ensures the entire line is assigned to %%e(in case%%e` contains separators like Space)
%%e thus contains something like U:\your files\PWD\sub 5\subsub 5\subsubsub\subsubsubsub100, so we need to remove the part up to \PWD\ to get the subdirectory name in a format suitable for the xcopy command. Batch cannot substring a metavariable like %%e directly, so we assign %%e to a user-variable dirname (which is therefore changing within the loop, so we need to use !dirname! to acess it's run-time value) and the following set removes all characters up to and including \PWD\. (See set /? from the prompt for documentation)
Then it's just a matter of performing the xcopy. Note that I've added an echo keyword to "disarm" the xcopy so that the xcopy command is merely listed to the console instead of being executed. Once you've verified that the command is correct, remove that echo to actually execute the xcopy.
The destination of the xcopy is </kbd> which tells xcopy that the destination is a directory, and to create it if it does not exist.
Add >nul to the xcopy command to suppress the report if you want.

Batch loopvariable manipulation

I have a small problem with a .bat file that I have to build to manipulate a specific .csv.
I want the .bat to read the line of the file, and then check for the first three letters of that line. At the end there should be n-files where file xxx.csv contains the lines of the input.csv with xxx as the first three letters of line.
First things first, I don't even know if it is possible to do it this job in a batch-file, because the file has >85000 lines and may even get much bigger. So if it is impossible you can directly tell me that.
for /f "delims=" %%a in (input.CSV) DO (
echo %%~a:~0,3
pause
)
I want to "output" the first three letters of %%a.
It would be great if you could help me.
Phil
Substring substitution only works with environment variables (%var%), but not with metavariables (%%a) (as Mofi already commented). And because you are setting and using a variable within the same command block, you need delayed expansion:
setlocal enabledelayedexpansion
for /f "delims=" %%a in (input.CSV) DO (
set "var=%%~a"
echo !var:~0,3!
pause
)
(there are methods without delayed expansion, but they make use of call, which slows things down)

Using Windows ren command to remove ! from filename

I have a bunch of files that are in the following format
filename-!#.ext
where # is an incrementing number to prevent collisions. I would like to just remove the ! from the filename so it reads as
filename-#.ext
It seems like I could do this with the ren command and wildcards but having a hard time getting it to work. I tried running this:
ren *!?.ext *?.ext
My thought is the * should match the filename- part, then !, then the ? for the numeric. However, the resulting file is named this:
filename-!#.ext.ext
and I can't quite figure out why.
The filename part can be dynamic, but won't contain any !'s. Any ideas?
I think you'll have to resort to a small batch file for this:
#echo off
setlocal
for %%a in (*!*.ext) do call :remove "%%~a"
goto :eof
:remove
set "FROM=%~1"
set "TO=%FROM:!=%"
ren "%FROM%" "%TO%"
goto :eof
The above will – for all files containing an exclamation mark and extension ext – call the remove function. This takes the original name (FROM) and determines the new name (TO) by using the %var:find=replace% syntax to remove any exclamation mark (replaces it with an empty string).
Notes
You cannot use find/replace with either %%a or %0 type variables, so you have to assign it to a named variable first.
I originally tried doing this "inline" with the for statement (e.g. for ... ( ending )) but to do that, you would have to enable delayed-expansion (because you would need to access a named variable in a loop). However, delayed-expansion uses ! (instead of %) to reference variables and this got in the way of the ! we were trying to remove. There may be a way of doing this, but I haven't found it. Using call will be slightly slower, but not significantly unless you've got thousands of files.
You don't really need to create TO (you could perform the replacement on the ren command-line) but I used it for clarity.
This will work for all files with an exclamation mark: it doesn't check the bit after is numeric.
I think it is not a good idea to let rename select the files, but instead do it with a for-loop and then execute the rename for every file:
for %%F in (*!*) do (
set "nxF=%%~nxF"
call ren "%%nxF%%" "%%nxF:!=%%"
)
You really have to use call here instead of delayedExpansion, because delayedExpansion would destroy the rename-arguments (because they contain !)
Based upon your provided information and just for the sake of offering something a little different, you could let delayed expansion do the work for you, (as it will remove the unwanted exclamation mark for you).!
#For %%A In ("*-!?.ext") Do #Set "_=%%A" & SetLocal EnableDelayedExpansion & Ren "!_!" "%%A" & EndLocal
You could probably also do it with a nested for loop:
#For %%A In ("*-!?.ext") Do #For /F "Tokens=1*Delims=!" %%B In ("%%A") Do #Ren "%%A" "%%B%%C"
…and from the Command Prompt:
For %A In ("*-!?.ext") Do #For /F "Tokens=1*Delims=!" %B In ("%A") Do #Ren "%A" "%B%C"

batch file to get file name up to specified character

I have filenames in a directory in the format of Mumbai Short Call Agentwise-MUMBAI SHORT CALL-3-01092016. I would like to strip off everything after the second hyphen and keep the first portion of the filename.
Is there a good website that could direct me in how to accomplish this? Or, maybe one of you dos batch experts can lead me in how to do this?
for /f "tokens=1,2,* delims=-" %%a in ('dir /b *-*-*') do #ECHO ren "%%a-%%b-%%c" "%%a-%%b%%~xc"
for every file with the given mask *-*.*: get first (%%a) and second part (%%b) plus extension of the rest (rest: %%c; Extension of the rest:%%~xc)
Notes:
- if you shorten filenames, be aware of possible duplicates!
#ECHO just lists the rename commands. Remove #ECHO, if the output satisfies you
See for /? or for /f for more information

Windows Batch help in setting a variable from command output [duplicate]

This question already has answers here:
Set output of a command as a variable (with pipes) [duplicate]
(6 answers)
Closed 7 years ago.
I need to run a simple find command and redirect the output to a variable in a Windows Batch File.
I have tried this:
set file=ls|find ".txt"
echo %file%
But it does not work.
If I run this command it works without problems:
set file=test.txt
echo %file%
So obviously my command output is not being set to my variable. Can anyone help? Thanks
I just find out how to use commands with pipes in it, here's my command (that extracts the head revision of an svn repo) :
SET SVN_INFO_CMD=svn info http://mySvnRepo/MyProjects
FOR /f "tokens=1 delims=" %%i IN ('%SVN_INFO_CMD% ^| find "Revision"') DO echo %%i
First of all, what you seem to expect from your question isn't even possible in UNIX shells. How should the shell know that ls|find foo is a command and test.txt is not? What to execute here? That's why UNIX shells have the backtick for such things. Anyway, I digress.
You can't set environment variables to multi-line strings from the shell. So we now have a problem because the output of ls wouldn't quite fit.
What you really want here, though, is a list of all text files, right? Depending on what you need it's very easy to do. The main part in all of these examples is the for loop, iterating over a set of files.
If you just need to do an action for every text file:
for %%i in (*.txt) do echo Doing something with "%%i"
This even works for file names with spaces and it won't erroneously catch files that just have a .txt in the middle of their name, such as foo.txt.bar. Just to point out that your approach isn't as pretty as you'd like it to be.
Anyway, if you want a list of files you can use a little trick to create arrays, or something like that:
setlocal enabledelayedexpansion
set N=0
for %%i in (*.txt) do (
set Files[!N!]=%%i
set /a N+=1
)
After this you will have a number of environment variables, named Files[0], Files[1], etc. each one containing a single file name. You can loop over that with
for /l %%x in (1,1,%N%) do echo.!Files[%%x]!
(Note that we output a superfluous new line here, we could remove that but takes one more line of code :-))
Then you can build a really long line of file names, if you wish. You might recognize the pattern:
setlocal enabledelayedexpansion
set Files=
for %%i in (*.txt) do set Files=!Files! "%%i"
Now we have a really long line with file names. Use it for whatever you wish. This is sometimes handy for passing a bunch of files to another program.
Keep in mind though, that the maximum line length for batch files is around 8190 characters. So that puts a limit on the number of things you can have in a single line. And yes, enumerating a whole bunch of files in a single line might overflow here.
Back to the original point, that batch files have no way of capturing a command output. Others have noted it before. You can use for /f for this purpose:
for /f %%i in ('dir /b') do ...
This will iterate over the lines returned by the command, tokenizing them along the way. Not quite as handy maybe as backticks but close enough and sufficient for most puposes.
By default the tokens are broken up at whitespace, so if you got a file name "Foo bar" then suddenly you would have only "Foo" in %%i and "bar" in %%j. It can be confusing and such things are the main reason why you don't ever want to use for /f just to get a file listing.
You can also use backticks instead of apostrophes if that clashes with some program arguments:
for /f "usebackq" %%i in (`echo I can write 'apostrophes'`) do ...
Note that this also tokenizes. There are some more options you can give. They are detailed in the help for command.
set command has /p option that tells it to read a value from standard input. Unfortunately, it does not support piping into it, but it supports reading a value from a first line of existing file.
So, to set your variable to the name of a first *.txt file, you could do the following:
dir /b *.txt > filename.tmp
set /p file=< filename.tmp
del /q filename.tmp
It is important not to add a space before or even after =.
P. S. No fors, no tokens.
Here's a batch file which will return the last item output by find:
#echo off
ls | find ".txt" > %temp%\temp.txt
for /f %%i in (%temp%\temp.txt) do set file=%%i
del %temp%\temp.txt
echo %file%
for has a syntax for parsing command output, for /f "usebackq", but it cannot handle pipes in the command, so I've redirected output to a temporary location.
I strongly recommend, given that you have access to ls, that you consider using a better batch language, such as bash or even an scripting language like python or ruby. Even bash would be a 20x improvement over cmd scripting.
The short answer is: Don't!
A windows shell env var can hold a max of 32 Kb and it isn't safe to save output from programs in them.
That's why you can't. In batch script you must adopt another programming style. If you need all of the output
from the program then save it to file. If you only need to check for certain properties then pipe the output into
a program that does the checking and use the errorlevel mechanism:
#echo off
type somefile.txt | find "somestring" >nul
if %errorlevel% EQU 1 echo Sorry, not found!
REM Alternatively:
if errorlevel 1 echo Sorry, not found!
However, it's more elegant to use the logical operators Perl style:
#echo off
(type somefile.txt | find "somestring" >nul) || echo Sorry, not found!
It's not available in DOS, but in the Windows console, there is the for command. Just type 'help for' at a command prompt to see all of the options. To set a single variable you can use this:
for /f %%i in ('find .txt') do set file=%%i
Note this will only work for the first line returned from 'find .txt' because windows only expands variable once by default. You'll have to enable delayed expansion as shown here.
what you are essentially doing is listing out .txt files. With that, you can use a for loop to over dir cmd
eg
for /f "tokens=*" %%i in ('dir /b *.txt') do set file=%%i
or if you prefer using your ls, there's no need to pipe to find.
for /f "tokens=*" %%i in ('ls *.txt') do set file=%%i
Example of setting a variable from command output:
FOR /F "usebackq" %%Z IN ( `C:\cygwin\bin\cygpath "C:\scripts\sample.sh"` ) DO SET BASH_SCRIPT=%%Z
c:\cygwin\bin\bash -c '. ~/.bashrc ; %BASH_SCRIPT%'
Also, note that if you want to test out the FOR command in a DOS shell, then you need only use %Z instead of %%Z, otherwise it will complain with the following error:
%%Z was unexpected at this time.

Resources