0

I'm trying to merge the output of 2 find commands and pipe it to the next operator. The arguments passed to both find executions are very similar so I'd like to keep it DRY

FIND_CMD_ARGS="-type f \( -iname \*.m -o -iname \*.swift \) -print0"
CMD_OUTPUT=`{ find $PROJECT_PATH -not -path '*/Pods/*' $FIND_CMD_ARGS; find . -path '*/Pods/MyProject*/*' $FIND_CMD_ARGS }`

For some reason this doesn't work and this is the output I get

find: illegal option -- n
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
       find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
find: -type f \( -iname \*.m -o -iname \*.swift \) -print0: unknown primary or operator

I think the issue is related to how the args in the FIND_CMD_ARGS environment variable are escaped but I can't figure out the right way to do it.

Any ideas? Thanks!

Rog
  • 103

1 Answers1

3

If you want a variable to store more than one argument, use an array:

find_args=(-type f \( -iname \*.m -o -iname \*.swift \) -print)
cmd_output=$(
  find "$PROJECT_PATH" ! -path '*/Pods/*' "${find_args[@]}"
  find . -path '*/Pods/MyProject*/*' "${find_args[@]}"
)

I also

  • added the missing quotes around your variable expansions
  • changed the variable name to lower case as it doesn't look like it should be an environment variable (and anyway, now that it's an array, it can't be exported to the environment).
  • changed the non-standard -not to standard ! (-iname is another non-standard predicate).
  • use the $(...) form of command substitution instead of the old deprecated `...` one.
  • changed -print0 to -print as bash variables can't store NUL bytes.

Another approach could have been to declare a function:

myfind() {
  find "$@" -type f \( -iname \*.m -o -iname \*.swift \) -print
}
cmd_output=$(
  myfind "$PROJECT_PATH" ! -path '*/Pods/*'
  myfind . -path '*/Pods/MyProject*/*'
)

Now, there's little you can do with that $cmd_output variable, as you can't reliably get the list of files back with -print instead of -print0.

With bash 4.4 or newer, you can use an array instead and use:

myfind() {
  find "$@" -type f \( -iname \*.m -o -iname \*.swift \) -print0
}
readarray -td '' files < <(
  myfind "$PROJECT_PATH" ! -path '*/Pods/*'
  myfind . -path '*/Pods/MyProject*/*'
)