I'm currently using set /P input = to get the users input for a batch script.
My problem now is that it is very likely for them to use the & character.
Is there any way to get user input containing these special characters?
Sure. Use setlocal enabledelayedexpansion, then refer to your input variable as !input! whenever needed. Enclosing your "var=prompt: " string in quotations is always good practice, too.
#echo off
setlocal enabledelayedexpansion
set /P "data=Enter a string to test: "
echo(!data!
If you run that and enter a string with an ampersand, it gets echoed without being evaluated.
(Note about using echo(!data! rather than echo !data!: That's just a way to prevent a user entry of /? from breaking things.)
Related
I am trying to make 4 URLs by taking input from user and changing last few prefix:
#echo off
type bot.txt
set /p id="Enter the ip adress:"
And link will look like this:
https://live.star.com/hls/live/2004211/2020/hin/15mindvr1440006161q6nli0p96c19january2020
and complete URL should look like :
https://live.star.com/hls/live/2004211/2020/hin/15mindvr1440006161q6nli0p96c19january2020/master_1.m3u8|user-agent=KAIOS/2.0
where master_1 will be master_2, master_3 and master_4
I want this to happen and save in a text file!
But, echo command is also not giving me a result!
set /p id="Enter the ip adress:"
echo Link 1 : %id%^
master_1.m3u8|user-agent=KAIOS/2.0
This keeps crashing the CMD
Is there any way to automate this?
Thanks!
So the user does not input an IP address as your prompt text set in advance, but an URL which can contain %, &, ?, | and other characters with a special meaning for the Windows command processor cmd.exe processing a batch file.
I suggest to use the following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
:UserPrompt
set "InputURL="
set /P "InputURL=Enter the URL: "
if not defined InputURL goto UserPrompt
set "InputURL=%InputURL:"=%"
if not defined InputURL goto UserPrompt
setlocal EnableDelayedExpansion
if not "!InputURL:~-1!" == "/" set "InputURL=!InputURL!/"
set "InputURL=!InputURL!master_1.m3u8|user-agent=KAIOS/2.0"
echo Link 1 : !InputURL!
endlocal
endlocal
echo/
pause
The environment variable InputURL is undefined explicitly before prompting the user.
The user can press just key RETURN or ENTER without entering anything at all. In this case the environment variable InputURL is still not defined as explicitly undefined before prompting the user. It would keep its current value if it would be defined before prompting the user.
The first IF condition just checks if the user typed a string at all and prompts the user again if nothing was input by the user.
Next all " are removed from the string input by the user. This action could result in execution of set "InputURL=" if the user entered just one or more " and nothing else and therefore the environment variable InputURL would be once again not defined anymore. For that reason the user is prompted once more on having just input one or more " intentionally or by mistake.
The double quote left to variable name InputURL and the double quote at end are very important here. It makes sure that the first (and only) argument string of command set is enclosed in double quotes. For that reason the Windows command processor interprets the characters like & and | inside the double quoted argument string as literal characters and not as operators.
Then delayed environment variable expansion is enabled which pushes the current states of command extensions (enabled) and delayed expansion (disabled) as well as the current directory and all environment variables on stack before enabling delayed expansion. All further commands are executed with a new set of environment variables until endlocal is executed by cmd.exe.
The usage of delayed expansion makes it possible to safely process the user input string further as the user input string does not anymore modify the command lines of the batch file before execution as it is done on using %InputURL% without modifying the file content of the batch file.
It is checked next in this temporary new environment if the user input string does not end with a / in which case the slash is appended to user input string.
Next is appended the fixed string containing the redirection operator | which is interpreted here as literal character because of being inside a double quoted argument string. So there is no need to escape the operator with ^ as it would be necessary in case of argument string of command set is not enclosed in double quotes exactly as done here with " left to variable name. A " between equal sign and string value assigned to the variable would not work here. For the reason see for example
How to set environment variables with spaces?
The command echo is used to output the modified URL with additional text using delayed expansion to be able to output the string without the usage of " because of echo would output also the double quotes.
See also: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
The previous environment is restored with endlocal which does not only result in disabling delayed expansion as it was before setlocal EnableDelayedExpansion, but in restoring also state of command extensions (not modified), the current directory (not modified) and the previous list of environment variables with their values. This means the environment variable InputURL has again the string value without the one or two modifications made between setlocal EnableDelayedExpansion and endlocal.
The last endlocal just explicitly restores the environment before starting this batch file.
Last the user is prompted for pressing any key to see the output and leave batch file processing because of reaching end of the batch file.
See also:
Microsoft article about Using command redirection operators
Single line with multiple commands using Windows batch file
I tried Googling this but I didn't quite find a clear solution so I'm asking you.
I'm not sure how to put this but, let's say we have a TEXTFile.txt that contains a line of a string with random letters and numbers (like AJS12U3254FU8AD). Now, what I want to do is ask the user to enter a number with set /p number=, and then echo the digit/character from the string that this number represents. For instance (on the previous string example) if the user inputs number 3, I want the batch script to echo the letter S, if he inputs 4 to echo 1 and so on.
In my little experience, i think this problem comes down to echoing certain digits from a variable, like echo %var:~0,1% but instead of using integer numbers to specify the digits, I want to insert the user's number in that process. Something like echo %var:~0,%number%%.
Is that possible? Or is there any other way I could do this?
Variables (with the exception of command line arguments) in batch is wrapped around with %, so you can't write %var:~0,%number%% because the variable ends at the first % and results in syntax error
You need to use delayed expansion
setlocal EnableDelayedExpansion
for /f "delims=" %%x in (TEXTFile.txt) do set var=%%x
set /p number="Input number: "
echo !var:~%number%,1!
Assuming you had the line of text you want as a variable line, you could do something like
#echo off
set number=3
set line=AJS12U3254FU8AD
for /l %%i in (%number%,1,%number%) do (
call set character=%%line:~%%i,1%%
)
echo %character%
A different method to force delayed expansion (which doesn't conflict with exclamation marks in the variable) is using a pseudo call.
The call forces another parsing of the line.
The first pass resolves the %number% while the doubled %% are reduced to a single one.
The second pass resolves the now single %.
#Echo off
Set "var=AJS12U3254FU8AD"
set /p "number=Enter a number:"
Call echo %%var:~%number%,1%%
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 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