0

I have a number of folders, each with audio files in it. I want to pick out 30% of each of this files and cut them (not copy) to another folder. I can see this post which can help me do this given that I know the number of files in each folder. Sadly the number may change and I want a single piped bash line that can do this. Is this possible? How do I choose 30% of the files and cut them to another folder?

havakok
  • 237
  • 3
  • 8

2 Answers2

5

With bash 4.4+ and on a GNU system, you can do:

readarray -td '' files < <(
  shopt -s nullglob dotglob
  printf '%s\0' * | sort -Rz
)

to populate the $files array with a shuffled list of all the files in the current directory.

Then you can move 30% of them with something like:

echo mv -- "${files[@]:0:${#files[@]}*30/100}" /target/directory/

(remove the echo when you're satisfied it's going to do what you want).

The equivalent in the zsh shell could be something like:

files=(*(NDnoe['REPLY=$RANDOM']))
echo mv -- $files[1,$#files*30/100] /target/directory/

That's the same approach, only terser and not needing external utilities. Translation:

  • shopt -s nullglob -> N glob qualifier (create an empty array when there's no file).
  • shopt -s dotglob -> D glob qualifier (do not exclude files whose name start with a dot).
  • GNU sort -Rz: noe['REPLY=$RANDOM'] (shuffle the list by sorting using a random order).
  • ${array[@]:offset:length} -> $array[first,last] (zsh now also supports the Korn shell syntax, but I find the zsh one more legible).
  • With bash we use NUL delimited records (-d ''/-z/\0) to be able to deal with arbitrary file names. It's not needed in zsh as the list is never transformed to a single string/stream.
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
1
ls | shuf -n $(( $(ls | wc -l) *  30 / 100))

All you need to do is to find the number of files and the percentage separately. For this

  1. $(ls | wc -l) will first calculate the number of files in the directory
  2. $(( x * 30 /100)) will do the arithmetic operation of finding the 30% of that number
  3. Finally , that number is passed to shuf -n to get a random list of files
amisax
  • 2,957
  • 17
  • 23
  • 2
    `ls | wc -l` will count the number of lines returned from `ls`, which may be the same as the number of files if no filename contains any newlines. Use `set -- *; echo "$#"` to count the number of names that `*` expands to instead. – Kusalananda Sep 23 '19 at 10:22
  • or you can use `ls -b` instead – αғsнιη Sep 23 '19 at 10:44