2

I have a findn function:

findn () {
    find . -iname "*$1*"
}

Using this function has one downside that I cannot use -print0 | xargs -0 command (I am using mac) following findn filename to extend the functionality of the find command if the filename contains empty spaces.

So, is there anyway I can keep both the functions of handy -iname "*$1*" and | xargs command at the same time?

I was thinking of using an alias to do it, but it doesn't have to be an alias.

  • What problem did you have using xargs with the function? – muru Jun 26 at 3:27
2

One way with GNU find or compatible (-iname is already a GNU extension anyway) could be to define the function as:

findn() (
  if [ -t 1 ]; then # if the output goes to a terminal
    action=-print  # simple print for the user to see
  else
    action=-print0 # NUL-delimited records so the output can be post-processed
  fi
  first=true
  for arg do
    if "$first"; then
      set -- "$@" '('
      first=false
    else
      set -- "$@" -o
    fi
    set -- "$@" -iname "*$arg*"
    shift
  done
  "$first" || set -- "$@" ')'
  exec find . "$@" "$action"
)

Then you can use it as:

findn foo bar

To see the file names that contain foo or bar (change the -o to -a above if you want instead the ones that contain both foo and bar).

And:

findn foo bar | xargs -r0 cat

If you want to apply a command on each file found by findn.

For a variant that does both and and not:

findn() (
  if [ -t 1 ]; then # if the output goes to a terminal
    action=-print  # simple print for the user to see
  else
    action=-print0 # NUL-delimited records so the output can be post-processed
  fi
  first=true
  for arg do
    if "$first"; then
      set -- "$@" '('
      first=false
    else
      set -- "$@"
    fi
    if [ "$arg" = ! ]; then
      set -- "$@" !
    else
      case $arg in
        (*[][*?\\]*)
          # already contains wildcard characters, don't wrap in *
          set -- "$@" -iname "$arg"
          ;;
        (*)
          set -- "$@" -iname "*$arg*"
          ;;
      esac
    fi
    shift
  done
  "$first" || set -- "$@" ')'
  exec find . "$@" "$action"
)

And then:

findn foo bar ! baz

For the filenames that contain both foo and bar and not baz.

In that variant, I also made it so that if the argument contained a wildcard character, it was taken as-is, so you can do:

findn foo ! 'bar*'

To look for files that do not start with bar. If you're using the zsh shell, you can make an alias:

alias findn='noglob findn'

To disable globbing on that command which allows you to write:

find foo ! bar*

You may want to make that a script (here a sh script is enough as that syntax is POSIX) instead of a function, so it can be called from anywhere instead of just your shell.

4

Your solution works just fine with xargs:

$ echo "foo bar one" > foobarone
$ echo "foo bar two" > fooBARtwo
$ findn "bar"
./fooBARtwo
./foobarone
$ findn "bar" | xargs cat
foo bar two
foo bar one

Or is there anything I have missed?

And if you modify your function a little bit, you could add additional parameters to your find command:

findn () {
  local name=$1
  shift
  find . -iname "*$name*" "$@"
}

Example:

$ findn bar ! -name '*two' -print0 | xargs -0 cat
foo bar one
  • 1
    find | xargs without -print0/-0 should not be used. It fails if the file names contain white space or quotes or backslashes. No need for that non-standard -print0/-0 anyway. With your improved findn command, you can just do findn bar -exec cat {} + – Stéphane Chazelas Jun 26 at 6:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.