When i run this snippet it blows up when i type the '|' character
SET /P _test=Enter your value:
echo %_test%
Enter your value: Hello|World
'World' is not recognized as an internal or external command,
operable program or batch file.
I would like %_test% to contain the text Hello|World as supplied, any ideas?
The only way to safely echo an arbitrary string is to use delayed expansion:
set /P _test="Enter your value: "
setlocal EnableDelayedExpansion
echo(!_test!
endlocal
The odd-looking syntax echo( allows even empty strings, and also the strings on, off or /?, which would otherwise be interpreted as keywords or options by the echo command.
Regard that changes to the variable _test after setlocal are gong to be lost after endlocal.
The following can be used to safely assign and input / output any string.
Note, Delayed expansion is Disabled at the time of string assignment in order to preserve ! expansion symbols in strings.
#Echo off
(Set LF=^
%= Above Empty lines Required =%)
Set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
::: - Macro to toggle delayed expansion and output / perform actions using var value
::: %#Output% <VarName>
Set #Output=For %%n in (1 2) Do if "%%n" == "2" (%\n%
For /F "Tokens=1 Delims=, " %%G in ("!Param!") Do (%\n%
Echo !%%~G! ^&^& Endlocal%\n%
)%\n%
) Else Setlocal EnableDelayedExpansion ^& Set Param=,
:Example
CLS
Call :input "Name" "Enter your Name:"
::: - To safely access any entered string Delayed Expansion should be enabled, especially with regards to redirection characters
::: - Conditional checks should be performed using doublequoting Ie: If "!VarName!" == "value" Command
::: - Must be enabled AFTER variable is defined in order to correctly set expansion characters
%#Output% Name
Pause
Goto :Example
:input <VarName> <Prompt>
::: - preserve input of expansion characters
Setlocal DisableDelayedExpansion
Set /P "Var=%~2"
::: - Ensure a value is assigned
If "%Var%" == "" Goto :input
Endlocal & Set "%~1=%Var%"
Exit /b
Simply enclose the string with quotes (") when you enter it
Do you mean something like this:
echo "Put in your name"
read -a words
var="${words[#]}"
echo $var
Related
So I know how to do simple string replacing with the SET command.
Example:
set /p a=
set a=%a:<=^<%
echo %a%
(This example will change the prompted variable to the same thing but with the < character to be ^< to be be properly echoed if needed to)
I want to do the same thing but with the % character but I can't get it to work.
#ECHO OFF
SETLOCAL
SET "something=%%he%%llo%%"
ECHO %something%
ECHO ============
SETLOCAL ENABLEDELAYEDEXPANSION
SET "anotherthing=!something:%%=x!"
endlocal&SET "anotherthing=%anotherthing%"
ECHO %something%
ECHO %anotherthing%
GOTO :EOF
Like this, you mean?
There was nothing wrong with your method, short of using doublequotes to protect from poison characters.
The main issue now, is in trying to see the content, because you'd be Echoing poison characters. To see it actually in place I included two methods, a Set command to list all variables beginning with a, (with a findstr thrown in to isolate only the one named a), and by using delayed expansion, which prevents the variable from being expanded when the command line is read/parsed. At the very bottom I included a method of showing it with the Echo command without using delayed expansion or doublequotes, as you can see to escape a caret, ^, you'll need in this case to use two more carets
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "a="
Set /P "a="
Set "a=%a:<=^<%"
Echo(%a%
Pause
(Set a) 2> NUL | %SystemRoot%\System32\findstr.exe /B /L /I "a="
Pause
SetLocal EnableDelayedExpansion
Echo(!a!
EndLocal
Pause
Echo(%a:^<=^^^<%
Pause
The full (known) rules for variable replacement can be found at SO:
Percent Expansion Rules
In short:
The search expression in a percent replacement expression can not start with a percent.
The search expression in a delayed replacement expression can not start with a exclamation mark.
But you can replace percent signs with delayed expression
setlocal EnableDelayedExpansion
set /p var=
set "var=!var:%%=REPLACE!"
echo(!var!
The percent sign itself has to be doubled here, because it collapse to a single percent in phase2 (percent expansion)
I need help to split a file name or folder name into two parts.
All files in the subfolders use always the same naming convention:
mainfolder/artist - title/artist - title.ext
I started with creating a batch file which writes .nfo files (recursive) for video files. Everything is working fine, but I just can't find a way to echoing the part1 (artist) before delimiter - and after it to have part2 (title).
Here is what I have now:
#echo off
TITLE NFO creator Musicvideos
COLOR B
ECHO -------------------------------------------------------------------------
ECHO Create NFO for Musicvideos, all existing will be overwritten
ECHO Push key to Start
ECHO -------------------------------------------------------------------------
ECHO.
PAUSE > NUL
REM Ende Header
for /r %%a in (*.mkv *.avi *.mp4) do (
(
echo ^<?xml version="1.0" encoding="UTF-8" standalone="yes"?^>
echo ^<musicvideo^>
for %%b in ("%%~na") do echo ^ ^<title^>%%~b^</title^>
echo ^ ^<^!-- start test set attributes --^>
for %%b in ("%%~na") do echo ^ ^<path^>%~dp0^</path^>
for %%b in ("%%~na") do echo ^ ^<name^>%%~na^</name^>
for %%b in ("%%~na") do echo ^ ^<filenameandpath^>%%~a^</filenameandpath^>
for %%b in ("%%~na") do echo ^ ^<basepath^>%%~a^</basepath^>
for %%b in ("%%~na") do echo ^ ^<before^>^</before^>
REM for "tokens=1,2 delims= - " %%b in ("%%~na") do (
REM echo ^ ^<before^>^%%a</before^>
REM echo ^ ^<after^>^%%b</after^>
REM )
REM test end
echo ^ ^<rating^>^0.000000^</rating^>
echo ^ ^<userrating^>^8^</userrating^>
echo ^ ^<epbookmark^>^0.000000^</epbookmark^>
echo ^ ^<year^>^</year^>
echo ^ ^<track^>^-1^</track^>
echo ^ ^<album^>^</album^>
echo ^ ^<artist^>^</artist^>
echo ^ ^<genre^>^</genre^>
echo ^ ^<outline^>^</outline^>
echo ^ ^<plot^>^</plot^>
echo ^ ^<tagline^>^</tagline^>
echo ^ ^<thumb^>^</thumb^>
echo ^ ^<status^>^</status^>
echo ^ ^<studio^>^</studio^>
echo ^ ^<art^>
echo ^ ^<fanart^>%~dp0^%%~na^-fanart.jpg^</fanart^>
echo ^ ^<poster^>%~dp0^%%~na^-poster.jpg^</poster^>
echo ^ ^<artistthumb^>%~dp0^%%~na^-artistthumb.jpg^</artistthumb^>
echo ^ ^<banner^>%~dp0^%%~na^-banner.jpg^</banner^>
echo ^ ^<clearlogo^>%~dp0^%%~na^-clearlogo.png^</clearlogo^>
echo ^ ^<discart^>%~dp0^%%~na^-discart.png^</discart^>
echo ^ ^<landscape^>%~dp0^%%~na^-landscape.jpg^</landscape^>
echo ^ ^</art^>
echo ^</musicvideo^>
)>"%%~dpna.nfo"
)
ECHO.
ECHO Creatin is done
ECHO Push key to exit
ECHO.
PAUSE > NUL
This is the way I would do it:
#echo off
setlocal
for /R %%A in (*.mkv *.avi *.mp4) do call :SplitFileName "%%~nA"
goto :EOF
:SplitFileName
set "FileName=%~1"
set "Title=%FileName: - =" & set "Artist=%"
echo Artist=%Artist%
echo Title=%Title%
exit /B
Further details at Split string with string as delimiter. Pay attention to the comment at such an answer...
I cannot find a reason for all the loops for %%b in ("%%~na") do you are using, particularly because you are often not even using %%b in their bodies.
Anyway, splitting file names at a certain sub-string can be easily done by first replacing the sub-string by a single character that is forbidden in file names (like :, for example) and then using that character as a delimiter in a for /F loop:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Loop through all matching files recursively:
for /R %%a in (*.mkv *.avi *.mp4) do (
rem // Store file name in variable:
set "NAME=%%~na"
rem /* Toggle delayed expansion to be able to write a variable
rem in the same block of code without losing `!`: */
setlocal EnableDelayedExpansion
rem // Remove everything before first occurrence of and including ` - `:
echo Title: !NAME:* - =!
rem // Replace ` - ` by `:` and use the latter as delimiter:
for /F "tokens=1 delims=: eol=:" %%b in ("!NAME: - =:!") do (
endlocal
echo Artist: %%b
)
)
endlocal
exit /B
The above code assumes that both the artist and the title portions are not empty. The title part may include the sub-string - on its own.
This is an example on how to split a file name on first hyphen character.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /R %%A in (*.mkv *.avi *.mp4) do (
for /F "eol=| tokens=1* delims=-" %%B in ("%%~nA") do (
set "Artist=%%B"
set "Title=%%C"
setlocal EnableDelayedExpansion
if "!Artist:~-1!" == " " set "Artist=!Artist:~0,-1!"
if defined Title if "!Title:~0,1!" == " " set "Title=!Title:~1!"
echo Artist=!Artist!
if defined Title (echo Title=!Title!) else echo %%~na has no title.
endlocal
)
)
endlocal
The outer FOR loop searches recursive in current directory and all subdirectories for non-hidden files with file extension mkv, avi or mp4 and assigns the full qualified file name (file path + file name + file extension) of a found file to loop variable A.
The inner FOR loop interprets just the file name without path and file extension as string to process.
The end of line character is redefined from default ; which can be at beginning of a file name to | which no file name can ever contain to avoid that a file name starting by chance with a semicolon is ignored by the inner FOR loop.
By default FOR with option /F processing a double quoted string splits up the string into substrings using normal space and horizontal tab as string delimiters and assigns just first space/tab separated string to the specified loop variable. This string splitting behavior is modified with tokens=1* delims=- to split the file name string on hyphens instead of spaces/tabs with ignoring hyphens at beginning of file name and assigning the string up to first hyphen inside the file name to specified loop variable B and everything after first (sequence of) hyphen(s) to next loop variable according to ASCII table without any further string splitting which is loop variable C.
In other words on a file name like Artist - title the string Artist with a space at end is assigned to loop variable B and the string title with a space at beginning is assigned to loop variable C. The two unwanted spaces need to be removed. This can be done using string substitution. But this must be done using delayed expansion not yet enabled because of file names containing an exclamation mark should be also processed correct. For that reason delayed environment variable expansion is next enabled inside the inner FOR loop.
The first IF condition checks if last character of artist string is really a space character and runs the string substitution to remove this space at end of artist string if this condition is true.
The second IF condition first checks if an environment variable Title is defined at all because of the commands of inner FOR loop are also executed on file name does not contain a hyphen at all in which case %%C expands to an empty string and so environment variable Title is deleted from list of environment variables.
The nested third IF condition checks if first character of value of defined environment variable Title is a space and runs the string substitution to redefine Title without first character if this condition is true.
Now artist and title strings can be output before delayed expansion is disabled again by restoring previous environment. Please read this answer for details about the commands SETLOCAL and ENDLOCAL and what exactly happens on each execution of SETLOCAL and ENDLOCAL because of there is much more done than just enabling and disabling delayed environment variable expansion on each found file name.
Note: The title part can contain also the character sequence space, hyphen, space as well as one or more hyphens, but of course the artist string should not contain a hyphen as this character is interpreted as separator between artist and title independent on spaces existing around the hyphen or not. Windows command processor is not designed for enhanced string manipulations like other script interpreters. A different code would be necessary if the condition for splitting file name in artist and title must be space, hyphen, space and not just hyphen.
Other batch file code using - to split file name into artist and title strings:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /R %%A in (*.mkv *.avi *.mp4) do call :ProcessFileName "%%~nA"
endlocal
goto :EOF
:ProcessFileName
set "FileName=%~1"
set "Title=%FileName:* - =%"
setlocal EnableDelayedExpansion
set "Artist=!FileName: - %Title%=!"
echo Artist=!Artist!
echo Title=!Title!
endlocal
goto :EOF
But this solution using just string substitutions in a subroutine does not work on title string containing an equal sign used as delimiter on string substitution. It is also much slower in comparison to above batch file solution on processing a large amount of file names.
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.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
See also Where does GOTO :EOF return to?
Assuming your title always have only a single - splitting artist from title:
#echo off
setlocal enabledelayedexpansion
for /r %%a in (*.mkv *.avi *.mp4) do (
set "fname=%%~na"
set "fname=!fname: - =-!"
for /f "tokens=1,2 delims=-" %%i in ("!fname!") do (
echo The artist is %%i
echo The title is %%j
echo The extension is %%~xa
)
)
We replace the space-space with a - and then delimit on it.
I would like to write a batch file to count the number of symbols "(" and ")" in file.txt
For example, I have file with this string:
"(((
( ((
( ("
Answer would be 8.
But I have code and my answer is 3 :
#echo off
set "f=file.txt"
set "sk9=("
set "sk0=)"
set /a "k9=0"
set /a "k0=0"
for /f "usebackq delims=" %%a in ("%f%") do for %%i in (%%a) do (
if "%%i"=="%sk9%" (
set /a k9=k9+1
) )
echo %k9%
Windows command processor cmd.exe executing batch files line by line is definitely not the right application for this task. It is designed to execute commands and applications. However, here is a batch file counting parentheses/round brackets in a text file.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "OpenBracketCount=0"
set "CloseBracketCount=0"
for /F "usebackq tokens=* eol=" %%I in ("File.txt") do (
set "TextLine=%%I"
call :ProcessLine
)
echo Number of opening brackets: %OpenBracketCount%
echo Number of closing brackets: %CloseBracketCount%
endlocal
goto :EOF
:ProcessLine
set "TextLine=%TextLine:"=%"
if not defined TextLine goto :EOF
set "TempLine=%TextLine:(=%"
if not defined TempLine goto BracketCount
set "TempLine=%TempLine:)=%"
if "%TempLine%" == "%TextLine%" goto :EOF
:BracketCount
if "%TextLine:~0,1%" == "(" ( set /A "OpenBracketCount+=1" ) else if "%TextLine:~0,1%" == ")" set /A CloseBracketCount+=1
set "TextLine=%TextLine:~1%"
if defined TextLine goto BracketCount
goto :EOF
Command FOR with using option /F to process lines from a text file ignores by default empty lines and lines starting with a semicolon which is the default for end of line option eol, and splits up the line into substrings (tokens) on spaces tabs. It is okay to ignore empty lines, but lines with a ; at beginning should not be ignored for counting parentheses.
Both unwanted line processing behaviors could be disabled with using usebackq^ delims^=^ eol^= to get the entire line assigned to the environment variable TextLine by specifying no delimiters and no end of line character. It is not possible to enclose this option string in double quotes in this special case which requires to escape the spaces and equal signs interpreted by default as argument string separators by caret character ^ to interpret them as literal characters.
But better is to use double quoted option string "usebackq tokens=* eol=" which defines also no end of line character, but keeps space and horizontal tab as delimiters. So assigned to loop variable I is the line after removing leading spaces/tabs. That is good here as it avoids processing lines containing only spaces/tabs and can reduce also the number of characters to process in the subroutine.
In the subroutine ProcessLine first all double quotes are removed from the line read from text file as a " could later in remaining command lines result in a syntax error on execution. It is of course possible that a line contains only one or more " resulting in environment variable TextLine is not defined anymore after removing all double quotes in which case the line definitely does not contain parentheses to count.
Next is assigned to environment variable TempLine the line read from text file (without leading spaces/tabs and without all double quotes) with all ( and ) removed from the line. The line does not contain a parenthesis if TempLine is equal TextLine and so the subroutine can be exited immediately to reduce the execution time of the batch file.
Otherwise the current line contains at least one round bracket and so it is necessary to compare each character of the remaining line with ( and ) to count them .
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.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
See also Where does GOTO :EOF return to?
This solution is based on an efficient subroutine that you may use to count any character you wish:
#echo off
setlocal
call :CountChar "(" test.txt LeftParen=
echo Number of left parens = %LeftParen%
goto :EOF
:CountChar char file result=
setlocal DisableDelayedExpansion
rem Create a copy of input file with the desired characters removed
rem Process all file lines in %%a FOR replaceable parameter
(for /F "usebackq delims=" %%a in ("%~2") do (
set "line=%%a"
rem Enable status to expand line variable
setlocal EnableDelayedExpansion
rem Output the variable without the desired character
echo(!line:%~1=!
endlocal
rem Store previous smaller output in another file
)) > smallerFile.txt
rem Get the size difference between both files
for %%a in ("%~2") do for %%b in (smallerFile.txt) do (
rem Store the result in third subroutine parameter
endlocal & set /A "%~3=%%~Za-%%~Zb"
)
del smallerFile.txt
exit /b
I have a version number 17.06.01.01 and I would like to know how many entries there are split by a period.
My last piece of code was;
setlocal ENABLEDELAYEDEXPANSION
for /F "tokens=1-10 delims=." %%a in ("17.09.01.04.03") do (
set /a "iCount+=1"
echo %%a, !iCount!
)
endlocal
I've tried a host of 'For' commands but seem to be getting further away each time.
replace every dot with a space (as a delimiter). Then count number of tokens:
#echo off
set "string=17.09.01.04.03"
set count=0
for %%a in (%string:.= %) do set /a count+=1
echo %count%
(May give false results, if there are other spaces, commas or tabs in the string, but should work nice for your example of version strings containing only numbers and dots)
#treintje:
echo off
set "string=1>7.0 9.0&1.04.0!3"
set count=0
:again
set "oldstring=%string%"
set "string=%string:*.=%"
set /a count+=1
if not "%string%" == "%oldstring%" goto :again
echo %count%
For the sample string you provided, Stephan's approach is perfectly sufficient.
However, if the string contains token separators (white-spaces, ,, ;, =), quotation marks ("), wild-cards (*, ?) or other special characters (^, &, (, ), >, <, |) it might probably fail. You could of course replace most of such characters by others in advance, and use delayed expansion rather than immediate one, but this still does not resolve all issues (for instance, you cannot replace all = or * characters due to the sub-string replacement syntax).
The following approach however can deal with every arbitrary combination of all such characters. Basically it replaces every period (.) with a new-line (line-feed) character and lets find /C /V "" count the total number of lines.
#echo off
set "string=17.09.01.04.03"
setlocal EnableDelayedExpansion
if defined string (set ^"strtmp=!string:.=^
%= empty string =%
!^") else set "strtmp="
for /F %%C in ('cmd /V /C echo(^^!strtmp^^!^| find /C /V ""') do set /A "count=%%C-1"
echo "!string!" contains %count% periods.
endlocal
The periods become replaced by line-feeds in advance, using delayed expansion in order not to fail if any special characters occur. The for /F loop executes the command line cmd /V /C echo(^^!string^^!| find /C /V "" and captures its output in a variable, reduced by one as find /C /V "" actually returns the number of lines, which are separated by one less line-feeds (hence periods), originally. The double-escaped exclamation marks ^^! are needed in order to ensure that the variable strtmp is actually expanded within the explicitly invoked inner-most cmd instance, because otherwise, the contained multi-line string is not correctly transported into the pipe (|).
A different approach comparing strLen before and after replacing the dots with nothing.
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
set "string=17.09.01.04.03"
set "str2=%string:.=%"
call :strlen string ret
call :strlen str2 ret2
Set /A "Dots=ret-ret2"
echo Number of dots in %string% is %Dots%
goto :Eof
:strLen string len
:$source http://www.dostips.com/?t=Function.strLen
(SETLOCAL ENABLEDELAYEDEXPANSION
set "str=A!%~1!"
set "len=0"
for /L %%A in (12,-1,0) do (set /a "len|=1<<%%A"
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A")
)
ENDLOCAL&IF "%~2" NEQ "" SET /a %~2=%len%
EXIT /b
Test with strings from comments:
Pass: set "string=Tim says:"There is a cat and a dog""
Fail: set "string=1>7.0% 9.0&1.04.%0!3"
Pass: set "string=Tim says:"There is a cat. And a dog""
At work I need to create a .bat script that reads the first 2 chars on the first line, of multiple files, and then echo them to the screen.
So far I have this:
#echo off
setlocal enabledelayedexpansion
for /r I:\Test\Filer %%F in (*.*) do (
set first2 =<%%F
set first2=%first2:~0,2%
echo %first2%
)
But all it writes is: ECHO is off
Anyone got an idea what I have done wrong?
You are missing /p! This should work:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /r I:\Test\Filer %%F in (*.*) do (
SET /p first2=<%%F
SET first2=!content:~0,2!
ECHO !first2!
)
PAUSE
#echo off
setlocal enabledelayedexpansion
for /r I:\Test\Filer %%F in (*.*) do (
set /p first2=<%%F
set "first2=!first2:~0,2!"
echo(!first2!
)
In delayedexpansion mode, %var% is the value of var at parse-time and !var! is the run-time value.
The ( in the echo will ensure that a newline is shown if var is not set (or "set" to a zero-length string)
The quotes in a set statement ensure that trailing spaces are not included in the value assigned to the variable.
Note that spaces are not allowed after the variablename in a string "set" statement; a variable with a trailing space in its name will be set, and this is not the same as the variable with no space in its name.
set /p is required to assign a value from a file. Be carefu of zero-length files and files containing a newline (CR LF) at the start!
Delayed Expansion will cause variables to be expanded at execution time rather than at parse time, this option is turned on with the SETLOCAL command. When delayed expansion is in effect variables may be referenced using !variable_name! (in addition to the normal %variable_name% )
#echo off
setlocal enableextensions enabledelayedexpansion
for /r I:\Test\Filer %%F in (*.*) do (
set /p first2=<"%%~F"
set "first2=!first2:~0,2!"
echo !first2!
)
Here in set /p first2=<"%%~F" use double quoted "%%~F" (for a case if a path or file name contains e.g. space) and ~ modifier (to avoid doubling of double quotes)
Resources:
EnableDelayedExpansion
Command Line arguments (Parameters) and their ~ modifier(s)