3

I would like to write a program that reads from a text file and prints in Terminal the words of that file character by character every one second.

For example, in a text file log.txt let's say I have this sentence:

I love Unix but I don't know programming.

I would like the code to read the previous sentence and print the letters, the spaces one by one every second.

don_crissti
  • 82,805
Zahi
  • 297

5 Answers5

9

Goro's answer will work, but it should be noted that command substitution removes trailing newlines as specified by POSIX standard. Thus it may not be desirable where you want to actually iterate over all charactes, even non-printable ones. Another issue is that C-style for loop is used in bash and ksh93, but not in standard (aka POSIX-comliant ) /bin/sh. The ${variable:index:offset} form of parameter expansion is also type of bashism and not specified by POSIX definitions of parameter expansion (though supported by ksh93 and zsh).

Nonetheless, there's a way to iterate over all characters in file portably and in a far more practical way. That's to use awk:

# all characters on the same line
$ awk '{for(i=1;i<=length;i++){ printf "%c",substr($0,i,1); system("sleep 1");}; print}' input.txt

# all characters on separate lines
$ awk '{for(i=1;i<=length;i++){ print substr($0, i, 1); system("sleep 1"); }}' input.txt

With this command substr() and system() are both specified in POSIX awk and will in fact iterate over all characters.

  • 1
    @Goro Already added a comment about that, see the edit – Sergiy Kolodyazhnyy Sep 08 '18 at 21:15
  • 2
    @don_crissti I need to find a better phrase than "bashism" because I always get ksh and zsh mentioned. Can we start using something like {ba,k,z}sh maybe ? :) – Sergiy Kolodyazhnyy Sep 08 '18 at 21:33
  • 2
    I think just mentioning non-posix or non-standard would do... – don_crissti Sep 08 '18 at 21:33
  • @don_crissti non-posix adds another level of ambiguity because POSIX mostly was based on ksh and Bourne shell, and bash has --posix flag and supoorts most of the POSIX features. Idk, there's gotta be a better way to phrase it – Sergiy Kolodyazhnyy Sep 09 '18 at 00:28
  • I don't find it ambiguous tbh... Also, you seem to be contradicting yourself: your answer says "not specified by POSIX" but you're saying that using "non-posix" is ambiguous... How is "non-posix" different than "not specified by POSIX" ? Anyway, feel free to use any term/phrase you find appropriate, my point was against bashism - which is the wrong term considering the fact that bash copied a lot from ksh (and even `zsh); most features that some people call bashisms have originated in other shells (and, obviously, are supported in those shells too). – don_crissti Sep 09 '18 at 10:09
3

You can do this

var=$(cat log.txt)
for (( i=0; i<${#var}; i++ )); do
 sleep 1 | echo -ne "${var:$i:1}"
done
echo ""
  • thk for quick answr. this is wht i want but why it priunt on the same line ike this `I love Unix but I don't know programming.[zahi@sonar]$, how print on separate line? – Zahi Sep 08 '18 at 20:30
  • Ah, please see my revisions –  Sep 08 '18 at 20:32
3

With bash or ksh93 you can read single characters using the shell's built-in read command:

while IFS= read -r -n 1 c; do 
  printf '%c' "$c"
  sleep 1
done < log.txt
printf '\n'
steeldriver
  • 81,074
1

Here's a Python solution:

python -c "from time import sleep
with open('/tmp/file.txt') as f:
 for line in f:
  for c in line:
   print(c, end='', flush=True);sleep(1);"

You should just be able to paste that on the command line and change the name of the input file.

user1717828
  • 3,542
0

You could do this with Perl as shown:

perl -pe 'BEGIN{$/=\1;$|=1;} sleep 1' log.txt
  • -p will autoprint the current record before fetching the next.
  • $/=\1 will make perl read the input stream a byte at a time, and assuming the characters are a byte sized.
  • $|=1 will output is buffered.