Reputation: 39
I want to ask about bash again. I want to get the file randomly from the directory. For example there are
13.525 file in 1 directory.
I was random the file and get the file
gr123.adl and for the next random I want the file
gr123.adl not to be selected again.
How should I implementing it with bash language?
Thank's for your help before
Regards Gustina M.S
Upvotes: 0
Views: 60
Reputation: 14490
I would probably look to do this in another language that has some better handling for situations like this if possible. For example, in python
you could do it like
files = os.listdir('.')
random.shuffle(files)
for path in files:
# do your code test stuff on path
Having a function that will return the next file name is tougher to do in bash
, but if you just want to operate on the files in a random order we can follow @shelter's recommendation and use arrays, combined with a randomizing function found in this answer. Here we will shuffle all the filenames in an array, then iterate over them:
shuffle() {
local i tmp size max rand
# $RANDOM % (i+1) is biased because of the limited range of $RANDOM
# Compensate by using a range which is a multiple of the array size.
size=${#array[*]}
max=$(( 32768 / size * size ))
for ((i=size-1; i>0; i--)); do
while (( (rand=$RANDOM) >= max )); do :; done
rand=$(( rand % (i+1) ))
tmp=${array[i]} array[i]=${array[rand]} array[rand]=$tmp
done
}
array=( * )
shuffle
for((i=0; i<${#array[*]}; i++ )); do
printf "Operating on %s\n" "${array[i]}"
# do whatever test makes sense on "${array[i]}"
done
if you really want a function that will return the "next" file we could do it a bit differently from above, setting a variable that we'll use as holding our current filename. So we ill replace the for loop at the bottom with another function definition and loop like so:
next_file() {
if [[ "$array_ind" -ge "${#array[*]}" ]]; then
cur=""
else
cur="${array[array_ind++]}"
fi
}
array_ind=0
# now we use next_file whenever we want `cur` to get the next file:
next_file
while [[ ! -z "$cur" ]]; do
printf -- "--%s--\n" "$cur"
next_file
done
Upvotes: 2
Reputation: 1943
If you really want this, then you need a function that will take arguments and keep track of files.
rand_file() {
track=~/${PWD##*/}.rand_file
touch $track
while read f; do
if ! grep -q "$f" $track; then
echo $f| tee -a $track
break
fi
done < <(ls |sort -R)
}
We are using a for loop, so that if we've gotten every file in the directory, it exits cleanly. We are tracking in files named after the directory, so that if the same named file is elsewhere, we don't use it as a previously returned file-- note that means you have to be using it in the PWD, you can code something better, but I'm not going to knock that part out here right now. Once all files have been returned, the function exits with nothing returned. You can delete the file in your home directory to reset the process.
Upvotes: 1
Reputation: 13239
You may try the following:
ls | sort -R | while read f; do echo $f; done
sort -R
is shuffling the files, the while
loop makes sure you get all file 1 by 1
EDIT:
If some of your files contains control characters (like \n
), you can try this:
OLDIFS=$IFS; IFS=$(echo -en "\b"); for f in $(ls -b | sort -R); do echo "$f"; done; IFS=$OLDIFS
This changes the input field separator to \b
(change it to anything suitable that doesn't match any character of all filenames).
ls -b
lists file of the with the control characters.
The for
loop is there to take files one by one.
At last, the IFS
is set to its original value.
Upvotes: 1