Display parameters in reverse order - batch-file

I need to create a batch file which will display on the screen up to 9 parameters, but displays them in reverse order. Name of the batch file is reverse11.bat
eg: C:\>REVERSE11.bat a b c d e <enter>
e d c b a REVERSE
I tried to do so like this, it kinda mess and didnt work. :(
SORT/R < %O > ANSWER
ECHO ANSWER
Whats wrong with it?

SORT sorts lines, not words, so you'll need to put each parameter on its own line.
setlocal enabledelayedexpansion
echo %1> unsorted.txt
echo %2>> unsorted.txt
echo %3>> unsorted.txt
:: etc...
sort /r unsorted.txt > sorted.txt
At this point you could display sorted.txt if you're okay with them all being on separate lines:
type sorted.txt
But if you want to get them all back onto a single line you'll have to process the file like this:
for /f %%a in (sorted.txt) do (
set out=!out! %%a
)
echo %out%

You must note that sort command Works on LINES, not WORDS! The Batch file below first divide the parameters in separate lines and store they in a temp file; the second part invoke sort /R on the file and collect its output lines in just one string:
#echo off
setlocal EnableDelayedExpansion
(for %%a in (%*) do echo %%a) > temp.txt
set output=
for /F "delims=" %%a in ('sort /R ^< temp.txt') do set output=!output! %%a
echo %output:~1%
del temp.txt

#ECHO OFF
SETLOCAL
SET reversed=%0
:loop
SET newparam=%1
IF NOT defined newparam ECHO %reversed%&GOTO :eof
SET reversed=%1 %reversed%
shift
GOTO loop
We start by setting the variable reversed to the value of the program name.
Set newparam to the value of the FIRST parameter (%1)
If that parameter exists,add it to the FRONT of the accumulated string, then SHIFT to move the parameters to one position lower (%2 becomes %1, %3 becomes %2, etc.) and loop back until...
the %1 parameter does NOT exist (because they've all been SHIFTed out) so echo the accumulated string in reversed and finish the routine.

Related

Combine lines in text file using batch

I want to make a program that takes the content of the second line of a text file and puts it on the first. (It doesn't matter if the second doesn't get edited)
for /f "tokens=1" %%t in (file.txt) do set string1=%%t
for /f "tokens=2" %%t in (file.txt) do set string2=%%t
echo %string1%%string2%>file.txt
I have two issues hat I can't seem to be able to fix.
One: the loops only include the first word of each line in the variables.
Two: Echo doesn't replace the first line of the file with the variables given and instead writes ECHO command deactivated (I have the French version of Windows 10 and simply translated what got written in the file, the text in English Windows version might be slightly different, but you get the idea)
If you have any suggestions, I would appreciate if you explain what the code you provide does (I always like to learn)
Your question is not clear and can be understood in several different ways. Anyway, this management is simpler with no for command:
#echo off
setlocal EnableDelayedExpansion
< file.txt (
rem Takes the content of the first line
set /P "line1="
rem Takes the content of the second line and puts it on the first
set /P "line2="
echo !line1!!line2!
rem It doesn't matter if the second line doesn't get edited
echo !line2!
rem Copy the rest of lines
findstr "^"
) > output.txt
move /Y output.txt file.txt
The FOR command uses a space as a delimiter by default. So you have to tell it to not use any delimiters with the DELIMS option. Also, you should be able to do this with a single FOR /F command. Just hold the previous line in a variable.
#ECHO OFF
setlocal enabledelayedexpansion
set "line1="
(for /f "delims=" %%G in (file.txt) do (
IF NOT DEFINED line1 (
set "line1=%%G"
) else (
echo !line1!%%G
set "line1="
)
)
REM If there are an odd amount of lines, line1 will still be defined.
IF DEFINED line1 echo !line1!
)>File2.txt
EDIT: I think I completely misunderstood your question. Once you clarify your question I will repost a code solution if needed.
Use skip to omit the first line and write the 2nd line twice. In general an edit of a file implies a rewrite to a new file and possibly a rename to retain the old file name.
:: Q:\Test\2018\07\25\SO_51508268.cmd
#Echo off
Set "flag="
( for /f "usebackq skip=1 delims=" %%A in ("file1.txt") Do (
If not defined flag (
Echo=%%A
Set flag=true
)
Echo=%%A
)
) >file2.txt
Del file1.txt
Ren file2.txt file1.txt
After running the batch a file1.txt with initially numbered lines 1..5 looks like this:
> type file1.txt
2
2
3
4
5

Avoid a null value in last for loop iteration?

I have a text file with one string per line (only a couple lines total). This file needs to be searched and each string stored. The script will ultimately prompt the user to choose one of the stored strings (if more than one string/line is present in the file), but the for loop is iterating an extra time when I don't want it to.
I'm using a counter to check the number of iterations, and I also read that it's probably a NL CR regex issue, but the finstr /v /r /c:"^$" in the for file-set like in this post Batch file for loop appears to be running one extra time/iteration doesn't work for me (but I probably don't understand it correctly).
The "pref" term is because the strings are to be eventually used as a prefix of files in the same folder...
#echo off
setlocal enabledelayedexpansion
set /a x=1
for /f %%a in (sys.txt) do (
set pref!x!=%%a && echo %^%pref!X!%^% && set /a x+=1
)
echo last value of x = !x!
for /L %%a in (1,1,!x!) do (
echo !pref%%a!
)
REM The rest would be to prompt user to choose one (if multiple) and
REM then use choice as a prefix with a ren %%a %prefX%%%a
If the "sys.txt" contains three lines with strings A, B, C respectively, then the output I currently get is:
pref1
pref2
pref3
last value of x = 4
A
B
C
ECHO is off.
ECHO is off. is not desired, clearly.
You just need to change your increment structure like this. (set it before each line starting from a base of 0)
#Echo Off
Setlocal EnableDelayedExpansion
Set "i=0"
For /F "UseBackQ Delims=" %%A In ("sys.txt") Do (
Set/A "i+=1"
Set "pref!i!=%%A"
Echo(pref!i!)
Echo(last value of i = %i%
For /L %%A in (1,1,%i%) Do Echo(!pref%%A!

Writing to different lines with Batch

I'm trying to make a reminder system within batch in which there are different lines of reminders. My batch program will write to different lines in a .txt file, but it isn't working. Could you please help and try to find the issues?
#echo off
echo Enter slot # for reminder
set /p n=
cls
echo Please type in the assignment name
set /p a=
echo ----------------------------------
echo Please type in the class
set /p c=
echo ----------------------------------
echo Please type in the date due
set /p d=
cls
if %n%==1 goto l1
if %n%==2 goto l2
if %n%==3 goto l3
if %n%==4 goto l4
if %n%==5 goto l5
if %n%==6 goto l6
:l1
echo Reminder for %c% Homework! %a%,%d% > Reminder.txt
:end
:l2
echo Reminder for %c% Homework! %a%,%d% >> Reminder.txt
:end
:l3
echo Reminder for %c% Homework! %a%,%d% >>> Reminder.txt
:end
:l4
echo Reminder for %c% Homework! %a%,%d% >>>> Reminder.txt
:end
:l5
echo Reminder for %c% Homework! %a%,%d% >>>>> Reminder.txt
:end
:l6
echo Reminder for %c% Homework! %a%,%d% >>>>>> Reminder.txt
:end
Hints to fix what you've got:
The > character won't let you write to specific lines, and there's no native support in Windows batch to do such a thing.
There are two operators that use the > character: >, which redirects output to a file (replacing any existing content), and >>, which appends (adds to the end of) a file.
You've got multiple instances of :end, but that's invalid. :end is a label, which is a unique reference to that point in the code. When you add more than one, some get ignored and you get undefined behaviors, which is bad.
It looks like you're trying to use :end to exit. Use goto :EOF for that. It jumps to the built-in label :EOF, short for End Of File.
You need to handle the case where n is none of the predefined values. Currently if someone entered 7 for n, your program would get to the logic after :l1 and run it, which is wrong. Put a goto :EOF there just in case.
How to approach solving this type of issue with batch:
The only way I can think of off the top of my head to modify a specific line is to iterate through all lines using a for /f loop, rewriting each line (to a temporary file) until you encounter the one you want to change, then write your new content instead of the existing content. Then when you're done iterating, you can replace the original file with that temporary file.
You would have to do this each time you wanted to change a new line. Batch is a really simple language that doesn't have useful constructs like arrays, or the many external tools that a shell scripting language like Bash would have. It's also got some really unsophisticated runtime evaluation.
Here's a partial solution that you can combine with a few lines from your code above to achieve what you want. It prompts you for a line number, then puts the content of the newContent variable (replace with your implementation) into the file at the specified line:
REM suppresses the echo of the commands in the program
#ECHO OFF
REM sets a feature that overcomes some of the weak runtime evaluation limitations that batch has
setlocal ENABLEDELAYEDEXPANSION
REM The name of your file
set fname=file.txt
REM If our file doesn't already exist, make a new one with 6 empty lines since that's all we want for now.
if EXIST "%fname%" goto alreadyExists
for /l %%b in (1,1,6) do echo.>>"%fname%"
:alreadyExists
REM The name of a temp file
set tfile=f2.txt
REM A counter to track the line number
set counter=0
REM Input to get the line number you wish to replace
set /p replacementLine=Type the line number that should be replaced:
REM The content that goes on the replaced line
set newContent=New entry
REM Read the file, iterate through all lines.
for /f "tokens=*" %%a in (file.txt) do (
REM Add one to the counter
set /a counter=!counter!+1
REM Use the redirect '>' operator for the first line
if "!counter!"=="1" (
if "!counter!"=="%replacementLine%" (
REM We're on the line we wish to replace, so use the replacement line content
echo.%newContent% >f2.txt
) else (
REM We're NOT on the line we wish to replace, so use the original line content
echo.%%a >f2.txt
)
) else (
REM else for lines other than the first, use the append redirect '>>'
if "!counter!"=="%replacementLine%" (
REM We're on the line we wish to replace, so use the replacement line content
echo.%newContent% >>f2.txt
) else (
REM We're NOT on the line we wish to replace, so use the original line content
echo.%%a >>f2.txt
)
)
)
REM Delete the original file
del "%fname%"
REM Replace it with the modified copy
ren "%tfile%" "%fname%"
You can replace a few lines at the top get the functionality you want.
you can't write to a specific line in a file with batch. Instead you have to rewrite the complete file.
Steps: a) read the file. b) change the desired line. c) (over)write the file with the new data.
#echo off
setlocal enabledelayedexpansion
if not exist reminder.txt call :InitFile :: create the file, if it doesn't exist
set /p "n=Enter slot # for reminder: "
set /p "a=type in the assignment name: "
set /p "c=type in the class: "
set /p "d=type in the date due: "
cls
call :ReadFile
set "_Line[%n%]=Reminder for %c% Homework: %a%,%d%"
call :WriteFile
type Reminder.txt
goto :eof
:ReadFile
set x=0
for /f "delims=" %%i in (reminder.txt) do (
set /a x+=1
set "_Line[!x!]=%%i"
)
goto :eof
:WriteFile
set x=0
(for /f "tokens=2 delims==" %%i in ('set _Line[') do echo %%i)>Reminder.txt
goto :eof
:InitFile
(for /l %%i in (1,1,6) do echo Reminder %%i [empty])>Reminder.txt
goto :eof
(Note: this would make trouble with more than 9 lines because of the alphabetical sorting with set _line[, but as you need only 6 lines, this should not be a problem for you)
Note: your input shouldn't contain !

How to split string with "=" without for loop in batch file

I would like split a string in two part with = as delimiter.
I saw this post but I do not manage ta adapt.
I try this:
set "str=4567=abcde"
echo %str%
set "var1=%str:^=="^&REM #%
echo var1=%var1%
Why it does not work?
While not a bulletproof solution (use the for, artoon), without more info, this can do the work
#echo off
setlocal enableextensions enabledelayedexpansion
set "str=4567=abcde"
rem Step 1 - remove the left part
set "str1=!str:%str%!"
rem Step 2 - Get the right part
set "right=!str:*%str1%!"
rem Step 3 - Get the left part
set "left=!str:%right%=!"
set "left=%left:~0,-1%"
echo [%left%] [%right%]
edited to adapt to comments (OP code in comments adapted to my code, or the reverse)
for /f "delims=" %%i in ('set') do (
setlocal enabledelayedexpansion
rem Step 1 - remove the left part
set "str=%%i"
for %%x in ("!str!") do set "str1=!str:%%~x!"
rem Step 2 - Get the right part
for %%x in ("!str1!") do set "right=!str:*%%~x!"
rem Step 3 - Get the left part
for %%x in ("!right!") do set "left=!str:%%~x=!"
set "left=!left:~0,-1!"
echo [!left!] [!right!]
endlocal
)
And no, as previously indicated this is not bulletproof and some of the variables show problems (had I said it is not bulletproof?).
What i don't understand is the requirement to not use a for loop and then use a for loop. It is a lot easier this way
for /f "tokens=1,* delims==" %%a in ('set') do (
echo [%%a] [%%b]
)
Another alternative (not as easy as the for, more stable than the previous one, non bulletproof) is
for /f %%a in ('set') do (
call :split left right %%a
echo [!left!] [!right!]
)
goto :eof
:split leftVar rightVar data
set "%~1=%~3"
setlocal enabledelayedexpansion
set "data=%*"
set "data=!data:*%1 %2 %3=!"
set "data=%data:~1%"
endlocal & set "%~2=%data%"
goto :eof
As npocmaka commented above, = has special meaning and cannot be replaced with traditional variable string manipulation. If you know the length of either side of the equal sign, you could strip off a number of characters. For example, if "4567" will always be 4 characters, you could set "var1=%str:~0,4%". Or if "abcde" will always be 5 characters, you could set "var1=%str:~0,-6%" (5 chars + 1 for the equal sign).
Otherwise, a for loop is your only other option without using 3rd party utilities.
for /f "delims==" %%I in ("%str%") do set "var1=%%I"
If you've got grep installed, you can do something like:
echo %str% | grep -P -o "^[^=]*"
... but you'd still need to capture its output with another for /f loop.
If you are allergic to for loops, and as an exercise in providing a solution to your question without any regard for efficiency, here's how you get the first half of your string without using a single for loop. Put grep and its dependencies in your %PATH%. Then:
echo %str% | grep -P -o "^[^=]*" >temp.txt
set /P "var1="<temp.txt
del temp.txt
echo %var1%
There, I fixed it!

Trying to reformat a very large csv with a batch file

I have an application that exports data in the format:
1a,1b,1c1,1c2,1c3, ... (up to 1c100),1d1,1d2,1d3, ... (up to 1d100)
2a,2b,2c1,2c2,2c3, ... (up to 2c100),2d1,2d2,2d3, ... (up to 2d100)
etc.
and I am trying to reformat this into
1a,1b,1c1,1d1
1a,1b,1c2,1d2
.
.
1a,1b,1c100,1d100
2a,2b,2c1,2d1
2a,2b,2c2,2d2
etc.
I figured that if this can be done a row at a time I can just loop through the file. However I can't find a way of doing a single row with either tokens, a list, or even as a string function. There is too much data to process in a single operation (each value is about 12 chars). Tokens limit at (roughly) 64/202, a list at about 107/202 and a string at about 1000/2300
Does anyone know how this can be written into a new file?
I was trying things like:
#echo off
setlocal enableDelayedExpansion
set dimCnt=0
<example.csv (
set /p "dimList=" >nul
for %%D in (!dimList!) do (
set /a dimCnt+=1
set "dim[!dimCnt!]=%%D"
)
)
echo
for /l %%I in (3 1 102) do echo !dim[1]!,!dim[2]!,!dim[%%I]!
</code>
..besides the fact that I have missed out the last variable in the line (need to add 100 to it), I can't get more than about 80-110 values out of the list (I guess it depends on value string length)
#echo off
setlocal enableextensions enabledelayedexpansion
(for /f "tokens=1,2,* delims=," %%a in (example.csv) do (
set "data=%%c"
set "i=0"
for %%f in ("!data:,=" "!") do (
set /a "i+=1"
set "d[!i!]=%%~f"
)
set /a "end=!i!/2"
set /a "j=!end!+1"
for /l %%i in (1 1 !end!) do (
for %%j in (!j!) do echo %%a,%%b,!d[%%i]!,!d[%%j]!
set /a "j+=1"
)
)) > output.csv
endlocal
This iterates over the file, getting the first two tokens in the line (%%a and %%b), the rest of the line (%%c) is splitted and each value stored in an environment variable array (kind of). Then, the array is iterated from the start and from the middle, reading the needed values to append to %%a and %%b and generating output file.
#ECHO OFF
SETLOCAL
(
FOR /f "tokens=1,2,*delims=," %%a IN (u:\long.csv) DO (
SET rpta=%%a
SET rptb=%%b
CALL :rptcd %%c
)
)>newfile.txt
GOTO :EOF
:rptcd
SET /a lines=100
SET lined=%*
FOR /l %%x IN (1,1,99) DO CALL SET lined=%%lined:*,=%%
:loop
IF %lines%==0 GOTO :EOF
SET /a lines-=1
CALL SET lined=%lined:*,=%
FOR /f "delims=," %%x IN ("%lined%") DO ECHO %rpta%,%rptb%,%1,%%x&shift&GOTO loop
GOTO :eof
This should get you going - just need to change the input filename and output filename...
Your code does not work because SET /P cannot read more than 1023 bytes. At that point it returns the data read so far, and the next SET /P picks up where it left off. Adapting your code to compensate will be very difficult. You would be better off using FOR /F as in MC ND's answer. But beware, batch has a hard limit of 8191 characters per line in pretty much all contexts.
Better yet, you could use another scripting language like JScript, VBS, or PowerShell. Performance will be much better, and the code much more robust and far less arcane. I love working with batch, but it simply is not a good text processing language.

Resources