Есть ли в bash встроенная команда для получения абсолютного пути относительного пути к файлу?

Просто попробуйте использовать приложение "xkill". Он предназначен именно для таких казусов.

2
23.12.2019, 15:37
4 ответа

Bash поставляется с realpathзагружаемым расширением. Начиная с bash -4.4, он должен быть установлен по умолчанию¹, поэтому вы сможете сделать:

BASH_LOADABLES_PATH=${BASH_LOADABLES_PATH:-/usr/local/lib/bash:/usr/lib/bash}
enable -f realpath realpath
realpath..
help realpath

После включения каждая загружаемая команда становится «настоящей» встроенной без дополнительных затрат на fork/exec и с возможностью манипулировать средой bash (, где это предлагается, например. mypidзагружаемый )...

Параметр -fуказывает путь к загружаемому бинарному файлу расширения, если его нет в ожидаемом месте или ваш дистрибутив не установил его правильно, вам нужно будет установить переменную BASH_LOADABLES_PATHв каталог, содержащий расширения (или используйте -fс полным путем к загружаемому файлу ). Если он установлен правильно, вам не понадобится первая строка выше.

При написании переносимых скриптов, которые предпочитают, но не требуют таких расширений, я все равно буду использовать функцию-оболочку — по аналогии с @Kusalananda — чтобы она могла вернуться к внешнему (, такому как readlinkили realpathиз coreutils).


  1. Это расширение доступно, начиная с bash -2.05, но до bash -4.4 расширения не компилировались и не устанавливались по умолчанию, хотя это просто сделать , если у вас есть работающая сборка среда (C-компилятор и т. д. ). Дистрибутивы могут включать или не включать расширения, к сожалению, RHEL 8 (и, следовательно, CentOS 8 )build bash -4.4 с преднамеренно удаленными загружаемыми расширениями.
1
27.01.2020, 21:58

Некоторые системы имеют команду «realpath», например. Убунту. Вы можете попробовать realpath ~/foo.txt, и это может дать что-то вроде /home/user/foo.txt, затем вы можете использовать basedir, чтобы обрезать имя файла.

-1
27.01.2020, 21:58

Оболочка bashне имеет встроенного -способа явного получения абсолютного пути по относительному имени пути.

В оболочке zshможно было сделать

pathname=../../home/kk/.zshenv
print -r -- $pathname:P

и получить /home/kk/.zshenvобратно (обратите внимание, что он также разрешает все символические ссылки везде, где это возможно, аналогично тому, как realpath()будет ).

В bashвы могли бы, для имени пути, обозначающего каталог, отличный от -, и предполагая, что у вас есть доступ к его родительскому каталогу, использовать

$ pathname=../../home/kk/.zshenv
$ ( OLDPWD=- CDPATH= cd -P -- "${pathname%/*}" && printf '%s/%s\n' "$PWD" "${pathname##*/}" )
/home/kk/.zshenv

То есть временно (в под-оболочке)cdв каталог, содержащий файл, а затем создать абсолютный путь, используя значение $PWDи компонент имени файла пути. -Pнеобходим, чтобы избежать специальной обработки cdкомпонентов ... Это имеет побочный эффект разрешения компонентов символических ссылок в самом каталоге. Однако, если сам файл является символической ссылкой, он не будет разрешен (в этом отличие от zshмодификатора :P).

Мы установили OLDPWDв -, чтобы обойти тот факт, что cd -P -- -выполняет chdir($OLDPWD)вместо chdir("-")(, этот трюк работает в bash, но не во всех других shреализациях ).

Для пути к каталогу проще:

$ pathname=../../home/kk
$ ( OLDPWD=- CDPATH= cd -P -- "$pathname" && pwd )
/home/kk

Это, очевидно, можно было бы поместить в функцию оболочки для удобства:

myrealpath () (
    if [[ -d $1 ]]; then
        OLDPWD=- CDPATH= cd -P -- "$1" && pwd
    else
        OLDPWD=- CDPATH= cd -P -- "${1%/*}" && printf '%s/%s\n' "$PWD" "${1##*/}"
    fi
)

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

Проверка этой функции:

$ myrealpath../../home/kk
/home/kk
$ myrealpath../../home/kk/.zshenv
/home/kk/.zshenv

В зависимости от ОС использование функции без аргументов вернет абсолютный путь к текущему каталогу или выдаст ошибку. В будущих версиях bashэто, вероятно, будет ошибкой на каждой системе, так как отказ cd ''станет требованием POSIX.

3
27.01.2020, 21:58

Я предлагаю следующее, которое с высокой вероятностью будет работать в ОС, отличных от -Linux (, включая AIX ). Чтобы вычислить абсолютный путь (, то есть путь, начинающийся с/):

abspath() {
  [[ $1 = /* ]] && printf "%s\n" "$1" || printf "%s\n" "$PWD/$1"
}

Для канонического пути (, т. е. пути со всеми развернутыми символическими ссылками):

canonical_path() {
  perl -mCwd -le ' print Cwd::abs_path $ARGV[0] ' -- "$1"
}

При тестировании с другими возможными решениями (, включая вызов realpath), вы заметите значительную разницу в зависимости от того, существует ли последний компонент пути и является ли последний компонент пути символической ссылкой.

0
27.01.2020, 21:58

Теги

Похожие вопросы