Як отримати абсолютний каталог файлу в bash?


82

Я написав скрипт bash, який приймає вхідний файл як аргумент і читає його.
Цей файл містить деякі шляхи (щодо його розташування) до додаткових використовуваних файлів.

Я хотів би, щоб сценарій перейшов до папки, що містить вхідний файл, для виконання подальших команд.

Отже, як отримати папку (і лише папку) із вхідного файлу? (У Linux.)


Ви вказуєте повний шлях до вхідного файлу або лише шлях відносно поточного робочого каталогу?
dmh

dhm: введення дає відносний шлях, я хочу абсолютний шлях мінус ім'я файлу.
BobMcGee

Відповіді:


152

Щоб отримати повний шлях, використовуйте:

readlink -f relative/path/to/file

Щоб отримати каталог файлу:

dirname relative/path/to/file

Ви також можете поєднати два:

dirname $(readlink -f relative/path/to/file)

Якщо readlink -fце недоступно у вашій системі, ви можете використовувати це * :

function myreadlink() {
  (
  cd "$(dirname $1)"         # or  cd "${1%/*}"
  echo "$PWD/$(basename $1)" # or  echo "$PWD/${1##*/}"
  )
}

Зверніть увагу, що якщо вам потрібно лише перейти до каталогу файлу, вказаного як відносний шлях, вам не потрібно знати абсолютний шлях, відносний шлях цілком законний, тому просто використовуйте:

cd $(dirname relative/path/to/file)

якщо ви хочете повернутися (поки сценарій запущений) до початкового шляху, використовуйте pushdзамість cdі, popdколи закінчите.


* Хоча myreadlinkвище досить добре в контексті цього питання, воно має певні обмеження щодо readlinkзапропонованого вище інструменту. Наприклад, він неправильно переходить за посиланням на файл із іншим basename.


17
Зауважте, що readlink -fна жаль, в OS X це не працює.
Еміль Сіт

Я не хочу весь шлях, я просто хочу папку. Але цього достатньо, щоб бути корисним: readlink -f relativeFileLocation | xargs
dirname

1
@EmilSit: ти маєш рацію - рідно. Але ви можете brew install coreutilsотримати його. stackoverflow.com/a/4031502/1022967 .
mpettis

3
Будь ласка, не рекомендуйте використовувати ${1%/*}або ${1##*/}як замінники dirnameі basename. Перший розривається, якщо ви надаєте йому просте ім'я файлу ( dirname foo.barправильно, буде .), а другий розбивається на каталог, що закінчується косою рисою ( basename /foo/bar/, буде, правильно, бути bar).
Каміло Мартін

1
@ChenLevy Деякі люди можуть схилятися використовувати його для того, щоб бути швидшими (я думаю basename/ dirnameне є вбудованими), і вони побачать, що це працює спочатку, але раптом зламається в якийсь момент у майбутньому, здавалося б, нічого доброго причина. Я не хотів би пропонувати це як варіант або заперечувати, що це насправді не є куленепробивною вірою.
Каміло Мартін

19

Погляньте на сторінку довідки realpath, я використовую це і щось на зразок:

CONTAININGDIR = $ (реальний шлях $ {FILEPATH% / *})

робити те, що звучить так, ніби ви намагаєтесь це зробити.


realpath робить для мене дивним, коли вирішує символічні посилання відносних
папок

Дякую! Працював у мене на Mac OS
Дмитро Клочков

6

Це буде працювати як для файлу, так і для папки:

absPath(){
    if [[ -d "$1" ]]; then
        cd "$1"
        echo "$(pwd -P)"
    else 
        cd "$(dirname "$1")"
        echo "$(pwd -P)/$(basename "$1")"
    fi
}

4
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"

Деякі пояснення:

  1. Цей сценарій отримує відносний шлях як аргумент "$1"
  2. Тоді ми отримаємо Dirname частина цього шляху (ви можете передати або реж або файл в цьому сценарії):dirname "$1"
  3. Тоді ми cd "$(dirname "$1");потрапляємо в цей відносний реж
  4. pwd -Pі отримати абсолютний шлях. -PВаріант дозволить уникнути симлінк
  5. Як завершальний крок ми echoце

Потім запустіть свій сценарій:

abs.sh your_file.txt

1

Спробуйте наш новий продукт бібліотеки Bash realpath-lib на GitHub, який ми надали спільноті для безкоштовного та необтяженого використання. Він чистий, простий і добре задокументований, тому в ньому чудово вчитися. Ви можете зробити:

get_realpath <absolute|relative|symlink|local file path>

Ця функція є ядром бібліотеки:

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

Це Bash 4+, не вимагає ніяких залежностей, а також надає get_dirname, get_filename, get_stemname та validate_path.


0

Проблема з наведеною вище відповіддю поставляється з файлами, що вводяться з "./" як "./my-file.txt"

Вирішення проблем (багатьох):

    myfile="./somefile.txt"
    FOLDER="$(dirname $(readlink -f "${ARG}"))"
    echo ${FOLDER}

-1

Я використовую readlink -f працює на Linux

так

FULL_PATH=$(readlink -f filename)
DIR=$(dirname $FULL_PATH)

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD

5
не використовуйте в Bash імена змінних великих літер, одного разу це зіткнеться з уже існуючими змінними ... о, і сьогодні цей день: чи знали ви, що змінна PWDвже існує в Bash, а її значення насправді є поточним каталогом? І також використовуйте більше цитат.
gniourf_gniourf
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.