Как получить абсолютный путь к файлу через Shell (BASH/ZSH/SH)?
Вопрос: Существует ли простая команда sh/bash/zsh/fish/... для вывода абсолютного пути к файлу, который я укажу?
Описание проблемы: Я нахожусь в директории /a/b и хотел бы вывести полный путь к файлу c в командной строке, чтобы я мог легко вставить его в другую программу: /a/b/c. Это просто, но небольшая программа, которая выполняет эту задачу, могла бы сэкономить мне несколько секунд при обработке длинных путей, что в итоге накапливается. Поэтому меня удивляет, что я не могу найти стандартную утилиту для этой задачи — разве её действительно нет?
Вот пример реализации, abspath.py:
#!/usr/bin/python
# Автор: Diggory Hardy <[email protected]>
# Лицензия: общественное достояние
# Цель: вывести абсолютный путь всех входящих путей
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, "Использование: ", sys.argv[0], " ПУТЬ."
sys.exit(1)
5 ответ(ов)
Попробуйте использовать команду readlink, которая позволяет разрешать символические ссылки:
readlink -e /foo/bar/baz
Этот скрипт на bash выполняет несколько операций для преобразования пути к файлу, переданному в качестве аргумента. Давайте разберем его по частям:
#! /bin/sh
Эта строка указывает операционной системе, что необходимо использовать sh для выполнения скрипта.
echo "$(...)
echo используется для вывода строки, а содержимое $() - это команда, которая будет выполнена.
cd "$(dirname -- "$1")" >/dev/null;
Сначала с помощью dirname извлекается путь к каталогу (директория) из полного пути, переданного в качестве аргумента $1. Затем происходит переход в этот каталог (cd). >/dev/null используется для подавления вывода команды, чтобы он не отображался на экране.
pwd -P
Команда pwd -P выводит полный путь к текущему рабочему каталогу после выполнения команды cd, разрешая любые символические ссылки.
/$(basename -- "$1")
Здесь basename получает имя файла из полного пути, переданного в $1, а затем добавляет этот файл к полученному пути.
Таким образом, весь скрипт возвращает абсолютный путь к файлу, переданному в качестве аргумента $1, разрешая при этом возможные символические ссылки в директориях. Это может быть полезно, если вы хотите получить "реальный" путь к файлу.
Если, например, вы хотите использовать этот скрипт, вызовите его с полным путем к файлу в качестве аргумента:
./your_script.sh /path/to/your/file.txt
Он вернет абсолютный путь к file.txt.
Вот перевод на русский в стиле ответа на вопрос на StackOverflow:
Забудьте о readlink и realpath, которые могут быть установлены или нет на вашей системе.
Расширяя ответ dogbane, вот как это можно выразить в виде функции:
#!/bin/bash
get_abs_filename() {
# $1 : относительное имя файла
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}
Теперь вы можете использовать эту функцию следующим образом:
myabsfile=$(get_abs_filename "../../foo/bar/file.txt")
Как это работает и почему?
Решение использует тот факт, что встроенная команда Bash pwd выводит абсолютный путь текущей директории, когда вызывается без аргументов.
Почему мне нравится это решение?
Оно портативно и не требует наличие readlink или realpath, которые часто отсутствуют в стандартной установке дистрибутивов Linux/Unix.
Что если директория не существует?
Как указано выше, функция выдаст ошибку и выведет сообщение на stderr, если указанный путь к директории не существует. Это может быть не тем, что вы хотите. Вы можете расширить функцию, чтобы обрабатывать эту ситуацию:
#!/bin/bash
get_abs_filename() {
# $1 : относительное имя файла
if [ -d "$(dirname "$1")" ]; then
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
fi
}
Теперь она вернет пустую строку, если одна из родительских директорий не существует.
Как вы обрабатываете завершающие '..' или '.' в вводе?
На самом деле, это дает абсолютный путь в таком случае, но не минимальный. Например, он будет выглядеть так:
/Users/bob/Documents/..
Если вы хотите разрешить '..', вам нужно изменить скрипт следующим образом:
get_abs_filename() {
# $1 : относительное имя файла
filename=$1
parentdir=$(dirname "${filename}")
if [ -d "${filename}" ]; then
echo "$(cd "${filename}" && pwd)"
elif [ -d "${parentdir}" ]; then
echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
fi
}
Надеюсь, это поможет вам в работе!
Команда readlink -m FILE возвращает абсолютный путь к файлу, указанному в FILE, преобразуя любые символические ссылки и очищая все лишние компоненты пути. Важное преимущество этой команды заключается в том, что она работает даже в случае, если файл не существует. Это делает её более удобной по сравнению с командами readlink -e FILE или realpath, которые требуют существования файла для корректной работы.
Команда find может оказаться полезной:
find $PWD -name ex*
find $PWD -name example.log
Эти команды перечисляют все файлы в текущем каталоге и подкаталогах, имена которых соответствуют указанному шаблону. Если вы ожидаете получить лишь несколько результатов (например, если это каталог, находящийся внизу дерева с небольшим количеством файлов), вы можете упростить команду до:
find $PWD
Я использую это на Solaris 10, где отсутствуют другие упомянутые утилиты.
Как выводить команды оболочки по мере их выполнения
Как указать приватный SSH-ключ для выполнения команды shell в Git?
Как узнать, в какой интерактивной оболочке я нахожусь (командная строка)
Расширение переменных внутри одинарных кавычек в команде Bash
Сравнение null и пустой строки в Bash