Register
It is currently Sat Nov 01, 2014 3:46 am

trying to use array to generate simple random song list


All times are UTC - 6 hours


Post new topic Reply to topic  [ 5 posts ] 
Author Message
 PostPosted: Fri Nov 29, 2013 2:19 pm   

Joined: Mon Jul 01, 2013 7:24 pm
Posts: 19
I have always avoided using arrays b/c they make my brain hurt trying to figure them out. But I have a need to generate a list from 1-10 random songs depending upon the number of songs user inputs. I have been using the array guide at http://mywiki.wooledge.org/BashGuide/Arrays to try and do what I want, but it is not quite working, yet. Here is what I have so far:

Code:
#!/bin/bash

dir="~/tmp/bashArray"

function getSong {
   sList=()
   cntr=1
   while [[ $cntr -le $noS ]] ; do
      echo "wL:${cntr}"
      sList+=( $(find ${dir} -type f -iname "*.mp3" -print0 | sort -z -R | perl -p -e 's/^(.*?\.mp3).*$/$1/') )
                ((cntr++))
        done

   echo "No. array elements: ${#sList[*]}"
   for index in ${!sList[*]} ; do
      echo "$index ${sList[$index]}"
      audacious "$index ${sList[$index]}"
   done
}

read -p "Enter no. of random songs you want to hear: " noS
getSong


Command output:
Code:
03:02 ~ $ ranSong.sh
Enter no. of random songs you want to hear: 2
wL:1
wL:2
No. array elements: 2
0 /home/naphelge/tmp/bashArray/03_perfect.mp3
1 /home/naphelge/tmp/bashArray/13_you_oughta_know.mp3
03:08 ~ $


However, audacious pukes out the error:
Code:
Cannot open /home/naphelge/0 /home/naphelge/tmp/bashArray/02_you_oughta_know.mp3: No such file or directory.

How can I strip off the leading '/home/naphelge/0' part of the index return result, so that just the path to the song is passed to audacious?

Also, what would be the most effiecient way to check the elements for duplicates and replace duplicates with a new entry that is also checked for being a duplicate until no duplicate element entries exist?


Top
 Profile  
 PostPosted: Fri Nov 29, 2013 6:41 pm   

Joined: Mon Jul 01, 2013 7:24 pm
Posts: 19
Ok after playing around a little more I have replaced:
Code:
echo "No. array elements: ${#sList[*]}"
   for index in ${!sList[*]} ; do
      echo "$index ${sList[$index]}"
      audacious "$index ${sList[$index]}"
   done

with:
Code:
#echo ${sList[@]}
   audacious $(echo ${sList[@]})

to pass the random song list to audacious a-ok.

But I would still like to find out if there is a way of checking array elements for duplicates and an efficient way to replace a duplicate entry with an alternately chosen song picked at random.


Top
 Profile  
 PostPosted: Fri Nov 29, 2013 9:27 pm   
User avatar

Joined: Wed Jun 08, 2011 8:27 am
Posts: 189
Location: outer Shpongolia
Remove duplicates before storing the paths in the array and don't worry about them.
Also, lack of quotes is your issue; you want each element to be expanded to a separate word.

Code:
#!/bin/bash

dir=~/tmp/bashArray

read -rp 'Enter no. of random songs you want to hear: ' n

while
    ((n > 0)) &&
    IFS= read -rd '' 'tracks[n--]'
do :
done \
    < <(sort -zuR \
        <(find "$dir" -type f \
               -name '*.mp3' \
               -print0))

audacious "${tracks[@]}"


Top
 Profile  
 PostPosted: Sat Nov 30, 2013 3:48 pm   

Joined: Mon Jul 01, 2013 7:24 pm
Posts: 19
hey jsz, thanks for the reply. Solutions works like a charm. I can follow some of what's going on (my BASH kung-fu is not so kung-fu), but I wonder if you wouldn't mind adding a few details for a slow learner.

Code:
IFS= read -rd '' 'tracks[n--]'

How does 'tracks[n--]' here get recognized as an array, and what do the two dashes after the read input var 'n' do?

Code:
do :

I see that the while loop reads input from the sort|find commands re-directed afterward, but what exactly is the significance of the colon? I guess 'do' expects/demands some sort of command, but I do not know what the colon means.

thanks


Top
 Profile  
 PostPosted: Mon Dec 02, 2013 8:37 pm   
User avatar

Joined: Wed Jun 08, 2011 8:27 am
Posts: 189
Location: outer Shpongolia
naphelge wrote:
Code:
IFS= read -rd '' 'tracks[n--]'

How does 'tracks[n--]' here get recognized as an array, and what do the two dashes after the read input var 'n' do?

It doesn't get recognized as an array but as a simple string that
will effectively be seen as an array's access representation by read(0).

To understand this, replace « tracks[n--] » with, e.g., « tracks[1] ».
Now, for each iteration, new input read is stored at index 1 of array tracks.

(Note that you still need to decrement n because of the while-loop condition. See below.)


It's possible to do that because you can assign array offsets directly.

E.g., you can do « tracks[1]=foo », to implicitly make tracks an array
which has the string « foo » at its second index (starting from 0).


Basically, as soon as the name[x] construct is used, name is seen as an
array, unless name is an associative array (which needs explicit declaration).

If name[x] gets assigned, the value at offset x gets either set or
replaced, after that name has been binded if it doesn't already exist.


If you want to go into more details, read the source.

read.c:bind_read_variable ; set read(0)'s VAR, i.e., tracks[x]
arrayfunc.c:valid_array_reference ; check whether « tracks[x] » represents an array access (it does)
arrayfunc.c:assign_array_element ; prepare tracks[x] assignment
arrayfunc.c:bind_array_variable ; create tracks if it doesn't exist, assign tracks[x]

----

The x-- syntax is called a post-decrement.

It's used to subtract 1 from a variable (x), then re-assign that variable
with the result, only after that the current line has been processed.


In our case, the variable n has at first the value that
represents the number of random tracks wanted.

While n is different than 0, we store the current input read at index n of the
implicit array tracks, then we post-decrement n so that at the next iteration n
will have the value of n - 1, and so on, until it gets to 0 and the while-loop is exited.

In the end, you get an array filled from index 1 to n, so with n elements.

----

naphelge wrote:
Code:
do :

I see that the while loop reads input from the sort|find commands re-directed afterward, but what exactly is the significance of the colon?
I guess 'do' expects/demands some sort of command, but I do not know what the colon means.

: is a shell builtin that does only exit with an exit status of 0. It's called the null command.
In other words, it does always return success, meaning it always succeeds.

It's used here to do nothing in the while-loop body, since we're already getting the values in an array thanks to read(0).

As you correctly pointed out, bash(1) expects to see commands after the keyword do and before
the keyword done, that's why we need to use a command that does nothing in this place.

You could instead store the input there, as well as decrement n.


Top
 Profile  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron


BashScripts | Promote Your Page Too
Powered by phpBB © 2011 phpBB Group
© 2003 - 2011 USA LINUX USERS GROUP