1

How can I, in a bash script, have a variable that takes user input of no more than 300 characters, and displays the number of characters left as the user types?

In this case the characters will be numbers corresponding to a feed from get-iplayer, with a maximum of 4 characters in a block separated from the next by a space.

The relevant script follows -

{
    read -n1 -p "Do you want to download some tv programmes? [y/n/q] " ynq ;
    case "$ynq" in 
        [Yy]) echo
      read -n300 -p "Please input the tv programme numbers to download [max 300 characters]  " 'tvbox'
              echo
              cd /media/$USER/back2/proggies/
              /usr/bin/get-iplayer --get $tvbox
              ;;
        [Nn]) echo;;     # moves on to next question in the script
        [Qq]) echo; exit;;            # quits
        * ) echo "Thank you ";;
     esac
};
Dmitri Chubarov
  • 13,673
  • 4
  • 30
  • 64
boudiccas
  • 247
  • 3
  • 12
  • "it displays the number of characters left please" - continuously? also, could you provide a short script which provides the right input, so the code can be tested? – Karoly Horvath Jan 24 '14 at 17:00
  • that's why you don't put it in the *comment*. also you missed the *point*. we don't have an `iplayer` and we do not want to install one. – Karoly Horvath Jan 24 '14 at 17:09
  • Additional information provided, but you don't need the 'get-iplayer', its just a scripting question. – boudiccas Jan 24 '14 at 17:25
  • 2
    This question was cross-posted on SuperUser: http://superuser.com/questions/706669/number-of-characters-left-in-a-bash-variable/706683#706683 – John1024 Jan 25 '14 at 05:54

2 Answers2

1

If you have a string consisting of space-separated words, you can iterate over it like this:

str="hello world nice to meet you"
for word in $str; do
    echo "word=$word"
done

On the other hand, if get-iplayer --get required one argument that is a string consisting of space-separated words, you need to quote the variable:

/usr/bin/get-iplayer --get "$tvbox"

I assume from your comment that if you enter "123 456 789 234 567 890 345", you need to call the program like:

/usr/bin/get-iplayer --get "123 456 789 234"
/usr/bin/get-iplayer --get "567 890 345"

If that's true:

printf "%s\n" $tbbox | paste - - - - | while read four; do 
    /usr/bin/get-iplayer --get "$four"
done

or

nums=( $tvbox )  # this is now an array
for ((i=0; i < ${#nums[@]}; i+=4)); do 
    /usr/bin/get-iplayer --get "${nums[@]:$i:4}"
done
glenn jackman
  • 207,528
  • 33
  • 187
  • 305
  • get-iplayer is just requesting numbers, in maximum blocks of four separated by spaces. – boudiccas Jan 24 '14 at 17:54
  • Perhaps I wasn't clear - the input should be in the form of "1234 357 98 5566 102 303 6633". No more than 4 numbers in a block which is separated from its neighbour by a space, and there could be 3 numbers of programmes to download, or there could be 33, but I want to limit it to 200 or 300 characters, and display the number of characters you have left available with the blank space counting as one number. Makes sense? – boudiccas Jan 24 '14 at 18:45
1

As I understand the question. It is about having an input prompt that will be dynamically updated as the digits are entered.

Here is a solution that is based on a small modification of an answer to another question posted on this site almost two years ago.

Take a script that reads each character in stty cbreak mode into a variable called $result and updates the prompt accordingly.

#!/bin/bash

# Input a line with a dynamically updated prompt
# and print it out

ask () {                       
    n=$1                    # the limit on the input length (<1000)
    if [ -z "$2" ] ; then   # the name of variable to hold the input
       echo "Usage $0: <number> <name>"; 
       return 2;
    fi
    result="" # temporary variable to hold the partial input
    while $(true); do
        printf '[%03d]> %s' "$n" "$result"
        stty cbreak
        REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
        stty -cbreak


        test "$REPLY" == "$(printf '\n')" && {
             printf "\n"
             eval "$2=$result" 
             return 0
        }
        test "$REPLY" == "$(printf '\177')" && {
             # On my terminal 0x7F is the erase character
             result=${result:0:-1}
             (( n = $n + 1 ))
             printf "\r\033[K"
             continue
         } 


        result="${result}$REPLY"
        (( n = $n - 1 ))
        if [ $n -eq 0 ] ; then
           printf "\n"
           eval "$2=$result"
           return 1
        fi
        printf "\r\033[K" # to clear the line
    done
}

echo "Please input the tv programme numbers to download [max 300 characters]"
ask 300 'tvbox'
echo "$tvbox"
# ... here goes the code to fetch the files ...

This script is half baked since it does not handle cursor movement escape characters correctly as read does. Yet it might put you on track.

Community
  • 1
  • 1
Dmitri Chubarov
  • 13,673
  • 4
  • 30
  • 64
  • I've just tried this, and it fails on the first 'ask', it being "ask 300 'tvbox'" saying "ask: command not found". I don't know how to progress it further, sorry. – boudiccas Jan 25 '14 at 13:12
  • http://paste.debian.net/78286/ I've used a pastebin as I've also included the script too, it shouldn't be deleted from there. – boudiccas Jan 25 '14 at 13:44
  • I have edited the script to use this new function. Please have a look http://paste.debian.net/78290/ – Dmitri Chubarov Jan 25 '14 at 14:09
  • Brilliant, thank you very much, it works exceptionally well! :) – boudiccas Jan 25 '14 at 14:14