5

If below should return true but it actually fails with an error reporting too many arguments when executed in bash shell. I think I followed all the guidelines with double quotes and can't figure out why it is failing for the life of me... Any ideas?

#!/usr/bin/bash
# These are the values in the environment
#echo ">"$PS_APP_HOME"<"  => >/app/psoft/app_85909<
#echo ">"PS_CUST_HOME"<"  => >/app/psoft/cust_85909<
#echo ">"$(pwd)"<"        => >/app/psoft/app_85909<

if [ "$(pwd)" != "$PS_APP_HOME" ] -o [ "$(pwd)" != "$PS_CUST_HOME" ]
5
  • 2
    FYI you don't have to run pwd to get the current dir in bash. bash automatically keeps the $PWD variable up-to-date with the current directory.
    – cas
    Sep 9, 2022 at 5:54
  • One important thing the answers don't emphasize (yet) is: [ is a command, nothing more. [ requires ] as the last argument, so it looks like some special syntax, but it's not. [ is neither a part of the syntax of if, nor a keyword in the shell that makes the shell parse things differently. [ usually is a builtin, but it still behaves like a command. [ can be /usr/bin/[ or so. OTOH [[ is special in Bash. Sep 9, 2022 at 5:55
  • 1
    You're using the wrong logical operator, it should be AND, not OR. See stackoverflow.com/questions/26337003/…
    – Barmar
    Sep 9, 2022 at 14:50
  • I don't believe I am using the wrong logical operator. It is intended to be an or as in:
    – Gordon
    Sep 10, 2022 at 0:23
  • Barmar, I think you are right. My logic would generate a fail message even if one of the locations is correct, which was what was being checked for. Thanks for the pointer! However, don't quite get why that would trigger too many arguments error.
    – Gordon
    Sep 10, 2022 at 0:48

2 Answers 2

8
[ "$(pwd)" != "$PS_APP_HOME" ] -o [ "$(pwd)" != "$PS_CUST_HOME" ]

calls the [ command with the following as arguments:

  1. output of pwd¹,
  2. !=,
  3. the contents of the PS_APP_HOME variable,
  4. ],
  5. -o,
  6. [,
  7. the output of another invocation of pwd,
  8. !=,
  9. the contents of the PS_CUST_HOME variable, and
  10. ].

The ] is meant to be the last argument, so when [ sees -o after the first ], it is confused.

[ has a deprecated -o operator for OR, but that's meant to be used as [ some-condition -o some-other-condition ]. It should however not be used as it makes for unreliable test expressions.

Here, using OR also doesn't make sense. The current working directory cannot be at the same time something ($PS_APP_HOME) and something else ($PS_CUSTOM_HOME), so at least one of "$(pwd)" != "$PS_APP_HOME" or "$(pwd)" != "$PS_CUST_HOME" is going to be true. Presumably you meant AND instead of OR. So:

  • with standard syntax:

    if [ "$PWD" != "$PS_APP_HOME" ] && [ "$PWD" != "$PS_CUST_HOME" ]; then
      echo current directory is neither the APP nor CUST home
    fi
    

    (where we run a second [ command if the first one was successful using the && shell (not [) operator).

  • Korn-like syntax:

    if [[ $PWD != "$PS_APP_HOME" && $PWD != "$PS_CUST_HOME" ]]; then
      echo current directory is neither the APP nor CUST home
    fi
    

    or

    if [[ ! ($PWD = "$PS_APP_HOME" || $PWD = "$PS_CUST_HOME") ]]; then
      echo current directory is neither the APP nor CUST home
    fi
    

    where [[...]] is a special construct with its own conditional expression micro-language code inside which also has some && (and) / || (or), ! (not) boolean operators.

Though you could also use case:

case $PWD in
  ("$PS_APP_HOME" | "$PS_CUST_HOME") ;;
  (*) echo current directory is neither the APP nor CUST home
esac

$PWD is like $(pwd) except it's more efficient as it doesn't need to fork another process and get its output through a pipe and means it still works if the path of the current working directory ends in newline characters.²

Beware the double quotes above are important, I've only put them where they are strictly necessary (to prevent split+glob in the arguments of [ and to prevent variable values to be taken as a pattern in the argument the != / = operators of the [[...]] construct or case), though having all expansions quoted would not harm.

Instead of doing lexical comparisons, also note that ksh/bash/zsh's [[....]] and most [ implementations including the [ builtin of bash support a -ef operator, to check whether two files are the same (after symlink resolution), so you could use that instead of =:

if [[ ! (. -ef $PS_APP_HOME || . -ef $PS_CUST_HOME) ]]; then
  echo current directory is neither the APP nor CUST home
fi

Or for sh (most shs):

if [ ! . -ef "$PS_APP_HOME" ] && [ ! . -ef "$PS_CUST_HOME" ]; then
  echo current directory is neither the APP nor CUST home
fi

Here also using . which unlike $PWD or $(pwd) is guaranteed to refer to the current working directory.

That way if $PWD is /opt/app or /some/link/to/app and $PS_APP_HOME is /opt/./app or /opt/foo/../app or /opt//app for instance, that will still work.


¹ stripped of all trailing newline characters, so maybe not the current working directory.

² in some shells, $PWD might give you stale information though if the current working directory has been renamed under your feet. But then again, that's also true of pwd in some shells which just output the value of $PWD and only update it upon cd/pushd/popd.

3
  • Simply cd to the desired directory and avoid all this. If you're not there already, you have to do it anyway. If you are already there, no error is returned.
    – Wastrel
    Sep 9, 2022 at 16:41
  • I prefer the user to be in one of the target directories to confirm intent. if they are in either one of the targets in question that may not be the intended target.
    – Gordon
    Sep 10, 2022 at 0:59
  • I ended up settling with if [ "$PWD" != "$PS_APP_HOME" && "$PWD" != "$PS_CUST_HOME" ] which works just fine. Thanks.
    – Gordon
    Sep 10, 2022 at 1:08
4
if [ "$(pwd)" != "$PS_APP_HOME" ] -o [ "$(pwd)" != "$PS_CUST_HOME" ]

This is syntactically incorrect, which is why you get the error you report.

If you want to do this as one test, it should be:

if [ "$(pwd)" != "$PS_APP_HOME" -o "$(pwd)" != "$PS_CUST_HOME" ]

Or, if you want to keep the expressions completely separate and have bash handle the logical "or" for you:

if [ "$(pwd)" != "$PS_APP_HOME" ] || [ "$(pwd)" != "$PS_CUST_HOME" ]
1
  • The second form with || is generally considered preferable.
    – Barmar
    Sep 9, 2022 at 14:49

You must log in to answer this question.

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