Unix Shell - echo a random filename in the cwd?

This is Interesting: Free IT Magazines  
Home > Archive > Unix Shell > January 2006 > echo a random filename in the cwd?





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author echo a random filename in the cwd?
usr.bin.python@gmail.com

2006-01-29, 9:31 pm

anyone know of a faster / shorter / better way of doing the following?

for x in *; do echo $RANDOM $x; done | sort -n | head -n1 | cut -d " "
-f 2

thanks.

Janis Papanagnou

2006-01-29, 9:31 pm

usr.bin.python@gmail.com wrote:
> anyone know of a faster / shorter / better way of doing the following?
>
> for x in *; do echo $RANDOM $x; done | sort -n | head -n1 | cut -d " "
> -f 2
>
> thanks.
>


Using only shell builtins...

set - * ; eval printf "%s\\\\n" \$$(( $RANDOM % $# ))


Janis
Janis Papanagnou

2006-01-29, 9:31 pm

Janis Papanagnou wrote:
> usr.bin.python@gmail.com wrote:
>
>
> Using only shell builtins...
>
> set - * ; eval printf "%s\\\\n" \$$(( $RANDOM % $# ))


I meant...

set - * ; eval printf "%s\\\\n" \$$(( $RANDOM % $# + 1 ))

Janis
usr.bin.python@gmail.com

2006-01-29, 9:31 pm

> >> anyone know of a faster / shorter / better way of doing the following?
> set - * ; eval printf "%s\\\\n" \$$(( $RANDOM % $# + 1 ))
> Janis



cool... mind explaining to me how it works?

bsh

2006-01-29, 9:31 pm

Janis Papanagnou wrote:
> Janis Papanagnou wrote:
> I meant: set - * ; eval printf "%s\\\\n" \$$(( $RANDOM % $# + 1 ))


No you didn't! ;) You meant:

set - * ; eval printf '%s\\n' \${(( RANDOM%$#+1 ))}

If there are 17 command line arguments (say) you don't
want to evaluation $17 -- that's ${1}7 in most shells.

And especially for ksh(1): doing variable substitution
within arithmetric substitution is (almost) always slower,
and problematic in some situations as well.

Also: I think using the squotes in the printf expression
is a little cleaner.

=Brian

Chris F.A. Johnson

2006-01-29, 9:31 pm

On 2006-01-26, usr.bin.python@gmail.com wrote:
> anyone know of a faster / shorter / better way of doing the following?
>
> for x in *; do echo $RANDOM $x; done | sort -n | head -n1 | cut -d " "
> -f 2


set -- *
eval "printf '%s\n' \"\${$(( $RANDOM % $# + 1 ))}\""


--
Chris F.A. Johnson, author | <http://cfaj.freeshell.org>
Shell Scripting Recipes: | My code in this post, if any,
A Problem-Solution Approach | is released under the
2005, Apress | GNU General Public Licence
Barry Margolin

2006-01-29, 9:31 pm

In article <1138244809.084677.96090@g47g2000cwa.googlegroups.com>,
usr.bin.python@gmail.com wrote:

>
>
> cool... mind explaining to me how it works?


Don't you think you'll learn more by trying to figure it out yourself?
Everything you need to know should be in the shell man page.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
John W. Krahn

2006-01-29, 9:31 pm

usr.bin.python@gmail.com wrote:
> anyone know of a faster / shorter / better way of doing the following?
>
> for x in *; do echo $RANDOM $x; done | sort -n | head -n1 | cut -d " "
> -f 2


perl -le'$x = 1; rand($x++) < 1 && ($file = $_) while <*>; print $file'


John
--
use Perl;
program
fulfillment
Stephane Chazelas

2006-01-29, 9:31 pm

On 25 Jan 2006 18:11:45 -0800, usr.bin.python@gmail.com wrote:
> anyone know of a faster / shorter / better way of doing the following?
>
> for x in *; do echo $RANDOM $x; done | sort -n | head -n1 | cut -d " "
> -f 2

[...]

(yours doesn't work properly with filenames containing spaces,
tabs, newlines or backslashes)

For shells with $RANDOM (that's not POSIX)

set ./*
shift "$(($RANDOM % $#))"

the file is in $1

The probability distribution for all the possible filenames will
not necessarily be even.


POSIXly:

awk '
BEGIN {
srand()
print ARGV[int(rand() * (ARGC - 1)) + 1]
exit
}' ./*



--
Stephane
Faz

2006-01-29, 9:31 pm

Stephane Chazelas wrote:
> On 25 Jan 2006 18:11:45 -0800, usr.bin.python@gmail.com wrote:
> [...]
>


To be honest, personally, I would stick to the op's as a 'better' way
as long as it's known the filenames won't have whitespace in them and
$RANDOM works.

It may not be faster nor shorter than all these others, but it sure is
more readable and thus maintainable..

:-)

Fazl
s/VolcanoMail/web/
s/com/de/

Janis Papanagnou

2006-01-29, 9:31 pm

bsh wrote:
> Janis Papanagnou wrote:
>
>
> No you didn't! ;) You meant:


No I didn't :-) Neither did I mean...

> set - * ; eval printf '%s\\n' \${(( RANDOM%$#+1 ))}


....because of: syntax error at line 1: `((' unexpected

I meant: \${$(( $RANDOM % $# + 1 ))}

Janis


> If there are 17 command line arguments (say) you don't
> want to evaluation $17 -- that's ${1}7 in most shells.
>
> And especially for ksh(1): doing variable substitution
> within arithmetric substitution is (almost) always slower,
> and problematic in some situations as well.
>
> Also: I think using the squotes in the printf expression
> is a little cleaner.
>
> =Brian
>

Janis Papanagnou

2006-01-29, 9:31 pm

usr.bin.python@gmail.com wrote:
>
> cool... mind explaining to me how it works?


The building blocks are...

set - * The shell expands all files and assigns their names to
$1, $2, ..., $<N>
$# The number of arguments <N>
$(( $RANDOM % $# + 1 ))
Make your arithmetic, a random number between 1 and <N>
eval ... \${ $expression }
Since the arithmetic needs to be expanded first to get
some index, you need to escape the first $ to not be
considered by the shell, and in a second "indirection"
step, which is done by eval, to get the actual argument.

Somewhat simplified the expansion steps are...

"Step 0": eval echo \${$(( $RANDOM % $# + 1 ))}
"Step 1": eval echo \${$(( 789 % 22 + 1 ))}
"Step 2": eval echo \${20}
"Step 3": echo ${20}
"Step 4": echo fileNr20


Janis
Chris F.A. Johnson

2006-01-29, 9:31 pm

On 2006-01-26, Faz wrote:
> Stephane Chazelas wrote:
>
> To be honest, personally, I would stick to the op's as a 'better' way
> as long as it's known the filenames won't have whitespace in them and
> $RANDOM works.


Spaces (or other special charactersm with the exception of
newline) are no problem if $x is quoted.

> It may not be faster nor shorter than all these others, but it sure is
> more readable and thus maintainable..


I should have resisted the temptation to make it shorter, and
written it this way:

set -- * ## Put filenames in the positional parameters
rand=$(( $RANDOM % $# + 1 )) ## Random number in range from 1 to no. of files
eval "printf '%s\n' \"\${$rand}\"" ## Print that parameter


To be portable (as many shells do not have $RANDOM) and faster than
the shell loop and pipeline, use awk to generate the random number:

set -- *
rand=$( awk -v max=$# 'BEGIN { srand()
printf "%.0f\n", rand() * (max - 1) + 1}' )
eval "printf '%s\n' \"\${$rand}\""


If you will need more than one random number within a short space
of time (less than a second), generate a list with a single call to
awk, and use them one at a time.

--
Chris F.A. Johnson, author | <http://cfaj.freeshell.org>
Shell Scripting Recipes: | My code in this post, if any,
A Problem-Solution Approach | is released under the
2005, Apress | GNU General Public Licence
bsh

2006-01-29, 9:31 pm

Janis Papanagnou wrote:
> bsh wrote:
> ...
> set - * ; eval printf '%s\\n' \${$(( RANDOM%$#+1 ))}


As carefully as I thought I checked the syntax of my
submission, a typo still occured; I did mean to have
the missing dollar character before the "((". Everything
else said is still appropos, though!

=Brian

Stephane CHAZELAS

2006-01-29, 9:31 pm

2006-01-26, 07:46(-08), Faz:
> Stephane Chazelas wrote:
>
> To be honest, personally, I would stick to the op's as a 'better' way
> as long as it's known the filenames won't have whitespace in them and
> $RANDOM works.
>
> It may not be faster nor shorter than all these others, but it sure is
> more readable and thus maintainable..

[...]

I wonder what makes you say so. First, the algorithm in the OP's
is not straightforward: prepend with a random number, sort and
pick the first looks less intuitive to me than, get the list of
files, pick a random number <n> between 1 and the size of the
list, and return the <n>th element.

Then, one may spend some time wondering why $RANDOM, $x were
left unquoted, what's the point, how would word splitting or
filename generation help here? Also, why wanting to expand ANSI
C escape sequences by using "echo"? Why skip everything after
the first space of filenames by selecting field 2 only? Why
sorting on the whole line and not only on the first field? Will
that $RANDOM work with every shell, -n1 with every head? Why
everything on one line? Too many questions.

At least:

for x in *
do
printf '%d\t%s\n' "$RANDOM" "$x"
done |
sort -k1,1n |
head -n1 |
cut -f2-

would have made more sense. Even if it doesn't work for
filenames with newlines or returns "*" if there aren't any
non-dot files in the current directory.

--
Stéphane
dave@earth.sh

2006-01-30, 8:42 am

On Fri, 27 Jan 2006 09:18:15 +0000, Stephane CHAZELAS <this.address@is.invalid> wrote:
>2006-01-26, 07:46(-08), Faz:
>[...]
>
>I wonder what makes you say so. First, the algorithm in the OP's
>is not straightforward: prepend with a random number, sort and
>pick the first looks less intuitive to me than, get the list of
>files, pick a random number <n> between 1 and the size of the
>list, and return the <n>th element.
>
>Then, one may spend some time wondering why $RANDOM, $x were
>left unquoted, what's the point, how would word splitting or
>filename generation help here? Also, why wanting to expand ANSI
>C escape sequences by using "echo"? Why skip everything after
>the first space of filenames by selecting field 2 only? Why
>sorting on the whole line and not only on the first field? Will
>that $RANDOM work with every shell, -n1 with every head? Why
>everything on one line? Too many questions.
>
>At least:
>
>for x in *
>do
> printf '%d\t%s\n' "$RANDOM" "$x"
>done |
> sort -k1,1n |
> head -n1 |
> cut -f2-
>
>would have made more sense. Even if it doesn't work for
>filenames with newlines or returns "*" if there aren't any
>non-dot files in the current directory.
>
>--
>Stéphane



If you have a Mac running OSX, you can take this
a step further with your very own iTunes Alarm Clock!
Just put it in a cronjob. You don't even have to worry
about the sound volume being too low unless your Mac
is too far from your bed. ;)


random_song() {
for f in * ; do
printf '%d\t%s\n' "$RANDOM" "$f"
done |sort -k1,1n |head -n1 |cut -f2-
}

MUSIC_DIR=/Users/dave/Music/iTunes
cd "$MUSIC_DIR"
osascript -e 'set volume 6' # range: 0-7 where 7 is highest sound volume
open -a iTunes "$MUSIC_DIR"/`random_song`


/etc/crontab:
0 7 * * 1-5 dave itunes.alarm.clock
30 8 * * 6-7 dave itunes.alarm.clock


~~~~~~~~~~~~
Dave Robbins
Earth Science
UC Santa Barbara
Chris F.A. Johnson

2006-01-30, 5:56 pm

On 2006-01-30, dave@earth.sh wrote:
>
> If you have a Mac running OSX, you can take this
> a step further with your very own iTunes Alarm Clock!
> Just put it in a cronjob. You don't even have to worry
> about the sound volume being too low unless your Mac
> is too far from your bed. ;)
>
>
> random_song() {
> for f in * ; do
> printf '%d\t%s\n' "$RANDOM" "$f"
> done |sort -k1,1n |head -n1 |cut -f2-
> }
>
> MUSIC_DIR=/Users/dave/Music/iTunes
> cd "$MUSIC_DIR"
> osascript -e 'set volume 6' # range: 0-7 where 7 is highest sound volume
> open -a iTunes "$MUSIC_DIR"/`random_song`


You don't need OSX (or sort or cut or head) to do that.

_random_song()
{
set -- *
songnum=$(( $RANDOM % $# + 1 ))
eval "_RANDOM_SONG=\${$songnum}"
}

cd $MUSIC_DIR ## Set MUSIC_DIR= to taste
xmms "$_RANDOM_SONG" & ## Or whatever player you like
xmmsctrl 85 ## Or: aumix -v 85

> /etc/crontab:
> 0 7 * * 1-5 dave itunes.alarm.clock
> 30 8 * * 6-7 dave itunes.alarm.clock


It is better to use a personal crontab file.

--
Chris F.A. Johnson, author | <http://cfaj.freeshell.org>
Shell Scripting Recipes: | My code in this post, if any,
A Problem-Solution Approach | is released under the
2005, Apress | GNU General Public Licence
Chris F.A. Johnson

2006-01-30, 5:56 pm

On 2006-01-30, Chris F.A. Johnson wrote:
> On 2006-01-30, dave@earth.sh wrote:
>
> You don't need OSX (or sort or cut or head) to do that.
>
> _random_song()
> {
> set -- *
> songnum=$(( $RANDOM % $# + 1 ))
> eval "_RANDOM_SONG=\${$songnum}"
> }
>
> cd $MUSIC_DIR ## Set MUSIC_DIR= to taste


Of course, you have to call the function:

_random_song

> xmms "$_RANDOM_SONG" & ## Or whatever player you like
> xmmsctrl 85 ## Or: aumix -v 85
>
>
> It is better to use a personal crontab file.




--
Chris F.A. Johnson, author | <http://cfaj.freeshell.org>
Shell Scripting Recipes: | My code in this post, if any,
A Problem-Solution Approach | is released under the
2005, Apress | GNU General Public Licence
David Serrano (Hue-Bond)

2006-01-30, 5:56 pm

John W. Krahn, jue20060126@08:40:43(CET):
>
> PERL -le'$x = 1; rand($x++) < 1 && ($file = $_) while <*>; print $file'


perl -le'@f=<*>;print $f[rand @f]'

In bash:

F=(*); echo ${F[ $(($RANDOM % ${#F[*]})) ]}

Neither of these work with dot files, though.


--
David Serrano
grulos

2006-01-30, 5:56 pm

usr.bin.python@gmail.com wrote:
> anyone know of a faster / shorter / better way of doing the following?
>
> for x in *; do echo $RANDOM $x; done | sort -n | head -n1 | cut -d " "
> -f 2


#!/bin/bash
b=(*); printf "%s\n" ${b[$(( RANDOM%${#b[@]} ))]}

--
http://grulos.blogspot.com/

Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com