I'm bored, and decided to write a script for a text-based adventure of mine using bash. Basically, it's supposed to animate a typewriter in certain cases for dramatic storytelling. I can do this manually in a file like so:
sleep 0.1
echo -n "h"
sleep 0.1
echo -n "e"
sleep 0.1
echo -n "l"
sleep 0.1
echo -n "l"
sleep 0.1
echo -n "o"
As you can imagine, it's dreadfully tedious. Instead, I want to pull characters from a string (or file) one character at a time, and apply the sleep command to each character.
So far, I have the following, that reads from a file. IFS= allows the spaces to retain, but not any other type of whitespace (such as newline).
IFS=
while read -n1 achar
do
echo $achar
done < aFile
Is there a way I can use this to get all whitespace? As a bonus question, can you tell me how to apply this to a user-defined script, so I don't have to read from a separate file? So, for example, lets say I have a string called "hello", and I can simply pass it to my function to animate as a typewriter whenever I run my file in the bash terminal.
Any help would be appreciated. Thanks!
Bash uses whitespace to determine where words begin and end. The first word is the command name and additional words become arguments to that command.
Always use double quotes around variable substitutions and command substitutions: "$foo" , "$(foo)" If you use $foo unquoted, your script will choke on input or parameters (or command output, with $(foo) ) containing whitespace or \[*? . There, you can stop reading.
bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.
#$ does "nothing", as # is starting comment and everything behind it on the same line is ignored (with the notable exception of the "shebang"). $# prints the number of arguments passed to a shell script (like $* prints all arguments). Follow this answer to receive notifications.
How about
#!/bin/bash
function typewriter
{
text="$1"
delay="$2"
for i in $(seq 0 $(expr length "${text}")) ; do
echo -n "${text:$i:1}"
sleep ${delay}
done
}
typewriter "Typewriters are cool." .1
echo # <-- Just for a newline
To answer the original question, you need to use an empty delimiter as well:
#!/bin/bash
getc() {
IFS= read -r -n1 -d '' "$@"
}
typewriter() {
while getc ch; do
sleep 0.1; echo "$ch"
done
}
Basically, read
will stop at newlines (the default delimiter) even if it hasn't consumed enough characters yet. The -r
flag also tells it to leave backslashes alone.
Full explanation here: http://jayferd.us/posts/2011-01-12-bash-adventures-read-a-single-character-even-if-its-a-newline
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With