Batch is not displaying empty value - windows

I am currently working on a batch script that would move files from a source to a destination based on a master file. I wanted to add a condition that if there is a missing value from the columns then it would skip that line. I tried all the solutions that I found but it won't trigger the if condition. Here's a snippet of the code:
for /f "usebackq tokens=1-3 skip=1 delims=," %%a in (%MasterFilePath%) do (
set dest=%%c
set src=%%b
if !dest!=="" do (echo destination for source !src! is empty)
Here's a sample of the master file:
FolderName,SrcPath,DestPath
FolderA,C:\Work\FolderA,
FolderB,C:\Work\FolderB,C:\Work\Sample\Dest2
FolderC,C:\Work\FolderC,C:\Work\Sample\Dest3

The FOR /F command will always treat consecutive delimiters as one delimiter. So if you delimited file has an empty field and it does not have quotes, the fields will shift to the left on you. So to work around this you can two FOR /F commands. The first one will read in the whole line. That variable is then forced to put quotes around every field using string substitution. You can then use the second FOR /f command to split the fields up into the amount of fields you need.
#ECHO OFF
set "MasterFilePath=file.txt"
FOR /F "usebackq skip=1 delims=" %%G IN ("file.txt") DO (
set "line=%%G"
setlocal enabledelayedexpansion
SET "line="!line:,=","!""
for /f "tokens=1-3 delims=," %%A IN ('echo !line!') do (
IF NOT "%%~B"=="" IF NOT "%%~C"=="" ECHO ROBOCOPY "%%~B" "%%~C"
)
endlocal
)
For the sake of brevity I am just checking if both the source and destination is not empty and then echoing the robcopy command to the screen.
With your input example you would see this.
ROBOCOPY "C:\Work\FolderB" "C:\Work\Sample\Dest2"
ROBOCOPY "C:\Work\FolderC" "C:\Work\Sample\Dest3"

Related

Windows CMD/BATCH Keep newest date stamped file

I have some files that I would like to sort through and keep the newest file.
I cannot do it by file attributes date modified or created, which I could do no problem.
Here is the naming convention of the files. FileABC_YYYYMMDD.txt
FileABC_20190201.txt
FileABC_20190125.txt
FileABC_20190118.txt
FileABC_20190111.txt
FileABC_20190104.txt
You can see that the date stamp is in the filename itself. These files are generated weekly. So I'd like to have a batch file loop through them and delete all but most currently dated file. I have really searched for how to do this best and I'm not finding much so I need ideas. I prefer a pure cmd solution but I'm open to powershell solutions as well.
What I am trying on my own is to parse out the date with...
#echo off
setlocal enabledelayedexpansion
for /f "tokens=* delims= " %%G IN ('dir/b /a-d "C:\Users\thomas.maus\Documents\Tom\dev\Test Batch Files\dev\sortbyFileDateName\FileABC_*.txt"') do (
set fileName=%%G
Set theDate=!fileName:~8,8!
echo !theDate!
)
Then I want to take those dates somehow from the results of the loop and do something like
if "%theDate%" GEQ "*****not sure what to put here*****" (
del *all the old files, also not sure what to put here*
)
How about this?
#echo off
for /f "skip=1" %%i in ('dir /o-n /b *.txt') do del %%i
If you just want to test it (see what it would delete) first, do:
#echo off
for /f "skip=1" %%i in ('dir /o-n /b *.txt') do echo %%i
If you do not care about the file dates but only the dates in the file names, you could do the following, given that the part FileABC is always the same and does not contain any _ on its own:
pushd "C:\Users\thomas.maus\Documents\Tom\dev\Test Batch Files\dev\sortbyFileDateName" && (
for /F "skip=1 delims= eol=|" %%F in ('
dir /B /A:-D "FileABC_????????.txt" ^
^| sort /R
') do (
del "%%F"
)
popd
)
Although sort /R does alphabetic sorting, this works because of your chosen date format, which ensures that alphabetic order equals alphanumeric one.
We just loop through the files, sorted by date in decending order, then skip the first file, now being the latest:
#for /f "skip=1" %%a in ('dir /b /o-d *.txt') do #echo #del %%a
Important!
This example will only echo the delete command as a safe measure so you do not delete files you should not have. To perform the actual delete, remove #echo from the line.
To understand more about the functions we used, run the following from cmd.exe
for /?
dir /?
As an additional option, just in case the filename prefix changes throughout and only the _YYYYMMDD.txt remains constant, you can still peform the task using that date as it is already alphabetically sortable.
Here's an example:
#Echo Off
Set "SrcDir=%UserProfile%\Documents\Tom\dev\Test Batch Files\dev\sortbyFileDateName"
For /F "Delims==" %%A In ('Set $ 2^>Nul') Do Set "%%A="
Set "_="
For /F Delims^=^ EOL^= %%A In ('Where "%SrcDir%":*_????????.txt 2^>Nul'
) Do Set "_=%%~nA" & Call Set "$%%_:~-8%%=%%A"
If Not Defined _ Exit /B
For /F "Tokens=1* Delims==" %%A In ('Set $ 2^>Nul') Do Set "_=%%B"
For /F Delims^=^ EOL^= %%A In ('Where "%SrcDir%":*_????????.txt^|Find /V "%_%"'
) Do Del /A /F "%%A"
This uses the fact that the Set command will output variables in alphabetical order.
Lines 2 to 4 just define and undefine the variables we will be using.
Lines 5 and 6 is a single line split over two lines for readability. This will set variables using the last eight characters of the files basenames, to the value of the full filename.
Line 7 is included to exit the script, just in case no .txt files with a basename ending with an underscore followed by eight characters were found in the directory set at line 2.
Line 8 is the special one here, it outputs each variable and corresponding value in alphabetical order. The output is set to a variable, which overwrites itself until the loop ends. This means that the newest file, last one alphabetically, is held with the value of the file named with the newest date.
Lines 9 & 10 are once again a single line split over two for readability. This loops over all matching files in the directory again and uses the Find command to exclude outputting the one which matches that held in the variable as the file with the newest date. Each file output is simply deleted using the Del command.
Please note that this script assumes you only have a single file with each date, as you've only stated that the files are generated weekly.

Truncate filename after a multiple of a special character in windows batch script?

I want to remove the part of a filename after the third "_" from thousand of files. The structure after the third "_" varies and contains "_" in some cases. The length of the first part varies so I can't just remove the first 15 characters. The result should be unique.
The filenames look like this:
00_TEXT_=Text00._AA1234L_AA1_1.pdf
00_TEX_=Text00._AA1234L_AA1_2.pdf
00_TEXT_=TextText00._DD2023A.pdf
00_TEXT_=Text00._AA2345L_BB1_1.pdf
00_TEXT_=Text00._AA2345L_BB1_2.pdf
The result should look like this:
AA1234L_AA1_1.pdf
AA1234L_AA1_2.pdf
DD2023A.pdf
AA2345L_BB1_1.pdf
AA2345L_BB1_2.pdf
Any idea why this is not working:
#echo off
setlocal enabledelayedexpansion
set deletestring=*_*_*_
for /f "delims==" %%F in ('dir /b ^| find "%deletestring%"') do (
set oldfilename=%%F
set newfilename=!oldfilename:%deletestring%=!
Ren "!oldfilename!" "!newfilename!"
)
I was able to get it working with this:
#echo off
setlocal enabledelayedexpansion
set deletestring=*_*_*_*
for /f "tokens=1,2,3,* delims=_" %%F in ('dir /b "%deletestring%"') do (
Ren "%%F_%%G_%%H_%%I" "%%I"
)
endlocal
Note that enabledelayedexpansion isn't really needed in the above.
Alternately, you could do this as a single line (no batch file needed):
for /f "tokens=1,2,3,* delims=_" %F in ('dir /b "*_*_*_*"') do Ren "%F_%G_%H_%I" "%I"
The idea is to simply split the matching filenames apart by underscores and then reconstruct the names during the rename process (%%F_%%G_%%H_%%I gives the original file name when going through the loop). Then rename the file to everything after the 3rd underscore, which is the %%I value.
Your FINDSTR search is wrong - a string of any characters (wildcard) is .*, not *.
Variable find/replace does not support wildcards, except for the !var:*search=! syntax that replaces everthing up until the first occurrence of "search".
There is no need for FINDSTR, all you need is DIR with normal wildcard masking.
You can use FOR /F to parse the name into tokens. I use two loops - the first to get the entire name, and the second to parse out the portion after the 3rd _.
The following should work:
#echo off
for /f "eol=: delims=" %%A in (
'dir /b /a-d *_*_*_*'
) do for /f "tokens=3* delims=_" %%B in ("%%A") do ren "%%A" "%%C"
Or you could use my jren.bat utility that renames files using regular expression replacement. It is a hybrid JScript/batch script that runs natively on any Windows machine from XP onward.
jren "^(.*?_){3}" ""
Use CALL JREN if you put the command within another batch script.

Insert Into Multiple Files in Windows shell script

Basically I'm trying to write a batch file to insert some code into multiple files. Here are the details of what I'm tring to accomplish:
1. The input string comes from a file test.txt.
2. The string needs to be inserted as the second line of destination files.
3. Destination files are all the .xml files under the same direction as the batch file.
I suppose I should use a FOR loop to go through all .xml files. Something like
for /f %%i in ('dir /b *.xml') do ()
I've read though some tutorials and posts but can't find a way to add anything to files in a loop. Using Echo or TYPE doesn't seems to work for each file in a loop. How do I modify files in a loop?
Also to insert to a certain number of line some post say the file needs to be put into a variable. But my files are pretty large, which I don't want to put into variables. Is there another way to insert into a certain line in a file?
#ECHO OFF
SETLOCAL
FOR /f "delims=" %%i IN ('dir /b *.xml') DO (
SET line2=Y
(
FOR /f "usebackqdelims=" %%x IN ("%%i") DO (
ECHO(%%x
IF DEFINED line2 TYPE Line2.txt
SET "line2="
)
)>"%%~ni.lmx"
)
GOTO :EOF
This should work for you - but it will delete empty lines.
#echo off
set /P string=< test.txt
for %%a in (*.xml) do (
(for /F "usebackq tokens=1* delims=:" %%b in ('findstr /N "^" "%%a"') do (
if %%b equ 2 echo %string%
set "line=%%c"
setlocal EnableDelayedExpansion
echo(!line!
endlocal
)) > "%%a.new"
)
New files have .xml.new extension; you may add a couple lines to delete original .xml files and rename .xml.new ones to .xml.

Batch script skipping blank entries in a .CSV when delim is ','

I have a .CSV that I am trying to sort through to create another file from the data, but when I run it through, it skips blank entries. For example, if a line is
value,value,value,,,value
and I try to get the 4th column, it would spit out 6th. Presumably because it is the next valid value. I don't want it to skip the blank entry as it can mess up the tables I'm trying to make. Anyone know how to resolve this? (Any tips are welcome as I suck at batch scripts)
Here is my script:
FOR /F "tokens=1,2,3,4,5,6,7,8,9,10,11,12,13,14 delims=," %%a in (file.csv) DO (
echo %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m %%n
)
pause
This is the standard behaviour of the FOR/F loop, consecutive delims only used as one delimiter.
But you can use a workaround with a second FOR/F.
Prefix each column with another character, split the line at the delim and remove the prefix.
setlocal EnableDelayedExpansion
FOR /F "delims=" %%L in (test.bat) DO (
set "line=%%L,,,,,,,,"
set "line=#!line:,=,#!"
FOR /F "tokens=1,2,3,4 delims=," %%a in ("!line!") DO (
set "param1=%%a"
set "param2=%%b"
set "param3=%%c"
set "param4=%%d"
set "param1=!param1:~1!"
set "param2=!param2:~1!"
set "param3=!param3:~1!"
set "param4=!param4:~1!"
echo !param1! !param2! !param3! !param4!
)
)
As jeb already mentiones in his answer, for /F treats consecutive delimiters as one. To avoid that, you could replace each delimiter , by "," and enclose each full line by "", so each field appears as being enclosed within "", which can easily be removed finally by the ~ modifier of the for /F variable; so there is no need to do any more string manipulations (like sub-string expansion) later on:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "usebackq delims=" %%# in ("file.csv") do (
set "LINE=%%#"
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=," %%A in (^""!LINE:,="^,"!"^") do (
endlocal
echo Field 1: %%~A
echo Field 2: %%~B
echo Field 3: %%~C
echo Field 4: %%~D
setlocal EnableDelayedExpansion
)
endlocal
)
endlocal
This might not work properly if the data contain , characters by themselves (remember that , is not considered as delimiter in case a field is enclosed within "" in the original CSV file).
The toggling of delayed expansion is done to not lose any exclamation marks in the data.

Read stdin stream in a batch file

Is it possible to use a piped stdin stream inside a batch file?
I want to be able to redirect the output of one command into my batch file process.bat list so:
C:\>someOtherProgram.exe | process.bat
My first attempt looked like:
echo OFF
setlocal
:again
set /p inputLine=""
echo.%inputLine%
if not (%inputLine%)==() goto again
endlocal
:End
When I test it with type testFile.txt | process.bat it prints out the first line repeatedly.
Is there another way?
set /p doesn't work with pipes, it takes one (randomly) line from the input.
But you can use more inside of an for-loop.
#echo off
setlocal
for /F "tokens=*" %%a in ('more') do (
echo #%%a
)
But this fails with lines beginning with a semicolon (as the FOR-LOOP-standard of eol is ;).
And it can't read empty lines.
But with findstr you can solve this too, it prefix each line with the linenumber, so you never get empty lines.
And then the prefix is removed to the first colon.
#echo off
setlocal DisableDelayedExpansion
for /F "tokens=*" %%a in ('findstr /n "^"') do (
set "line=%%a"
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
echo(!line!
endlocal
)
Alternatively, on some environments (like WinRE) that don't include findstr, an alternative with find.exe might suffice. find will accept a null search string "", and allows search inversion. This would allow something like this:
#echo off
setlocal DisableDelayedExpansion
for /F "tokens=*" %%a in ('find /v ""') do (
...
The set "line=!line:*:=!" syntax is:
set requires one parameter that is a=b.
If a contains a space or something, you'll have to use the quotation marks around this parameter. Here I don't see any
!line:*:=!
For this syntax, you can type 'set /?' to see the official description on using variables.
!var! is like %var%, to get the value. But !var! means delayed expansion.
line var name
the first : variable modification mark.
**:= **:=(empty), replace the string in the variable's value matches "*:"(virtually from the string start to first : occurence) with (empty), i.e. delete the substring from start to first colon.
FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO (
> CON ECHO.%%B
>> %File% ECHO.%%B
)
Source here: http://www.robvanderwoude.com/unixports.php#TEE
Alternatively, on some environments (like WinRE) that don't include findstr, an alternative with find.exe might suffice. find will accept a null search string "", and allows search inversion. This would allow something like this:
#echo off
setlocal DisableDelayedExpansion
for /F "tokens=*" %%a in ('find /v ""') do (
set "line=%%a"
echo(!line!
)

Resources