Поведение bash при удалении каталога, в котором вы находитесь

Я использую GNU bash, version 4.3.42(1)-release (x86_64-redhat-linux-gnu) .

Я создал фиктивный hello и переехал туда. Когда-то в /tmp/hello , я удалил сам каталог.

 $ pwd /tmp/hello $ rmdir $PWD 

Я проверил, и я все еще был в этом каталоге.

 $ pwd /tmp/hello 

Затем я попытался перейти к самому каталогу, и, конечно, это было невозможно (хотя я уже был там):

 $ cd $PWD bash: cd: /tmp/hello: No such file or directory 

Но потом я попробовал cd . , поскольку он должен быть таким же, как cd $PWD . И это сработало … как-то:

 $ cd . cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory 

К моему удивлению, теперь я был в новом «чем-то»:

 $ pwd /tmp/hello/. 

И если я попытаюсь перейти в предыдущий каталог, это, конечно, не удастся:

 $ cd - bash: cd: /tmp/hello: No such file or directory 

Смешная вещь также в том, что даже на странице с man работает:

 $ man cd man: can't change directory to '': No such file or directory man: command exited with status 255: (cd && LESS=-ix8RmPm Manual page cd(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$PM Manual page cd(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$ MAN_PN=cd(1) less -s) 

Почему все это происходит?

Соглашаясь с @BinaryZebra, справедливо отметить, что этот вопрос задан раньше (например, « Удалите каталог изнутри с помощью интерфейса командной строки [дубликат] и можно процитировать ad infinitum ), а наблюдаемые функции хорошо известен.

Комментарий о поведении, относящемся к POSIX, более интересен, поскольку он может дать некоторое представление о ожидаемом поведении. Соответствующие документы POSIX

  • sh-shell, стандартный интерпретатор командного языка
  • cd – изменить рабочий каталог
  • pwd – вернуть имя рабочего каталога

В разделе « Использование приложения » cd указано

Так как cd влияет на текущую среду выполнения оболочки, она всегда предоставляется как встроенная оболочка.

В разделе Описание компакт-диска в пункте 10 приведена самая важная информация:

  1. Затем утилита cd выполняет действия, эквивалентные функции chdir (), вызываемой с curpath как аргумент пути. Если эти действия не сработают по какой-либо причине, утилита cd должна отобразить соответствующее сообщение об ошибке, а оставшаяся часть этого шага не будет выполнена. Если параметр -P не действует, переменная среды PWD должна быть установлена ​​на значение, которое curpath имеет при входе на шаг 9 (т. Е. Перед преобразованием в относительный путь). Если параметр -P действует, переменная среды PWD должна быть установлена ​​в строку, которая будет выводиться через pwd -P . Если для нового каталога или любого родителя этого каталога недостаточно разрешения для определения текущего рабочего каталога, значение переменной среды PWD не указывается.

То есть, PWD – это вывод команды cd , а не входной сигнал , и в основном игнорируется командой cd . Принять к сведению окончательное утверждение относительно «неуказанного». POSIX отказывается сообщать, что происходит с $PWD в случае сбоя вызова chdir() .

При обсуждении переменных среды для pwd примечания POSIX, касающиеся PWD :

Абсолютный путь к текущему рабочему каталогу. Если приложение устанавливает или отключает значение PWD , поведение pwd не указывается.

То есть POSIX отказывается указывать любой другой способ, которым может быть задан параметр $PWD кроме как успешное использование команды cd (в свою очередь, сопоставление успешного вызова с chdir() ).

В любом документе нет формулировки, которая может быть истолкована как указание, что путь «кэшируется», но только то, что рабочий каталог существует (предположительно как значение ) в среде выполнения оболочки . В обсуждении самого cd не упоминается возможность продолжения существования каталога без имени. Также сам документ cd не обращает внимания на несуществующий каталог. Это делается в описании функции chdir() как одна из нескольких причин сбоя. Например

[ENOENT]
Компонент пути не указывает существующий каталог или путь – пустая строка.

Описание chdir() не упоминает особого обращения с "." возможно, потому, что POSIX избегает подробного описания процедуры, с помощью которой абсолютный путь вычисляется с помощью getcwd() , например, позволяя файловым системам без "." и ".." . Если бы это было так, кто-то мог бы добавить формулировку, чтобы указать, что реализации могут рассматривать chdir(".") Как no-op.

Возвращаясь к шагу 10, он явно говорит:

Если эти действия не сработают по какой-либо причине, утилита cd должна отобразить соответствующее сообщение об ошибке, а оставшаяся часть этого шага не будет выполнена.

Обновление PWD выполняется после этого предложения, поэтому нет возможности изменить его значение в случае сбоя.

Возвращаясь к pwd , он говорит о опции -L

Если переменная среды PWD содержит абсолютный путь к текущему каталогу, который не содержит имена файлов dot или dot-dot, pwd должен записать этот путь в стандартный вывод.

и позже

Если не указано ни -L или -P , утилита pwd должна вести себя так, как если бы было указано -L .

Таким образом, существует цепочка act-as-ifs, но нигде явно не указано, что оболочка использует переменную PWD как контейнер для рабочего каталога. Скорее, есть точки, отмеченные как неуказанные, которые допускают возможность того, что какое-то приложение, совместимое с POSIX, может выбрать другой способ организации своих данных, если бы он соответствовал точкам действий как.

Повторение описания какой-либо личной интерпретации документации POSIX не поможет OP. Есть еще несколько вопросов, которые подробно объяснили POSIX. И все же этот вопрос.


Некоторые базовые описания:
A. Известной характеристикой Linux / Unix является то, что файл (каталог) может быть удален во время его использования. Вот почему вы можете продолжать смотреть фильм после того, как вы его удалите. Только при удалении inode записи в каталоге файл действительно исчез.

B. Также случается, что оболочка сохраняет внутреннее значение для реального pwd, даже если значение переменной среды PWD изменено.

Первый комментарий в этом принятом ответе @yaegashi показывает, где искать (источник pwd):

Я прав. См. Источник pwd_builtin() . Он просто печатает содержимое каталога_current_working_directory. – yaegashi 31 июля в 16:01

Как показано, оболочка по-прежнему сохраняет правильное значение pwd («/ home / user») даже начиная с нечетного значения PWD.

C. Легко понять, что ценность, хранящаяся в оболочке, может оказаться несовместимой с реальностью в некоторых случаях.

Выполнение cd . внутри стертого каталога, кажется, один из тех угловых случаев. Также кажется, что оболочка добавляет точку к последнему известному pwd при неудаче.


Ваши вопросы:

1 . Удалите директорию для PWD и выполните встроенный pwd.

 $ pwd /tmp/hello $ rmdir $PWD; pwd /tmp/hello 

Встроенный pwd сообщает значение pwd, которое оболочка сохраняет в памяти.
Внешний /bin/pwd сообщит об ошибке.
Интересно сообщить, что pwd -P (даже если встроенный для Bash) сообщит об ошибке:

 $ pwd -P pwd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory 

И, после использования встроенного pwd -P нормальный pwd также терпит неудачу. Я считаю, потому что значение pwd в памяти обновляется.

2. cd $ PWD

Затем я попытался перейти к самому каталогу, и, конечно, это было невозможно (хотя я уже был там):

  $ cd $PWD bash: cd: /tmp/hello: No such file or directory 

Любая реальная попытка следовать по пути к каталогу не удастся в тот момент, когда каталог не может быть прочитан (например, когда он не существует).

3. cd.

Но потом я попробовал cd . , поскольку он должен быть таким же, как cd $ PWD. И это сработало … как-то:

  $ cd . cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory 

Оболочка пытается получить доступ к пути, который, как он знает, является pwd, и он терпит неудачу. И это то, что сообщает оболочка, провал!. Здесь нет проблем.

4. Новый путь.

К моему удивлению, теперь я был в новом «чем-то»:

 $ pwd /tmp/hello/. 

Значение в памяти для PWD вышло из-за синхронизации с реальностью, и точка (.) Была добавлена ​​к нему. Как любое повторное использование cd . добавит точку.

 $ cd .; pwd /tmp/hello/./. 

И, совершенно неожиданно, cd .. сделает только pwd и $ PWD.

 $ cd ..; pwd; echo $PWD .. .. $ cd ..; pwd; echo $PWD ../.. ../.. 

5. Предыдущее ДН.

И если я попытаюсь перейти в предыдущий каталог, это, конечно, не удастся:

 $ cd - bash: cd: /tmp/hello: No such file or directory 

Это связано с тем, что предыдущее значение PWD (хранимое в bash в OLDPWD) не могло быть выполнено.
Это было (как вы сообщили выше) /tmp/hello/. , который (будучи cd $OLDPWD ) не может быть выполнен, и cd $OLDPWD или cd $OLDPWD .

 $ echo $OLDPWD # at this point following your exact procedure. /tmp/hello/. 

6. Персональные страницы

Смешная вещь также в том, что даже на странице с человеком не работает:

Человеческие страницы работали для меня в этот момент, ну, во всяком случае, во всех смыслах.

POSIX требует, чтобы текущий путь к каталогу был кэширован, а удаленный каталог все еще существует без имени.

cd . просто добавила эту строку в текущее кэшированное имя.