set /p is not working as advertised - batch-file

I've got a problem with redirecting input from a file into set/p
Here is my script:
#echo off
echo Reading: {%1}
type %1
echo(
echo Starting...
set VAR=
set /P VAR=<%1
echo VAR is {%VAR%}...
I've read elsewhere (https://stackoverflow.com/a/7827243) that the syntax I am using will work. It does not!
Here is my output:
Reading: {Fruit.txt}
Pears
Apples
Oranges
Kiwi
Grapes
Kumquat
Starting...
VAR is { ■P}...
So - What gives?

Your file is in Unicode (UTF16) format, and SET /P does not work with Unicode. The TYPE command does work with Unicode, and it converts the output to ANSI. You can redirect the output of TYPE to a new text file, and then your SET /P will work.
#echo off
type %1>new.txt
set VAR=
set /P VAR=<new.txt
echo VAR is {%VAR%}...
EDIT
To get the second line instead of the first:
#echo off
type %1>new.txt
<new.txt (
set /P VAR=
set VAR=
set /P VAR=
)
echo VAR is {%VAR%}...

If the purpose is to read lines of text from a file, why do you need the set /p command?
for /f "delims=" %%a in ('type "file name.txt"') do echo %%a
If you type the command at the cmd.exe command line, you would write %a (one % symbol) rather than %%a (i.e., double the % symbol when using in a shell script [batch file]).
Bill

Here's another technique for reading only the first line of a file using for /f and type that doesn't rely on set /p:
#echo off
setlocal enableextensions enabledelayedexpansion
set VAR=
set CURRLINE=0
for /f "delims=" %%a in ('type "test.txt"') do (
set /a CURRLINE+=1
if !CURRLINE! EQU 1 (
set VAR=%%a
goto :DONE
)
)
:DONE
echo %VAR%
endlocal
The advantage here is there's no need to write out a separate file.
However, note that either of these approaches (set /p or for /f with type) will have problems with special shell characters in the input file (<, >, |, etc.). To get around this problem, one could use a small utility I wrote a while back called shellesc.exe (http://www.westmesatech.com/sst.html) to "escape" the special characters. But if you use these tools, then you can also use linex.exe to pick the line you want and get the result with a little bit less code:
#echo off
setlocal enableextensions
set VAR=
for /f "delims=" %%a in ('type "test.txt" ^| linex -l 1 ^| shellesc') do set VAR=%%a
echo %VAR%
endlocal
Note that this approach has the additional advantage of not "choking" on special shell characters (shellesc).
HTH,
Bill

Related

How to fix the reference to another variable in batch?

So, I'm trying to create a custom format image viewer in batch file witch reads from this file named image.ansii2 :
lines=18
line1=[15]---------------
line2=[15]----[160]------[15]-----
line3=[15]---[160]----------[15]--
line4=[15]---[94]---[223]---[0]-[223]-[15]----
line5=[15]--[94]-[223]-[94]-[223]----[0]-[223]---[15]--
line6=[15]--[94]-[223]-[94]--[223]----[0]-[223]---[15]-
line7=[15]--[94]--[223]-----[0]----[15]--
line8=[15]----[223]--------[15]---
line9=[15]---[160]--[26]-[160]--[26]-[160]-[15]-----
line10=[15]--[160]---[26]-[160]--[26]-[160]---[15]---
line11=[15]-[160]----[26]----[160]----[15]--
line12=[15]-[223]--[160]-[26]-[220]-[26]--[220]-[26]-[160]-[223]--[15]--
line13=[15]-[223]---[26]------[223]---[15]--
line14=[15]-[223]--[26]--------[223]--[15]--
line15=[15]---[26]---[15]--[26]---[15]----
line16=[15]--[94]---[15]----[94]---[15]---
line17=[15]-[94]----[15]----[94]----[15]--
line18=[15]---------------
and the program (ansii2.bat) looks like this :
#echo off
for %%a in (%1) do (set ext=%%~xa)
if "%1" == "" (echo No file was specified&pause&exit /b)
if not "%ext%" == ".ansii2" (echo The file specified didn't have the expected extension [%ext%] -^> [.ansii2]&pause&exit /b)
title Ansii2 %1
:0
echo [0m
cls
for /f "delims== tokens=1,2" %%G in (%1) do set "%%G=%%H"
set loop=0
:loop
set /a loop+=1
set line=line%loop%
set "image=echo %!line!:[=[48;5;%"
set "image=%image:]=m%"
set "image=%image:(=[38;5;%"
set "image=%image:)=m%"
set "image=%image:-= %"
%image%
echo %line%
if %loop% == %lines% (goto exitloop)
goto :loop
:exitloop
timeout /t -1 >nul
goto 0
I think that the bug comes from the line 14 but I don't know what to do to fix it...
Could someone help me?
Your attempt at substitution is off, and the method by which you read the file can be improved upon:
All your variables are formatted in the source file, so a for loop can be used to read and assign them by splitting the string at the = delimiter
Delayed expansion is used to perform the substitution, using:
!varname:search=replace! ; where varname is referenced using the meavariable of the for loops first token.
#Echo off & CD "%~dp0"
cls
Setlocal EnableExtensions EnableDelayedExpansion
For /F %%e in ('Echo prompt $E^|cmd')Do Set "\E=%%e"
(For /f "usebackq Tokens=1,2 delims==" %%G in ("%~$Path:1")Do (
Set "%%G=%%H"
Set "%%G=!%%G:[=%\E%[48;5;!"
Set "%%G=!%%G:(=%\E%[38;5;!"
Set "%%G=!%%G:]=m!"
Set "%%G=!%%G:)=m!"
Set "%%G=!%%G:-= !"
If not "%%G" == "lines" <nul set /p "=!%%G!%\E%[0m%\E%[E"
)) > Con
Endlocal
Other notes:
In the event the supplied file cannot be found the following will be output:
The system cannot find the file .
<nul set /p "=!variable!" ; allows safe output of poison characters
%\E%[E ; Equivalent to Outputting a linefeed.
Data structure for your source file:
using single characters for substitution prevents them from being used as characters in the ascii art. use paired characters or unique strings to prevent this. Ie :
\i=%\E%[48;5;
\e=%\E%[38;5;
\m=m

Find current console size in batch

I am working on a batch file and i need to print hyphens ( - ) across the screen as a separator. Is there a fast (under two seconds) command that can do this?
I have done multiple search queries and could not find the answer on various websites.
(code to find screen size)
for /l %%a in (1,1,%screen size var%) do (set "line=%line%-")
echo %line%
The output should show a line of hyphens across the console.
This is untested, but based upon the output from Mode CON as used in gjpio's answer:
#Echo Off
For /F "Skip=4Tokens=1*Delims=:" %%A In ('Mode CON')Do (For /L %%C In (1,1,%%B)Do #Set/P "=-"<Nul)&Echo( &GoTo :Draw
:Draw
Pause
If you intend to use the separator multiple times within your script you could save it to a variable:
#Echo Off
For /F "Skip=4Tokens=1*Delims=:" %%A In ('Mode CON')Do (For /L %%C In (1,1,%%B)Do (Call Set "separator=%%separator%%-"))&GoTo :Next
:Next
Echo Welcome to %~nx0
Echo %separator%
Pause
As a final afterthought, and just in case you feel that it would perform quicker, I thought I'd better provide a version using delayed expansion too:
#Echo Off&SetLocal EnableDelayedExpansion&Set "separator="
For /F "Skip=4Tokens=1*Delims=:" %%A In ('Mode CON')Do (For /L %%C In (1,1,%%B)Do Set "separator=!separator!-")&GoTo :Next
:Next
Rem Uncomment the next line if you don't want to use delayed expansion in the rest of the script
::EndLocal&Set "separator=%separator%"
Rem Your code goes here
Echo Welcome to %~nx0
Echo %separator%
Pause
As an addition to all of the above, you could also leverage powershell to do this too:
#Echo Off
For /F %%A In ('Powershell -NoP "Write-Host('-' * $(Get-Host).UI.RawUI.WindowSize.Width)"')Do Set "separator=%%A"
Echo Welcome to %~nx0
Echo %separator%
Pause
If you can work out the batch commands retrieve the columns value you could use the output from the MODE command;
MODE CON
C:\Users\gjp>mode con
Status for device CON:
----------------------
Lines: 9001
Columns: 120
Keyboard rate: 31
Keyboard delay: 1
Code page: 850
I would suggest you to use the following code:
#echo off
setlocal EnableDelayedExpansion
for /F "skip=4 tokens=2" %%A IN ('mode CON') do (
for /L %%B IN (1 1 %%A) do set "hyphen=!hyphen!-"
echo !hyphen!
goto :subroutine
)
:subroutine
echo You may continue here
pause
which is a bit complicated, but should do what you want.
The code searches for the columns in mode CON's command output and adds to hyphen variable these accordingly.

Batch .txt reader

So, basically I want a Batch file to read a .txt. The problem is that the Batch file needs to update everytime a new line gets written to the .txt
#echo off
set "pc=%1"
FOR /F "delims=:" %%A IN ('findstr /N .* "%pc%"') DO set "zeilen=%%A"
type %pc%
set /A zeilen1=%zeilen%
:loop
if not %zeilen% == %zeilen1% (
set "line="
set zeilen2=%zeilen% - 1
for /f %%a in ('more/e +%zeilen2% ^< %pc%') do (
if not defined line set "line=%%a"
)
echo %line%
set /A zeilen+=1
)
FOR /F "delims=:" %%A IN ('findstr /N .* "%pc%"') DO set "zeilen1=%%A
goto loop
I also can't use the type command (line 9-13) because I don't want to refresh the whole .txt only the last line.
sry for my poor english
Thanks
To start the Batch you need to do something like this call batch.cmd txtname.txt
A basic tail command can be written like so. Credit to #dbenham for his initial solution on DosTips.com
#echo off
call :Loop <"tailme.txt"
exit
:Loop
set "line="
set /p "line="
if defined line (
echo %line%
) else (
pathping -q 1 -p 300 localhost >nul
)
goto :loop
If you don't wish to use third party options and wish to keep it pure batch, it is very possible. From your question, it sounds like you wish to read the last line of a text file and have it update that text each time the text file is edited. Further more, this batch file much be call'ed to when it needs to be used.
To do this, we can compare the date it was last modified using forfiles in an for loop. The reason for this is that if we use the file properties EX: ECHO Last-Modified Date : %%~ta we will not get the properties down to seconds. Thus the file will only compare down to the minutes.
Now that we can grab the last modified properties we can use an IF statement to look for when the file get a new time stamp. From there we can use a modified script that reads only the last line of a text file (Configurable by set /a LINES=LINES+1 LINES+1 - Infin) made by #Patrick Cuff
To call this batch file you will want to use call ReadFile.bat txtname.txt
Call - Command
ReadFile.bat - Name of batch script
txtname.txt - Name of textfile to read
Bellow is the full script.
ReadFile.bat
#ECHO OFF
#GOTO READ
:LOOP
Rem | Look for changes
FOR /f %%a in ('forfiles /M %1 /C "cmd /c echo #fdate-#ftime"') DO (set FileTimeCurrent=%%a)
IF "%FileTimeLoad%"=="%FileTimeCurrent%" (goto LOOP) else (goto READ)
:READ
cls
Rem | Get current date
FOR /f %%a in ('forfiles /M %1 /C "cmd /c echo #fdate-#ftime"') DO (set FileTimeLoad=%%a)
Rem | Get the number of lines in the file
set LINES=0
for /f "delims==" %%I in (%1) do (
set /a LINES=LINES+1
)
Rem | Print the last line
set /a LINES=LINES-1
more +%LINES% < %1
goto LOOP
For help on any of the commands do the following:
call /?
set /?
for /?
if /?
So on.

Batch File: Assign random line of text file as variable for later use

I'm trying to write a very simple batch file for personal use...It's complete except for one thing I'm stumped on. Hopefully this is an easy fix (I'm effectively illiterate when it comes to code).
Basically what I'm trying to do is have the script choose a random line from a text file, do this a couple times with a couple different text files, then I wish to assign the output from each text file to a variable so that I can easily use them in various combinations...then repeat the process.
Here is what I have right now...
#ECHO OFF
:START
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A list1random=(%RANDOM% %% %NumLines%)
IF "%list1random%"=="0" (SET "list1random=") ELSE (SET "list1random=skip=%list1random%")
FOR /F "usebackq tokens=* %list1random% delims=" %%A IN (`TYPE %list1%`) DO (
>> output.txt ECHO %%A
)
:Finish
ENDLOCAL
GOTO START`
This procures the random line, and spits it to a text file. All is well, next step, take that random result and assign it to a variable...
#ECHO OFF
:START
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A list1random=(%RANDOM% %% %NumLines%)
IF "%list1random%"=="0" (SET "list1random=") ELSE (SET "list1random=skip=%list1random%")
FOR /F "usebackq tokens=* %list1random% delims=" %%A IN (`TYPE %list1%`) DO (
SET output1=%%A
)
>> output.txt ECHO %output1%
:Finish
ENDLOCAL
GOTO START
Now the output ceases to be random...instead it is always the last line of the referenced text file.
EDIT: The site suggested another question that was similar to mine. However, that person was having trouble getting the script to choose a valid line. I get a valid line every time, and a random one too (when I check it via echo), but a non-random line when proceeding on, assigning the output to a variable. I don't understand because it seems like a post-facto derandomization. I.E. the difference between the two scripts has nothing to do with procuring the random result, only what to do with that result AFTER it has it, right?
I appreciate any help in advance, this is the last step before I know everything I need to finish this, I'm excited!
Sorry, you're right...anyways, I figured out a simple workaround, probably not the quickest in terms of processing time, but whatever. Basically allow the initial part of the script to spit out the random result to a text file (as seemed to work just fine) then reference the text file as a variable.
#ECHO OFF
:START
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A listchoice=(%RANDOM% %% %NumLines%)
IF "%listchoice%"=="0" (SET "listchoice=") ELSE (SET "listchoice=skip=%listchoice%")
FOR /F "usebackq tokens=* %listchoice% delims=" %%A IN (`TYPE %list1%`) DO (
>> listoutput.txt ECHO %%A
)
Set /p list=<listoutput.txt
>> result.txt ECHO %list%
:Finish
DEL listoutput.txt
GOTO START
This is easy to do in PowerShell using the built-in Get-Random cmdlet.
$line = (Get-Content file.txt | where { $_ } | Get-Random)
Which makes it also easy in batch.
set filename=file.txt
for /f "tokens=*" %%a in ('powershell -ex bypass -c "gc %filename% | ? { $_ } | Get-Random"') do (
set "var=%%a"
)
The where { $_ } clause is only necessary to filter out any blank lines. You can omit it if you know your file has none.

Batch File to compare value in the first line in each of the 2 different txt files

I have 2 files (say A.txt abd B.txt) having a numeric value in the first line of each (say "8" in A.txt and "9" in B.txt).
I have to compare the value at the first line only between the 2 files and based upon whether numeric value in A.txt is greater than B.txt or not, have to call another batch file.
Could anyone please help me in achieving it?
One way to set a variable to a line in a text file is to use set /P with a redirect.
#echo off
setlocal
set /P "a=" <"A.txt"
set /P "b=" <"B.txt"
if %a% gtr %b% call anotherbatchfile.bat
goto :EOF
If it wasn't the first line you wanted to set, but, say, the 5th, you'd just use several set /P commands within the same redirection, something like this:
<"A.txt" (
set /P "a="
set /P "a="
set /P "a="
set /P "a="
set /P "a="
)
rem # or...
<"A.txt" (
for /L %%I in (1,1,5) do set /P "a="
)
Or you could use for /f to read a text file. help for in a console window for more info. Just for giggles, I'll also demonstrate how to use call to define a function within a batch script.
#echo off
setlocal
for %%I in (a b) do call :setfirstline "%%I.txt" %%I
if %a% gtr %b% call anotherbatchfile.bat
goto :EOF
:setfirstline <txtfile> <var_to_return>
for /f "usebackq delims=" %%I in ("%~1") do (set "%~2=%%I" & goto :EOF)

Resources