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.

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

2 Answers 2

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
  • 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 {} + Commented Jun 26, 2019 at 6:11
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .