Echo a long string with specified start column for new lines - windows

I am writing a small console application. This app should print a help when -h is passed. This is what I'm trying to get:
Program that prints help.
Usage:
prg [OPTIONS]
Options:
-h - print help.
-x - some very very very very very very very very very very very very very very
very very very very long parameter description.
-y - another very very very very very very very very very very very very very
very very very very long parameter description.
I'm using echo. -x - {long description here} to print parameter description. And of course when the string is too long it is printed like this:
-x - some very very very very very very very very very very very very
very very very very long parameter description.
So the question is how can I print a long string with something like left border specified?

#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
for /f %%I in ('powershell ^(Get-Host^).UI.RawUI.WindowSize.width') do set width=%%I
SET "spaces= "
:splp
IF "!spaces:~%width%!"=="" SET spaces=%spaces%%spaces%&GOTO splp
CALL :display 0 "Program that prints help."
CALL :display 0 "Usage:"
CALL :display 2 "prg [OPTIONS]"
CALL :display 0
CALL :display 0 "Options:"
CALL :display 2 "-x" 5 "- some very very very very very very very very very very very very very very very very very very long parameter description."
CALL :display 2 "-y" 5 "- another very very very very very very very very very very very very very very very very very long parameter description."
GOTO :EOF
:display
SET "remainder="
SET "line=!spaces:~0,%1!"
IF "%~2"=="" GOTO write
IF "%3"=="" SET "line=%line%%~2"&GOTO write
SET /a indent=%1+%3
SET "remainder=%~2%spaces%"
SET "line=%line%!remainder:~0,%3!
SET "remainder=%~4"
:remainlp
SET /a eol=width-indent-1
:seteollp
SET "eolchar=!remainder:~%eol%,1!"
IF NOT DEFINED eolchar SET "line=%line%%remainder%"&set "remainder="&goto write
IF NOT "%eolchar%"==" " set /a eol-=1&GOTO seteollp
SET "line=%line%!remainder:~0,%eol%!
SET /a eol+=1
set "remainder=!remainder:~%eol%!"
:write
ECHO(%line%
IF NOT DEFINED remainder GOTO :EOF
SET "line=!spaces:~0,%indent%!"
GOTO remainlp
I'll claim no originality for the magic powershell line - it came from Rojo's response here

Have you tried using tabs?
For example:
echo.^ -x - some very very very very very very very very very very
echo.^ ^ very very very very very very very
echo.^ ^ very long parameter description.
I've used the '^' character to show where the tabs are and had to split the message over a few lines to stop the word wrapping.
Here is the script I just tried (option x uses tabs and option y does not)
#echo OFF
echo. Usage:
echo. prg [OPTIONS]
echo.
echo. Options:
echo. -h - print help.
echo. -x - some very very very very very very very very very very
echo. very very very very very very very
echo. very long parameter description.
echo. -y - another very very very very very very very very very very very very very
echo. very very very very long parameter description.
And I get the following output (option x uses tabs and option y does not)
C:\Temp>usage.bat
Usage:
prg [OPTIONS]
Options:
-h - print help.
-x - some very very very very very very very very very very
very very very very very very very
very long parameter description.
-y - another very very very very very very very very very very very
very very
very very very very long parameter description.

echo off
setlocal enabledelayedexpansion
set "textx=some very very very very very very very very very very very very very very very very very very long parameter description."
REM find screen widht (subtract 8 for a leading tab and two for a right "border" :
for /f "tokens=2 delims=: " %%i in ('mode^|find "Columns"') do set /a colums=%%i-10
call :wrap "-x" "%textx%"
call :wrap "-y" "another quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite long parameter description."
exit /b
:wrap <option> <text>
set "text=%~2"
set /p ".=%~1"<nul
for /l %%i in (0,%colums%,1000) do echo. !text:~%%i,%colums%!|findstr ".."
goto :eof
NOTE: there is a between the echo. and !text....
You may have to replace Columns in find "Columns" with the correct string (it's language dependent)
I subtracted 10 from the screen width: 8 for the leading and two to end the line two chars before the windows ends.
1000 is the max length of the string. Adapt as needed. (the higher the value, the slower the output)
|findstr ".." supresses the output of empty lines
This has a few disadvantages:
there is no space in front of -x (set does not support it)
It will wrap at the end of the line even if it's in the middle of a word. Doing a "word wrap" could be done, but with a lot more code.
It has problems with special chars (for example &, !, |)

Related

Why I can't get output from this batch script? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
This program stops automatically! When I enter correct name and password (i.e it is not executing from 8th line). Please check if I made any mistakes.
#echo off
:s
set t=0
set /p a= enter your name
set /p b= enter your password
if %a%==andrew(set /a t=%t%+1)
if %b%==123 (set /a t=%t%+2 )
if %t%==3 ( echo welcome andrew
echo ---------------------------------------------------------------------------
:r
set /p c=want to change your settings -yes -no
if %c%==no (echo ok
goto e
)
if %c%==yes (set /p p= what you want to change -name(1) -password(2)
if %p%==1 (set /p a=enter name to change)
if %p%==2 (set /p a=enter password to change)
)
)
if %t%==2 echo %a% is not authorised
if %t%==1 echo enter correct password
if %t%==0 echo Both name and id are wrong enter again
goto r
goto s
:e
pause
Set #echo on and you may see something like this when the script is executed:
--> set t=0
--> set /p a= enter your name
enter your nameandrew
--> set /p b= enter your password
enter your password123
--> if andrew == andrew(set /a t=0+1)
--> if 123 == 123 (set /a t=0+2 )
(echo was unexpected at this time.
--> if ==no (echo ok
The script stopped with error:
(echo was unexpected at this time.
Line 7 showed OK:
if 123 == 123 (set /a t=0+2 )
though, next command output seen is:
if ==no (echo ok
which means %c% is undefined as you can see a value missing left of the == that caused the error.
This could be happening as to the opening parentheses in line 8:
if %t%==3 ( echo welcome andrew
The parser will see the opening parentheses and will keep reading the lines until it reaches a closing parentheses. It will substitute the percentage enclosed variables before execution. This makes %c% undefined as %c% is not defined before the opening parentheses on line 8 began the read.
So it seems the parser read:
if %t%==3 ( echo welcome andrew
echo ---------------------------------------------------------------------------
:r
set /p c=want to change your settings -yes -no
if %c%==no (echo ok
goto e
)
And does variable substitution of known values of t=3 and c=:
if 3==3 ( echo welcome andrew
echo ---------------------------------------------------------------------------
:r
set /p c=want to change your settings -yes -no
if ==no (echo ok
goto e
)
The error of if ==no (echo ok is shown.
Your question in the title:
why i cant get output from this batch script?
It is the opening parentheses in line 8. So one should ask thy self... What is that opening parentheses doing there in line 8 when it will include a label :r and a set /p that sets a variable c that can cause an undefined variable?
If the opening parentheses is meant to be there, view set /? that may show an example like:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
The example code and the details in set /? informs you how set variables work.
The label between the parentheses would still be a concern as it can cause error in a read block of code.
There is a couple of things. Do not set single character variables or label names. rather use set vara than using set a but making things recognisable like using variables that means something, makes things less confusing.
when matching variables with a value using == double quote each side. i.e if "%var" == "123"
For selection, use choice instead of set /p
Also, when using set /a the better way to add is set /a vart+=1 instead of set /a %vart%+1
This should do more or less what you want.
#echo off
:start
set vart=0
set /p "name=Enter your name: "
set /p "pass=Enter your password: "
if /i "%name%" == "andrew" set /a vart+=1
if "%pass%" == "123" set /a vart+=2
if %vart% equ 3 echo welcome andrew
echo ---------------------------------------------------------------------------
:settings
choice /c YN /m "Change your settings?"
if %errorlevel% equ 0 goto opt0
goto opt%errorlevel%
:opt0
echo You Pressed CTRL+C and selected "N"
exit /b 1
:opt2
pause
goto :eof
:opt1
choice /c UP /m "Change your User or Password?"
if %errorlevel% equ 0 goto opt0
goto chng%errorlevel%
:chng2
set /p "name=Enter name to change: "
goto :eof
:chng1
set /p "pass=Enter password to change: "
if %vart% equ 2 echo %a% is not authorised
if %vart% equ 1 echo enter correct password
if %vart% equ 0 echo Both name and id are wrong enter again
goto settings
goto start
Please read the syntax of the commands, such as if and set before posting.
Addressing some problems in your code:
First,
if %a%==andrew(set /a t=%t%+1)
is intercepted by cmd.exe as:
if %a% is andrew(set, run a command called /a t=%t%+1. Also, please double quote all set /p ...= in your script, even though it can't handle poison characters.
Second, you have unescaped closing parenthesis ) in your if, which can cause interception errors.

Windows Batch, passing arguments with spaces

I'm busy writing a Windows batch script and I'm having some problem with arguments.
My batch script is the following
#echo off
setlocal EnableDelayedExpansion
:: RETRIEVE ARGS WITH SPACES
set VAR01=%~1
set VAR02=%~2
set VAR03=%~3
:: CONFIRM IT WORKED
echo %VAR01%
echo %VAR02%
echo %VAR03%
endlocal
exit /b
And I am trying to pass it arguments that include spaces.
run_batch.bat "arg var 01" "arg var 02" "arg var 03"
But when it runs I am getting the output
arg var 01
ECHO is off
ECHO is off
Why is it only working correctly for the first argument and how can it be fixed?
NOTE
There was never anything wrong with the code, it seems there were invisible special characters that there causing issues. Must have been the text editor or something along those lines. How do I remove this question?
This should work:
#echo off
setlocal EnableDelayedExpansion
:: RETRIEVE ARGS WITH SPACES
set VAR01=%~1
set VAR02=%~2
set VAR03=%~3
:: CONFIRM IT WORKED
echo %VAR01%
echo %VAR02%
echo %VAR03%
endlocal
exit /b
The wrong part was:
set VAR01=%~1
set VAR01=%~2
set VAR01=%~3
but should be:
set VAR01=%~1
set VAR02=%~2
set VAR03=%~3
A small example script for your perusal.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM EXIT IF NO ARGS RECEIVED
IF "%~1"=="" EXIT
REM SET MINIMUM NUMBER OF ARGS
SET "i=1"
REM RETRIEVE ALL ARGS
:ARGS
SET "VAR0%i%=%~1"
SHIFT
IF NOT "%~1"=="" (
SET/A i+=1
GOTO :ARGS
)
REM INFORM NUMBER OF ARGS
CLS
ECHO=THERE WERE %i% ARGS
REM CONFIRM IT WORKED
ECHO=
SET VAR0
REM ALTERNATIVE CONFIRMATION
ECHO=
FOR /L %%A IN (1,1,%i%) DO IF DEFINED VAR0%%A ECHO=!VAR0%%A!
TIMEOUT -1
ENDLOCAL
EXIT
I think there was no problem aside from a typo. A quite tricky way to show the args and put them to numbered vars I learned from DosTips.org (but can't find the exact link) is:
:: Expand-Args.cmd
#Echo off
Set Args=,1=%~1,2=%~2,3=%~3,4=%~4,5=%~5,6=%~6,7=%~7,8=%~8,9=%~9
Set "Args=%*%Args:,="&Set "Arg%"
Set Arg
The batch will have this output run with:
Expand-Args one "2 2" three 4 five 6 "7 7" 8 9
Arg1=one
Arg2=2 2
Arg3=three
Arg4=4
Arg5=five
Arg6=6
Arg7=7 7
Arg8=8
Arg9=9
Args=one "2 2" three 4 five 6 "7 7" 8 9
Edit To not interfere with content in double quotes the delimiter comma might be replaced with an uncommon char like ALT+0127

batch replace spaces between quotes in file and then remove all quotes

I want create a batch to replace spaces with a + sign if the space is in between quotes. Then I want to remove the quotes from a text file. How can I accomplish this?
So I want to change a line like this:
2016-01-11 14:45:09 Server 127.0.0.1 GET /global/images/logo_small.jpg - 80 - 173.252.120.117 "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)" "-" www.vietnam.ttu.edu 200 200 200 1868 0
To this line.
2016-01-11 14:45:09 Server 127.0.0.1 GET /global/images/logo_small.jpg - 80 - 173.252.120.117 facebookexternalhit/1.1+(+http://www.facebook.com/externalhit_uatext.php) - www.vietnam.ttu.edu 200 200 200 1868 0
Thanks
You could use JREPL.BAT to arrive at a very concise and efficient solution. JREPL is a pure script based (JScript/batch) regular expression text processing utility that runs on any version of Windows from XP onward.
jrepl "\q| " "q=!q;''|q?'+':' '" /j /x /t "|" /jbegln "q=false" /f test.txt /o -
For this solution I use the /T option, which is very similar to the unix tr utility, or the sed y command.
I define two search terms, the first for a quote (The \X option enables the \q escape sequence), and the second for a space.
The /J option treats replacement strings as JScript. The first replacement string for the quote toggles a "q" variable TRUE or FALSE, and replaces the quote with an empty string. The second replacement string conditionally replaces the space with a plus or space, depending on the state of the "q" variable.
The /JBEGLN option initializes the "q" variable to FALSE at the beginning of each line.
The /F option specifies the input file, and the /O - option specifies that the output overwrites the original file.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q34732271.txt"
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO ECHO %%a&SET "line="&CALL :process %%a
GOTO :EOF
:process
SET "addition=%~1"
IF not DEFINED addition ECHO %line:~1%&GOTO :eof
IF "%~1"==%1 (
REM quoted
SET "line=%line% %addition: =+%"
) ELSE (
SET "line=%line% %addition%"
)
shift
GOTO process
You would need to change the setting of sourcedir to suit your circumstances.
I used a file named q34732271.txt containing your data for my testing.
The echo %%a shows your one line of data on the screen and the echo within the :process routine shows that line processed.
Batch is not an ideal language to process strings as it exhibits sensitivity to many symbols. This process should work provided you are happy to have space-strings compressed and the source string does not contain , ;,tab % or any other symbol that cmd treats specially.
Here is a pure batch-file solution that walks through the characters in each line in file line.txt, replaces all SPACEs in between a pair of quotation marks "" by + signs and stores the result in text_new.txt. The input string may contain any characters, even special ones:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Define global constants here:
set "INFILE=line.txt"
set "OUTFILE=line_new.txt"
set "SEARCH= "
set "REPLACE=+"
set "KEEPQUOTES="
set "QUOTE="""
set "QUOTE=%QUOTE:~,1%"
set "QFLAG="
> "%OUTFILE%" (
for /F usebackq^ delims^=^ eol^= %%L in ("%INFILE%") do (
set "LINE=%%L"
call :SUB LINE
)
)
endlocal
exit /B
:SUB
setlocal EnableDelayedExpansion
set "LINE=!%1!"
set "LINENEW="
set /A "POS=0"
:LOOP
set "CHAR=!LINE:~%POS%,1!"
set /A "POS+=1"
if not defined CHAR (
echo(!LINENEW!
endlocal
exit /B
)
if "!CHAR!"=="!QUOTE!" (
if defined QFLAG (
set "QFLAG="
) else (
set "QFLAG=Quoted"
)
if defined KEEPQUOTES (
set "LINENEW=!LINENEW!!CHAR!"
)
) else if defined QFLAG (
if "!CHAR!"=="!SEARCH!" (
set "LINENEW=!LINENEW!!REPLACE!"
) else (
set "LINENEW=!LINENEW!!CHAR!"
)
) else (
set "LINENEW=!LINENEW!!CHAR!"
)
goto :LOOP

Cannot use !random! in batch file

#echo off
setlocal enableDelayedExpansion
set /p startIndex="Start index: "
set /p endIndex="End index: "
for /l %%a in (%startIndex% 1 %endIndex%) do (
set /a seed = !random!
echo %seed%
)
set /p endfile="Wait for it...."
I expect that this script will print out some random numbers. But it does not work. It just printed out some lines with same content: "Echo is off ."
How can I fix this code ?
You need to say
echo !seed!
because the whole loop is parsed at the beginning, and seed is expanded (to nothing, as it doesn't exist yet) before the loop starts running. You therefore need to use delayed expansion.
Delayed expansion is also required on referencing the value of variable seed defined and assigned with a random value within a block defined with ( ... ).
#echo off
setlocal EnableDelayedExpansion
set /p "startIndex=Start index: "
set /p "endIndex=End index: "
for /l %%a in (%startIndex% 1 %endIndex%) do (
set "seed=!random!"
echo !seed!
)
set /p "endfile=Wait for it ..."
Further option /a is not necessary to assign a random number to variable seed as no arithmetic expression to evaluate. But be careful with spaces around equal sign. All spaces are ignored by set on using option /a, but are not anymore ignored by command set on a simple assignment without option /a.
And also take care about where first double quote is written on line with command set as this makes a big difference.
For details about spaces around equal sign and first double quote position see answer on
Why is no string output with 'echo %var%' after using 'set var = text' on command line?

how to get batch file parameters from Nth position on?

Further to How to Pass Command Line Parameters in batch file how does one get the rest of the parameters with specifying them exactly? I don't want to use SHIFT because I don't know how many parameters there might be and would like to avoid counting them, if I can.
For example, given this batch file:
#echo off
set par1=%1
set par2=%2
set par3=%3
set therest=%???
echo the script is %0
echo Parameter 1 is %par1%
echo Parameter 2 is %par2%
echo Parameter 3 is %par3%
echo and the rest are %therest%
Running mybatch opt1 opt2 opt3 opt4 opt5 ...opt20 would yield:
the script is mybatch
Parameter 1 is opt1
Parameter 2 is opt2
Parameter 3 is opt3
and the rest are opt4 opt5 ...opt20
I know %* gives all the parameters, but I don't wan't the first three (for example).
Here's how you can do it without using SHIFT:
#echo off
for /f "tokens=1-3*" %%a in ("%*") do (
set par1=%%a
set par2=%%b
set par3=%%c
set therest=%%d
)
echo the script is %0
echo Parameter 1 is %par1%
echo Parameter 2 is %par2%
echo Parameter 3 is %par3%
echo and the rest are %therest%
#echo off
setlocal enabledelayedexpansion
set therest=;;;;;%*
set therest=!therest:;;;;;%1 %2 %3 =!
echo the script is %0
echo Parameter 1 is %1
echo Parameter 2 is %2
echo Parameter 3 is %3
echo and the rest are: %therest%
This works with quoted arguments and with arguments that have equal signs or commas, as long as first three arguments didn't have these special delimiter chars.
Sample output:
test_args.bat "1 1 1" 2 3 --a=b "x y z"
Parameter 1 is "1 1 1"
Parameter 2 is 2
Parameter 3 is 3
and the rest are: --a=b "x y z"
This works by replacing %1 %2 %3 in original command line %*. The first five semicolons are there just to make sure that only the first occurrence of these %1 %2 %3 is replaced.
The following code uses shift, but it avoids to parse the command line using for and lets the command line interpreter do this job (regard that for does not parse double-quotes properly, for instance argument set A B" "C is interpreted as 3 arguments A, B", "C by for, but as 2 arguments A, B" "C by the interpreter; this behaviour prevents quoted path arguments like "C:\Program Files\" from being handled correctly):
#echo off
set "par1=%1" & shift /1
set "par2=%1" & shift /1
set "par3=%1" & shift /1
set therest=
set delim=
:REPEAT
if "%1"=="" goto :UNTIL
set "therest=%therest%%delim%%1"
set "delim= "
shift /1
goto :REPEAT
:UNTIL
echo the script is "%0"
echo Parameter 1 is "%par1%"
echo Parameter 2 is "%par2%"
echo Parameter 3 is "%par3%"
echo and the rest are "%therest%"
rem.the additional double-quotes in the above echoes^
are intended to visualise potential whitespaces
The remaining arguments in %therest% might not look like the way they were originally concerning the delimiters (remember the command line interpreter also treats TABs, ,, ;, = as delimiters as well as all combinations), because all delimiters are replaced by a single space here. However, when passing %therest% to some other command or batch file, it will be parsed correctly.
The only limitation I encountered so far applies to arguments containing the caret character ^. Other limitations (related to <, >, |, &, ") apply to the command line interpreter itself.
#ECHO OFF
SET REST=
::# Guess you want 3rd and on.
CALL :SUBPUSH 3 %*
::# ':~1' here is merely to drop leading space.
ECHO REST=%REST:~1%
GOTO :EOF
:SUBPUSH
SET /A LAST=%1-1
SHIFT
::# Let's throw the first two away.
FOR /L %%z in (1,1,%LAST%) do (
SHIFT
)
:aloop
SET PAR=%~1
IF "x%PAR%" == "x" (
GOTO :EOF
)
ECHO PAR=%PAR%
SET REST=%REST% "%PAR%"
SHIFT
GOTO aloop
GOTO :EOF
I like to use subroutines instead of EnableDelayedExpansion. Above is extract from my dir/file pattern processing batch. Don't say this cannot handle arguments with =, but at least can do quoted path with spaces, and wildcards.
One more alternative :-)
some of the other answers did not deal with quotes, or with extra spaces between parameters. Disclaimer: I have not tested this extremely well.
This is all just a workaround for %* not being updated after a shift ugg!
I implemented it as a POP starting from the technique by #Pavel-P.
It relies on the batch parser to remove extra spaces
It depends on a global variable 'Params'
~ removes quotes -- leaving it up to you to re-quote as desired. Remove the ~ if you don't want that.
:PopFirstParam
#set varname=%~1
#set %varname%=%~2
#rem #echo %varname% = '!%varname%!'
#if '%3' equ '' set Params=&goto :eof
#call :popone %Params%
#goto :eof
:popone
#set Params=;;;%*
#set Params=!Params:;;;%1 =!
#goto :eof
Usage:
#setlocal enabledelayedexpansion
#set Params=%*
#call :PopFirstParam P1 %Params%
#call :PopFirstParam P2 %Params%
#call :PopFirstParam P3 %Params%
#call :PopFirstParam P4 %Params%
#rem etc . . .
Specifically I use it to optionally run commands asynchronously:
#setlocal enabledelayedexpansion
#rem set variables that decide what to be run . . . .
#call :RunIfDefined doNum1 "Title 1" mycmd1.exe some "params" here
#call :RunIfDefined doNum2 "MyTitle 2" mylongcmd2.exe some "params" here
#call :RunIfDefined doNum3 "Long Title 3" mycmd3.exe some "params" here
#goto :eof
:RunIfDefined
#set Params=%*
#call :PopFirstParam DefineCheck %Params%
#if not defined %DefineCheck% goto :eof
#call :PopFirstParam WindowTitle %Params%
#call :PopFirstParam Command %Params%
#rem %Params% should now be the remaining params (I don't care about spaces here)
#echo start "%WindowTitle%" "%Command%" %Params%
#start "%WindowTitle%" "%Command%" %Params%
#goto :eof
C:\test> set doNum1=yes
C:\test> set doNum3=yes
C:\test> call dothings.bat
start "Title 1" "mycmd1.exe" some "params" here
start "Long Title 3" "mycmd3.exe" some "params" here
too bad DOS doesn't have a #include

Resources