Получить путь к папке с файлом из пути к файлу

400

В Bash, если VAR="/home/me/mydir/file.c"как получить "/home/me/mydir"?

Talespin_Kit
источник

Ответы:

649

dirnameи basenameинструменты, которые вы ищете для извлечения компонентов пути:

$ VAR=/home/me/mydir/file.c

$ DIR=$(dirname "${VAR}")

$ echo "${DIR}"
/home/me/mydir

$ basename "${VAR}"
file.c

Они не внутренние команды Bash , но они являются частью стандарта POSIX (см dirname, basename) , и поэтому должны быть доступны на подавляющем большинстве систем , которые будут под управлением Bash.

paxdiablo
источник
4
Почему использование скобок вокруг имен переменных, а не "$ VAR", например?
user658182
1
stackoverflow.com/questions/8748831/… отвечает на поставленный выше вопрос.
user658182
1
@ user658182 В этом конкретном примере это делается по привычке, а не по необходимости.
заброшенная корзина
95
$ export VAR=/home/me/mydir/file.c
$ export DIR=${VAR%/*}
$ echo "${DIR}"
/home/me/mydir

$ echo "${VAR##*/}"
file.c

Чтобы избежать зависимости от basenameиdirname

Эммануэль Дево
источник
3
Так как оба являются частью POSIX, зависимость не должна быть проблемой.
13:30
1
orkoden, ты прав. Цель моего ответа - показать, что нет необходимости выполнять два дополнительных процесса. bash самодостаточен для варианта использования.
Эммануэль Дево
2
Я использую метод Эммануэля, потому что хочу передать имя файла или папки, а затем вычислить путь к папке. Использование этого регулярного выражения делает правильную вещь, тогда как функция dirname возвращала родительскую папку, когда я вводил папку.
AnneTheAgile
8
Однако, если в $ VAR нет информации о пути, $ {VAR% / *} / test выдает неожиданное значение, равное $ VAR / test, тогда как $ (dirname $ VAR) даст более предсказуемое и подходящее значение ./test. Это большое дело, потому что первый попытается обработать имя файла как каталог, а второй будет в порядке.
davemyron
19

На заметку, если у вас есть только имя файла или относительный путь, само dirnameпо себе не поможет. Для меня ответ в итоге оказался readlink.

fname='txtfile'    
echo $(dirname "$fname")                # output: .
echo $(readlink -f "$fname")            # output: /home/me/work/txtfile

Затем вы можете объединить два, чтобы получить только каталог.

echo $(dirname $(readlink -f "$fname")) # output: /home/me/work
jerblack
источник
1
если не существует более одного компонента пути, вы должны использовать, readlink -m "$fname"чтобы
канонизировать
7

Если вы хотите, чтобы целевые файлы были символической ссылкой, сначала вы можете проверить их и получить оригинальный файл. Условие if ниже может помочь вам.

if [ -h $file ]
then
 base=$(dirname $(readlink $file))
else
 base=$(dirname $file)
fi
Тахсин Туркоз
источник
5

Я играл с этим и придумал альтернативу.

$ VAR=/home/me/mydir/file.c

$ DIR=`echo $VAR |xargs dirname`

$ echo $DIR
/home/me/mydir

Часть, которая мне понравилась, это было легко расширить резервную копию дерева:

$ DIR=`echo $VAR |xargs dirname |xargs dirname |xargs dirname`

$ echo $DIR
/home
Eurospoofer
источник
1

Вот скрипт, который я использовал для рекурсивной обрезки. Замените $ 1 на каталог, который вы хотите, конечно.

BASEDIR="$1"
IFS=$'\n'
cd $BASEDIR
 for f in $(find . -type f -name ' *')
 do 
    DIR=$(dirname "$f")
    DIR=${DIR:1}
    cd $BASEDIR$DIR
    rename 's/^ *//' *
 done
kmchen
источник
0

Сначала я заменил /на пустое место ( ). Затем я удалил все символы перед пустым пробелом ( ). В конце я удалил первый символ, который является пустым пространством ( ).

$ VAR="/home/me/mydir/file.c"

$ echo $VAR | tr '/' ' ' | sed 's/^.* / /' | cut -c2-
file.c
Alper
источник
0
HERE=$(cd $(dirname $BASH_SOURCE) && pwd)

где вы получите полный путь с new_path = $(dirname ${BASH_SOURCE[0]}). Вы изменяете текущий каталог с помощью cd new_path и затем запускаете, pwdчтобы получить полный путь к текущему каталогу.

aerijman
источник