Получить текущее имя каталога (без полного пути) в скрипте Bash


821

Как бы получить текущее имя рабочего каталога в скрипте bash или, что еще лучше, просто команду терминала.

pwdдает полный путь текущего рабочего каталога, например, /opt/local/binно я хочу толькоbin


Ответы:


1119

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

result=${PWD##*/}          # to assign to a variable

printf '%s\n' "${PWD##*/}" # to print to stdout
                           # ...more robust than echo for unusual names
                           #    (consider a directory named -e or -n)

printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
                           # ...useful to make hidden characters readable.

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

dirname=/path/to/somewhere//
shopt -s extglob           # enable +(...) glob syntax
result=${dirname%%+(/)}    # trim however many trailing slashes exist
result=${result##*/}       # remove everything before the last / that still remains
printf '%s\n' "$result"

Альтернативно, без extglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /

31
В чем разница между $ {PWD ## * /} и $ PWD?
Mr_Chimp

24
@Mr_Chimp первый - это операция расширения параметра, которая обрезает остальную часть каталога, чтобы предоставить только базовое имя. В моем ответе есть ссылка на документацию по расширению параметров; не стесняйтесь следовать этому, если у вас есть какие-либо вопросы.
Чарльз Даффи

24
@stefgosselin $PWD- это имя встроенной переменной bash; все встроенные имена переменных указаны заглавными буквами (чтобы отличать их от локальных переменных, которые всегда должны иметь хотя бы один символ нижнего регистра). result=${PWD#*/}делает не вычисляться /full/path/to/directory; вместо этого он удаляет только первый элемент, делая его path/to/directory; использование двух #символов делает сопоставление шаблона жадным, сопоставляя столько символов, сколько возможно. Прочитайте страницу расширения параметров, связанную в ответе, для получения дополнительной информации.
Чарльз Даффи

5
Обратите внимание, что выходные данные from pwdне всегда совпадают со значением $PWD, из-за сложностей, возникающих при cdпрохождении через символические ссылки, имеет ли bash установленный -o physicalпараметр и так далее. Раньше это было особенно неприятно при работе с автоматически монтируемыми каталогами, когда запись физического пути вместо логического давала бы путь, который, в случае его использования, позволял бы автомонтированию самопроизвольно отключать каталог, который он использовал.
Алекс Норт-Киз

3
Ницца! Кто-то должен написать книгу для старых ребят из Borne, таких как я. Может быть, есть один там? У старого системного администратора может быть какая-то шумиха, говорящая что-то вроде: «В свое время мы только shи, cshесли вы хотели, чтобы клавиша Backspace работала, вам нужно было прочитать всю sttyсправочную страницу, и нам понравилось!»
Красный Крикет

336

Используйте basenameпрограмму. Для вашего случая:

% basename "$PWD"
bin

20
Разве это не цель basenameпрограммы? Что не так с этим ответом, кроме пропущенных кавычек?
Nacht - Восстановить Монику

11
@Nacht basenameдействительно внешняя программа , которая делает правильно, но работает любую внешнюю программу для вещи Баша может делать вне коробки , используя только встроенные функции глупо, подвергаясь влиянием на производительность ( fork(), execve(), wait()и т.д.) для нет причин.
Чарльз Даффи

3
... кроме того, мои возражения basenameздесь не применимы dirname, потому что dirnameимеет функциональность, ${PWD##*/}которой нет - например, преобразование строки без косой черты .. Таким образом, хотя использование dirnameв качестве внешнего инструмента приводит к снижению производительности, оно также имеет функциональность, которая помогает компенсировать его.
Чарльз Даффи

4
@LouisMaddox, немного кибитцинга: functionключевое слово несовместимо с POSIX, не добавляя никакого значения к стандартизированному синтаксису, а отсутствие кавычек может привести к ошибкам в именах каталогов с пробелами. wdexec() { "./$(basename "$PWD")"; }может быть предпочтительной альтернативой.
Чарльз Даффи

28
Конечно, использование basenameменее «эффективно». Но это, вероятно, более эффективно с точки зрения производительности разработчика, потому что его легко запомнить по сравнению с уродливым синтаксисом bash. Так что, если вы помните это, пойти на это. В противном случае basenameработает так же хорошо. Кто-нибудь когда-нибудь должен был улучшить «производительность» bash-скрипта и сделать его более эффективным?
USR

139
$ echo "${PWD##*/}"

​​​​​


2
@jocap ... за исключением того, что использование echo там без указания аргумента неверно . Если имя каталога содержит несколько пробелов или символ табуляции, оно будет свернуто в один пробел; если он имеет подстановочный знак, он будет расширен. Правильное использование будет echo "${PWD##*/}".
Чарльз Даффи

9
@jocap ... также я не уверен, что "эхо" на самом деле является законной частью ответа. Вопрос заключался в том, как получить ответ, а не в том, как напечатать ответ; например, если бы целью было присвоить ее переменной, это name=${PWD##*/}было бы правильно и name=$(echo "${PWD##*/}")неправильно (в смысле ненужной неэффективности).
Чарльз Даффи

24

Вы можете использовать комбинацию pwd и basename. Например

#!/bin/bash

CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`

echo "$BASENAME"

exit;

18
Пожалуйста, не надо. Обратные метки создают подоболочку (таким образом, операцию разветвления) - и в этом случае вы используете их дважды ! [В качестве дополнительного, хотя и стилистического, смысла - имена переменных должны быть в нижнем регистре, если вы не экспортируете их в среду или они не являются специальным, зарезервированным регистром].
Чарльз Даффи

11
и в качестве второго стиля придирки следует заменить на $ (). все еще вилки, но более удобочитаемые и с меньшими затратами.
Джереми Уолл

1
По соглашению переменные среды (PATH, EDITOR, SHELL, ...) и переменные внутренней оболочки (BASH_VERSION, RANDOM, ...) полностью пишутся с заглавной буквы. Все остальные имена переменных должны быть строчными. Поскольку имена переменных чувствительны к регистру, это соглашение позволяет избежать случайного переопределения переменных среды и внутренних переменных.
Рани Албег Вайн

2
Зависит от конвенции. Можно утверждать, что все переменные оболочки являются переменными среды (в зависимости от оболочки), и поэтому все переменные оболочки должны быть в заглавных буквах. Кто-то еще может поспорить, основываясь на области видимости: глобальные переменные - это заглавные буквы, а локальные переменные - строчные, и все они глобальные. Еще один может поспорить, что создание переменных в верхнем регистре добавляет дополнительный уровень контраста с командами, засоряющими сценарии оболочки, которые также представляют собой короткие строчные, но значимые слова. Я могу или не могу / согласиться / с любым из этих аргументов, но я видел все три в использовании. :)
dannysauer

17

Как насчет grep:

pwd | grep -o '[^/]*$'

Я бы не рекомендовал, потому что кажется, что он получает некоторую информацию о цвете, тогда как pwd | xargs basenameэто не так… вероятно, это не так важно, но другой ответ проще и более последовательный в разных средах
davidhq

8

Мне нравится выбранный ответ (Чарльз Даффи), но будьте осторожны, если вы находитесь в директории с символическими ссылками и хотите указать имя целевой директории. К сожалению, я не думаю, что это можно сделать в выражении расширения с одним параметром, возможно, я ошибаюсь. Это должно работать:

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}

Чтобы увидеть это, эксперимент:

cd foo
ln -s . bar
echo ${PWD##*/}

сообщает "бар"

DIRNAME

Чтобы показать ведущие каталоги пути (без использования fork-exec / usr / bin / dirname):

echo ${target_PWD%/*}

Это, например, преобразует foo / bar / baz -> foo / bar


5
К сожалению, readlink -fэто расширение GNU, и поэтому не доступно на BSD (включая OS X).
Сюн Чиамов

8
echo "$PWD" | sed 's!.*/!!'

Если вы используете оболочку Bourne или ${PWD##*/}недоступны.


4
К вашему сведению, ${PWD##*/}это POSIX sh - каждый современный / bin / sh (включая dash, ash и т. Д.) Поддерживает его; чтобы поразить настоящую Борн на недавнем боксе, вам нужно будет использовать слегка устаревшую систему Solaris. Помимо этого - echo "$PWD"; пропуск кавычек приводит к ошибкам (если в имени каталога есть пробелы, подстановочные знаки и т. д.).
Чарльз Даффи

1
Да, я использовал старую систему Solaris. Я обновил команду для использования кавычек.
аномальное


5

Удивительно, но никто не упомянул об этой альтернативе, которая использует только встроенные команды bash:

i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"

В качестве дополнительного бонуса вы можете легко получить имя родительского каталога с помощью:

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"

Они будут работать на Bash 4.3-alpha или новее.


как ни странно ? просто шучу, но другие гораздо короче и их легче запомнить
codewandler

Это довольно забавно = P До этого не слышал о $ IFS (внутренний разделитель полей). мой bash был слишком стар, но можете проверить его здесь: jdoodle.com/test-bash-shell-script-online
Стэн Курджиль

5

Использование:

basename "$PWD"

ИЛИ

IFS=/ 
var=($PWD)
echo ${var[-1]} 

Верните внутренний разделитель имен файлов (IFS) обратно в пробел.

IFS= 

После IFS есть один пробел.


4
basename $(pwd)

или

echo "$(basename $(pwd))"

2
Требуется больше кавычек, чтобы быть правильными - как есть, выходные данные pwdразделяются на строки и расширяются глобально, прежде чем будут переданы basename.
Чарльз Даффи

Таким образом, basename "$(pwd)"- хотя это очень неэффективно по сравнению с just basename "$PWD", что само по себе неэффективно по сравнению с использованием раскрытия параметра вместо вызова basenameвообще.
Чарльз Даффи

4

Для поиска жокеев, таких как я:

find $PWD -maxdepth 0 -printf "%f\n"

Измените на, $PWDчтобы "$PWD"правильно обрабатывать необычные имена каталогов.
Чарльз Даффи

3

я обычно использую это в скриптах sh

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder

Вы можете использовать это для автоматизации вещей ...


readlink: illegal option -- f usage: readlink [-n] [file ...]


1

Просто используйте:

pwd | xargs basename

или

basename "`pwd`"

2
Это во всех отношениях худшая версия ответа, ранее предоставленного Аркадием ( stackoverflow.com/a/1371267/14122 ): xargsФормула неэффективна и содержит ошибки, когда имена каталогов содержат буквенные символы новой строки или символы кавычек, а вторая формулировка вызывает pwdкоманду в подоболочке, вместо того, чтобы получить тот же результат через встроенное расширение переменной.
Чарльз Даффи

@CharlesDuffy Тем не менее это правильный и практичный ответ на вопрос.
Бахф

1

Если вы хотите видеть только текущий каталог в области приглашения bash, вы можете отредактировать .bashrcфайл в ~. Изменение \wк \Wв строке:

PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

Запустить source ~/.bashrc и он будет отображать только имя каталога в области подсказки.

Ссылка: /superuser/60555/show-only-current-directory-name-not-full-path-on-bash-prompt


0

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

$basename <path-of-directory>

0

Я настоятельно предпочитаю использовать gbasename, который является частью GNU coreutils.


К вашему сведению, это не входит в мой дистрибутив Ubuntu.
Katastic Voyage

@KatasticVoyage, он есть в Ubuntu, он просто называется basenameтам. Обычно gbasenameон вызывается только на MacOS и других платформах, которые в противном случае поставляются с базовым именем не-GNU.
Чарльз Даффи

-1

Следующие команды приведут к печати вашего текущего рабочего каталога в bash-скрипте.

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

2
(1) Я не уверен, где мы гарантируем, что список аргументов таков, что $1будет содержать текущий рабочий каталог. (2) pushdи popdне служит никакой цели здесь , потому что что - то внутри обратных кавычек делаются в субоболочке - поэтому он не может повлиять на каталог родительской оболочки, чтобы начать с. (3) Использование "$(cd "$1"; pwd)"будет более читабельным и устойчивым к именам каталогов с пробелами.
Чарльз Даффи
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.