I want to split a string in two parts, without using any for loop.
For example, I have the string in a variable:
str=45:abc
I want to get 45 in a variable and abc in another variable. Is it possible in batch file?
pattern is like somenumber:somestring
You could split the str with different ways.
The for loop, you don't want use it.
The trailing part is easy with the * (match anything until ...)
set "var2=%str:*:=%"
The leading part can be done with a nasty trick
set "var1=%str::="^&REM #%
The caret is needed to escape the ampersand,
so effectivly the colon will be replaced by "&REM #
So in your case you got the line after replacing
set "var1=4567"&REM #abcde
And this is splitted into two commands
set "var1=4567"
REM #abcde`
And the complete code is here:
set "str=4567:abcde"
echo %str%
set "var1=%str::="^&REM #%
set "var2=%str:*:=%"
echo var1=%var1% var2=%var2%
Edit 2: More stable leading part
Thanks Dave for the idea to use a linefeed.
The REM technic isn't very stable against content with quotes and special characters.
But with a linefeed trick there exists a more stable version which also works when the split argument is longer than a single character.
#echo off
setlocal enableDelayedExpansion
set ^"str=456789#$#abc"
for /F "delims=" %%a in (^"!str:#$#^=^
!^") do (
set "lead=%%a"
goto :break
)
:break
echo !lead!
Solution 3: Adpated dbenhams answer
Dbenham uses in his solution a linefeed with a pipe.
This seems a bit over complicated.
As the solution uses the fact, that the parser removes the rest of the line after an unescaped linefeed (when this is found before or in the special character phase).
At first the colon character is replaced to a linefeed with delayed expansion replacement.
That is allowed and the linefeed is now part of the variable.
Then the line set lead=%lead% strips the trailing part.
It's better not to use the extended syntax here, as set "lead=%lead%" would break if a quote is part of the string.
setlocal enableDelayedExpansion
set "str=45:abc"
set ^"lead=!str::=^
!"
set lead=%lead%
echo "!lead!"
You can try this . If its fixed that numbers to left of the colon will be always 2 & to the right will be 3. Then following code should work assuming your str has the value.
set "str=45:abc"
echo %str%
set var1=%str:~0,2%
set var2=%str:~3,3%
echo %var1% %var2%
Keep me posted. :)
It seems pointless to avoid using a FOR loop, but it does make the problem interesting.
As jeb has pointed out, getting the trailing part is easy using !str:*:=!.
The tricky bit is the leading part. Here is an alternative to jeb's solution.
You can insert a linefeed into a variable in place of the : using the following syntax
setlocal enableDelayedExpansion
set "str=45:abc"
echo !str::=^
!
--OUTPUT--
45
abc
The empty line above the last ! is critical.
I'm not sure why, but when the output of the above is piped to a command, only the first line is preserved. So the output can be piped to a FINDSTR that matches any line, and that result directed to a file that can then be read into a variable using SET /P.
The 2nd line must be eliminated prior to using SET /P because SET /P does not recognize <LF> as a line terminator - it only recognizes <CR><LF>.
Here is a complete solution:
#echo off
setlocal enableDelayedExpansion
set "str=45:abc"
echo(!str::=^
!|findstr "^" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1! var2=!var2!
Update
I believe I've mostly figured out why the 2nd line is stripped from the output :)
It has to do with how pipes are handled by Windows cmd.exe with each side being processed by a new CMD.EXE thread. See Why does delayed expansion fail when inside a piped block of code? for a related question with a great answer from jeb.
Just looking at the left side of the piped command, I believe it is parsed (in memory) into a statement that looks like
C:\Windows\system32\cmd.exe /S /D /c" echo {delayedExpansionExpression}"
I use {delayedExpansionExpression} to represent the multi-line search and replace expansion that has not yet occurred.
Next, I think the variable expression is actually expanded and the line is broken in two by the search and replace:
C:\Windows\system32\cmd.exe /S /D /c" echo 43
abc"
Only then is the command executed, and by normal cmd.exe rules, the command ends at the linefeed. The quoted command string is missing the end quote, but the parser doesn't care about that.
The part I am still puzzled by is what happens to the abc"? I would have thought that an attempt would be made to execute it, resulting in an error message like 'abc"' is not recognized as an internal or external command, operable program or batch file. But instead it appears to simply get lost in the ether.
note - jeb's 3rd comment explains why :)
Safe version without FOR
My original solution will not work with a string like this & that:cats & dogs. Here is a variation without FOR that should work with nearly any string, except for string length limits and trailing control chars will be stripped from leading part.
#echo off
setlocal enableDelayedExpansion
set "str=this & that:cats & dogs"
set ^"str2=!str::=^
!^"
cmd /v:on /c echo ^^!str2^^!|findstr /v "$" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1! var2=!var2!
I delay the expansion until the new CMD thread, and I use a quirk of FINDSTR regex that $ only matches lines that end with <cr>. The first line doesn't have it and the second does. The /v option inverts the result.
Yes, I know this is a very old topic, but I just discovered it and I can't resist the temptation of post my solution:
#echo off
setlocal
set "str=45:abc"
set "var1=%str::=" & set "var2=%"
echo var1="%var1%" var2="%var2%"
You may read full details of this method here.
In the Light of people posting all sorts of methots for splitting variables here i might as well post my own method, allowing for not only one but several splits out of a variable, indicated by the same symbol, which is not possible with the REM-Method (which i used for some time, thanks #jeb).
With the method below, the string defined in the second line is split into three parts:
setlocal EnableDelayedExpansion
set fulline=one/two/three or/more
set fulline=%fulline%//
REM above line prevents unexpected results when input string has less than two /
set line2=%fulline:*/=%
set line3=%line2:*/=%
set line1=!fulline:/%line2%=!
set line2=!line2:/%line3%=!
setlocal DisableDelayedExpansion
echo."%line1%"
echo."%line2%"
echo."%line3%"
OUTPUT:
"one"
"two"
"three or/more//"
i recommend using the last so-created partition of the string as a "bin" for the remaining "safety" split-characters.
Here's a solution without nasty tricks for leading piece
REM accepts userID#host
setlocal enableDelayedExpansion
set "str=%1"
set "host=%str:*#=%"
for /F "tokens=1 delims=#" %%F IN ("%str%") do set "user=%%F"
echo user#host = %user%#%host%
endlocal
Related
I need to do a very simple thing: substitute one frase with another. This must be done by CMD batch file (for Windows 7). The frase to be subsitituted can be in any position in txt file line (and in many lines).
The problem is the frase to be substituted contains ":" and "!" characters. I am not very skilled in batch files (to put it mildly), though I spend some hours especially to learn about this specific problem. It looks very complicated for me. At last, by chance, I overided the issue, but... I feel it is a barbarity how I did it.
The real line with the frase which should be substituted is e.g.:
"21:12:45 WARNING: No video signal present!"
The frase which should be substituted is:
"WARNING: No video signal present!"
The frase, which it should be substituted with is:
"Recognition suspended"
I have found this code: https://www.computerhope.com/forum/index.php?topic=41188.0
It works fine, except cannot work with "!" as I see, and escape char "^" never works. But I noticed that although it doesn't work properly - it trims the exclamation mark. Here are real strings before (b) and after (a):
(b)20:42:18 WARNING: No video signal present!
(a)20:42:18 WARNING: No video signal present
So I add 2 other lines to the code and this does the thing. The whole code is now:
#echo off
setlocal enabledelayedexpansion
set txtfile=D:\wfc\testlib\test.txt
set newfile=D:\wfc\testlib\new_test.txt
if exist "%newfile%" del /f /q "%newfile%"
for /f "tokens=*" %%a in (%txtfile%) do (
set newline=%%a
set newline=!newline:No video signal present!=!
set newline=!newline:No video signal present=!
set newline=!newline:WARNING:=Suspend recognition!
echo !newline! >> %newfile%
)
First crucial line cuts "!",
second line substitutes "No video signal present" with nothing (trims it),
third line substitutes the rest "Warning:" with desirable "Suspend recognition".
And at the end I have:
(b)20:42:18 WARNING: No video signal present!
(a)20:42:18 Suspend recognition
I feel this could be done elegantly. Besides I am not sure, if my way is not dangerous for some reason (data damage etc.). Please help.
Yes, this can be done easier.
First of all, it is not the line set newline=!newline:No video signal present!=! that removes the ! character, but it is the line set newline=%%a, because there is delayed variable expansion enabled when the for variable reference %%a is expanded, which happens before delayed expansion, hence an orphaned exclamation mark appears that is simply removed.
The key to success is to disable delayed expansion during expansion of %%a and to enable it afterwards. In the sub-string replacement syntax the : does not cause any problems; neither does the ! in the search string, given it is not the first character. So the following code should work:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "txtfile=D:\wfc\testlib\test.txt"
set "newfile=D:\wfc\testlib\new_test.txt"
> "%newfile%" (
for /F usebackq^ delims^=^ eol^= %%a in ("%txtfile%") do (
set "newline=%%a"
setlocal EnableDelayedExpansion
echo(!newline:WARNING: No video signal present!=Recognition suspended!
endlocal
)
)
endlocal
exit /B
I also changed the following items:
improved the syntax of set by using quotes to avoid trouble with some special characters;
avoided interim variable newline, so did the string manipulation directly in the echo line;
quoted file path/name to avoid problems with white-spaces, hence using usebackq option;
replaced the tokens=* by the delims= option in order to keep leading white-spaces; to avoid loss of lines that begin with ; due to the default eol option, I used the odd-looking unquoted option string syntax as this is the only way (!) to have no delims and eol defined at the same time (you cannot write "delims= eol=" as this would define " as the eol character; "eol= delims=" would define the SPACE, and "eol=delims=" the d);
redirected whole for loop, so no initial file deletion necessary, and the performance is a lot better, because the output file has to be opened and closed once only, not in every iteration;
echo Text > "file.ext" returns a trailing SPACE (actually the one in front of >); this is avoided by the above item; in general, this can be avoided when using the reversed redirection syntax > "file.ext" echo Text;
echo !VAR! returns ECHO is on|off. in case variable VAR is empty; to avoid this or other unexpected output, the odd-looking but safe syntax echo(!VAR! is used;
avoided immediate (%) expansion when delayed expansion is enabled to avoid loss of !;
To keep empty lines that appear in the original file, change the code to this:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "txtfile=D:\wfc\testlib\test.txt"
set "newfile=D:\wfc\testlib\new_test.txt"
> "%newfile%" (
for /F "delims=" %%a in ('findstr /N /R "^" "%txtfile%"') do (
set "newline=%%a"
setlocal EnableDelayedExpansion
set "newline=!newline:WARNING: No video signal present!=Recognition suspended!"
echo(!newline:*:=!
endlocal
)
)
endlocal
exit /B
The findstr command is used to prefix every line of the input file by a line number plus a colon (/N option). Then the sub-string replacement is done. Finally, everything up to and including the first :, hence the line number prefix becomes removed.
Substituting a phrase is pretty easy to do in a .bat file script.
powershell -NoLogo -NoProfile -Command ^
"Get-Content -Path '.\subfrase-in.txt' |" ^
"ForEach-Object { $_ -replace 'WARNING: No video signal present!', 'Recognition suspended' }"
I understand that this is a common problem but I am unable to decipher the answers and suggestion in other questions to make my specific script work.
#echo on
setlocal EnableDelayedExpansion
set directory=%1
set file_list=%2
for /f "tokens=*" %%i in (%file_list%) DO (
set newName=!%%i:~0,5!
move "%directory%\%%i" "%directory%\!%newName%!s.jpg" )
endlocal
This is giving me this output with echo on
set newName=!image.png:~0,5!
move "\\server\path\to\image.png" "\\server\path\to\!!s.jpg"
So clearly newName is null when I need it, so I can't even begin to troubleshoot the most likely syntax issues when trying to cut it to 5 characters.
THANKS!
set "newName=%%i"
set "newName=!Newname:~0,5!"
You can't substring a metavariable like %%i. You need to assign it to a standard environment variable, then substring using the method indicated (!var...! in delayedexpansion mode because the altered value of the variable is required within a code block)
The quotes simply delimit the strings involved to ensure that any invisible stray spaces on the line are not included.
So I'm currently writing a tutorial about security and for that reason I have to write a vbe file (encoded script written in VBScript) using a batch file.
So, I just have to write this to a file:
##~^mgAAAA==6 P3MDKDP"+k;:PH+XY~###&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###&2zEAAA==^#~#
(Note: There are some characters that cannot be printed above).
But the problem is that I never managed to write it successfully, I tried escaping all the characters using instructions from http://www.robvanderwoude.com/escapechars.php and it didn't work.
I tried using DelayedExpansion like this:
SET "foo=##~^mgAAAA==6 P3MDKDP"+k;:PH+XY~###^&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###^&2zEAAA==^#~# "
setlocal EnableDelayedExpansion
(
echo !foo!
) > test.vbe
And it did not work either, I have problems with characters that are not escaped.
Any ideas?? Thanks!!
The reason is obvious, that is a quotation mark after [...P3MDKDP]. Since you assign the variable "foo" to jumble characters with a open and a close quotation mark, like so SET "foo=...", batch think you stop assigning "foo" after [...P3MDKDP]. This leaves [+k;:PH+XY~.....] alone, without assigning to a variable or working with commands. Batch can't recognize it, and so the command prompt quit automatically.
What you can do is, assign the part after the quotation mark to another variable, I named it "foo2" in the following example:
#echo off
setlocal enabledelayedexpansion
SET "foo=##~^mgAAAA==6 P3MDKDP""
SET "foo2=+k;:PH+XY~###^&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###^&2zEAAA==^#~# "
echo !foo!!foo2!>test.vbe
pause >nul
And also, if you add another quotation mark before / after the quotation mark, like so [P3MDKDP ""], even though you did not assign the second part to a new variable, it still work, but it output an extra quotation mark in the string.
maybe this little trick helps you:
#echo off
for /f "delims=[]" %%n in ('find /n "REM DATA:" "%~dpnx0"') do set /a n=%%n
more +%n% "%~dpnx0">test.vbe
REM rest or your batchfile
goto :eof
REM DATA:
##~^mgAAAA==6 P3MDKDP"+k;:PH+XY~###^&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###^&2zEAAA==^#~#
(this trick avoids any character escaping or splitting the string. Can also be used to write a multiline text)
Related: Using batch echo with special characters
How do I deal with using escape characters on text that might or mightn't be a special character?
Suppose we have user input:
Set /p var=prompt:
Now, I need to make sure that the text gets interpreted as text even if the user enters something like a special character. But I cannot simply add ^ before the variable...because that'd cancel the variable. The ^^%var% and ^%%var% options don't seem to work either.
How do I go about doing this?
You should realize that the escapes are required in the source code of your program or when you expand a variable via %variable% or in the replaceable parameter of a for command. This is not required if you expand a variable via !delayed! expansion. So, your problem may be easily solved this way:
setlocal EnableDelayedExpansion
Set /p var=prompt:
echo !var!
The standard method to avoid the problem when you read a file that may have special characters is enclosing the value of the replaceable parameter in quotes when the value is asigned with Delayed Expansion disabled, and then Enable Delayed Expansion in order to manage the value. Of course, this method forces to insert an endlocal command inside the loop:
for /F "delims=" %%a in (anyFile.txt) do (
set "line=%%a"
setlocal EnableDelayedExpansion
echo Line with special characters: !line!
endlocal
)
Not possible. The shell processes the user input before your script does. Your script won't even know the user typed an escape character.
I am writing a CMD/batch file (running under Win-7 cmd.exe) and I seem to be getting hung up on delayed variable expansion.
I am using text file input that is in the form:
9 .CN=ISRJX.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 8-20-13
10 .CN=FXXLISHER.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 10-13-13
11 .CN=QXX004F.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 10-14-13
12 *.CN=QXX1001OB.OU=Linc.OU=thisco.O=UCOM.T=UCOM. 10-15-13
as contents in "inputfile.txt". Purpose is to extract the first word after ".CN=", at this point in the process.
Note that I can't strip based on number of chars before "CN=" because the number of chars will vary.
My script is:
setlocal enableextensions enableDelayedExpansion
REM #echo off
for /f "tokens=3 delims==." %%a in (inputfile.txt) do (
set "acct =%%a"
echo. %%a,%acct%
)
endlocal
I've tried every set of combination of quote, !, % etc. and both enabled & disabled delayed expansion, and still can't get this to work. For the most part, when I use ! I end up echoing the actual !. i.e. "echo !acct!" will echo the actual text "!acct!".
I have read many examples and answers, here and elsewhere, about delayed variable expansion. I just can't figure out how to work around it in this example, where I want "acct" to expand within the loop.
Suspect this is a simple punctuation problem, but I'm out of ideas. When I run this, I see acct set to the value for %%a, but when it echoes, clearly it doesn't expand to the new value -- instead it will echo whatever it was set to previously, or blank.
I have also tried disabledelayedexpansion, which makes no difference in my results (including when I use !acct! instead of %acct%.)
Remove the space after the acct variable name and use the ! marks.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /f "tokens=3 delims==." %%A in (inputfile.txt) do (
set "acct=%%A"
echo. %%A,!acct!
)
endlocal