Feasability question: fortunes

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
noprogrammer
Posts: 36
Joined: 29 Oct 2009 11:55

Feasability question: fortunes

#1 Post by noprogrammer » 29 Oct 2015 06:30

Recently, doing a research I found this.
I didn't check it myself, I very seldom work with Windows PowerShell...

Twelve years ago I tinkered with Cygwin, I liked its fortunes package. Thanks to that,
opening a console I got random MOTD. As long as Cygwin was installed, I was able to
use it within cmd, too. I'm not sure how the The PowerShell script works in detail.

Do you see a way to mimic the fortunes functionality with cmd—or do you think it's impossible?
The platform is Windows 7x64 or newer.

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Feasability question: fortunes

#2 Post by Aacini » 29 Oct 2015 19:39

Code: Select all

@echo off
setlocal

rem Emulation of Linux fortune program in Batch
rem Antonio Perez Ayala

rem Get the starting line of all "fortunes" in fortune.txt file (needed just once)
if not exist fortunes.txt findstr /N "^%%" fortune.txt > fortunes.txt

rem Get the number of fortunes
for /F %%a in ('find /C "%%" ^< fortunes.txt') do set "lines=%%a"

rem Select a random fortune
set /A "rnd=lines*%random%/32768"

rem Get the starting line of the random fortune
for /F "delims=:" %%a in ('more +%rnd% fortunes.txt') do set "line=%%a" & goto break1
:break1

rem Show the fortune
for /F "delims=" %%a in ('more +%line% fortune.txt') do (
   if "%%a" equ "%%" goto break2
   echo %%a
)
:break2

Output example:

Code: Select all

C:\> fortune.bat
"It's a summons."
"What's a summons?"
"It means summon's in trouble."
                -- Rocky and Bullwinkle

C:\> fortune.bat
Real Users know your home telephone number.

C:\> fortune.bat
First, a few words about tools.
Basically, a tool is an object that enables you to take advantage of
the laws of physics and mechanics in such a way that you can seriously
injure yourself.  Today, people tend to take tools for granted.  If
you're ever walking down the street and you notice some people who look
particularly smug, the odds are that they are taking tools for
granted.  If I were you, I'd walk right up and smack them in the face.
                -- Dave Barry, "The Taming of the Screw"

C:\> fortune.bat
Since we're all here, we must not be all there.
                -- Bob "Mountain" Beck

Sometimes the program takes too long to show the "fortune". This happen in the "more +%line% fortune.txt" command when the number of line is high. This point may be fixed with the aid of an auxiliary .exe program.

Antonio

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Feasability question: fortunes

#3 Post by penpen » 30 Oct 2015 11:23

There are fortunes with a '%' character in it and the file starts and ends with a fortune , so you should change the line

Code: Select all

for /F %%a in ('find /C "%%" ^< fortunes.txt') do set "lines=%%a"
to

Code: Select all

for /F %%a in ('findstr /N "^%%$" "fortunes.txt" ^|find /C "%%"') do set /A "lines=1+%%a"


penpen

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Feasability question: fortunes

#4 Post by Aacini » 30 Oct 2015 16:42

@penpen: No. That was already done in this line:

Code: Select all

if not exist fortunes.txt findstr /N "^%%" fortune.txt > fortunes.txt

The base file is "fortune.txt". Previous line get the start of all fortunes and store they in "fortuneS.txt" file, so the count of find /C command using this last file is correct.

Also, the line number is used in "more +n" command, that have an implicit "+1" value...

Antonio

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Feasability question: fortunes

#5 Post by penpen » 30 Oct 2015 19:34

No. That was already done in this line:

Code: Select all

if not exist fortunes.txt findstr /N "^%%" fortune.txt > fortunes.txt
Somehow i've missed this line... .

Then you should change this line to:

Code: Select all

if not exist fortunes.txt findstr /N "^%%$" fortune.txt > fortunes.txt

There is at least one fortune starting with a '%' character:
fortune.txt line 2953ff wrote:%
%DCL-E-MEMBAD, bad memory
-VMS-F-PDGERS, pudding between the ears
%


penpen

ShadowThief
Expert
Posts: 1166
Joined: 06 Sep 2013 21:28
Location: Virginia, United States

Re: Feasability question: fortunes

#6 Post by ShadowThief » 30 Oct 2015 21:27

Wouldn't it be easier to have the entire fortune on one line and use the ^ followed by two blank lines for a newline character? Then you just have to pick a random number and display that line.

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Feasability question: fortunes

#7 Post by penpen » 31 Oct 2015 06:10

Aacini wrote:Sometimes the program takes too long to show the "fortune". This happen in the "more +%line% fortune.txt" command when the number of line is high. This point may be fixed with the aid of an auxiliary .exe program.
I have replaced the more command by a "for /F" (with skip). Now it takes ~97,5% less time.

In addition i've found another bug:
The first fortune is not preceeded by a '%'-line so you have to manually add 0:% to "fortunes.txt".

Here is the resulting code (a little bit ugly as "skip=0" is not allowed):

Code: Select all

setlocal

rem Emulation of Linux fortune program in Batch
rem Antonio Perez Ayala

rem Get the starting line of all "fortunes" in fortune.txt file (needed just once)
if not exist fortunes.txt > fortunes.txt (
   echo 0:%%
   findstr /N "^%%$" fortune.txt
)

rem Get the number of fortunes
for /F %%a in ('find /C "%%" ^< fortunes.txt') do set "lines=%%a"

rem Select a random fortune
set /A "rnd=lines*%random%/32768"

rem Get the starting line of the random fortune
if "%rnd%" == "0" ( set "skip= "
) else set "skip=skip=%rnd%"
for /F "usebackq %skip% delims=:" %%a in ("fortunes.txt") do (
   if "%rnd%" == "0" ( set "skip= "
   ) else set "skip=skip=%%a"
   goto break1
)
:break1

rem Show the fortune
for /F "usebackq %skip% delims=" %%a in ("fortune.txt") do (
   if "%%a" equ "%%" goto break2
   echo %%a
)
:break2


ShadowThief wrote:Wouldn't it be easier to have the entire fortune on one line and use the ^ followed by two blank lines for a newline character? Then you just have to pick a random number and display that line.
Then you would have to escape the tilde character ('^') or similar, because there are fortunes with that character, too:
You also cannot use another character to avaoid this, because there is no guarantee that future database files doesn't contain fortunes with that character.
So i doubt it would be easier to find a robust solution.


penpen

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Feasability question: fortunes

#8 Post by Aacini » 31 Oct 2015 11:23

penpen wrote:In addition i've found another bug:
The first fortune is not preceeded by a '%'-line so you have to manually add 0:% to "fortunes.txt".

Here is the resulting code (a little bit ugly as "skip=0" is not allowed):

Code: Select all

. . . . .

penpen


I think it is simpler this way:

Code: Select all

@echo off
setlocal

rem Emulation of Linux fortune program in Batch
rem Antonio Perez Ayala

rem Get the starting line of all "fortunes" in fortune.txt file (needed just once)
if not exist fortunes.txt findstr /N "^%%$" "fortune.txt" > fortunes.txt

rem Get the number of fortunes (including the first one)
for /F %%a in ('find /C "%%" ^< "fortunes.txt"') do set /A "lines=%%a + 1"

rem Select a random fortune
set /A "rnd=lines*%random%/32768 - 1"

rem Get the starting line of the random fortune
set "skip="
if %rnd% lss 0 goto showFortune
if %rnd% gtr 0 set "skip=skip=%rnd%"
for /F "usebackq %skip% delims=:" %%a in ("fortunes.txt") do set "skip=skip=%%a" & goto showFortune

rem Show the fortune
:showFortune
for /F "usebackq %skip% delims=" %%a in ("fortune.txt") do (
   if "%%a" equ "%%" goto break
   echo %%a
)
:break

Antonio

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Feasability question: fortunes

#9 Post by penpen » 31 Oct 2015 13:30

I found a way, to speed it up another 60%, but the first line in "fortunes.txt" now contains thenumber of fortunes - 1:

Code: Select all

@echo off
setlocal

rem Emulation of Linux fortune program in Batch
rem Antonio Perez Ayala

rem Get the number and starting line of all "fortunes" in fortune.txt file (needed just once)
if not exist fortunes.txt > fortunes.txt (
   findstr /N "^%%$" "fortune.txt" | find /C "%%"
   findstr /N "^%%$" "fortune.txt"
)

rem Get the number of fortunes (excluding the first one)
< fortunes.txt set /P "lines="

rem Select a random fortune
set /A "rnd=lines*%random%/32768"

rem Get the starting line of the random fortune
if %rnd% == 0 ( set "skip="
) else for /F "usebackq skip=%rnd% delims=:" %%a in ("fortunes.txt") do set "skip=skip=%%a" & goto showFortune

:showFortune
rem Show the fortune
for /F "usebackq %skip% delims=" %%a in ("fortune.txt") do (
   if "%%a" equ "%%" goto break
   echo %%a
)
:break


penpen

noprogrammer
Posts: 36
Joined: 29 Oct 2009 11:55

Re: Feasability question: fortunes

#10 Post by noprogrammer » 24 Nov 2015 16:13

Quite impressing, thanks alot!

I digged up an approach that works with the tool gawk.
My biggest fortunes file includes 728 quotes. I finally switched to UTF-8 encoding, too.

A single cmd script doing all the fortunes functionality would be very nice.
My current approach includes 3 files, a cmd script, gawk and the following awk script* for the randomising:

Code: Select all

BEGIN {RS="%\n"; ORS =""; srand()}
{cookie[NR]=$0}
END {print cookie[int(rand() * NR + 1)]}

This is my customized fortunetest.cmd script:

Code: Select all

@echo off
Setlocal enableExtensions
Cd /d "%~dp0"
For /f "tokens=3 delims=:. " %%? In ('Chcp') Do @Set "ocp=%%~?"
Chcp 65001 >nul
gawk -f fortune.awk "%~1" > fortune.txt
Chcp %ocp% >nul
Endlocal

I already fetch the fortune.txt, ie. the random adage as "upcoming" MOTD (fortunetest.cmd myfortunefile).
That proof of concept seems to work already for my scenario.

However, I'm always open for improvements!
__
* based upon ideas of "Michael S. Sanders"

Post Reply