/dev/" /> /dev/" /> /dev/"/>

bash script not playing nicely with other command line utilities

156 views Asked by At

I created and added the following bash function to my bash config script a few weeks ago:

cd() {
  if [ "$PS1" ]
    then
    if [ "$1" ]
      then pushd "$1" >/dev/null && ls $LS_OPTIONS
      else pushd >/dev/null && ls $LS_OPTIONS
    fi
  else
    if [ "$1" ]
      then pushd "$1" >/dev/null
      else pushd >/dev/null
    fi
  fi
}

I haven't had problems with it until recently when it has prevented some other commands from behaving properly and I have to comment out the function. For example, when trying to clone a heroku app, I got:

environment: line 8: pushd: -P: invalid number
pushd: usage: pushd [-n] [+N | -N | dir]
environment: line 8: pushd: -P: invalid number
pushd: usage: pushd [-n] [+N | -N | dir]
environment: line 10: pushd: no other directory

And when trying to use rbenv to install ruby, it would throw an error, something like "pwd did not return a directory", until I commented out this function.

I know just enough bash to be dangerous and I'm not sure what in the function might be causing the headaches.

1

There are 1 answers

0
chepner On BEST ANSWER

Overriding cd means that any code that expects the "regular" cd will use your function instead. The first problem is the that your function assumes the first argument will be the directory, but your error indicates that some uses are passing different options (like -P) as the first argument. You can fix this rather easily, simply by passing all arguments instead of just the first one. This also handles the zero argument case at the same time.

cd() {
  if [ "$PS1" ]
    then
    pushd "$@" >/dev/null && ls $LS_OPTIONS
  else
    pushd "$@" >/dev/null
  fi
}

However, the -P in the error message indicates the next problem. cd and pushd don't take the same options, so code assuming it is calling cd can pass options that pushd doesn't recognize.

$ help cd | head -1
cd: cd [-L|[-P [-e]] [-@]] [dir]
$ help pushd | head -1
pushd: pushd [-n] [+N | -N | dir]

pushd, however, can add to the directory stack without changing the directory, so you could use both commands in your function. The builtin command lets you call the original cd without getting stuck in infinite recursion.

cd () {
  # Identify the directory argument, if any
  for arg; do
    case $arg in
      -L | -P | -e | -@) ;;
      *) dir="$arg"; break ;;
    esac
  done

  # Process the real cd command
  builtin cd "$@" && 
    if [ "$dir" ]; then
      # If there was a directory argument, push it on the stack
      pushd -n "$dir"
    fi &&
    if [ "$PS1 " ]; then
      # If it's an interactive shell, run ls
      ls $LS_OPTIONS
    fi
}

It may be simpler, though, to acknowledge that you really want to override pushd and train yourself to use it instead of cd.

pushd () {
  builtin pushd "$@" &&
    if [ "$PS1" ]; then
      ls $LS_OPTIONS
    fi
}