简体   繁体   中英

Bash script reading random lines in a text file

I have a one line bash script that reads random lines in a text file:

#!/bin/bash
shuf -n 1 excuses.txt

When I call the script, it gives me a random one-liner but I notice that the command I used in the script is really not all that random and when I run the script multiple times I may see the same line being read (even though it's random). My question is: Is there another way to randomly print a line of text that may be even more random than what I have in my code? Please note, I have a 1,000 lines that can be read from this text file.

EDIT: Here are my results when I used shuf to randomize the text file:

[root@ftpserver bofh] ./bastardScript.sh
Atilla the Hub
[root@ftpserver bofh] ./bastardScript.sh
kernel panic: write-only-memory (/dev/wom0) capacity exceeded.
[root@ftpserver bofh] ./bastardScript.sh
We had to turn off that service to comply with the CDA Bill.
[root@ftpserver bofh] ./bastardScript.sh
YOU HAVE AN I/O ERROR -> Incompetent Operator error
[root@ftpserver bofh] ./bastardScript.sh
Change in Earth's rotational speed
[root@ftpserver bofh] ./bastardScript.sh
Atilla the Hub

So as you can see within the time I use the script (about 6-10 times):

[root@ftpserver bofh] ./bastardScript.sh
Atilla the Hub

Comes up. I want to see if it can get anymore random than that.

#!/bin/bash
cat excuses.txt | sort --random-sort | head -n 1

If you want to ensure that shuf is using a high-entropy source:

shuf -n 1 --random-source=/dev/random <excuses.txt

That said, what it sounds like you really want is to not see the same excuse twice (until they've all been consumed). That being the case, I'd consider shuffling the file once, storing a counter, and showing each line in turn.

# In Bash 3 or earlier, you'll need to hardcode a FD number here.
exec {lock_fd}>excuses.count
flock -x "$lock_fd"

[[ -e excuses.shuffled ]] || {
  shuf <excuses.txt >excuses.shuffled || exit
  echo 1 >excuses.count
}
[[ -s excuses.shuffled ]] || exit
counter=$(<excuses.count)
line=$(sed -n "${counter} p" <excuses.shuffled)
if [[ $line ]]; then
  echo "$line"
  echo "$((counter + 1))" >excuses.count
else
  # ran out of lines; delete files and restart this script to reshuffle
  rm -- excuses.shuffled # clear our now-used shuffle
  exec {lock_fd}<&-      # release the lock
  exec "$0" "$@"         # restart the script
fi

What you want is clearly less random.

If you don't want to repeat older lines, you need some way to remember previous results.

What I would do is shuffle the whole file and store it somewhere. Then each time the script is called grab the last line and remove it. Once you detect the file is empty you have to do another shuffle.

If you worry about concurrency, you'll need proper file locking.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM