Что такое ~ (тильда) при использовании в качестве префикса к пути?


11

Изменить: Это дубликат /programming/998626/meaning-of-tilde-in-linux-bash-not-home-directory/ . У меня нет репутации, чтобы закрыть этот вопрос как дубликат.

Я не имею в виду ~домашний каталог, а скорее это:

$ ls ~foo/bar
/some/mount/point/foo/bar

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

$ mount | ag "/dev "
devfs on /dev (devfs, local, nobrowse)
$ ls /dev/stdin
/dev/stdin
$ ls ~stdin
zsh: no such user or named directory: stdin . 
# bash has a similar error message: 
ls: ~stdin: No such file or directory

Как ~называется в этом контексте? Как это работает?

Изменить: дополнительная информация на основе некоторых комментариев ниже:

  1. Я могу подтвердить, что fooэто не имя пользователя в моей системе.
  2. При попытке автозаполнения ls -lah ~отображаются не все параметры. т.е. я могу cd ~qux, когда quxне появляется в автозаполнении. Опять quxне пользователь в моей системе.
  3. Если это имеет значение /some/mount/point, это сетевой ресурс.
  4. Все детали предполагают некоторую именованную траекторию, особенность оболочки Z для раскрытия пути, но это работает и в bash, которая, очевидно, не поддерживает такие вещи, как именованные пути оболочки Z.

5
~fooдомашний каталог пользователя foo. Если пользователь не указан, по умолчанию используется текущий пользователь.
DopeGhoti

Но в этом случае /some/mount/pointопределенно не мой домашний каталог. cd ~берет меня на - /Users/$username/какие матчи$HOME
RD

1
zshтакже, кажется, использует тильду для обозначения именованных каталогов.
DopeGhoti

Я тоже это подозревал !! За исключением некоторой причины, серия команд, которые я выложил выше, работает и в bash ( bash -c "ls ~foo/bar"), которая не имеет именованных каталогов. Более того, даже в zsh, если я проверяю env, я не вижу ни одного настроенного каталога. Я нахожусь на Mac OS, и я чувствую, что это какая-то особенность, характерная для OS X.
RD

1
Вы только сказали ~foo. Возьми реальную строку (не пример foo) и сделай grep "actual username" /etc/passwd. ~textдолжен работать только для возможных имен пользователей для входа в систему в соответствии с руководством bash (не обязательно означает, что он действительно может войти в систему; в случае пользователей системы, таких как ~lp, например). Во всех моих тестах это ~stringсоответствует stringимени пользователя.
Сергей Колодяжный

Ответы:


14

Что такое ~ foo

Цитата из руководства bash (с добавлением акцента):

Если слово начинается с символа кавычки без кавычек (`~ '), все символы, предшествующие первой косой черте без кавычек (или все символы, если нет косой черты без кавычек), считаются префиксом тильды. Если ни один из символов в Префикс тильды указан в кавычках, символы в префиксе тильды, следующие за тильдой, рассматриваются как возможное имя для входа.

~foo расширяется до fooдомашнего каталога пользователя точно так, как указано в /etc/passwd. Обратите внимание, что это может включать системные имена пользователей; это не обязательно означает, что пользователи или что они могут войти в систему локально (например, они могут войти через ключи SSH).

На самом деле, как отмечено в комментариях , bashбудет использоваться getpwnamфункция. Эта функция сама определяется POSIX стандартом, следовательно , должно существовать на большинстве Unix-подобных системах, в том числе MacOS X . Эта функция не ограничивается /etc/passwdтолько и ищет другие базы данных, такие как LDAP и NIS. Отдельная выдержка из bash исходного кода , tilde.cфайла, начиная со строки 394:

  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
     password database. */
  dirname = (char *)NULL;
#if defined (HAVE_GETPWNAM)
  user_entry = getpwnam (username);
#else
  user_entry = 0;

Практический пример

Ниже вы можете увидеть тесты с именами пользователей в моей системе. Обратите внимание на соответствующую passwdзапись и результатls ~username

$ grep '_apt' /etc/passwd
_apt:x:104:65534::/nonexistent:/bin/false
$ ls ~_apt
ls: cannot access '/nonexistent': No such file or directory
$ grep '^lp' /etc/passwd
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
$ ls ~lp
ls: cannot access '/var/spool/lpd': No such file or directory

Даже если, например, _aptучетная запись заблокирована, как показано в выводе, passwd -S aptона все равно отображается как возможное имя для входа:

_apt L 11/29/2017 0 99999 7 -1

Обратите внимание: это не особенность MacOS, а особенность оболочки.


Я сомневаюсь, что оболочка может рассчитывать на что-либо о дате истечения срока действия аккаунта, заблокирован ли он или что-то еще при расширении тильды. Например, в Linux дата истечения срока хранения хранится /etc/shadowвместе с другой защищенной аутентификационной информацией, такой как пароль. Ссылка на passwdэто как раз делает пароль недействительным: это не помешает аутентификации другими способами.
ilkkachu

@ilkkachu Да, ты прав. Я проверил это с добавлением testuserи изменением срока действия пароля. Имя пользователя по-прежнему отображается на вкладке «Завершение». Я убрал этот бит из ответа
Сергей Колодяжный

1
Единственная проблема с этим ответом заключается в том, что он цитирует bashсправочную страницу (и, кажется, старая). ОП на самом деле использует zsh(могло бы быть более понятным), хотя в комментариях упоминается, что он bashведет себя так же.
Кен Уэйн ВандерЛинде

2
Вы можете использовать getent passwd fooвместо того, grep foo /etc/passwordчтобы следовать тем же механизмам поиска, что и оболочка, возможно, включая сетевые службы каталогов, которые упоминает @Abigail.
RJHunter

1
Принято как ответ для бита типа LDAP и NIS, который в моем случае оказался правильным!
RD

5

Вкратце, почему вы ищете что-то ~foo/bar, это потому, что у вас есть имя пользователя fooв системе с папкой barв его домашнем каталоге.

Посмотрите это решение в другом сообществе, которое объясняет, почему (тильда) ~- это больше, чем просто «домашний каталог».

Если в binвашей системе есть имя пользователя , вы можете просмотреть содержимое домашнего каталога bin с помощью команды:

ls ~bin

Еще одна вещь, которую вы можете попробовать, это использовать завершение табуляции после ввода следующего в приглашении (не перевод каретки, просто используйте клавишу табуляции):

ls -lah ~ tab

чтобы увидеть список домашних каталогов пользователей, которые ~будут расширяться, если вы продолжите. Пример (усеченный) вывод вкладки завершенияls -lah ~ tab

$ ls -lah ~ [tab]
~antman/            ~games/
~bin/               ~mail/

1
+1 для завершения вкладки. Это ясно показывает все имена пользователей, с которыми bash может получить как можно больше имен для входа /etc/passwd. Это сразу помогает уточнить, что происходит, если пользователь, конечно, знает имена пользователей в своей системе.
Сергей Колодяжный

2
Спасибо за подсказку по завершению работы bash, что привело к некоторым интересным результатам автозаполнения. Несмотря на то ~foo, что я выполнял автозаполнение, я могу подтвердить, что fooв моей системе нет пользователя (и в действительности его нет /etc/passwd). Кроме того, все папки / файлы /some/mount/pointотображаются в автозаполнении, что очень интересно.
RD
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.