How do I get the file randomly from the directory without doing it redundantly in bash programming

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

Answers (3)

Eric Renouf
Eric Renouf

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

SaintHax
SaintHax

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

oliv
oliv

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

Related Questions