Относительные пути, основанные на расположении файла вместо текущего рабочего каталога [дубликат]


этот вопрос уже есть ответ здесь:

  • Получение исходного каталога скрипта Bash изнутри 51 ответы

дано:

some.txt
dir
 |-cat.sh

С cat.sh имея содержание:

cat ../some.txt
под управлением ./cat.sh внутри dir работает во время работы ./dir/cat.sh на том же уровне, что и dir нет. Я ожидаю, что это будет связано с различными рабочими каталогами. Есть ли легко было сделать путь ../some.txt относительно местоположения cat.sh?
3 55

3 ответа:

что вы хотите сделать, это получить абсолютный путь к скрипту (доступен через ${BASH_SOURCE[0]}) и затем использовать это, чтобы получить родительский каталог и cd в начале скрипта.

#!/bin/bash
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )

cd "$parent_path"
cat ../some.text

это сделает ваш сценарий оболочки работать независимо от того, где вы его вызываете. Каждый раз, когда вы запускаете его, это будет, как если бы вы бежали ./cat.sh внутри dir.

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

ответ @ Martin Konecny дает правильный ответ, но - как он упоминает - он работает только в том случае, если фактический скрипт не вызывается через ссылка проживающего в разное каталог.

этот ответ охватывает этот случай: a решение, которое также работает, когда скрипт вызывается через ссылка или даже цепочка символических ссылок:


Linux / GNU readlink устранение:

если ваш скрипт должен работать на Linux только или вы знаете, что GNU readlink находится в $PATH используйте readlink -f, что удобно разрешает символическую ссылку на его ultimate цель:

 scriptDir=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")

обратите внимание, что GNU readlink имеет 3 связанные опции для разрешения символической ссылки на полный путь конечной цели:-f (--canonicalize),-e (--canonicalize-existing), и -m (--canonicalize-missing) - см. man readlink.
Поскольку цель по определению существует в этом сценарии, можно использовать любой из трех вариантов; я выбрал -f здесь, потому что это самый известный один.


мульти-(Unix-like -) платформенное решение (включая платформы с только POSIX набор утилит):

если ваш скрипт должен работать на любой платформе, что:

  • есть readlink утилиты, но не хватает -f вариант (в смысле GNU разрешения символической ссылки на ее конечную цель) - например, macOS.

    • macOS использует старая версия реализации BSD readlink; обратите внимание, что последние версии FreeBSD / PC-BSD do поддержка -f.
  • даже не имеет readlink, но и POSIX-совместимое служебные программы - например, HP-UX (спасибо, @Charles Даффи.)

следующее решение, вдохновленноеhttps://stackoverflow.com/a/1116890/45375, определяет вспомогательная функция оболочки,rreadlink(), который разрешает данную символическую ссылку на ее конечную цель в цикле-эта функция фактически POSIX-совместимая реализация GNU readlink ' s -e опции, которая составляет аналогично , за исключением того, что конечная цель должна .

Примечание: функция bash функция, и является POSIX-совместимым только в том смысле, что используются только утилиты POSIX с POSIX-совместимыми опциями. Для версии этой функции написано в POSIX-совместимом коде оболочки (для /bin/sh), см. здесь.

  • если readlink доступно, используется (без опций) - true на большинстве современная платформа.

  • в противном случае, выход из ls -l анализируется, что является единственным POSIX-совместимым способом определения цели символьной ссылки.
    будьте осторожны: это сломается, если имя файла или Путь содержит литеральную подстроку -> - что, однако, маловероятно.
    (Обратите внимание, что платформы, которые не хватает readlink может по-прежнему предоставлять другие, не POSIX методы для разрешения символьной ссылки; например, @Charles Duffy упоминает HP-UX's find утилиты поддержка %l char формат. с его -printf первичный; в интересах краткости функция не пытается обнаружить такие случаи.)

  • устанавливаемую утилиты (скрипт) форма функции ниже (с дополнительной функциональностью) можно найти как rreadlink в реестре НПМ; на Linux и macOS, установите его с [sudo] npm install -g rreadlink; на других платформах (предполагая, что у них есть bash), следуя руководство по установке инструкции.

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

#!/usr/bin/env bash

# Helper function.
rreadlink() ( # execute function in a *subshell* to localize the effect of `cd`, ...

  local target= fname targetDir readlinkexe=$(command -v readlink) CDPATH= 

  # Since we'll be using `command` below for a predictable execution
  # environment, we make sure that it has its original meaning.
  { \unalias command; \unset -f command; } &>/dev/null

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [[ -L $target || -e $target ]] || { command printf '%s\n' "$FUNCNAME: ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [[ $fname == '/' ]] && fname='' # !! curiously, `basename /` returns '/'
      if [[ -L $fname ]]; then
        # Extract [next] target path, which is defined
        # relative to the symlink's own directory.
        if [[ -n $readlinkexe ]]; then # Use `readlink`.
          target=$("$readlinkexe" -- "$fname")
        else # `readlink` utility not available.
          # Parse `ls -l` output, which, unfortunately, is the only POSIX-compliant 
          # way to determine a symlink's target. Hypothetically, this can break with
          # filenames containig literal ' -> ' and embedded newlines.
          target=$(command ls -l -- "$fname")
          target=${target#* -> }
        fi
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we
  # have a normalized path.
  if [[ $fname == '.' ]]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [[ $fname == '..' ]]; then
    # Caveat: something like /var/.. will resolve to /private (assuming
    # /var@ -> /private/var), i.e. the '..' is applied AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

# Determine ultimate script dir. using the helper function.
# Note that the helper function returns a canonical path.
scriptDir=$(dirname -- "$(rreadlink "$BASH_SOURCE")")

только одна строка будет в порядке.

cat "`dirname `"/../some.txt