can find search multiple variations of order for specified input vars

StackOverflow https://stackoverflow.com/questions/23396875

  •  13-07-2023
  •  | 
  •  

Вопрос

I want to be able to run a find that uses all possible variations of order of a user's input values ($1 $2 $3 .. $9 or the value of $# really) to see if a file exists. But once the user enters just 3 params (search patterns) the find commands starts to get a little unwieldy:

docs=($(find /dir1 /dir2 -type f -iname "*$1*$2*$3" -o -iname "*$1*$3*$2" -o -iname "*$2*$1*$3" -o -iname "*$2*$3*$1*" -o -iname "*$3*$1*$2*" -o -iname "*$3*$2*$1*" ))

Is there a way to tell find to take specified multiple patterns and search every possible variation of order to see if a file exists? For most cases I imagine only 2-4 input params will be fed by the user, but I want to allow for searches using upto 9 input vars, and I hate to imagine what a find command when $# = 9 might look like.

Это было полезно?

Решение

I love your question. You need to generate all permutations of the positional parameters. Lots of fun:

#!/bin/bash

perms() {
   if (($#==1)); then 
      ary_perms=( "$1" )
      return
   fi
   local i
   local ary=()
   for ((i=1;i<=$#;++i)); do
      perms "${@:1:i-1}" "${@:i+1}"
      ary+=( "${ary_perms[@]/#/${!i}$sep}" )
   done
   ary_perms=( "${ary[@]}" )
}

sep='*'
perms "$@"

searchargs=()
for s in "${ary_perms[@]}"; do
   searchargs+=( -o -iname "*$s*" )
done
searchargs=( "${searchargs[@]:1}" )

find /dir1 /dir2 "${searchargs[@]}"

The function perm generates all permutations of the given arguments, in a way 100% safe regarding funny symbols (spaces, newlines, etc.). It's a straightforward recursive function so it might explode quickly if you feed it with too many arguments, but hey, you don't want to generate all the permutations of a set with 40 elements, do you?


Now this is of course a very bad method! You certainly don't want to do this.

Instead of

find /dir1 /dir2 -type f -iname "*$1*$2*$3*" -o -iname "*$1*$3*$2*" -o -iname "*$2*$1*$3*" -o -iname "*$2*$3*$1*" -o -iname "*$3*$1*$2*" -o -iname "*$3*$2*$1*"

why not just this?

find /dir1 /dir2 -type f -iname "*$1*" -iname "*$2*" -iname "*$3*"

(there's an implicit and between the -iname's).

If you need a script to build up the search command:

#!/bin/bash

searchargs=()
for i; do
    searchargs+=( -iname "*$i*" )
done
find /dir1 /dir2 -type f "${searchargs[@]}"

much better, eh?

Of course this is not strictly equivalent to the previous mumbo with permutations, if you have repeated arguments. But hey, who cares, really? code simplicity wins here.


Oh, a final remark, doing:

docs=( $(find ...) )

is very bad (think of filenames with spaces).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top