5

Suppose I am editing some files. I have (say) a terminal open and a gui file manager. I am currently in /my/very/long/and/winding/directory/path in the terminal. In my gui, I delete /my/very/long/and/winding/directory Back in the terminal, I try cd .. and I get an error, since the directory no longer exists. No big deal. I could type cd /my/very/long/and/winding to get back where I want to go. But if I'm too lazy to type that, is there a nice way to go back to the nearest directory that still exists?

edit: cd ../../ is one solution, in this particular case. But I'm wondering if there is a more general answer, where I don't know how many steps back I need to take.

SauceCode
  • 2,345

3 Answers3

6

To go back by one level of directory based on the directory path rather than the .. link:

cd $PWD:h

Or the portable method:

cd "${PWD%/*}"

(quotes optional in zsh; quotes optional in other shells if the directory name doesn't contain whitespace or \[*?)

Repeat the :h or /* as many times as desired to go further up in the directory hierarchy.

Alternatively, type cd $PWD then press Tab to expand $PWD to its value and manually edit the result. (Depending on your completion and line editor settings, you may need to add a / after $PWD before pressing Tab, to press the key that you've bound to expand-or-complete (default Tab) or expand-or-complete-prefix or expand-word (default Ctrl+X *).)

3

Assuming PWD is correct, one can back out in ZShell thusly.

% cd ~/tmp
% mkdir -p a/a/a/a/a/a/a/a/a/a/a
% cd !$
cd a/a/a/a/a/a/a/a/a/a/a
% rm -rf ~/tmp/a
% undir
% pwd
/Users/jmates/tmp
% 

The custom undir function does the walk-back-out-the-path-chain loop:

function undir {
  local dir
  dir=$PWD:h

  while [[ $dir != / ]]; do
    builtin cd -q $dir 2>/dev/null
    if [[ $? -eq 0 ]]; then
      dir=/
    else
      dir=$dir:h
    fi
  done
}

Note that you cannot rely on the pwd command, as that gives different results than the shell-maintained PWD variable:

% mkdir -p a/a/a/a/a/a/a/a/a/a
% cd !$
cd a/a/a/a/a/a/a/a/a/a
% rm -rf ~/tmp/a
% pwd
/Users/jmates/tmp/a/a/a/a/a/a/a/a/a/a
% cd ..
% pwd
.
% echo $PWD
/Users/jmates/tmp/a/a/a/a/a/a/a/a/a
% 

Shells that are not zsh will undoubtably require other solutions.

thrig
  • 34,938
  • I hoped there might be a 'vanilla' bash solution, without using any custom functions. If not, I guess this is the next best thing! – SauceCode Aug 25 '15 at 23:54
  • @SauceCode - maybe like this for bash: p=$(dirname "$PWD"); while [[ ! -d "$p" ]]; do p=$(dirname "$p"); done; cd "$p" – don_crissti Aug 25 '15 at 23:59
  • @don_crissti Nice. With an alias that would be quite straightforward to use. – SauceCode Aug 26 '15 at 00:05
  • Note that in zsh you can do array[-1]=() to pop an element off the end on an array. See also $dir:h to get the dirname (head) of a path. – Stéphane Chazelas Aug 26 '15 at 10:52
0

If you removed the directory you are in, cd .. will not work as the current directory exists without name and without connection to the rest of the world. So .. does no longer exist.

Every bourne shell alike should allow to write a function that creates a copy of $PWD and traverses the path as far as it still exists.

schily
  • 19,173
  • Posix shells and zsh fail as well. With regards to the bourne shell, you are mistaken. You may be right for a bourne shell before 1989. Modern shells including bourne track paths for symbolic links but this does not help here. – schily Aug 26 '15 at 12:49
  • You seem to missinterpret things. All modern shells just check where they are after chdir. All shells call chdir(".."). Ksh and zsh call chdir("$PWD/..") in addition but this of course fails as well. – schily Aug 26 '15 at 13:55
  • What I reported has been tested with bash, ksh and zsh. I tested the default behavior on Solaris. If you see different behavior, there must be constraint that causes the deviating behavior you observed. – schily Aug 26 '15 at 17:33
  • OK, that's part misunderstanding/talking about different things and Linux vs Solaris. After more checking, there's also some differences for bash between sh mode and non-sh mode. The behaviour of ksh88 and ksh93 also differs. Most of what you said and what I've said is true in some contexts and inaccurate in others. A long article would be needed here to summarize the different behaviours. See also Symbolic link recursion - what makes it “reset”? for some more info. I'll delete my comments for now. – Stéphane Chazelas Aug 26 '15 at 19:37
  • OK, I checked ksh again and found that I was too quick and oversight somethiing; ksh does a stat on $PWD/. and this of course fails as well. So I cannot see a real different behavior. The funny notice for ksh is that if you recreate a directory of the same name of the removed directory, it "works" even though the state dis not change. My impression is that ksh does not follow the rules from the POSIX cd documentation. The way I understand the POSIX standard, even cd ../.. with two directories missing should result in a "successful" chdir to what the user expects. – schily Aug 27 '15 at 09:54
  • As the POSIX standard requires to do string operations on $PWD first, before doing the actual chdir(), I am now planning to change the Bourne Shell to follow this text as it seems to be a real improvement. Given the fact that neither bash, nor ksh, nor zsh currently deal with a removed directory that is skipped by a chdir .., it seems that no shell currently follows the POSIX standard and the Bourne Shell might become the first correct one...Note that this makes sense when $PWD always holds the normalized real path and when the user likes a physical chdir ".." based on this $PWD. – schily Aug 27 '15 at 10:04
  • See the link I gave. Some shells check that $PWD still correspond to the current directory before doing a cd (and recompute $PWD). Some don't (dash and ksh93u+ on Linux at least will do a chdir(dirname($PWD)) even if the current directory has been removed or moved elsewhere). Also note that on Linux, chdir("..") works in a deleted directory (even ../.. when both directories were deleted as the kernel keeps track of that somehow). – Stéphane Chazelas Aug 27 '15 at 10:35
  • OpenSolaris added pwd() as a syscall to allow Linux userspace emulation. Solaris still behaves as expected for removed files: there is no name anymore. – schily Aug 27 '15 at 11:45
  • My main concern with that logical handling of .. (as already expressed at that linked answer) is that it's only applied to the shell's cd and pwd, not to other commands, which is possible cause of confusion. IMO, it's a ksh/POSIX misfeature. tcsh has a set symlinks = expand option to expand those .. in any unquoted argument to any command, but I don't like that idea either. Can cause bad surprises as in ssh host rm ../foo. – Stéphane Chazelas Aug 27 '15 at 11:50
  • I'll have a look at your other thread, but this may take some time as your text is very long. – schily Aug 27 '15 at 13:55