LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 12-31-2015, 12:59 AM   #1
capthookb
LQ Newbie
 
Registered: Oct 2005
Posts: 17

Rep: Reputation: 0
Help with bash/whiptail script


I am writing a script in order to be able to choose the stream url, among a few radio stations, so i can listen to with mplayer.
I have stations.txt text file that has one stream url per line.

stations.txt contents:
Code:
station1
station2
station3
station4

I have written so far the following bash script test.sh:
Code:
#!/bin/bash


STATIONS=()
IFS=$'\n' read -d'' -r -a STATIONS < stations.txt

cnt=${#STATIONS[@]}
for ((i=0;i<cnt;i++)); do
        STATIONS[i]="$i ${STATIONS[i]}"
        echo ${STATIONS[i]}
done


whiptail --title "Choose radio station:" --menu "select your choice" 16 78 5 "${STATIONS[@]}"
The problem is that whiptail, displays two stations per line and not each station in each own line, so i can select the one that i want. You can see the attached image for the visual output of the script.

What could be wrong?

By the way i'm using zsh, but i guess it doesn't matter because i include #!/bin/bash in the first line of the script. Is this true?
Attached Thumbnails
Click image for larger version

Name:	bash.png
Views:	168
Size:	21.7 KB
ID:	20410  
 
Old 12-31-2015, 03:28 AM   #2
AnanthaP
Member
 
Registered: Jul 2004
Location: Chennai, India
Posts: 952

Rep: Reputation: 217Reputation: 217Reputation: 217
I think the serial number and description need to be as separate strings. So to render them to whiptail from the array, i might go something like

Quote:
j=0
for ((i=0;i<cnt;i++)); do
STATIONS2[j]="$i"
j++ ;
STATIONS2[j]="${STATIONS[i]}"
j++
echo ${STATIONS[i]}
done
OK

Last edited by AnanthaP; 12-31-2015 at 03:30 AM.
 
Old 12-31-2015, 04:41 AM   #3
capthookb
LQ Newbie
 
Registered: Oct 2005
Posts: 17

Original Poster
Rep: Reputation: 0
Thank you!
Indeed, it worked after making an array with number and description in different elements.
So, in case anyone else wants something like that this is my complete script:

1)radio.sh:
Code:
#!/bin/bash


STATIONS=()
cat stations.txt | sed 's/-/\\-/g' > stations.tmp
IFS=$'\n' read -d'' -r -a STATIONS < stations.tmp 

MENUS=()
URLS=()
cnt=${#STATIONS[@]}
j=1;
for ((i=0;i<cnt;i++)); do
	j=$(($i*2))
	MENUS[$j]="$(($i+1))"
	j=$(($i*2+1))
	MENUS[$j]=$(echo ${STATIONS[$i]} | cut -d '|' -f 2)
	URLS[$i]=$(echo ${STATIONS[$i]} | cut -d '|' -f 1)
done

while true
do
	OPTION=$(whiptail --title Networking --menu "select your choice" 16 78 5 "${MENUS[@]}" 3>&1 1>&2 2>&3)
	exitstatus=$?
	if [ $exitstatus = 0 ]; then
		let OPTION=$OPTION-1
		echo "Playing url:" ${URLS[$OPTION]}
		mplayer ${URLS[$OPTION]}
	else
        	exit
	fi
done
and stations.txt:
Code:
http://realfm.live24.gr:80/realfm | RealFM
http://legacy-100-ams.servers.mediacdn.com/maxfm93-4-patra | Max Fm
http://stream.cretanservers.gr/1045fm | Rithmos Kriti
http://netradio.live24.gr:80/melodia | Melodia
Each line in stations.txt starts with the stream url, pipe | as seperator and then the station description.
The script displays the station description only, and upon selection it launches mplayer with the apropriate url.
 
Old 12-31-2015, 12:47 PM   #4
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192
cat, sed and redirection not required if you are not worried about changing the original file:
Code:
cat stations.txt | sed 's/-/\\-/g' > stations.tmp

sed -i 's/-/\\-/g' stations.txt
Also, here is an alternative for populating your arrays:
Code:
# current
STATIONS=()
cat stations.txt | sed 's/-/\\-/g' > stations.tmp
IFS=$'\n' read -d'' -r -a STATIONS < stations.tmp 

MENUS=()
URLS=()
cnt=${#STATIONS[@]}
j=1;
for ((i=0;i<cnt;i++)); do
	j=$(($i*2))
	MENUS[$j]="$(($i+1))"
	j=$(($i*2+1))
	MENUS[$j]=$(echo ${STATIONS[$i]} | cut -d '|' -f 2)
	URLS[$i]=$(echo ${STATIONS[$i]} | cut -d '|' -f 1)
done

# alternative
c=1
while IFS="| " read url name
do
  menu+=($((c++)))
  menu+=("$name")
  urls+=("$url")
done<stations.txt
 
Old 01-01-2016, 01:33 AM   #5
capthookb
LQ Newbie
 
Registered: Oct 2005
Posts: 17

Original Poster
Rep: Reputation: 0
Thank you for your suggestions grail!.
cat-ing into a new file every time i run the script isn't very efficient or good looking code but it works.
Do you know why read doesn't "read" the whole url if it contains dashes in the first place?
 
Old 01-01-2016, 01:47 AM   #6
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192
Not a 100 sure on why you need to escape the dashes, but my method does not require the escaping or you could use mapfile instead of read and it also does not have issues with the dashes.

Just to be clear, my alternative replaces ALL of the original text above it, ie. the sed and the like are not needed.
 
Old 01-01-2016, 08:31 AM   #7
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,783

Rep: Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083Reputation: 2083
Quote:
Originally Posted by capthookb View Post
Do you know why read doesn't "read" the whole url if it contains dashes in the first place?
Seems like a bug to me. It only happens with read -a, not plain read.
 
Old 01-03-2016, 03:38 AM   #8
AnanthaP
Member
 
Registered: Jul 2004
Location: Chennai, India
Posts: 952

Rep: Reputation: 217Reputation: 217Reputation: 217
Keeping your original file, here would be my solution.

Code:
#!/bin/bash

clear
STATIONS=()
s2=()
IFS=$'\n' read -d'' -r -a STATIONS < stations.txt

cnt=${#STATIONS[@]}
j=0
for ((i=0;i<cnt;i++)); do
	s2[j]=`expr $i + 1`
	j=`expr $j + 1`
	s2[j]=${STATIONS[i]}
	j=`expr $j + 1`
done


whiptail --title "Choose radio station:" --menu "select your choice" 16 78 5 "${s2[@]}"
 
Old 01-03-2016, 04:03 AM   #9
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192Reputation: 3192
@AnanthaP - Not sure if you got different results, but here is what STATIONS has in it for me:
Code:
$ IFS=$'\n' read -d'' -r -a STATIONS < stations.txt
$ echo "${STATIONS[@]}"
http://realfm.live24.gr:80/realfm | RealFM http://legacy
This was the issue mentioned around hyphens being part of the information read into the array.
 
Old 01-03-2016, 05:52 AM   #10
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,876

Rep: Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315
actually I think it is caused because of the empty delimiter. using -d! will make it working:
Code:
IFS=$'\n' read  -d! -a STATIONS < stations.txt
I do not know if it was a bug or not, but based on the man page of bash
Code:
       read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
              One  line is read from the standard input, or from the file descriptor fd supplied as an argument to the -u option, and the first word is assigned to
              the first name, the second word to the second name, and so on, with leftover words and their intervening separators assigned to the  last  name.   If
              there  are fewer words read from the input stream than names, the remaining names are assigned empty values.  The characters in IFS are used to split
              the line into words using the same rules the shell uses for expansion (described above under Word Splitting).  The backslash  character  (\)  may  be
              used to remove any special meaning for the next character read and for line continuation.  Options, if supplied, have the following meanings:
              -a aname
                     The  words  are assigned to sequential indices of the array variable aname, starting at 0.  aname is unset before any new values are assigned.
                     Other name arguments are ignored.
              -d delim
                     The first character of delim is used to terminate the input line, rather than newline.
empty delimiter is not supported.


to AnanthaP:
s2[j]=`expr $i + 1`
do not use backticks in for cycles if not really necessary, your solution will fork a huge amount of processes (and will run therefore very slowly)

MENUS[$j]="$(($i+1))"
works much better

To capthookb:
$(echo ${STATIONS[$i]} | cut -d '|' -f 2)
similar issue, it will for several processes, do not use $( ) and pipe chains if not really required (inside a loop)
use parameter substitution, see man page of bash again:
echo ${STATIONS[2]% |*}
echo ${STATIONS[2]#* | }
 
Old 01-03-2016, 09:05 AM   #11
Ramurd
Member
 
Registered: Mar 2009
Location: Rotterdam, the Netherlands
Distribution: Slackwarelinux
Posts: 703

Rep: Reputation: 111Reputation: 111
Just a quicky:

Quote:
MENUS[$j]="$(($i+1))"
works much better
Now, if performance is your issue, try the difference between:
Code:
(( $i + 1 ))
and
Code:
(( i + 1 ))
The dollar has to be expanded by the shell and then worked upon, whereas the latter form does not need this and thus is vastly faster.

so, with a little bit of rewriting:

Code:
cnt=${#STATIONS[@]}
j=0
for ((i=0;i<cnt;i++)); do
	s2[${j}]=$((i + 1 ))
	(( j++ ))
	s2[${j}]=${STATIONS[${i}]}
	(( j++ ))
done
It could be compacted even more, but this should do... also added a few dollars in the matrix-counter and added curly braces for readability.
 
Old 01-05-2016, 12:43 PM   #12
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,876

Rep: Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315Reputation: 7315
I do not really understand: both (( $i + 1 )) and (( i + 1 )) will be evaluated by the same shell and - if I knew it well - regardless of the $ sign the result will be exactly the same. But correct me if I was wrong.
 
Old 01-06-2016, 06:25 AM   #13
Ramurd
Member
 
Registered: Mar 2009
Location: Rotterdam, the Netherlands
Distribution: Slackwarelinux
Posts: 703

Rep: Reputation: 111Reputation: 111
Eveluated by the same shell, but different parts of that same shell; one that is more efficient...

You can try it out with a very simple script:

Code:
#!/bin/bash

no_dollar()
{
        y=0
        for (( i=0 ; i<10000000 ; i++))
        do
                (( y=i ))
        done
}

dollar()
{
        y=0
        for (( i=0 ; $i<10000000 ; i++ ))
        do
                (( y = ${i} ))
        done
}


time no_dollar
sleep 2
time dollar
and you'll see the difference. To be able to see a difference, one has to iterate enough times...
On my hardware I get this output, as you can see: a bit more than 20 seconds difference in about 1 minute of runtime... that's about 30 difference. (I did not do these calculations on a machine, rather from the top of my head... they may be off!)

Code:
./performance.sh

real    0m56.921s
user    0m51.711s
sys     0m5.177s

real    1m17.051s
user    1m7.305s
sys     0m9.701s
 
Old 10-08-2021, 05:46 PM   #14
BioTundra
LQ Newbie
 
Registered: Oct 2021
Posts: 1

Rep: Reputation: Disabled
Hello ( after 5 years )

Even though some of you maybe do not see this, your solutions solved my problem as well.
THANK YOU VERY MUCH

I am new to bash scripting and whiptail. That is why what i will say won't be correct.


>
-From the counting starts from 1, while the array starts from 0.

-If you chose option 6, it would pick the item 5 from the array.

-The result (it's a number) from whiptail will be stored in a variable.

-Then it will subtract 1 from the result.

-The result of the subtraction corresponds to the item from the main array.

-After that it will print the right item from the main array.


Thank you very much
( i registered on this forum especially to reply to this, because i am happy )


Code:
#!/bin/bash

clear
STATIONS=()
s2=()
mapfile  -t STATIONS < <( cat array.txt)

cnt=${#STATIONS[@]}
j=0
for (( i=0 ; i<cnt ; i++ )); do
        s2[$j]="$(($i+1))"
        #s2[j]=$(expr $i + 1)
        j=$(expr $j + 1)
        s2[j]=${STATIONS[i]}
        j=$(expr $j + 1)
done


choice=$(whiptail --title "Choose radio station:" --menu "select your choice" 0 0 0 "${s2[@]}" 3>&1 1>&2 2>&3)

echo 'whiptail choice is: ' $choice
number=$(expr $choice - 1)
echo 'number is: ' $number
echo 'string is: ' ${STATIONS[$number]}
 
Old 10-09-2021, 12:32 AM   #15
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 19,872
Blog Entries: 12

Rep: Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053
Quote:
Originally Posted by BioTundra View Post
That is why what i will say won't be correct.
What a strange thing to say. But strange is good.
Anyhow, welcome to LQ!
Quote:
Originally Posted by BioTundra View Post
If you chose option 6, it would pick the item 5 from the array.
I haven't looked deeply at the code, but that sounds about right: arrays start at 0, whereas numbered lists usually start at 1.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Converting Script from Linux (GNU) Bash 4 to Solaris Bash 2.05 - Any cheat sheet? oly_r Solaris / OpenSolaris 6 05-03-2013 08:25 AM
is it possible to use multiple inputbox with whiptail kankan55 Linux - Software 0 02-16-2013 05:44 AM
whiptail with multiple inputbox kankan55 Linux - Newbie 1 02-14-2013 11:25 AM
SSH connection from BASH script stops further BASH script commands tardis1 Linux - Newbie 3 12-06-2010 08:56 AM
Alsaconf needs 'whiptail' or 'dialog' Exeis Linux - Software 3 08-27-2008 09:02 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 01:35 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration