LinuxQuestions.org
Visit the LQ Articles and Editorials section
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 07-25-2005, 06:54 PM   #1
samel_tvom
Member
 
Registered: Aug 2004
Posts: 133

Rep: Reputation: 15
bash: put output from `ls` into an array


Hi!

I have a little script which puts the output from ls into an array.
However, it does not work if a file in the directory has a whitespace (or more) in its filename since my script will interpret that line from ls as two lines (or more depending on how many whitespaces their are)

my script (named array) looks like this:

#!/bin/bash
array=(`ls`)

len=${#array[*]}

i=0
while [ $i -lt $len ]; do
echo "$i: ${array[$i]}"
let i++
done


If I do an ls the result is:

[samel@arch bla]$ ls
array foobar

and the output from the script:

[samel@arch bla]$ ./array
0: array
1: foobar

BUT if I rename "foobar" to "foo bar"

the script gives:

[samel@arch bla]$ ./array
0: array
1: foo
2: bar


How should I solve this? I rather not use "ls | sed -ne numer" in a whileloop that puts line by line into the array, I only want to do ls once or so.

I actually intend to put the output from find, and if I do "a find" and it gives some hundreds line I don't want to repeat that every time in a whileloop since it takes a lot of time to get it finished.

Thanks a bunch!
 
Old 07-25-2005, 08:39 PM   #2
Matir
Moderator
 
Registered: Nov 2004
Location: San Jose, CA
Distribution: Ubuntu
Posts: 8,507

Rep: Reputation: 118Reputation: 118
Code:
IFS='
'
Then run it on find or ls -1.

This will cause it to ONLY split on newlines.
 
Old 07-25-2005, 08:46 PM   #3
samel_tvom
Member
 
Registered: Aug 2004
Posts: 133

Original Poster
Rep: Reputation: 15
thanks, that worked perfect!
what does
IFS='
'
do? what is IFS? some global variable of some sort?
 
Old 07-25-2005, 08:50 PM   #4
Matir
Moderator
 
Registered: Nov 2004
Location: San Jose, CA
Distribution: Ubuntu
Posts: 8,507

Rep: Reputation: 118Reputation: 118
IFS is the "Internal Field Separator". It's a character (or list of characters) that bash uses for filename globbing, etc. By putting the quotes a line apart, it includes just a newline. Despite what one would expect, IFS='\n' does NOT work.
 
Old 07-25-2005, 09:31 PM   #5
slackbull
LQ Newbie
 
Registered: Jul 2005
Distribution: Slack, LFS, "other"
Posts: 10

Rep: Reputation: 0
I ended up doing it like this

Code:
#!/bin/bash
IFS=!
ARRAY=(`find * -printf %f!`)
COUNT="0"

for i in ${ARRAY[*]}; do
    echo "$(($COUNT+1)): $i"
    let COUNT++;
done
 
Old 07-25-2005, 10:02 PM   #6
Matir
Moderator
 
Registered: Nov 2004
Location: San Jose, CA
Distribution: Ubuntu
Posts: 8,507

Rep: Reputation: 118Reputation: 118
Out of curiousity, what was your goal? If only to count files, why not just "find * | wc -l"?
 
Old 07-26-2005, 09:23 AM   #7
samel_tvom
Member
 
Registered: Aug 2004
Posts: 133

Original Poster
Rep: Reputation: 15
oh, the counting was not the thing. I just had len=${#array[*]} to make sure all of the "filenames from ls" got it's own index in the array, so if I had a directory with x files in it, echo $len would output x.

If I had a directory with x files, all with on whitespace in their names, the output from echo $len would be 2x and that's wrong. But now it's solved.

The reason why I want to use this with find, is that I wanted to improve my script that makes a playlist for mplayer. We all know how frustrating it is if you watch a TV-show and one episode is finished and you want to play the next.
With my script (the old one) you go into a directory and type
$playlist episode4.avi
and a playlist will be made from episode4.avi to the last file in the directory.
 
Old 07-26-2005, 10:08 AM   #8
samel_tvom
Member
 
Registered: Aug 2004
Posts: 133

Original Poster
Rep: Reputation: 15
my script now looks like this


#!/bin/bash
IFS='
'
types="*\.avi|*\.rm|*\.mpeg|*\.mpg|*\.mp3|*\.mp3"
playlist=(`find -name "*" | egrep -i "$types" | sort`)
length=${#playlist[*]}

if [ "x$1" != "x" ]; then
addtoplaylist=false;
else
addtoplaylist=true;
fi

touch playlist.playlist
for (( i=0 ; $i < $length ; i=$i+1 ))
do
line=${playlist[i]}
if [ "x`echo ${playlist[i]} | grep "$1"`" != "x" ]; then
addtoplaylist=true
fi
if [ "x$addtoplaylist" = "xtrue" ]; then
echo ${playlist[i]} >> playlist.playlist
fi

done

mplayer -fs -playlist playlist.playlist
rm playlist.playlist


it works fine for my purpose (that is going into a TV-show-episode-x directory since it usually only contains 22 files or so) but if I go into my music directory (I wouldn't really use this script to play my files because there are much better programs for that).
The thing that is taking time in the while-loop is

if [ "x`echo ${playlist[i]} | grep "$1"`" != "x" ]; then
addtoplaylist=true
fi

$1 is the argument you pass to the script. That argument is the file which I want to start to make the playlist from, so it is the file I first want to watch.
If I have
if [ "x${playlist[i]}" = "x$1" ]; then
addtoplaylist=true
fi

then I have to use ./ in my first argument:
$myscript ./episodeiwanttowatch.avi
beacuse all strings in ${playlist} starts with ./

is there another way to tell if $1 is a substring of ${playlist[i]}?

Thanks for your help btw, really appreciate it!
 
Old 07-26-2005, 03:27 PM   #9
slackbull
LQ Newbie
 
Registered: Jul 2005
Distribution: Slack, LFS, "other"
Posts: 10

Rep: Reputation: 0
Instead of greping, echoing, and setting addtoplaylist, here is some ways I'd do some things. Of course everyones different, and I'm not too familiar with any standards.

The first part just exits if theres no $1 instead of using and retesting addtoplaylist.

Then use the ${variable/search/replace} ThingeeMaBob (parameter substitution I think?) to strip the ./ off the $1 variable, and the $PLAYLIST array. It won't touch the variables if their is no ./ in them. That way you can use either formats. It's a builtin type deal so should be quick.

Then just send it through those test constructs and you should be good.

I didn't mess around with the IFS in there or anything, just wanted to give a few quick examples you can work with, if you like them.

Code:
#!/bin/bash -x

# Exit if no $1
: ${1?Need a parameter dude}

# Strips ./ off if its there, leaves it alone if it's not.
# Do it here instead of in the loop.
FILENAME=${1/\.\//}

# Get your array of filenames
PLAYLIST=(`find -name "*"`)

# Test constructs the same as if/then/else
for i in ${PLAYLIST[*]/\.\//}; do

    [ $FILENAME == $i ] && echo "Yeah, it matches"  || echo "No match"
    
done
 
Old 07-26-2005, 05:41 PM   #10
twantrd
Senior Member
 
Registered: Nov 2002
Location: CA
Distribution: redhat 7.3
Posts: 1,438

Rep: Reputation: 52
This is nice.

Quote:
[samel@arch bla]$ ./array
0: array
1: foobar
What if something outputted like:

Code:
0: array
1. foobar

Which file do you want to move? 
<user selects 1>
How would one perform some action on the number that is associated to the filename?

-twantrd
 
Old 07-26-2005, 08:16 PM   #11
Matir
Moderator
 
Registered: Nov 2004
Location: San Jose, CA
Distribution: Ubuntu
Posts: 8,507

Rep: Reputation: 118Reputation: 118
Or, a oneliner that should do the same thing.... matching $1 and building a playlist. I may be wrong on your intent, sorry.
Code:
#!/bin/bash
find . -name "$1*" | egrep '[\.mp3|\.mpeg|\.mpg|\.avi]$' > MYPLAYLIST
Alter extensions to suit taste.
 
Old 07-26-2005, 10:43 PM   #12
twantrd
Senior Member
 
Registered: Nov 2002
Location: CA
Distribution: redhat 7.3
Posts: 1,438

Rep: Reputation: 52
Thanks for the help matir but that wasn't what i wanted . What i wanted was:

1. list files in a directory
2. prepend numbers to them
3. Prompt the user which file they want
4. Perform some command based on the input

So, for example...it would spit out:

Code:
1. file.txt
2. file1.txt
3. file2.txt

Which file do you pick?
So, the script will echo "Which file do you pick?" and wait for input. If the user inputs "1", an action will be performed on file.txt. Therefore, I don't know how to write the input that is associated with the filename piece. Thanks...

-twantrd
 
Old 07-26-2005, 10:55 PM   #13
Matir
Moderator
 
Registered: Nov 2004
Location: San Jose, CA
Distribution: Ubuntu
Posts: 8,507

Rep: Reputation: 118Reputation: 118
twantrd: sorry, my response was to the OP, not you (not that I mind helping you).

Using code from some of the early posts, just do something with "read ITEMNUM" and then access ${arrayname[$ITEMNUM]}
 
Old 07-27-2005, 02:25 AM   #14
twantrd
Senior Member
 
Registered: Nov 2002
Location: CA
Distribution: redhat 7.3
Posts: 1,438

Rep: Reputation: 52
Oh duh, I should've read up more about accessing elements in arrays. Thanks Matir!

-twantrd
 
Old 07-28-2005, 02:26 PM   #15
samel_tvom
Member
 
Registered: Aug 2004
Posts: 133

Original Poster
Rep: Reputation: 15
Thanks guys, I'll check out FILENAME=${1/\.\//}
and see if iy helps me, now I have an ugly hack,
if [ "x${playlist[i]}" = "x$1" -o "x${playlist[i]}" = "x./$1" ]; then ...
it works fine and it doesn't have to be more complicated.

now my script looks like this. It's really fast, it makes a playlist over 300 files in less than 1 second (compared to the 10 second with grepping echos...)

#!/bin/bash
IFS='
'
types="*\.avi|*\.rm|*\.mpeg|*\.mpg|*\.mp3|*\.mp3"
playlist=(`find -name "*" | egrep -i "$types" | sort`)
length=${#playlist[*]}

if [ "x$1" != "x" ]; then
addtoplaylist=false;
else
addtoplaylist=true;
fi

touch playlist.playlist
for (( i=0 ; $i < $length ; i=$i+1 ))
do
line=${playlist[i]}
if [ "x${playlist[i]}" = "x$1" -o "x${playlist[i]}" = "x./$1" ]; then
addtoplaylist=true
fi
if [ "x$addtoplaylist" = "xtrue" ]; then
echo ${playlist[i]} >> playlist.playlist
fi

done

mplayer -fs -playlist playlist.playlist
rm playlist.playlist

If you watch TV-series this script is nice. If you want all the files in a dir, you just don't pass an argument to the script.

have a good one!
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

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
Bash 2 dimensional array ldp Linux - General 5 07-29-2005 11:29 AM
[bash] Put words from file to array mispunt Programming 4 11-04-2004 10:53 AM
Can I put execl command output into a file? CrazyDragon Programming 1 12-17-2003 04:34 AM
store output of cmd in array? h/w Programming 6 10-09-2003 08:46 PM
array in bin/bash how to x2000koh Programming 12 05-09-2003 04:51 PM


All times are GMT -5. The time now is 03:02 PM.

Main Menu
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
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration