Получить последний dirname / filename в аргументе пути к файлу в Bash

228

Я пытаюсь написать хук после фиксации для SVN, который размещен на нашем сервере разработки. Моя цель - попытаться автоматически извлечь копию зафиксированного проекта в каталог, где он размещен на сервере. Однако мне нужно иметь возможность читать только последний каталог в строке каталога, переданной сценарию, чтобы оформить заказ в том же подкаталоге, где размещены наши проекты.

Например, если я сделаю SVN-коммит в проекте «example», мой скрипт получит «/ usr / local / svn / repos / example» в качестве первого аргумента. Мне нужно получить только «пример» из конца строки, а затем объединить его с другой строкой, чтобы я мог оформить заказ в «/ server / root / example» и сразу же увидеть изменения.

TJ L
источник

Ответы:

344

basename удаляет префикс каталога пути:

$ basename /usr/local/svn/repos/example
example
$ echo "/server/root/$(basename /usr/local/svn/repos/example)"
/server/root/example
STH
источник
2
Базовое имя определенно то, что я ищу. Как можно получить базовое имя аргумента, хранящегося в переменной? НапримерSUBDIR="/path/to/whatever/$(basename $1)"
TJ L
5
@ tj111: звуки , как нет $1, или $1пусто
STH
к сожалению, если вы переносите команды, basename не очень хорошая идея. просто что-то иметь в виду
DTC
98

Следующий подход может быть использован для получения любого пути имени пути:

some_path=a/b/c
echo $(basename $some_path)
echo $(basename $(dirname $some_path))
echo $(basename $(dirname $(dirname $some_path)))

Вывод:

c
b
a
Цзинго Яо
источник
1
не работает с путями, в которых есть пробелы ... вы можете преодолеть это с помощью кавычек ... что-то работаетecho "$(basename "$(dirname "$pathname")")"
Рэй Фосс
75

Bash может получить последнюю часть пути без вызова внешнего basename:

subdir="/path/to/whatever/${1##*/}"
Приостановлено до дальнейшего уведомления.
источник
2
На моем Mac использование подстроковой записи более чем на порядок быстрее, чем dirname / basename для случая, когда вы делаете что-то тривиальное с каждым из нескольких тысяч файлов.
Джордж
1
d=/home/me/somefolder;subdir="/$d/${1##*/}"В итоге я получил что-то вроде //home/me/somefolder//$ d на самом деле из цикла for d in $(find $SOMEFOLDER -maxdepth 1 -type d);Использование subdir=$(basename $d)работ, как и ожидалось.
Баттл Буткус
1
@ButtleButkus: Вы должны использовать whileвместо того, forчтобы перебирать выходные данные find( find -print0 | xargs -0лучше) или использовать globbing: for d in $SOMEFOLDER/*/(последний слеш работает как -type d- вы можете использовать **в Bash 4 для рекурсии, если вы shopt -s globstar, но сообщение «Список аргументов слишком длинный» возможно). Обратите внимание, что ${1}часть команды представляет первый аргумент скрипта или функции. Вам может понадобиться использовать ${d##*/}или другую переменную или спецификацию аргумента или убедиться, что аргумент передается в$1
Пауза до дальнейшего уведомления.
2
@DennisWilliamson Большое спасибо за обмен . Для любых будущих читателей, которые начинают задаваться вопросом, почему это не работает, или я единственный тупой здесь 😂. Выше ответ предполагает, что $1содержит the path from which last component is to be taken out. Я пропустил эту часть. Мой вариант использования: target_path='/home/user/dir1/dir2/dir3/'; target_path="${target_path%/}"; last_component=${target_path##*/}; echo $last_component- Работает 😉
Vinay Vissh
2
Смотрите здесь для объяснения того, как и почему ${1##*/} работает: unix.stackexchange.com/a/171786/15070
Матфея