This is my first post to this site, though I use it quite often. I wrote a batch file to do the following, ideally. Note: There are alot of echos in here, to help me debug.
1.) Clean up old log files, this works file
2.) Ping a list of systems and output successful results to one file and bad to another
3.) Check if software exists, if not download it
4.) Install software on endpoint
The issue I am encountering is the successful ping output files has trailing spaces. Because of this, the install commands don't work. It puts the trailing space in the UNC path. Since the spaces exist, the run command doesn't work.
HELP PLEASE, My desk is breaking, due to my forehead smashing into it.
#echo off
cls
set file=_0_Add_Systems_Here.txt
set log1=_3_Responsive.txt
set log2=_4_Non-Responsive.txt
set dir=%cd%
FOR /F "usebackq" %%i IN (`hostname`) DO SET host=%%i
echo Run Cleanup
start _2_Dont_Run_Me.bat
echo Ping
for /F "tokens=*" %%a in (%file%) do #ping %%a | find "TTL=" > nul && echo %%a >> %log1% || echo %%a >> %log2%
echo Output systems
for /F "tokens=*" %%a in (%log1%) do echo "%%a"
echo Check for Framepkg
if not exist framepkg.exe Copy \\<Removed>\c$\MFE\framepkg.exe | echo copying frame package.
if exist framepkg.exe echo Found frame package.
echo copy framepackage to system
for /F "tokens=*" %%b in (%log1%) do copy %dir%\framepkg.exe \\%%b\c$\framepkg.exe /y >> _5_McAfee_Deployment_Output.txt
for /F "tokens=*" %%b in (%log1%) do echo \\%%b\c$\framepkg.exe
echo start agent install
for /F "tokens=*" %%c in (%log1%) do psexec \\%%c cmd /c "c:\framepkg.exe /install=agent /forceinstall" >> \\%host%\%dir%\_5_McAfee_Deployment_Output.txt
....&& >>%log1% echo %%a||>>%log2% echo %%a
should fix your problem (the extra spaces in your code appear to cause the problem; the redirector location within the statement is not relevant - first is as good as last)
OR
....&& echo %%a\ >> %log1% || echo %%a\ >> %log2%
AND
for /F "delims=\" %%b in (%log1%)....
but that would place extra \ in your log files.
(reason for \ is that it can't exist in valid data)
Related
I have many pdf files I need to Copy, Rename and Move.
Copy and move works – I just can’t figure out how the rename part it’s done.
The files look like this:
2021-05-05_10-10-12-609_Testperson_Nancy_2512489996_19490816_OD_20210429112706.pdf
2021-06-05_11-11-12-135_Testperson_with_many _Names_0708681234_19490817_OD_20210429112715.pdf
and I need them to be renamed to:
251248-9996_19490816_OD_20210429112706.pdf
070868-1234_19490817_OD_20210429112715.pdf
So need to substring from the fourth _ from the right (without the first _) with a - between [6] and [7].
This is what I got so far:
SET input=c:\temp\rename\input
SET backup=c:\temp\rename\backup
SET output=c:\temp\rename\output
if not exist "%backup%" mkdir "%backup%"
for /f "tokens=*" %%F IN ('DIR /S /B "%input%\*.pdf"') DO (
xcopy /-y "%%F" "%backup%" & move "%%F" "%output%"
)
c:\temp\rename\input (for original files)
c:\temp\rename\backup (backup dir for orginal files)
c:\temp\rename\output (renamed files)
This must be run as a scheduled batch file job on Windows Server (I know how to run the job)
Give this a go. I would suggest you first make a backup of your files before running this in on your production files:
#echo off & setlocal enabledelayedexpansion
set "input=c:\temp\rename\input"
set "backup=c:\temp\rename\backup"
set "output=c:\temp\rename\output"
mkdir "%backup%">nul 2>&1
for /f "delims=" %%i in ('dir /b /s /a-d "%input%\*.pdf"') do (
set "cnt=-4"
set "_tmp=%%~i"
set "_tmp=!_tmp: =-!
set "_strip=!_tmp:_= !"
for %%c in (!_strip!) do (
set /a cnt+=1
)
if !cnt! gtr 1 if defined _tmp call :parser !cnt! "%%i"
)
goto :eof
:parser
for /f "tokens=%~1,* delims=_" %%a in ("%~2") do set "fin=%%~b"
echo xcopy /-y "%~2" "%backup%"
echo move "%~2" "%output%\%fin:~0,6%-%fin:~6%"
Some things to note. This does not yet do the actual copy or move, it will just echo the results, as a safety measure. So once you see it does what it should, then you can remove echo from the last two lines.
Lastly this is assuming the file format as you gave it, but it does cater for whitespace as well as double _ but it will go south if you have another _ separated section which does not form part of the standard format.
Demo
Given the 4 examples you gave in your question and comment (with the exception of the C:\tmp\Input in my example as I use a different directory to test):
You could probably do this easier using PowerShell, but to remain on topic, here's a batch-file which gets some assistance from powershell:
#Echo Off
SetLocal EnableExtensions
Set "input=C:\temp\rename\input"
Set "backup=C:\temp\rename\backup"
Set "output=C:\temp\rename\output"
For /F "Tokens=1-2 Delims=|" %%G In (
'%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile
"Get-ChildItem -LiteralPath '%input%' -Filter '*_*_*_*_*.pdf' -File | "
"ForEach-Object { $_.FullName + '|' + "
"(($_.Name).Split('_')[-4]).Insert(6,'-') + '_' + "
"(($_.Name).Split('_')[-3..-1] -Join('_')) }"') Do (
Echo=%SystemRoot%\System32\xcopy.exe "%%G" "%backup%\" && (
Echo=Move /Y "%%G" "%output%\%%H"))
Pause
Currently the script will just show you the commands it will run, if you're happy with the output, then delete Echo= from lines 12 and 13 and optionally the last line. As a side note, I used a trailing backward slash for the xcopy destination, this will cause %backup% to be created, if it does not already exist.
please be gentle...
I am trying to create a .bat script to create another .bat script using variables set in the first script.
All of my other 'echo's' have outputted in the correct format to SS_Update.bat. But I am struggling with a few lines that fail to copy across correctly. In "Setup New SS.bat" I have... (out of context)
echo for /r %Tempfolder% %%%a in (*.zip) do set sszip=%%%~nxa >> %USERPROFILE%\Documents\%Site%\SS_Update.bat
echo for /r %Tempfolder% %%%a in (*.zip) do set tempfile=%%%~dpnxa >> %USERPROFILE%\Documents\%Site%\SS_Update.bat
echo for /f %%%i in (%currentss%) do set date1=%%%~ti >> %USERPROFILE%\Documents\%Site%\SS_Update.bat
echo for /f %%%i in (%extractedss%) do set date2=%%%~ti >> %USERPROFILE%\Documents\%Site%\SS_Update.bat
echo for /f %%%i in ('DIR /B /O:D %currentss% %extractedss%') do echo Current SS is older than the Extracted SS or missing... >> %USERPROFILE%\Documents\%Site%\SS_Update.bat
The results in "SS_Update.bat" are...
for /r C:\Users\Pelican\Documents\Temp %%~nxa
for /r C:\Users\Pelican\Documents\Temp %%~dpnxa
for /f %currentss%~ti
for /f %extractedss%~ti
for /f %D ') do echo Current SS is older than the Extracted SS or missing...
I understand that I need to escape some special characters i.e I wanted %% so I used %%%. I have tried all the combinations I can think of using examples from Escape Characters but I keep getting far from what I am after.
Any help would be appreciated!
You can ease the task by prepending the lines with a var which includes the echo and redirection, so the code looks normal aside from necessary doubling % and escaping <>|&.
Set _=^>^> "%USERPROFILE%\Documents\%Site%\SS_Update.bat" Echo
%_% for /r %Tempfolder% %%%%a in (*.zip) do set sszip=%%%%~nxa
%_% for /r %Tempfolder% %%%%a in (*.zip) do set tempfile=%%%%~dpnxa
%_% for /f %%%%i in (%currentss%) do set date1=%%%%~ti
%_% for /f %%%%i in (%extractedss%) do set date2=%%%%~ti
%_% for /f %%%%i in ('DIR /B /O:D %currentss% %extractedss%') do echo Current SS is older than the Extracted SS or missing...
I would prefer to enclose the commands within a single redirect. (the only issue is in escaping the internal closing parentheses using the standard circumflex character). To escape a single percent character you use another percent character, so for two to be echoed you'll need four.
>>"%USERPROFILE%\Documents\%Site%\SS_Update.bat" (
ECHO FOR /R "%Tempfolder%" %%%%A IN (*.zip^) DO SET "sszip=%%%%~nxA"
ECHO FOR /R "%Tempfolder%" %%%%A IN (*.zip^) DO SET "tempfile=%%%%~dpnxA"
ECHO FOR /F %%%%I IN (%currentss%^) DO SET "date1=%%%%~tI"
ECHO FOR /F %%%%I IN (%extractedss%^) DO SET "date2=%%%%~tI"
ECHO FOR /F %%%%I IN ('DIR/B/OD %currentss% %extractedss%'^) DO (
ECHO ECHO Current SS is older than the Extracted SS or missing...^)
)
We have a batch file that installs several programs as part of the developers setup. This is ran periodically when we get new versions of used components. So it would be nice only to install if the versions are different.
At the command prompt I can run this and get back the version installed:
wmic datafile where name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe' get version /format:list
Which gives the output Version=12.1.369.0.
However when I put this into a batch file like this and try to extract the version:
echo off
FOR /F "tokens=2 delims==" %%I in ('"wmic datafile where^(name^="C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe" get version /format:list"') DO (SET "RESULT=%%I")
ECHO %RESULT%
I get the response \\Common was unexpected at this time.
Some parts may be redundant as I've been trying stuff off the 'Net to correct this.
What have I missed?
You have a set of misplaced double quotes, as well as an extra (.
WMIC uses SQL syntax, and strings are enclosed in single quotes.The internal single quotes do not interfere with the command enclosing single quotes.
You can put double quotes around the WHERE clause (not including the WHERE keyword) to avoid some escape issues within the FOR DO() clause.
#echo off
FOR /F "tokens=2 delims==" %%I IN (
'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list'
) DO SET "RESULT=%%I"
ECHO %RESULT%
But this may not quite be the whole solution. You can't see it with the above code, but RESULT actually contains a trailing carriage return (0x0D). This is due to a quirk with how FOR /F handles WMIC unicode output. Every line of WMIC output will have the extra trailing carriage return.
As long as you always access RESULT using %RESULT% (normal expansion), then you will not have any problems. But if you should ever need delayed expansion, then you can have problems, as demonstrated below.
#echo off
setlocal enableDelayedExpansion
FOR /F "tokens=2 delims==" %%I IN (
'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list'
) DO SET "RESULT=%%I"
ECHO %RESULT%xxx
ECHO !RESULT!xxx
One convenient method to strip the unwanted carriage return is to use an extra level of FOR.
#echo off
setlocal enableDelayedExpansion
FOR /F "tokens=2 delims==" %%I IN (
'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list'
) DO FOR /F "delims=" %%A IN ("%%I") DO SET "RESULT=%%A"
ECHO %RESULT%xxx
ECHO !RESULT!xxx
Here's the subroutine I use for this in my own software update batch script:
:getfattr
set %1=
setlocal
set "name=%~f2"
set "name=%name:\=\\%"
for /f "delims=" %%A in ('wmic datafile where "name='%name:'=\'%'" get %1 /format:list') do #^
for /f "delims=" %%B in ("%%A") do endlocal & set "%%B" & goto :eof
echo>&2 getfattr failed
endlocal
goto :eof
It can get any file attribute supported by wmic datafile get. For example, here's how you might get the file version for the currently installed Adobe Reader:
call :getfattr version "%ProgramFiles(x86)%\Adobe\Reader 11.0\Reader\AcroRd32.exe"
echo "!version!"
After doing that, environment variable version will contain the requested version string. If :getfattr fails, version is guaranteed to be unset.
A test execution trace for that example looks like this (delayed expansion was already enabled, though this is not assumed by :getfattr):
>call :getfattr version "C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe"
>set version=
>setlocal
>set "name=C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe"
>set "name=C:\\Program Files (x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe"
>for /F "delims=" %A in ('wmic datafile where "name='C:\\Program Files (x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe'" get version /format:list') do #for /F "delims=" %B in ("%A") do endlocal & set "%B" & goto :eof
>endlocal & set "Version=11.0.18.21" & goto :eof
>echo "!version!"
"11.0.18.21"
As you can see, it's pretty direct and doesn't faff about too much. It does, however, tiptoe through a minefield of cmd and wmic gotchas.
First, the name of the attribute you want to get is also the name used for the variable you want the result to end up in (version in the test above). Inside the subroutine, that name is %1, so set %1= clears it.
The filename you pass in needs a bit of preprocessing before it can be safely handed to wmic and a shell variable is required for that, so setlocal is issued to avoid stomping the caller's variables.
set "name=%~f2" copies the name to an environment variable after stripping off any surrounding double-quotes and expanding it to a full pathname. Double quotes surround the entire set argument to prevent grief caused by ampersands or parentheses in pathnames.
wmic queries use a SQL-like syntax, where string values are surrounded by single quote ' characters and \ is an escape that suppresses any special meaning of the following character. Since both of these are legal in Windows pathnames, all occurrences of either need a \ prefix. set "name=%name:\=\\%" escapes embedded backslashes, and the '%name:'=\'%' construct in the wmic command line escapes embedded single quotes and adds the required surrounding ones.
cmd's parser doesn't turn off special processing between single quotes, and the name no longer has any surrounding double quotes, so embedded spaces, parentheses or ampersands could potentially break things. To guard against that, wmic's entire name= argument gets double quoted. There's no need for special handling for double quotes already inside the name, because double quotes are prohibited in Windows filenames so there can't be any.
The for command line containing the wmic command ends with a #^ sequence. The ^ serves to attach the next line as the payload of the outer for command; the # prevents that payload being echoed in an execution trace even if ECHO is on.
That echo suppression is done mainly because the inner for exists only to get rid of the spurious CR characters injected by cmd's buggy conversion of wmic's output from Unicode to ASCII (the same technique used in #dbenham's answer) and if it's allowed to echo, those CRs just filthy up the trace with confusing overwrites. As a side benefit, the inner for won't execute its own payload when the line it's handed from the outer for contains only a CR, a version-dependent number of which wmic insists on emitting. The inner for's payload does get echoed if ECHO is on, so tracing still captures all the useful happenings.
That payload consists of three &-separated commands, which for will expand as a single command line before cmd gets to process the individual commands. In particular, this means that set "%%B" gets expanded before endlocal runs, which puts the variable created by that set outside the setlocal/endlocal scope and makes it available to the caller.
%%B will always expand in the format name=value because of the /format:list switch passed to wmic; the name will be the same as that specified with the get verb, and this is how the name you pass in ends up choosing the variable you get back. The entire name=value argument to set is quoted in case the requested attribute contains shell-special characters. This makes :getfattr itself safe, but you might want to use !delayed! expansion rather than %premature% expansion wherever you actually use the variable it hands back to you.
The & goto :eof on that same line breaks from both for loops and returns to :getfattr's caller as soon as the inner one actually does anything, just in case you pass in some weird name and wmic get produces more than one non-blank line of output.
The last three lines only ever run if wmic produces no output at all, which is what happens when it fails.
and two ways without external tools
1.WMIC
WMIC DATAFILE WHERE name="C:\\install.exe" get Version /format:Textvaluelist
Pay attention to the double slashes of file name.
Ready to use script/subroutine:
#echo off
:wmicVersion pathToBinary [variableToSaveTo]
setlocal
set "item=%~1"
set "item=%item:\=\\%"
for /f "usebackq delims=" %%a in (`"WMIC DATAFILE WHERE name='%item%' get Version /format:Textvaluelist"`) do (
for /f "delims=" %%# in ("%%a") do set "%%#"
)
if "%~2" neq "" (
endlocal & (
echo %version%
set %~2=%version%
)
) else (
echo %version%
)
exit /b %errorlevel%
Example (it needs a full file path):
call wmicVersion.bat "C:\Windows\System32\cmd.exe" cmdver
echo %cmdver%
2.MAKECAB
as the WMIC is not installed on home versions of windows here's a way with makecab that will run on every windows machine:
; #echo off
;;goto :end_help
;;setlocal DsiableDelayedExpansion
;;;
;;;
;;; fileinf /l list of full file paths separated with ;
;;; fileinf /f text file with a list of files to be processed ( one on each line )
;;; fileinf /? prints the help
;;;
;;:end_help
; REM Creating a Newline variable (the two blank lines are required!)
; set NLM=^
; set NL=^^^%NLM%%NLM%^%NLM%%NLM%
; if "%~1" equ "/?" type "%~f0" | find ";;;" | find /v "find" && exit /b 0
; if "%~2" equ "" type "%~f0" | find ";;;" | find /v "find" && exit /b 0
; setlocal enableDelayedExpansion
; if "%~1" equ "/l" (
; set "_files=%~2"
; echo !_files:;=%NL%!>"%TEMP%\file.paths"
; set _process_file="%TEMP%\file.paths"
; goto :get_info
; )
; if "%~1" equ "/f" if exist "%~2" (
; set _process_file="%~2"
; goto :get_info
; )
; echo incorect parameters & exit /b 1
; :get_info
; set "file_info="
; makecab /d InfFileName=%TEMP%\file.inf /d "DiskDirectory1=%TEMP%" /f "%~f0" /f %_process_file% /v0>nul
; for /f "usebackq skip=4 delims=" %%f in ("%TEMP%\file.inf") do (
; set "file_info=%%f"
; echo !file_info:,=%nl%!
; )
; endlocal
;endlocal
; del /q /f %TEMP%\file.inf 2>nul
; del /q /f %TEMP%\file.path 2>nul
; exit /b 0
.set DoNotCopyFiles=on
.set DestinationDir=;
.set RptFileName=nul
.set InfFooter=;
.set InfHeader=;
.Set ChecksumWidth=8
.Set InfDiskLineFormat=;
.Set Cabinet=off
.Set Compress=off
.Set GenerateInf=ON
.Set InfDiskHeader=;
.Set InfFileHeader=;
.set InfCabinetHeader=;
.Set InfFileLineFormat=",file:*file*,date:*date*,size:*size*,csum:*csum*,time:*time*,vern:*ver*,vers:*vers*,lang:*lang*"
example output (it has a string version which is a small addition to wmic method :) ):
c:> fileinfo.bat /l C:\install.exe
file:install.exe
date:11/07/07
size:562688
csum:380ef239
time:07:03:18a
vern:9.0.21022.8
vers:9.0.21022.8 built by: RTM
lang:1033
3 Using shell.application and hybrid batch\jscript.Here's tooptipInfo.bat :
#if (#X)==(#Y) #end /* JScript comment
#echo off
rem :: the first argument is the script name as it will be used for proper help message
cscript //E:JScript //nologo "%~f0" %*
exit /b %errorlevel%
#if (#X)==(#Y) #end JScript comment */
//////
FSOObj = new ActiveXObject("Scripting.FileSystemObject");
var ARGS = WScript.Arguments;
if (ARGS.Length < 1 ) {
WScript.Echo("No file passed");
WScript.Quit(1);
}
var filename=ARGS.Item(0);
var objShell=new ActiveXObject("Shell.Application");
/////
//fso
ExistsItem = function (path) {
return FSOObj.FolderExists(path)||FSOObj.FileExists(path);
}
getFullPath = function (path) {
return FSOObj.GetAbsolutePathName(path);
}
//
//paths
getParent = function(path){
var splitted=path.split("\\");
var result="";
for (var s=0;s<splitted.length-1;s++){
if (s==0) {
result=splitted[s];
} else {
result=result+"\\"+splitted[s];
}
}
return result;
}
getName = function(path){
var splitted=path.split("\\");
return splitted[splitted.length-1];
}
//
function main(){
if (!ExistsItem(filename)) {
WScript.Echo(filename + " does not exist");
WScript.Quit(2);
}
var fullFilename=getFullPath(filename);
var namespace=getParent(fullFilename);
var name=getName(fullFilename);
var objFolder=objShell.NameSpace(namespace);
var objItem=objFolder.ParseName(name);
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb787870(v=vs.85).aspx
WScript.Echo(fullFilename + " : ");
WScript.Echo(objFolder.GetDetailsOf(objItem,-1));
}
main();
used against cmd.exe :
C:\Windows\System32\cmd.exe :
File description: Windows Command Processor
Company: Microsoft Corporation
File version: 6.3.9600.16384
Date created: ?22-?Aug-?13 ??13:03
Size: 347 KB
This is my filever bat file.
#echo off
If "%~1"=="" goto help
If "%~1"=="/?" goto help
If /i "%~1"=="/h" goto help
If "%~1"=="-?" goto help
If /i "%~1"=="-h" goto help
set filepath=%~f1
set file=%filepath:\=\\%
wmic datafile where name^="%file%" get version|findstr /i /v /c:"version"
echo %errorlevel%
goto finish
:help
Echo.
Echo. FileVer
Echo. -------
Echo.
Echo. Purpose:
Echo.
Echo. Reports the version number for an exe, dll, and similar files.
Echo.
Echo. Usage:
Echo.
Echo. filever ^<executable file^>
Echo.
Echo. filever [/h ^| -h ^| /? ^| -?] Starts this help
Echo.
echo. Examples:
Echo.
Echo. filever c:\windows\explorer.exe
Echo. filever "C:\Program Files\Windows NT\Accessories\wordpad.exe"
Echo. filever shell32.dll
Echo.
Echo. For Help
Echo.
Echo. filever
Echo. filever /?
Echo.
:finish
rem Pause if command double clicked
If /i "%cmdcmdline:~0,6%"=="cmd /c" pause
You seem to have an extra set of quotes around the whole command
One problem with for loops is it wmic outpus a blank line after the version.
set filepath=%~f1
set file=%filepath:\=\\%
for /f "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"') do set a=%%A & echo %%A
Put it in a file. Although the file has two blank lines.
set filepath=%~f1
set file=%filepath:\=\\%
wmic datafile where name^="%file%" get version|findstr /i /v /c:"version">test.txt
Maybe this could work
set filepath=%~f1
set file=%filepath:\=\\%
for /f "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"') do if not ""==%%A set a=%%A & echo %%A
Or call another batch file and don't return.
Here's a way to getrid of blank lines.
set filepath=%~f1
set file=%filepath:\=\\%
for /f "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"^|findstr /i /v /r "^$"') do set a=%%A & echo %A%
wmic datafile where name^="%file%" get version|findstr /i /v /c:"version"|findstr /i /v /r "^$">test.txt
Here is an alternative method, bypasses WMIC for powershell Get-WmiObject.
#ECHO OFF
start /b powershell.exe -command "Get-WmiObject -Class CIM_DataFile -Filter \"Name='C:\\Program Files (x86)\\Knuckle.dll'\" | Select-Object Version"
PAUSE
Returns this result when double clicking a .cmd batch file.
Version
-------
0.0.0.0
I have created the following simple batch file.... What I'm trying to accomplish here is to connect using Remote Command (RCMD utility) to a list o servers defined in (clusters.txt) and delete a list of video files difines in (assets.txt)... I want to delete those assets on every server. This Batch file works however it has to connect and disconnect to delete every piece of asset and that's exactly what I'm trying to avoid. I want to be able to connect to 1 server and delete my list of assets %%B using scp del cmd then disconnect from that server and go to the next one on the list %%A and delete all the assets from there with just one connection instead of having to connect and disconnect 200 times.
#echo off
FOR /F "tokens=*" %%A IN (clusters.txt) DO FOR /F "tokens=*" %%B in (assets.txt) DO rcmd \\%%A \vstrmkit\scp del %%B
This is what the list of files looks like:
11E8A51A*
11E8A51D*
11E8A614*
11E88E4E*
11E88E4C*
and the list of server it's just computer hostnames with letters and numbers:
SEA88630-N0
Any help would be greatly appreciate it.
Without knowing the server and client versions of the OS, it is possible to have some kind of command line limitations. But, let's try
#echo off
rem prepare environment
setlocal enableextensions enabledelayedexpansion
rem Define the command that will be executed for each element
set "command=echo \vstrmkit\scp del"
rem Prepare the command for the list of files into a environment variable
set "command=set c=%command% "
for /f "tokens=*" %%f in (assets.txt) do set "command=!command!/#c#%%~f"
rem Adjust the command variable to generate a valid concatenation of commands
set "command=%command:/=&%"
rem Connect to each of the servers sending the generated command
rem In the last step, change the # with ! to generate on remote, when
rem delayed expansion is applied, the correct final command to execute
setlocal disabledelayedexpansion
for /f "tokens=*" %%a in (clusters.txt) do rcmd \\%%a cmd.exe /v:on /c "%command:#=!%"
Note that the line where the main command in defined, there is a echo inserted for testing. If everything seems ok, remove echo to do it work.
EDITED - Option number 2
It seems that there is some problem with quotes. The previous code works in my system but not in the OP. Let's try another one. It also works on my system.
#echo off
rem prepare environment
setlocal enableextensions disabledelayedexpansion
rem Define the command that will be executed for each element
set "command=echo \vstrmkit\scp del"
rem Define a temporary file to contain commands to process
set "tempFile=%temp%\%~nx0.tmp"
rem Generate the content of the temp file with the list of commands
rem to execute for each server
(
for /f "tokens=*" %%f in (assets.txt) do echo %command% %%f
echo exit
) > "%tempFile%"
rem Connect to each of the servers sending the generated command list
for /f "tokens=*" %%a in (clusters.txt) do (
rcmd \\%%a < "%tempFile%"
)
del /f /q "%tempFile%" > nul 2>nul
endlocal
#ECHO OFF
SETLOCAL
SET "filelist="
FOR /f "delims=" %%A IN (assets.txt) DO CALL SET filelist=%%filelist%% %%A
FOR /F "tokens=*" %%A IN (clusters.txt) DO echo rcmd \\%%A \vstrmkit\scp del %filelist%
GOTO :EOF
The required commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO rcmd to rcmd to execute the deletion
Possible solution if the command can't take multiple arguments:
Create a batch file called delasset.bat and copy it either into \vstrmkit on each target machine or some other directory on the path of each machine (eg c:\windows or c:\windows\system32)
delasset.bat should contain
#ECHO OFF
ECHO DEL %*
(Again, I've simply ECHOed the DEL - you'd need to remove the echo after verifying to activate)
Then change ...rcmd \\%%A \vstrmkit\scp del %filelist% to ...rcmd \\%%A \vstrmkit\scp DELASSET %filelist%
(it's case-insensitive; I've UPPERCASED it for emphasis)
Another possible way:
#ECHO OFF
SETLOCAL
SET "filelist="
FOR /f "delims=" %%A IN (assets.txt) DO CALL SET filelist=%%filelist%%:%%A
SET filelist=%filelist:~1%
FOR /F "tokens=*" %%A IN (clusters.txt) DO echo rcmd \\%%A \vstrmkit\scp del %filelist::=^&\vstrmkit\scp del %
GOTO :EOF
which should generate (eg)
rcmd \\SEA88630-N0 \vstrmkit\scp del 11E8A51A*&\vstrmkit\scp del 11E8A51D*&\vstrmkit\scp del 11E8A614*&\vstrmkit\scp del 11E88E4E*&\vstrmkit\scp del 11E88E4C*
Note that filelist is now built with colons separating the items. Colon cannot be part of a filename. The first character (which will be a colon, since filelist has :filenamefromfile appended to it) is then stripped off (see set/? from the prompt for substring documentation)
The hieroglypics in %filelist::=^&\vstrmkit\scp del % mean 'with filelist, substitute for (first :) [the characters between the first : and the = - which happens to be a colon] with the string &\vstrmkit\scp del (ie. all characters between the = and the closing % - with the additional modification that since & is a special character to cmd (it means 'do the thing before the &, then the thing after') the & needs to be "escaped" by a caret (^) which says "this is a special character that I want to use as an ordinary character".
Leaving the echo firmly in place will allow you to see precisely what should be delivered. Sadly, I've no idea whether it's correct...
Something like this should help.
I don't now for what you have an * after the file-name but it have to be removed by
the script. Or is that a .* ???
EDIT : IF you have various file with the same name and various ("Unknown") extensions then you have to test for the extensions with a new FOR loop :
#echo off
setlocal EnableDelayedExpansion
FOR /F "tokens=*" %%A IN ('type clusters.txt') DO (
FOR /F "tokens=*" %%B in ('type assets.txt') DO (
set $asset=%%B
rcmd.exe \\%%A \vstrmkit\scp del !$asset:~0,-1!.*))
Sorry I know similar things have been asked here but basically I'm trying to read a text file within a batch script and evaluate what has been written to the file.
The job is a print job that sends a file to a printer, I have it echoing the output from the command to a log file. I then want to read in what the output was and if there was an error I will then send an email so we know when things stop working.
It always appends to the end of the file so I know if there's an error the 4th from last line will begin with "Error:". So my question is how can I read that in to a variable so I can perform an IF statement. I've got the emailing part sorted it's just reading from the file that I'm struggling with.
Any help would be much appreciated. Here's an example of the content of the file when there's an error:
----
C:\XG1\DGS01\prints\000000398200001.XG1
19/03/2013
15:02
1 file(s) copied.
Error: print server unreachable or specified printer does not exist.
1 file(s) moved.
It leaves one blank line at the end of the file so I'm going with the last line minus 4.
Thank you
as a line in your batch file:
for /f "tokens=1*delims=:" %%i in (thenameofyourfile) do if "%%i"=="Error" set message=%%j
echo message was "%message%"
Actually, that will report if ANY lines are in the format you describe.
#ECHO OFF
SETLOCAL
(SET message=)
FOR /f "tokens=1,2*delims=[]:" %%i IN (
' TYPE thenameofyourfile^|find /n /v "" '
) DO (
SET lastline=%%i
IF "%%j"=="Error" SET errorline=%%i&SET message=%%k
)
SET /a target=%errorline% + 3
IF %target% neq %lastline% (SET message=)
IF DEFINED message ECHO error found %message%
should get the line ONLY if it's the fourth last line in the file - the "+ 3" being the line-count required (well, minus 1)
BUT - remember that if this is, as it seems, a log file that it's possible (I'd imagine) that further entries may appear AFTER the error (for further jobs) so the target for the Line beginning "Error:" may not be the fourth-last...
OTOH, using the line(s) I first posted, once an "Error:..." line appears, it will be detected EVERY time - you'd need to reset the logfile in your mail-send procedure (save existing & recreate empty?)
Solution without any loop (does test if any error):
#findstr Error: printer.log >nul 2>&1
#if %errorlevel% equ 0 echo Send email now!
And this code does only test the fourth line before the last line for an error:
#echo off &setlocal enabledelayedexpansion
for /f %%i in ('findstr /n "^" printer.log') do (
set line4=!line3!&set line3=!line2!&set line2=!line1!&set line1=%%i)
echo %line4%|findstr Error: >nul 2>&1
if %errorlevel% equ 0 echo Send email now!
If you want to start reading the fourth line from the bottom of your log file, you can count the number of lines in your log file, subtract 4, then more +%count% to get the tail of the log.
#echo off
setlocal
set logfile=printerlog.log
for /f "tokens=2 delims=:" %%I in ('find /c /v "" "%logfile%"') do set lines=%%I
set /a "tail4=%lines% - 4"
for /f %%I in ('more +%tail4% "%logfile%" ^| find /i "Error:"') do (
rem do your email voodoo here
)
If your log file will exceed 65,535 lines, I recommend looping through the file using JScript or VBScript instead of a batch loop. Skipping that many lines with more will cause more to crash, so you'd have to loop line-by-line and increment a counter. However, batch for loops are terribly slow.
Here's the same script that replaces more with JScript-ish file reading.
#if (#a==#b) #end /*
:: batch portion
#echo off
setlocal
set logfile=printerlog.log
for /f "tokens=2 delims=:" %%I in ('find /c /v "" "%logfile%"') do set lines=%%I
set /a "tail4=%lines% - 4"
for /f "delims=" %%I in ('cscript /nologo /e:jscript "%~f0" %tail4% "%logfile%" ^| find /i "Error:"') do (
rem do your email voodoo here
rem %%I contains the matching line.
rem After one match, stop processing further
goto :EOF
)
goto :EOF
::JScript portion */
var i=0, fso = new ActiveXObject("Scripting.FileSystemObject").OpenTextFile(WSH.Arguments(1),1);
while (!fso.AtEndOfStream) {
if (i++ < WSH.Arguments(0)) fso.SkipLine();
else WSH.Echo(fso.ReadLine());
}
fso.Close();
I believe the fastest and least resource-intensive way to check for errors is with GNU tail. If you can, get the .zip binaries and put tail.exe in your path or where your batch script can access it. Then:
#echo off
setlocal
tail -n 4 printerlog.log | find /i "Error:" >NUL && (
echo Error found. Sending email.
rem do email stuff
)
Or if you wish to capture the text of the error for your email:
#echo off
setlocal
for /f "delims=" %%I in ('tail -n 4 printerlog.log ^| find /i "Error:"') do (
echo Error found: %%I
rem do email stuff
goto :EOF
)
tail is much more efficient than all these other methods of counting the number of lines in the log file and looping through the log file line-by-line, whether looping in batch or in JScript.