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
Related
this should be pretty simple, i just want a bat file that when placed in a folder will rename all files within the folder and delete the final 4 characters, but not the extension
ex.
img (1).jpg
becomes
img.jpg
ive tried
#echo off
setlocal enabledelayedexpansion
if not exist %1 goto :eof
for /f %%A in ('find /V /C "" ^<%1') do set lines=%%A
for /f "tokens=1,* delims=[]" %%A in ('find /V /N "" ^<%1') do (
if %%A LSS %lines% (
echo %%B
) else (
set rec=%%B
echo !rec:~0,-4!
)
)
but it doesnt work. theres no error, it runs, it just doesnt do anything
I modified your code as follows:
1/ switched to a test directory (PUSHD testdirectoryname) and back (POPD)
2/ changed the %1 to a fixed filename
3/ Inserted some debug lines
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the source directory 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"
PUSHD "%sourcedir%"
if not exist q71992787.txt goto :eof
TYPE "%sourcedir%\q71992787.txt"
for /f %%A in ('find /V /C "" ^<q71992787.txt') do set lines=%%A
SET li
find /V /N "" <q71992787.txt
for /f "tokens=1,* delims=[]" %%A in ('find /V /N "" ^<q71992787.txt') do (
if %%A LSS %lines% (
echo %%B
) else (
set rec=%%B
echo !rec:~0,-4!
)
)
POPD
GOTO :EOF
The result, other than the debug lines, was a list of the filenames in the file
q71992787.txt, other than the very last filename, which was shortened by 4 characters.
This differs from your result "it just doesnt do anything". This is why I mention it.
So - to your solution:
I can see no reason for calculating the number of lines in the file. If there are 10 lines for example, then reading the same file again with 'find /v /n` will still find 10 lines.
The if %%A LSS %lines% will echo %%B (the filename) if %%A (the line number) is LESS (LSS) than 10; that is, from 1 to 9 - just show the filename.
The else clause will only be processed if %%A is 10 or greater, so only on the last line.
I'm sure this wasn't what you intended to do.
I'd advise you to use set "var1=value" for setting STRING values - this avoids problems caused by trailing spaces. Quotes are not needed for setting arithmetic values (set /a`)
Where you alter your filename, setting rec to %%B means the entire filename, %%B. You don't show us a sample from the file %1, so I'll presume it's img (1).jpg as your narrative mentions. rec will thus become img (1).jpg and then be shortened to img (1) by removing the last 4 characters of the string.
Since you seem to want to remove the last 4 characters of the name part and leave the extension, then you need to read the documentation for for (for /? from the prompt) which will tell you
set "rec=%%~nB"
will assign just the name part and you could then
set "rec=!rec:~0,-4!%%~xB"
echo !rec:~0,-4!%%~xB
to set or show the manipulated name.
As to why you get no result at all; This could be because the file encoding of %1 is unicode or is *nix format (, not line endings)
And I do hope you're not using a *nix emulator like cygwin where find becomes a file-locator, not a string-analyser.
The file renaming task for all *.jpg and *.jpeg files in the current directory of which file name ends with a space and a single digit number in round brackets can be done with a batch file with following command lines:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir "* (?).jpg" "* (?).jpeg" /A-D /B /ON 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:" ([0123456789])\.jpe*g$"') do (
set "FileName=%%~nI"
setlocal EnableDelayedExpansion
ren "!FileName!%%~xI" "!FileName:~0,-4!%%~xI"
endlocal
)
endlocal
That code works also for image file names containing one or more exclamation marks and one or more round brackets before the last four characters in file name to remove.
Note: The command REN outputs an error message if the current directory contains the two files photo (1).jpg and photo (2).jpg on renaming the second file to photo.jpg because of this is not possible after first file already renamed to photo.jpg. There cannot be two files with same name in same directory.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
dir /?
echo /?
endlocal /?
findstr /?
ren /?
set /?
setlocal /?
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.
Doing it in PowerShell would be much easier
ls *.jpg |% { ren $_ -ne ($_.BaseName.Substring(0, $_.BaseName.Length - 4) + $_.Extension) -wi }
-wi or -WhatIf is the option for dry running. After checking that the new names are OK you can remove that option. Full command line without alias:
Get-ChildItem -File *.* | ForEach-Object { Rename-Item -WhatIf -Path $_ `
-NewName ($_.BaseName.Substring(0, $_.BaseName.Length - 4) + $_.Extension) }
I want to write a script to prompt user for file path and list all files found. The file path can contain wildcards. Something similar to this. But the batch script version of it. For example:
C:\Somewhere\user*\app\version-*.*\start.exe
The files might be located like this:
C:\Somewhere\user345\app\version-1.0\start.exe
C:\Somewhere\user898\app\version-1.2\start.exe
C:\Somewhere\user898\app\version-1.3\start.exe
I tried to use FOR and it turns out to be so much harder than expected because FOR does not support wildcards in the middle of a path.
Is there a way to list these files? (Maybe without using for?)
I think this recursive solution works pretty well; you may name it WCDIR.bat:
#echo off
setlocal
if "%~1" neq "" set "next=%~1" & goto next
echo Show files selected by several wild-cards
echo/
echo WCDIR wildcardPath
echo/
echo Each folder in the path may contain wild-cards
echo the last part must be a file wild-card
goto :EOF
:next
for /F "tokens=1* delims=\" %%a in ("%next%") do set "this=%%a" & set "next=%%b"
if defined next (
for /D %%a in ("%this::=:\%") do (
setlocal
cd /D "%%~a" 2>NUL
if not errorlevel 1 call :next
endlocal
)
) else (
for /F "delims=" %%a in ('dir /B /A:-D "%this%" 2^>NUL') do echo %%~Fa
)
exit /B
EDIT: I fixed a small bug in the last for /F command.
For example, the output of WCDIR.bat C:\Windows\Sys*\find*.exe command in my Windows 8.1 64-bits computer is:
C:\Windows\System32\find.exe
C:\Windows\System32\findstr.exe
C:\Windows\SysWOW64\find.exe
C:\Windows\SysWOW64\findstr.exe
You can try with the command Where /?
The WHERE command is roughly equivalent to the UNIX 'which' command. By default, the search is done in the current directory and in the PATH.
#echo off
Where /R "%programfiles%" *winrar.exe
pause
#echo off
:: Example d'input
set UserInput=*drive*
:: building the Pattern
set cmd=%Userinput%.exe
:: storage Where.exe command in a macro, the execution will be faster
set whereCmd=where.exe /r c:\windows\ %cmd%
:: execution of macro and output formatting
for /f %%a in ('%whereCmd%') do echo %%~nxa --^> %%a
pause
Obviously it is possible to set title that contains cmd's standard delimiters but through TITLE command looks impossible to set a title that starts with a delimiter.
It is possible to create a new instance of CMD with such title:
start "==" cmd.exe
but not possible for the same instance.
It is possible also with .NET and Console.Title property but when it's called from batch file the tile lasts as long as the compiled exe runs:
#if (#X)==(#Y) #end /* JScript comment
#echo off
setlocal
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
set "jsc=%%v"
)
if not defined jsc (
echo !!! Installation of .NET framework needed !!!
pause
exit /b 3
)
rem echo %jsc%
::if not exist "%~n0.exe" (
del /q /f %~n0.exe
call "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
::)
call %~n0.exe %*
endlocal & exit /b %errorlevel%
*/
import System;
var arguments:String[] = Environment.GetCommandLineArgs();
//Console.WriteLine(Console.Title);
if (arguments.length>0){
Console.Title=arguments[1];
}
//Console.WriteLine(Console.Title);
(Probably is possible through code injection)
Is there a windows 'native' way?
With Win7 x64, I can produce it when a linefeed is in front of the problematic characters.
#echo off
setlocal EnableDelayedExpansion
set LF=^
title !LF!,bc
In Win7 there seems no difference in the height or position of the title when LF is used.
Tested with
cmd /V:on
#for /L %n in (1 1 1111) do #(title !LF!=Hello& title +Hello)
But the current program is shown for the time it's running in the title, so the TITLE command itself produces a flicker.
I found another way:
title ^A,string
where ^A is produced by entering 0 1 while holding down the ALT-key ( <pressALT><0><1><releaseALT> ).
You can get it into a file with echo ^A>>batch.bat and then moving it to the correct position with notepad (it's not shown in notepad, like a whitespace; broader than a space, but not as broad as a TAB)
The LF is the answer to the question , but there's also a workaround. I think I've checked all the ASCII characters and looks like FS and GS characters are not displayed in title of command prompt (here's an example with FS):
#echo off
setlocal
::dbenham's hexprint was used here
::http://www.dostips.com/forum/viewtopic.php?p=23888
::Define a Linefeed variable
set LF=^
::above 2 blank lines are critical - do not remove.
::Create a FS variable
call :hexprint "0x1C" FS
title %FS%=;=
exit /b
:hexPrint string [rtnVar]
for /f eol^=^%LF%%LF%^ delims^= %%A in (
'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(%~1"'
) do if "%~2" neq "" (set %~2=%%A) else echo(%%A
exit /b
But this is not a 'perfect' solution as AppActivate or Tasklist command will detect the FS and GS characters (no such problem with LF).
Also SOH character can be used but it is displayed as space.
FS can be produced also with ctrl+]
GS with ctrl+\
SOH with ctrl+A
Obviously it is possible to set title that contains cmd's standard delimiters but through TITLE command looks impossible to set a title that starts with a delimiter.
It is possible to create a new instance of CMD with such title:
start "==" cmd.exe
but not possible for the same instance.
It is possible also with .NET and Console.Title property but when it's called from batch file the tile lasts as long as the compiled exe runs:
#if (#X)==(#Y) #end /* JScript comment
#echo off
setlocal
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
set "jsc=%%v"
)
if not defined jsc (
echo !!! Installation of .NET framework needed !!!
pause
exit /b 3
)
rem echo %jsc%
::if not exist "%~n0.exe" (
del /q /f %~n0.exe
call "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
::)
call %~n0.exe %*
endlocal & exit /b %errorlevel%
*/
import System;
var arguments:String[] = Environment.GetCommandLineArgs();
//Console.WriteLine(Console.Title);
if (arguments.length>0){
Console.Title=arguments[1];
}
//Console.WriteLine(Console.Title);
(Probably is possible through code injection)
Is there a windows 'native' way?
With Win7 x64, I can produce it when a linefeed is in front of the problematic characters.
#echo off
setlocal EnableDelayedExpansion
set LF=^
title !LF!,bc
In Win7 there seems no difference in the height or position of the title when LF is used.
Tested with
cmd /V:on
#for /L %n in (1 1 1111) do #(title !LF!=Hello& title +Hello)
But the current program is shown for the time it's running in the title, so the TITLE command itself produces a flicker.
I found another way:
title ^A,string
where ^A is produced by entering 0 1 while holding down the ALT-key ( <pressALT><0><1><releaseALT> ).
You can get it into a file with echo ^A>>batch.bat and then moving it to the correct position with notepad (it's not shown in notepad, like a whitespace; broader than a space, but not as broad as a TAB)
The LF is the answer to the question , but there's also a workaround. I think I've checked all the ASCII characters and looks like FS and GS characters are not displayed in title of command prompt (here's an example with FS):
#echo off
setlocal
::dbenham's hexprint was used here
::http://www.dostips.com/forum/viewtopic.php?p=23888
::Define a Linefeed variable
set LF=^
::above 2 blank lines are critical - do not remove.
::Create a FS variable
call :hexprint "0x1C" FS
title %FS%=;=
exit /b
:hexPrint string [rtnVar]
for /f eol^=^%LF%%LF%^ delims^= %%A in (
'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(%~1"'
) do if "%~2" neq "" (set %~2=%%A) else echo(%%A
exit /b
But this is not a 'perfect' solution as AppActivate or Tasklist command will detect the FS and GS characters (no such problem with LF).
Also SOH character can be used but it is displayed as space.
FS can be produced also with ctrl+]
GS with ctrl+\
SOH with ctrl+A
I'm trying to work up a batch file to combine two pieces of text on one line. The first is the filename; the second is the first line of text beginning with "To: ". I have been struggling for hours and this is as far as I've gotten:
#echo off
IF EXIST fullnames.txt DEL fullnames.txt
FOR /F %%g IN ('dir /b *.eml') DO (
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO %FILENAME%%TOLINE% >> fullnames.txt
)
and it doesn't work. I am getting errors or incorrect results almost regardless of what I put down for the filename line; haven't even begun to test the toline part. Any suggestions?
You already used FOR /F to capture the output of the DIR command. Capturing the output of FINDSTR is no different.
However, it is more efficient to use a simple FOR in place of the FOR /F with the DIR command.
You used %~f1 when I think you intended %%~fg.
You cannot expand a variable set within parentheses using %var%, you must use !var! delayed expansion instead. Type SET /? from the command line for more information - read the section starting with "Finally, support for delayed environment variable expansion has been
added..."
However, in your case, you can easily avoid using delayed expansion (not that it is a problem).
Instead of deleting any existing "fullnames.txt" and then appending output to it, it is more efficient to enclose the entire construct within parentheses and redirect all output to the file using the over-write mode.
#echo off
(
for %%F in (*.eml) do (
for "delims=" %%A in ('findstr /b /c:"To: " "%%F"') do echo %%F %%A
)
) >fullnames.txt
But the above solution, simple as it is, is much more complicated than it needs to be.
FINDSTR can process multiple files specified with wildcards, and it will prefix each matching line with the filename followed by a colon.
You can get your results simply from the command line without even using a batch file (or you could put this in a batch file):
findstr /b /c:"To: " *.eml >fullnames.txt
Edit
If you are concerned that a file might contain multiple lines starting with "To: ", and you only want to use the first line, then it is back to using a batch file:
#echo off
setlocal enableDelayedExpansion
set "prevFile="
(
for /f "tokens=1* delims=:" %%A in ('findstr /b /c:"To: " *.eml') do (
if "%%A" neq "!prevFile!" echo %%A: %%B
set "prevFile=%%A"
)
) >fullnames.txt
The above solution could fail if a filename contains !. Also, a path could be used with *.eml as long as the path does not contain a drive letter. Both the drive and ! issues can be resolved with additional modifications.
#echo off
setlocal EnableDelayedExpansion
if exist fullnames.txt del fullnames.txt
for %%f in (*.eml) do (
set toline=
for /F "delims=" %%l in ('findstr /B /C:"To: " "%%f"') do (
if not defined toline set "toline=%%l"
)
echo %%f!toline! >> fullnames.txt
)
EDIT: Simpler method added
The set toline= command delete 'toline' variable before each file is processed, so just the first "To: " matching line found is assigned to it and later shown using Delayed Expansion. However, this process may be achieved in a simpler way that doesn't require Delayed Expansion, as dbenham suggested:
#echo off
if exist fullnames.txt del fullnames.txt
for %%f in (*.eml) do (
set firstFind=
for /F "delims=" %%l in ('findstr /B /C:"To: " "%%f"') do (
if not defined firstFind set firstFind=now & echo %%f%%l >> fullnames.txt
)
)
You can't assign and use environment variables inside a for loop. Use delayed variable expansion or call a subroutine.
Delayed would look something like this
setlocal EnableDelayedExpansion
#echo off
IF EXIST fullnames.txt DEL fullnames.txt
FOR /F %%g IN ('dir /b *.eml') DO (
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO !FILENAME!!TOLINE! >> fullnames.txt
)
However, that doesn't look like it would work correctly anyway. I would do it like this
FOR /F %%g IN ('dir /b *.eml') DO call :process %%g
goto :eof
:process
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO %FILENAME%%TOLINE% >> fullnames.txt