команда bash / fish для вывода абсолютного пути к файлу

448

Вопрос: существует ли простая команда sh / bash / zsh / fish / ... для печати абсолютного пути к файлу, который я передаю?

Случай использования: Я нахожусь в директории , /a/bи я хотел бы, чтобы напечатать полный путь к файлу cв командной строке , так что я могу легко вставить его в другую программу: /a/b/c. Простая, но небольшая программа для этого, вероятно, может сэкономить мне около 5 секунд, когда дело доходит до обработки длинных путей, что в итоге складывается. Поэтому меня удивляет, что я не могу найти стандартную утилиту для этого - неужели ее нет?

Вот пример реализации, abspath.py:

#!/usr/bin/python
# Author: Diggory Hardy <diggory.hardy@gmail.com>
# Licence: public domain
# Purpose: print the absolute path of all input paths

import sys
import os.path
if len(sys.argv)>1:
    for i in range(1,len(sys.argv)):
        print os.path.abspath( sys.argv[i] )
    sys.exit(0)
else:
    print >> sys.stderr, "Usage: ",sys.argv[0]," PATH."
    sys.exit(1)
dhardy
источник
Я бы сказал, что ответ @ DennisWilliamson (с опцией -m) лучше (обычно) быть более переносимым и работать с несуществующими файлами.
TTT
Или ответ Флимма; оба являются хорошими решениями. Bannier's, однако, лучше всего отвечает на мой первоначальный вопрос.
Дарди
3
Пользователи OSX: посмотрите этот ответ
Ян

Ответы:

562

Попробуй realpath.

$ realpath example.txt
/home/username/example.txt
Бенджамин Банье
источник
133
+1, очень хорошая программа. Но обратите внимание, что это внешняя команда (а не встроенная оболочка) и может не присутствовать в каждой системе по умолчанию, т.е. вам может потребоваться ее установка. В Debian он находится в отдельном пакете с тем же именем.
Роман Чепляка
5
Я добавил на свой путь следующую информациюrealpath : github.com/westonruter/misc-cli-tools/blob/master/realpath
Уэстон Рутер,
1
@Flimm: он по-прежнему даст вам правильный путь с некоторым относительным путем. Если вы хотите узнать о существовании файла, используйте другой инструмент, например, test.
Бенджамин Баннье
14
@Dalin: попробуйте установить coreutils. Двоичный файл называется grealpath.
Тони Пеня-Альба
5
Этот инструмент был представлен в GNU coreutils 8.15 (в 2012 году), поэтому он довольно новый.
Марб
319

Попробуйте readlinkразрешить символические ссылки:

readlink -e /foo/bar/baz
Приостановлено до дальнейшего уведомления.
источник
50
Я бы предпочел использовать '-f' вместо '-e', чтобы мы могли видеть абсолютный путь к несуществующему файлу.
Глук
5
Это выглядит как самый простой для меня, но при этом быть портативным. readlink - это порт ядра gnu coreutils, поэтому он будет установлен практически на любой дистрибутив Linux, кроме некоторых встроенных.
2010 г.
14
Мне кажется, что -mэто лучший вариант для использования.
Matt Joiner
1
@Dalin: /usr/bin/readlinkна Маверикс. Или вы имели в виду, что эти опции не найдены в OS X? Кажется, что это чахлая версия BSD ...: p
иконоборчество
14
@iconoclast Эти параметры не доступны на OSX, и в соответствии с BSD readlinkстраница руководства: only the target of the symbolic link is printed. If the given argument is not a symbolic link, readlink will print nothing and exit with an errorтак readlink не будет работать для этой цели на OSX
SnoringFrog
257
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
кендырь
источник
8
Не забудьте процитировать все вещи. Если вы не понимаете, почему, попробуйте свою программу на файл a b(два пробела между a и b, SO ест один из них).
Роман Чепляка
1
также попробуйте это на "/" это превращается в "///"
Джордж
35
пока что единственное решение POSIX, которое не требует написания и компиляции исполняемого файла, поскольку readlink и realpath не являются POSIX
Сиро Сантилли (Ciro Santilli)
25
Я использовал, function realpath { echo $(cd $(dirname $1); pwd)/$(basename $1); }чтобы сделать себе realpathкоманду (в настоящее время принятый ответ) на OS X. Спасибо!
Джеймс Бедфорд
1
Я использовал этот метод для определения имен файлов журналов для скриптов:LOG=$(echo $(cd $(dirname $0); pwd)/$(basename $0 .sh).log)
DrStrangepork
85

Забудьте о readlinkи realpathкоторые могут или не могут быть установлены на вашей системе.

Развивая ответ кендырь в выше здесь выражается в виде функции:

#!/bin/bash
get_abs_filename() {
  # $1 : relative filename
  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}

затем вы можете использовать его так:

myabsfile=$(get_abs_filename "../../foo/bar/file.txt")

Как и почему это работает?

Решение использует тот факт, что встроенная pwdкоманда Bash будет печатать абсолютный путь к текущему каталогу при вызове без аргументов.

Почему мне нравится это решение?

Это портативное и не требует ни readlinkили realpathчто часто не существует по дефолту не установить данный Linux / Unix дистрибутив.

Что если dir не существует?

Как указано выше, функция потерпит неудачу и напечатает на stderr, если указанный путь к каталогу не существует. Это может быть не то, что вы хотите. Вы можете расширить функцию для обработки этой ситуации:

#!/bin/bash
get_abs_filename() {
  # $1 : relative filename
  if [ -d "$(dirname "$1")" ]; then
    echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
  fi
}

Теперь он вернет пустую строку, если один из родительских каталогов не существует.

Как вы справляетесь с ".." или "." на входе?

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

/Users/bob/Documents/..

Если вы хотите разрешить «..», вам нужно сделать скрипт следующим образом:

get_abs_filename() {
  # $1 : relative filename
  filename=$1
  parentdir=$(dirname "${filename}")

  if [ -d "${filename}" ]; then
      echo "$(cd "${filename}" && pwd)"
  elif [ -d "${parentdir}" ]; then
    echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
  fi
}
peterh
источник
Хорошая функция, но, видимо, вы не поняли вопрос. Я хотел что-то для интерактивного использования, а не сценарии.
Дарди
3
Я хотел бы добавить, что realpathпоставляется из пакета coreutils, который также содержит такие инструменты, как who, touchили cat. Это довольно стандартный набор инструментов GNU, так что вы можете быть уверены, что он установлен практически на любой машине под управлением Linux. Тем не менее, вы правы: с ним есть 2 проблемы: i) вы, скорее всего, пропустите его при установке по умолчанию на не GNU unix системы (такие как OpenBSD) ii) этот инструмент был представлен в версии 8.15 (так что он довольно новый, начиная с 2012 года), так что вы пропустите его в долгосрочных стабильных системах (например, RHEL 6).
Марбу
1
По крайней мере, кажется, что Continuum Analytics (создатели дистрибутива Anaconda Python ) понравился этот ответ. Он реализован (со ссылкой здесь) в сценарии «активации» , который используется для активации виртуальных сред, созданных инструментом conda. Так что ... хороший!
wjv
1
Это работает только для каталогов (так как их больше нет) и не может справиться с «..» и «.». Смотрите мой ответ, который должен обрабатывать все: stackoverflow.com/a/23002317/2709
Александр Климетчек
4
@marbu realpath отсутствует в Ubuntu 14.04 или Debian Wheezy. Со временем это может стать чем-то стандартным, но, конечно, не сейчас. Также обратите внимание, что ОП ничего не сказал о Linux или GNU. Баш более широко используется, чем это.
mc0e
77
$ readlink -m FILE
/path/to/FILE

Это лучше чем readlink -e FILEили realpath, потому что это работает, даже если файл не существует.

Флимм
источник
Ницца. Это также работает с файлами / каталогами, которые существуют, но не являются символическими ссылками.
спокойная мята
У меня есть ссылка для чтения доступна на OS X 10.9 (Mavericks), так что это, безусловно, лучший ответ здесь.
Кеннет Хост
6
@KennethHoste: -mопция не доступна на Маверикс.
иконоборчество
2
Определенно это не на установке DEFAULT OSX.
Фран Марсоа
работает на Linux (CentOS), спасибо, потому что RealPath не существует для него
Jimh
66

Этот относительный путь к функции оболочки конвертера абсолютного пути

  • не требует никаких утилит (только cdи pwd)
  • работает для каталогов и файлов
  • ручки ..и.
  • обрабатывает пробелы в dir или именах файлов
  • требует, чтобы файл или каталог существовал
  • ничего не возвращает, если по данному пути ничего не существует
  • обрабатывает абсолютные пути как ввод (пропускает их по существу)

Код:

function abspath() {
    # generate absolute path from relative path
    # $1     : relative filename
    # return : absolute path
    if [ -d "$1" ]; then
        # dir
        (cd "$1"; pwd)
    elif [ -f "$1" ]; then
        # file
        if [[ $1 = /* ]]; then
            echo "$1"
        elif [[ $1 == */* ]]; then
            echo "$(cd "${1%/*}"; pwd)/${1##*/}"
        else
            echo "$(pwd)/$1"
        fi
    fi
}

Образец:

# assume inside /parent/cur
abspath file.txt        => /parent/cur/file.txt
abspath .               => /parent/cur
abspath ..              => /parent
abspath ../dir/file.txt => /parent/dir/file.txt
abspath ../dir/../dir   => /parent/dir          # anything cd can handle
abspath doesnotexist    =>                      # empty result if file/dir does not exist
abspath /file.txt       => /file.txt            # handle absolute path input

Примечание: это основано на ответах от nolan6000 и bsingh , но исправляет регистр файлов.

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

Александр Климетчек
источник
Спасибо. Это то, с чем я работаю в OSX
Грег
Это не работает для каталогов с пробелами, то есть: $ abspath 'a b c/file.txt'это потому, что аргумент to cdне заключен в кавычки. Чтобы преодолеть это, вместо цитирования всего аргумента echo(есть ли причина для этих кавычек?) Указывайте только аргументы пути. Т.е. заменить echo "$(cd ${1%/*}; pwd)/${1##*/}"на echo $(cd "${1%/*}"; pwd)/${1##*/}.
Георгий
@george Спасибо за замечание, я исправил это.
Александр Климетчек
1
Я просмотрел десятки полукоксованных решений, и это определенно кажется самым кратким и точным решением только для bash. Вы можете уменьшить его еще больше, заменив echo "$(cd "$1"; pwd)"на (cd "$1"; pwd). Здесь нет необходимости для эха.
Six
@Six Правда, обновлено. Скобки ( ... )по-прежнему необходимы, поэтому это cdпроисходит в подоболочке, так как мы не хотим менять рабочий каталог для любых вызывающих abspath.
Александр Климетчек,
24

Команда findможет помочь

find $PWD -name ex*
find $PWD -name example.log

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

find $PWD

Я использую это в Solaris 10, в котором нет других упомянутых утилит.

lessthanideal
источник
1
я не впился этим комментарием при первом прочтении; Ключевым моментом здесь является то, что если вы дадите абсолютный путь к команде поиска, то она выдаст результаты в абсолютном пути. так что использование $ PWD - это абсолютный путь, по которому вы находитесь, поэтому вы получите вывод как абсолютный.
simbo1905
4
Если вы полагаетесь PWD, вы можете просто использовать $PWD/$filename.
Джонни
Этот подход может быть улучшен с помощью -maxdepth 1. Также не забывайте про цитирование - потенциальные проблемы безопасности здесь, в зависимости от того, как вы их используете.
mc0e
1
@jonny: $ PWD / $ filename не работает, если $ filename уже абсолютное.
mc0e
Это невозможно, если имя файла задано как «./filename.txt» (с начальной точкой и косой чертой).
Марк Стосберг
15

Если у вас нет утилит readlink или realpath, вы можете использовать следующую функцию, которая работает в bash и zsh (не уверен насчет остальных).

abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }

Это также работает для несуществующих файлов (как и функция python os.path.abspath).

К сожалению abspath ./../somefile, не избавляется от точек.

Глук
источник
1
Выглядит портативно для меня. С другой стороны, сломается, например, на имя файла, содержащее перевод строки.
Роман Чепляка
1
Для дальнейшего улучшения замените echo "$1"на printf "%s\n" "$1"(то же самое со вторым эхом). echoможет интерпретировать обратную косую черту внутри своих аргументов.
Роман Чепляка
Опять ты прав! На самом деле поведение команды echo в zsh отличается от bash.
Глюк
1
Строго говоря, в POSIX поведение echo не определено, если аргументы содержат обратную косую черту. Тем не менее, он определен в расширении XSI (а именно, echo должен интерпретировать escape-последовательности). Но и bash, и zsh слишком далеки от соответствия POSIX ...
Роман Чепляка,
Извините, но я не вижу смысла. Я уже предоставил ответ в виде скрипта с большим количеством функций, и это не нужно использовать в скрипте оболочки.
Дарди
10

Вот функция zsh-only, которая мне нравится за ее компактность. Он использует модификатор расширения 'A' - см. Zshexpn (1).

realpath() { for f in "$@"; do echo ${f}(:A); done }
wjv
источник
Вау, это элегантное решение !!
ShellFish
7

Существует правило , нет такого понятия , как в файл (это утверждение означает , что там может быть больше , чем один в целом, поэтому использование определенного артикля не подходит). An - это любой путь, начинающийся с корня "/" и обозначающий файл без неоднозначности независимо от рабочего каталога (см., Например, википедию ). absolute pathabsolute path

A relative path- это путь, который должен интерпретироваться, начиная с другого каталога. Это может быть рабочий каталог, если им relative pathманипулирует приложение (хотя и не обязательно). Когда он находится в символической ссылке в каталоге, он, как правило, предназначен для связи с этим каталогом (хотя пользователь может иметь в виду и другое использование).

Следовательно, абсолютный путь - это просто путь относительно корневого каталога.

Путь (абсолютный или относительный) может содержать или не содержать символические ссылки. Если это не так, это также несколько непроницаемо для изменений в структуре связи, но это не обязательно требуется или даже желательно. Некоторые люди называют canonical path(или canonical file nameили resolved path) a, absolute pathв котором все символические ссылки были разрешены, т.е. были заменены путем к тому, на что они ссылаются. Команды realpathи readlinkобе ищут канонический путь, но только realpathимеют возможность получить абсолютный путь, не удосуживаясь разрешить символические ссылки (наряду с несколькими другими опциями, чтобы получить различные типы путей, абсолютные или относительные к некоторому каталогу).

Это требует нескольких замечаний:

  1. Символьные ссылки могут быть разрешены, только если все, на что они должны ссылаться, уже создано, что, очевидно, не всегда так. Команды realpathи readlinkопции для учета этого.
  2. каталог на пути позже может стать символической ссылкой, что означает, что путь больше не является canonical. Следовательно, концепция зависит от времени (или окружающей среды).
  3. даже в идеальном случае, когда все символические ссылки могут быть разрешены, canonical pathв файле может быть больше одной ссылки по двум причинам:
    • раздел, содержащий файл, возможно, был смонтирован одновременно ( ro) в нескольких точках монтирования.
    • могут быть жесткие ссылки на файл, что означает, что файл существует в нескольких разных каталогах.

Следовательно, даже с гораздо более ограничительным определением canonical path, может быть несколько канонических путей к файлу. Это также означает, что классификатор canonicalнесколько неадекватен, поскольку обычно подразумевает понятие уникальности.

Это расширяет краткое обсуждение темы в ответе на другой похожий вопрос в Bash: получить абсолютный путь относительно

Мой вывод заключается в том, что realpathлучше разработан и гораздо более гибким, чем readlink. Единственное использование readlinkэтого не распространяется realpathна вызов без опции, возвращающей значение символической ссылки.

Babou
источник
Я не против того, чтобы меня опровергли, но мне нравится понимать, почему я могу чему-то научиться, как техническому, так и относительно того, что считается правильной практикой на сайте. Ну ... по крайней мере это побудило меня зарегистрироваться в Meta Stack Overflow, чтобы лучше понять местную социологию. Я рекомендую это. - Тем не менее, если у кого-то есть мнение о неуместности моего ответа, мне это интересно.
Бабу
1
(а) на вопрос есть правильный ответ 2 + 1/2 года назад, (б) существует (один) абсолютный путь, соответствующий относительному пути (что я и хотел), хотя, возможно, не к файлу, как вы указал. Кстати, любые комментарии о realpathпротив readlinkили даже о символических ссылках излишни к тому, что я спросил.
Дарди
1
Второй комментарий: короткие ответы очень часто полезнее длинных лекций. Переполнение стека обычно используется, чтобы задать конкретные вопросы.
Дарди
5
1- тот факт, что у вопроса был правильный ответ в течение длительного времени, не означает, что он закрыт. Вопросы не предназначены только для личного пользования. Действительно, есть значки для ответов, которые собирают больше голосов, чем подтвержденный ответ. А с новыми инструментами «лучший» ответ может со временем меняться. Многие люди используют старые ответы, доступные через веб-поиск.
Бабу
1
2 - Вы настаиваете, что существует (один) абсолютный путь, соответствующий относительному пути . Хотя я и догадываюсь, что вы подразумеваете под «соответствующим», то есть путем, полученным из оригинала посредством заданного набора преобразований, ваше утверждение все еще неверно. Существует уникальный «соответствующий» канонический путь. Слово canonicalотносится именно к этой уникальности относительно набора преобразований. Но, например, / dev / cdrom - это абсолютно хороший абсолютный путь, хотя на самом деле это ссылка на / dev / sr0. Оба указывают на одно и то же устройство. Мой оригинальный ответ указывал на соответствующую веб-статью.
Бабу
5

dogbane Ответ с описанием того, что грядет на:

#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

Объяснение:

  1. Этот скрипт получает относительный путь в качестве аргумента "$1"
  2. Затем мы получаем часть dirname этого пути (вы можете передать dir или file в этот скрипт):dirname "$1"
  3. Затем мы входим cd "$(dirname "$1")в этот относительный каталог и получаем абсолютный путь для него, выполняя pwdкоманду оболочки
  4. После этого мы добавляем basename к абсолютному пути:$(basename "$1")
  5. В качестве последнего шага мы echoэто
Евгений Коньков
источник
2

Для каталогов dirnameсрабатывает ../и возвращается ./.

Функцию nolan6000 можно изменить, чтобы исправить это:

get_abs_filename() {
  # $1 : relative filename
  if [ -d "${1%/*}" ]; then
    echo "$(cd ${1%/*}; pwd)/${1##*/}"
  fi
}
bsingh
источник
1
Добро пожаловать на ТАК! Для небольших изменений и замечаний добавление комментария к существующему ответу может быть более уместным, чем добавление ответа, особенно если в этом скопированном ответе отсутствует много другой информации исходного ответа. Как только вы набрали чуть больше репутации, вы можете добавлять комментарии к ответам других людей. А пока попробуйте собрать репутацию, задавая уникальные, ценные вопросы или предоставляя хорошие ответы.
Cfi
1
Поскольку вы имеете в виду ответ nolan6000, обратите внимание, что автор dhardy уже прокомментировал, что он не примет ответ nolan6000, потому что он не ищет сценарий. Так что, строго говоря, ваш ответ не отвечает на вопрос.
Cfi
функция может быть добавлена .profileи, следовательно, доступна для интерактивного использования.
Адиб
Это не будет работать, если $1указано простое имя файла: get_abs_filename foo-> none (или <current_dir>/foo/fooif fooявляется каталогом).
ivan_pozdeev
2

Это не ответ на вопрос, а для тех, кто занимается сценариями:

echo `cd "$1" 2>/dev/null&&pwd||(cd "$(dirname "$1")";pwd|sed "s|/*\$|/${1##*/}|")`

он обрабатывает / .. ./ и т. д. правильно. У меня тоже вроде работает на OSX

Alek
источник
2

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

#!/bin/bash
/usr/bin/find "$PWD" -maxdepth 1 -mindepth 1 -name "$1"

Я не уверен, почему, но в OS X при вызове сценария «$ PWD» расширяется до абсолютного пути. Когда команда find вызывается из командной строки, это не так. Но он делает то, что я хочу ... наслаждайся.

fred.johnsen
источник
$PWDвсегда имеет абсолютный путь рабочего каталога. Кроме того, findне будет терпеть косые черты, поэтому вы не отвечаете на вопрос, который задали. Более быстрый способ сделать то , что вы хотите сделать , было бы простоecho "$PWD/$1"
Adam Katz
2

Самое простое, если вы хотите использовать только встроенные функции:

find `pwd` -name fileName

Только два лишних слова для ввода, и это будет работать на всех системах Unix, а также OSX.

архиватор ARJ
источник
Я бы добавил -maxdepth 1раньше -name(предположим, что файл находится в текущем каталоге). Без этого он будет работать с файлами в любом подкаталоге pwd, но не с другими.
Уолтер Тросс
В самом деле, вы правы, вопрос указывает, что файл будет в текущем каталоге. Что вы имеете в виду "но не с другими"?
Ардж
Я имею в виду findне найти файл за пределами дерева каталогов внизу pwd. Например, если вы попытаетесь find . -name ../existingFile, это не удастся.
Уолтер Тросс
Справедливо, да, это предполагает, что вы хотите напечатать полный путь к файлу в вашем cwd (согласно оригинальному вопросу) или в любом месте дерева cwd (не в оригинальном вопросе).
Ардж
1
#! /bin/bash

file="$@"
realpath "$file" 2>/dev/null || eval realpath $(echo $file | sed 's/ /\\ /g')

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

$ cd && touch a\ a && rm A 2>/dev/null 
$ fullpath "a a"
/home/user/a a
$ fullpath ~/a\ a
/home/user/a a
$ fullpath A
A: No such file or directory.
моллюск
источник
Что это решает? realpath "foo bar"-> /home/myname/foo bar. Если вы не можете взять в кавычки аргументы командной строки, это не realpathвина.
ivan_pozdeev
Согласитесь, это скорее удобная обертка.
ShellFish
1

Альтернатива для получения абсолютного пути в Ruby :

realpath() {ruby -e "require 'Pathname'; puts Pathname.new('$1').realpath.to_s";}

Работает без аргументов (текущая папка) и относительного и абсолютного пути к файлу или папке в качестве аргумента.

Атика
источник
0

Ответить с Homebrew

realpath лучший ответ, но если он не установлен, сначала нужно запустить brew install coreutils чтобы установить coreutils с множеством потрясающих функций. Написание пользовательской функции и ее экспорт - это слишком много работы и риск ошибки для чего-то подобного, вот две строки:

$ brew install coreutils
$ realpath your-file-name.json 
Zorayr
источник
-1

Привет, ребята, я знаю, что это старая ветка, но я просто отправляю это для ссылки любому, кто посетил это, как я. Если я правильно понял вопрос, думаю, locate $filenameкоманда. Он отображает абсолютный путь к предоставленному файлу, но только если он существует.

icyyd
источник
4
locateутилита для поиска файлов / путей по имени Он может выполнять эту работу, но также может находить другие совпадения и может не найти существующий файл, не вызвав updatedbсначала root.
Дхарди