I'm using FreeBSD and I understand and use the -prune option on find to prevent the command descending into a subdirectory. But I find the necessary syntax very clumsy and awkward. It also doesn't prevent the pruned dir itself showing up which is fixable but annoying.

I'd like to create a short script/alias which has the effect of adding a new -noenter arg to the find command if it's given before any other primaries (for simplicity).

The aim is that I can type either of:

  • find -xs /dir -noenter '$*' -name '*.conf' -ls
  • find -x -s /dir -noenter '$*' -name '*.conf' -ls

and it'll convert the args and execute:

  • /usr/bin/find -xs /dir \( -name '$*' -prune -or \( -name '*.conf' -and ! \( -type d -and -name '$*' \) \) \) -ls
  • /usr/bin/find -x -s /dir \( SNIPPED \) -ls

but I can also type as normal any routine "find" command and it'll pass it transparently to /usr//bin/find to execute.

Logically the script needs to identify the first arg after the dir name, and then test if it's:

  • ... equal to -noenter and followed by at least 2 more args (in which case I know how to build the arg I need to pass to /usr/bin/find by knowing which args were before/after it); or
  • ... equal to -noenter but not followed by at least 2 more args (= error); or
  • ...anything else or dir was the last arg or wasn't found (= pass entire original args to /usr/bin/find unaltered).

I can do all of this except one thing - how do I code the part of the script that examines argv to tell which arg number (if any) is the dir argument?

My choice of shell is sh for scripting.


If what you want is to test for the existence and location of a specific argument, all you need to do is parse the command line and test each argument for a match with your "expected" value. So, you need to test each argument for the value -noenter and then print a line stating the position and name of the variable -noenter:

for i in `seq 1 $#`; do
    ref=`eval "echo \\$$i"`
    if [ $ref == '-noenter' ]; then echo "arg $i is $ref"; break; fi

Now the same thing, only the position of the argument -noenter is stored in $mitsos and the position of the argument before that in $mary:

for i in `seq 1 $#`; do
  ref=`eval "echo \\${$i}"`
  if [ $ref == '-noenter' ]; then
    mary=`eval "echo \\${$eva}"`

and now $mitsos has the position of -noenter or no value and $mary the value of the argument preceding -noenter. At this point, if $mary has the value /dir you know the next argument is -noenter and you may continue with your implementation of find, otherwise you use the system's version or print an error message. Finally, if you don't care for the position of -noenter you may omit the line mitsos=$i.

  • Thank you for understanding what I wanted! The answer isn't quite right though. It assumes -noenter is valid in any position and just looks for it, but that's not a correct assumption (find -x -noenter ..... should be an error). It needs to check the arg after the "dir" specifically, I think? I can probably figure it out from what you've written, so it's extremely close. Do you want to edit the answer so I can accept it, or? – Stilez Apr 4 '18 at 13:40
  • That's not a problem, because this way you'll have a variable with the position of -noenter or no variable. So, you may check the value of variable $eva, where eva=$(($mitsos-1))and if the argument at position $eva is not equal to dir you continue with your implementation, otherwise you use the system's. I'll edit the answer to reflect that. – gmelis Apr 4 '18 at 18:33
  • That kind of works. I suppose given its a custom command script/mod, mis-invocation can be left as an edge case for now :) I wouldn't like to pass it to others to use, without a bit more checking for valid syntax though, but that's just my feeling :) – Stilez Apr 6 '18 at 13:19
  • The method is right but I just found the eval statement is wrong. doesn't handle multiple spaces, or >=10 args. Based on yours, these handle the issues: eval 'echo "${'$i'}" ' (output) or eval 'value="${'$i'}" ' (assignment). Note ending+restarting the quote marks to include "$i" as a value instead of backslash+$ repeated. Still doesn't handle shell lookalikes on the command line eg \( or |, not sure what can be done about this. Credit is all yours but if it's easy can you update to fix, or at least update to note the exceptions, so others get a better outcome if they use this answer? – Stilez Apr 7 '18 at 4:09
  • I updated the answer for more than 9 arguments; you can just use curly braces if you don't use single quotes, which can sometimes be bothersome. The escapes will probably be more of the problem, and the pipe is most probably a deal breaker, but I'll see if I can figure a way around the first, at least. – gmelis Apr 7 '18 at 20:24

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.