Good ol' -bash: cd ..: command not found
.
I have a script I wrote to traverse up to a named directory. It had been working and now for the life of me I don't know what's changed.
$ type up
up is a function
up ()
{
local arg="$@";
if [ -z "$arg" ]; then
cd ..;
return 1;
fi;
local arr=();
local sep='/';
local IFS="$sep";
local DIRS;
read -ra DIRS <<< "$(pwd)";
for ((i=${#DIRS[@]}-1; i>=0; i-- ))
do
if [ "${DIRS[i]}" = "$arg" ]; then
local first=${arr[0]};
arr=("${arr[@]:1}");
$(printf "%s" "cd $first" "${arr[@]/#/$sep}" && echo "/");
return 1;
else
arr+=('..');
fi;
done;
echo "Directory \"$arg\" could not be found in path. Check spelling and try again." 1>&2;
return 0
}
Steps
- if no arg is passed, go up a directory
- if an arg is passed:
grab the
pwd
and split on/
iterate over the list of dirs walking from the back of the queue
if the current dir doesn't match the arg, add
..
to a listif the dir matches the command line arg, unpack
..
, delimit with/
, and execute the string as a commandprintf "%s" "cd $first" "${arr[@]/#/$sep}" && echo "/" # printf "%s" "cd $first" : prints 'cd ..' (command & the first placeholder) # "${arr[@]/#/$sep}" : prints the remaining '..' joined by '/' # echo "/" : adds a trailing '/' to the relative path # which could have been included in the # previous step but separated for comprehension # Given, pwd = /Users/dev/workspace/project/foo # `up workspace` navigates to 'cd ../../'
Debugging (from the terminal)
> cd ..
# works as expected
> $(echo "cd ..")
# works as expected --> maybe something with the "/" is breaking it?
> $('cd ..')
# -bash: cd ..: command not found
> $('cd ../')
# -bash: cd ../: No such file or directory
> which cd
# /usr/bin/cd
> echo $PATH
# /usr/bin
> type cd
# cd is a shell builtin
> alias cd
# -bash: alias: cd: not found
> shopt expand_aliases
# expand_aliases on
Unsetting most of the environment variables (e.g., CDPATH) or opening a shell with a clean environment (i.e. env -i bash --noprofile --norc
) did not help in bash3.2 on OSX.
Comments
My bash is a little rusty, so feel free to give pointers on a better way, that's as maintainable. I know return
may not be necessary (or correct) and I've seen some find
implementations, which were probably more efficient, but more difficult to follow.
cd ..
to change up one directory, then do that, don't wrap it in a subshell to capture its output in a string, which is what$( )
does.if
block, but in the remaining logic it can have a variable amount of relative paths (see last comment in Step #4 for an example). It might becd ../../../../../
.dir=$(printf ...)
, orprintf -v dir ...
), and then runcd "$dir"
.$(echo "cd ../../../")
which results incd ..: command not found
. The debug statements are just for confirmation. I've also tried outputting to a variable with-v out
and that didn't prove fruitful. I'm partially troubled because this was working at one point.