I'm trying to get a batch script to randomly choose between two names and output one at a time, kinda something like this:
#echo off
::I would not be prompted to enter these, they would already be programmed into the script.
ChooseRandomSet=Maria
Sean
Matt
Laura
ChooseRandom
So, then on the user side, you would see:
C:\>script.bat
Matt was randomly chosen.
C:\>script.bat
Sean was randomly chosen.
C:\>
Thanks!
First create a text file with all the names on separate lines:
Maria
Sean
Matt
Laura
Then create a .bat file with the following code:
#ECHO OFF
IF "%~1"=="" (ECHO No text file specified & GOTO :EOF)
IF NOT EXIST %1 (ECHO Text file doesn't exist. & GOTO :EOF)
FOR /F "" %%I IN ('FIND /C /V "" ^<%1') DO SET /A lines=%%I
IF %lines%==0 (ECHO Text file is empty or unreadable & GOTO :EOF)
SET /A skip=(%RANDOM%*32768+%RANDOM%)%%lines
<%1 (
FOR /L %%I IN (1,1,%skip%) DO (
SET /P line=
)
SET line=
SET /P line=
)
ECHO(%line%
This will print out a name from the file, it will also check for certain errors like:
No text file specified
File specified but non-existent
Empty text file
Disclaimer:
I used this bat script for something but I didn't write it, I might've changed a few things over the years but it's largely as I found it a while back. (not a Windows guy myself.. :P)
Update: I also found an answer to this myself just by experimenting. This chooses one option out of two possible choices:
#echo off
if %random% gtr %random% (
echo heads
) else (
echo tails
pause
Related
Well I have a simple for loop to take user input and creat a file with this input, then ask for the next input and create "file2" etc., but I can't seem to figure out how my user can quit the loop with a certain input (maybe "exit" or "end").
setlocal enabledelayedexpansion
set x=1
:runName
set /p names="Enter header for file !x!:"
for %%a in (%names%) do (>>file!x!.txt (echo %%~a) set /a x+=1)
if !names!=end goto:eof else goto:runName
:eof
echo press any key to close
pause>nul
Ps. Sorry for the poor formatting, I cant figure how to enter my code in the grey field from my phone and dont have a computer at the moment.
I'm not quite shure why the input prompt says header.
Here is my best guess what you may have meant:
#Echo off
setlocal enabledelayedexpansion
set x=1
:runName
Set "names="
set /p names="Enter header for file%x%:"
if not defined names goto :end
if /I "%names%" Equ "end" goto :end
(
for %%a in (%names%) do (echo:%%~a)
) >file!x!.txt
set /a x+=1
goto :runName
:end
echo press any key to close
pause>nul
Running
Enter header for file1:anton bertha caesar
Enter header for file2:anchorage berlin
Enter header for file3:END
press any key to close
File content
> type file*.txt
file1.txt
anton
bertha
caesar
file2.txt
anchorage
berlin
I can't seem to figure out how my user can quit the loop
If you'll allow a suggestion, mine would be don't do that.
Almost any batch script is best written to accept command-line options and files as input. Once you trap the user into providing input interactively -- anything more than Y or N, and even that's a nuisance -- you're putting him in a bad spot. He can't re-run your script without going through the tedium again. He can't use tab completion, or pick from a list. He's trapped in a user-interface element that hasn't been improved in 30 years, and wasn't very good then.
I don't know what to suggest specifically. The example you provide is so trivial it would be easier to provide the file directly using Notepad than to create it with a script. Then you might want a verification script to make sure the provided input file is valid.
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
for /f "SKIP=0 EOL=; TOKENS=* DELIMS=" %%i in ('dir /b c:\windows\*.* ^|FIND /i *.exe') do (
CHOICE /D N /T 5 /C YN /M "Please pre Y to exit"
IF !ERRORLEVEL!==1 GOTO :END
IF !ERRORLEVEL!==2 ECHO CONTINUE
timeout 5
#echo %%i
)
:END
ENDLOCAL
I have many files in many folders that I need to rename.
And example is
from cgs2016-09-05-05-40-34.xls
to cgs0905.xls
and
from cgs2016-09-06-05-40-34
to cgs0906.xls
etc
Any help would be greatly appreciated!
#Jamaz Try out the following code below on a sample of your files. Again please use it on a test sample of your files so it does not cause you issues if it makes a mistake. Thank you and please up-vote if this works for you.
setlocal enabledelayedexpansion
REM Collect list of file names in current directory that have the .xls file extension. Output to text file.
dir /b "*.xls" >xls.txt
REM For loop examines the text file for individual file names.
FOR /F "tokens=1" %%# in (.\xls.txt) do (
REM SET variable "#" to equal "token"
Set "token=%%#"
REM Extract the first 3 characters (year) from the file name and set is to variable "token"
Set "tokenchar=!token:~0,3!"
REM Extract the month characters from the file name and set the variable as "tokenmonth"
Set "tokenmonth=!token:~8,2!"
REM Extract the day characters from the file name and set the variable as "tokenday"
Set "tokenday=!token:~11,2!"
ren "%%#" "!tokenchar!!tokenmonth!!tokenday!.xls"
echo %%#
)
Pause
not the best way, but works for your examples:
#echo off
setlocal enabledelayedexpansion
for %%x in (*.xls) do (
set "filename=%%x"
ECHO ren "%%x" "!filename:~0,3!!filename:~8,2!!filename:~11,2!.xls"
)
remove the ECHO if output is ok.
Because the last nineteen characters, date and time stamp, are more likely to be constant than the first three, (especially over multiple folders), I'd change both the previous answers to cater for that rationale.
#Echo Off
SetLocal EnableDelayedExpansion
(Set _rf=C:\Users\jamaz\TestDir)
(Set _fe=xls)
If Not Exist "%_rf%\" Exit/B
For /R "%_rf%" %%I In (*.%_fe%) Do (Set "_fn=%%~nI"
Echo=Ren "%%I" "!_fn:~,-19!!_fn:~-14,2!!_fn:~-11,2!%%~xI")
Timeout -1 1>Nul
EndLocal
Exit/B
As the OP was not clear about whether the code was for multiple same level folders or subfolders rooted from a single location, I went for the latter as the previous responses had already covered that.
Change your chosen file path and extension on lines 4 and 5
If you are happy with the console output, remove echo= from line 10 and delete line 11
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 !
Good Day to All,
Please pardon me if my question may appear odd. I was not able to pinpoint past examples that suit the ideal script that i wish to accomplish.
Currently, i have the following script. Essentially is to delete files from folders whose location is listed in a text file (del_location.txt).
For /F "tokens=*" %%A in (del_location.txt) do del \Q "%%A"
I did not choose to use Forfiles, as the network i am working on in my company is at Windows 2000 without the ability to run Forfiles and due to security reasons, are not able to install any patches.
There are multiple location in del_location.txt and there are instances that my users may not wish to delete all the listed location. Hence i am wondering how do i write a "menu" function, that reads off the listed location in del_location.txt and ECHO it as a selection. Lets the user choose the selection in a Yes/No fashion before the commencement of the delete sequence? Thus allowing the user to "walk off" from the terminal after choosing what he wants to delete.
*Also, from as far as i can find online, without Forfiles, i would not be able to choose to delete only files that are older than N days. Am i right to state so?
Your help and advices are greatly appreciated.
You can set each lines in del_location.txt to an array. Since batch don't support array, then we need to do it manually.
#echo off
setlocal enabledelayedexpansion
:main
set lines=0
For /F "tokens=*" %%A in (del_location.txt) do (
set array[!lines!]=%%A
set /a lines+=1
echo !lines!^) %%A
)
echo. & echo.
set /p choice=Which file do you want to delete? ^>
if %choice% lss 1 goto main
if %choice% gtr %lines% goto main
set /a choice-=1
del /Q !array[%choice%]!
echo. & echo.
echo "!array[%choice%]!" was deleted successfully ^^!
echo. & echo.
choice /c:yn /m "Do you want to delete more files? [y/n]" /n
if %errorlevel% == 1 goto main
if %errorlevel% == 2 exit /b
This script only provided for delete 1 file per turn. So if you want to batch delete, try to modify it yourself, ask anyone or me if you stuck at a point.
I think there's a way to delete files based on date other than forfiles method. Btw, dir SOURCE /o:d list and sort out files based on date.
I am creating an MS DOS batch script that needs to list every .bat file in the current directory, but not show autoexec.bat or other utilities or systems .bat files that shouldn't be run by the user.
I currently have DIR "*.bat" /B /P
This lists all .bat files appropriately, but it shows autoexec.bat. How would I exclude that from the list? Also slightly important, how could I chop off the file extensions and show more than the 7-characters DOS limits files to?
Constraints: I am not able to use a DOS version above WinME. That is the version I am using.
Thanks for any help.
EDIT:
There is plenty of information on the internet about doing this, but it is all in the windows command processor, not MS DOS. Please understand that DOS and the Command Prompt are not the same thing.
#echo off
setlocal EnableDelayedExpansion
rem Add more names separated with slashes here:
set exclude=/autoexec/
for %%a in (*.bat) do (
if "!exclude:/%%~Na/=!" equ "%exclude%" (
echo %%~Na
)
)
EDIT: Some explanations added
Batch file processing is slow, so you should use techniques that allows a Batch file to run faster. For example:
Try to use the minimum lines/commands to achieve a certain result. Try to avoid external commands (*.exe files) like find, findstr, fc, etc. specially if they work on small amounts of data; use if command instead.
Use for %%a in (*.bat)... instead of for /F %%a in ('dir /B *.bat').... The second method requires to execute cmd.exe and store its output in a file before for command can process its lines.
Avoid pipes and use redirections instead. A pipe require the execution of two copies of cmd.exe to process the command at each side of the pipe.
A simple way to check if a variable contain a given string is trying to delete the string from the variable: if the result is different then the string exists in the variable: if "!variable:%string%=!" neq "%variable%" echo The string is in the variable.
Previous method may also be used to check if a variable have anyone of a list of values: set list=one two three, if "!list:%variable%=!" neq "%list%" echo The variable have one value from the list. If the values of the list may have spaces, they must be separated by another delimiter.
EDIT: New version added as answer to new comments
The easiest way to pause one page at a time is to use more filter this way:
theBatchFile | more
However, the program must reorder the output in order to show it in columns. The new version below achieve both things, so it does not require more filter; you just need to set the desired number of columns and rows per page.
#echo off
setlocal EnableDelayedExpansion
rem Add more names separated with slashes here:
set exclude=/autoexec/
rem Set the first two next variables as desired:
set /A columns=5, rows=41, wide=(80-columns)/columns, col=0, row=0
rem Create filling spaces to align columns
set spaces=
for /L %%a in (1,1,%wide%) do set spaces= !spaces!
set line=
for %%a in (*.bat) do (
if "!exclude:/%%~Na/=!" equ "%exclude%" (
rem If this column is less than the limit...
set /A col+=1
if !col! lss %columns% (
rem ... add it to current line
set name=%%~Na%spaces%
set "line=!line!!name:~0,%wide%! "
) else (
rem ... show current line and reset it
set name=%%~Na
echo !line!!name:~0,%wide%!
set line=
set /a col=0, row+=1
rem If this row is equal to the limit...
if !row! equ %rows% (
rem ...do a pause and reset row
pause
set row=0
)
)
)
)
rem Show last line, if any
if defined line echo %line%
Antonio
attrib +h autoexec.bat
should hide autoexec.bat and it should thus not appear in the list
DIR "*.bat" /B /P | find /v "autoexec" | for %i in (*.bat) do #echo %~ni
Using for to process each file name individually:
setlocal enabledelayedexpansion
for /f %%i in ('dir "*.bat" /b') do (
set system=0
if "%%i"=="autoexec.bat" set system=1
if "%%i"=="somesystem.bat" set system=1
if !system!==0 echo %%i
)
Another method without variables:
for /f %%i in ('dir "*.bat" /b') do call :test %%i
goto continue
:test
if "%1"=="autoexec.bat" goto :eof
if "%1"=="somesystem.bat" goto :eof
echo %1
goto :eof
:continue
For both, you can add new filenames to exclude from the list.