Две точки

    скриншот консоли, который рвет шаблон

    На картинке выше вы можете наблюдать, как ls считает, что linkylink/.. это не то же самое, что текущий каталог. При этом cd, кажется, с ним не согласен.

    Начну рассказ со всем знакомых веб-адресов, которые похожи на системные пути.

    Две точки в путях URI (в вебе)


    Интерпретация точек описана в секции 5.2.4 RFC 3986.
    Работает это так: каждый сегмент из двух точек уничтожает предыдущий сегмент:

    /a/b/c/../../g <=> /a/g

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

    example.com/../../../etc/passwd <=> example.com/etc/passwd

    Правила были придуманы, чтобы относительные пути (../img/pic.png) можно было преобразовывать в абсолютные префиксом из uri-контекста:

    1. /a/css/index.css ссылается на ../img/pic.png
    2. в /a/css/index.css уничтожается все после последнего слеша => /a/css/
    3. ../img/pic.png прибавляется к /a/css/ => /a/css/../img/pic.png
    4. точки интерпретируются => /a/img/pic.png

    Эти операции обычно делаются браузером, когда он преобразовывает относительные uri в абсолютные. Также, согласно стандарту, операция уничтожения точек должна выполняться при приведении uri к каноничному виду — нормализации — в том числе абсолютных uri.

    Веб-сервера в дикой природе не сталкиваются с запросами содержащими точки и каждый обрабатывает их по-своему.

    В целом, исходя из правила нормализации следует, что в uri вида 'http://example.com/a/b/../c' 'b' не обязана существовать.

    Две точки в шелле


    Схожим образом себя ведет шелловая команда cd: две точки всегда ведут на один сегмент путя выше, как бы отменяя предыдущий переход в подкаталог. Но, в отличие от uri, шелл проверяет существование промежуточных каталогов.

    Если вы считаете первое естественным, то эта публикация для вас. На самом деле шелл эмулирует такое поведение cd: во всех остальных местах *nix ".." работает по-другому.


    Даже встроенная команда source (или ее синоним ".") имеет отличное поведение от cd

    Разница проявляется на символьных ссылках на каталоги: для cd переход по такой ссылке обратим через "..", тогда как остальная система будет воспринимать ".." как физического родителя каталога, на который ссылка указывает.

    По-другому первое поведение называется logical (-L у pwd и cd), в противоположность physical (-P).

    Две точки в файловой системе


    В *nix ".." — это реальный подкаталог, единственный физический родитель, независимый от символьных ссылок. Если посмотреть внутрь файловой системы ext2, то описание подкаталогов ".." и "." ничем не будет отличаться от других, кроме того, что они перечисляются вначале.

    В некоторых случаях (например, в FreeBSD) этот факт даже используется для вычисления пути к рабочему каталогу (который там отдельно не хранится), через последовательные переходы по "..".

    Переход по некоторому пути — это то же самое, что последовательный переход в подкаталоги.

    Нетрудно заметить, что если мы попали в некоторый каталог по символьной ссылке, то у нас не останется информации, чтобы вернуться: ".." будет указывать в физического родителя. Но как тогда cd работает с логическими путями, включающими в себя симлинки? Для этого шелл запоминает путь, по которому он пришел в каталог. Логический путь, построенный шеллом, доступен через переменную окружения $PWD или через pwd [-L].


    Две точки требуют внимания, особенно в скриптах.

    PS: Символьных ссылок на каталоги очень много в sysfs:

    bug@earth /sys/class/net/lo % ls
    addr_assign_type  flags              phys_port_id
    address           gro_flush_timeout  phys_switch_id
    addr_len          ifalias            power
    ...
    bug@earth /sys/class/net/lo % ls ..
    lo
    bug@earth /sys/class/net/lo % pwd -P; pwd -L
    /sys/devices/virtual/net/lo
    /sys/class/net/lo
    bug@earth /sys/class/net/lo % cd ..; ls
    eth0  lo
    
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 18

      0
      Точки в MySQL :)
        +5
        Хмм…
        1. «Вообще, в *nix нигде не хранится путь до рабочего каталога процесса, а вместо этого для каждого процесса система запоминает inode (идентификатор файла) текущего и корневого каталогов. Когда где-нибудь требуется путь, он вычисляется последовательными переходами вверх по файловой иерархии, от текущего каталога, до корня, запоминая каждый шаг.»
        Linux конечно не unix, однако в /proc/idПроцесса путь хранится в виде симлинка exec

        Кроме того у Вас не указано, что же такое linkylink?
          0
          Надо было его назвать symlink, статья стала бы понятней…
            0
            Если вы про /proc/PID/exe, то он хранится как inode каталога + inode файла, в этом можно убедиться вот так:

            bug@earth ~ % echo $$
            1459
            bug@earth ~ % ls -l /proc/1459/exe
            lrwxrwxrwx 1 bug bug 0 Jan 29 01:37 /proc/1459/exe -> /bin/zsh5
            bug@earth ~ % sudo mv /bin/zsh5 foo
            bug@earth ~ % ls -l /proc/1459/exe
            lrwxrwxrwx 1 bug bug 0 Jan 29 01:37 /proc/1459/exe -> /home/bug/foo

            Путь к exe вычисляется каждый раз.

            А linkylink это как раз символьная ссылка на каталог, на которых видна разница в поведении cd и других утилит.
              0
              bug@mars:~/bar/five$ ls -l linkylink
              lrwxr-xr-x  1 bug  staff  13 01 янв  2016 linkylink -> ../six/seven/
                +1
                Нужно это в выводе на картинках сделать, а то действительно непонятно.
                  0
                  Готово.
                +1
                Хм, я был очень неправ. Оно действительно хранится как путь и каким-то образом обновляется.

                И даже больше, getcwd тоже читает готовый путь.

                Поправлю в статье.
                  0
                  Возможно что вычисление пути через переход к корню я видел в какой-то реализации libc.
              +2
              И тут мне вспомнился анекдот про два путя
                +3
                Травите для незнающих
                  +2
                  Возможно: «Если вас съели — не отчаивайтесь: у вас всегда есть два выхода.»
                  0
                  «Пока я с вами тут шутю, поезд на Воркутю тю-тю»?
                  0
                  если сделать:
                  rm -rf /*
                  

                  — то команда cd будет единственной командой, которая останется работать, казалось бы почему?)
                    +2
                    Попробовал — нет, pwd, например, тоже работает. Но вот браузер уже действительно почему-то не запускается.
                      +2
                      Ну вообще да, не единственная, вот все которые останутся:
                      Скрытый текст
                      !          builtin    cp         esac       function   let        pwd        source     ulimit
                      ./         caller     declare    eval       getopts    ll         read       suspend    umask
                      :          case       dirs       exec       hash       local      readarray  test       unalias
                      [          cd         disown     exit       help       logout     readonly   then       unset
                      [[         command    do         export     history    ls         return     time       until
                      ]]         compgen    done       false      if         mapfile    rm         times      wait
                      alias      complete   echo       fc         in         mv         select     trap       while
                      bg         compopt    elif       fg         jobs       popd       set        true       {
                      bind       continue   else       fi         kill       printf     shift      type       }
                      break      coproc     enable     for        l.         pushd      shopt      typeset    
                      

                      Подозреваю, что все потому что они подгружаются вместе с шеллом и работают внутри него, не порождая новый процесс.
                        +1
                        Они встроены в шелл (причем pwd бывает как встроенная, так и внешняя).
                        А вот реализовать cd как внешнюю утилиту невозможно (как культурно можно поменять рабочий каталог у другого процесса?).
                          0
                          Ещё останутся самые важные для попытки что-то починить (не после rm -rf /*, конечно) — это '|', '>', '>>', '<'.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое