Отримайте поточну назву каталогів (без повного шляху) у Bash-скрипті


819

Як я можу отримати лише поточне ім'я робочої директорії в bash-скрипті, а ще краще, просто термінальній команді.

pwdдає повний шлях до поточного робочого каталогу, наприклад, /opt/local/binале я хочу лишеbin


Відповіді:


1115

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

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_Cimimp

24
@Mr_Chimp the first - це операції з розширення параметрів, які обрізають решту каталогу, надаючи лише базове ім'я. Я маю посилання у своїй відповіді на документацію щодо розширення параметрів; сміливо слідкуйте за цим питанням, якщо у вас є якісь питання.
Чарльз Даффі

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

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

3
Приємно! Хтось повинен написати книгу для старих хлопців з Борна, як я. Може, там є один? Це може мати crotchity старі системним адміністратором , кажучи такі речі , як, «тому в мій день ми тільки shі cshякщо ви хочете клавішу повернення на роботу ви повинні були прочитати всю довідкову sttyсторінку, і нам сподобалося!»
Червоний крикет

335

Використовуйте basenameпрограму. Для вашого випадку:

% basename "$PWD"
bin

20
Чи не це мета basenameпрограми? Що не так у цій відповіді, окрім пропуску цитат?
Нахт - Відновлення Моніки

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

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

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

28
Звичайно, використання basenameменш "ефективне". Але це, ймовірно , більш ефективним з точкою зору продуктивності розробника , тому що це легко запам'ятати по порівнянні з потворним синтаксисом Баша. Тож якщо ви це пам’ятаєте, ідіть до цього. Інакше basenameпрацює так само добре. Хто-небудь коли-небудь доводилося вдосконалювати "продуктивність" баш-скрипту і зробити його більш ефективним?
usr

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

Сігналы абмеркавання


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

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

24

Можна використовувати комбінацію pwd та базової назви. Напр

#!/bin/bash

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

echo "$BASENAME"

exit;

18
Будь ласка, ні. Повернення створює нижню частину оболонки (таким чином, операція виделки) - і в цьому випадку ви використовуєте їх двічі ! [Як додатковий, хоч і стилістичний прислівник - імена змінних повинні бути малі, якщо ви не експортуєте їх у навколишнє середовище або вони є спеціальним, зарезервованим випадком].
Чарльз Даффі

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

1
За умовою, змінні середовища (PATH, EDITOR, SHELL, ...) та внутрішні змінні оболонки (BASH_VERSION, RANDOM, ...) повністю капіталізуються. Усі інші назви змінних мають бути малими літерами. Оскільки назви змінних залежать від регістру, ця умова дозволяє уникнути випадкового перекриття зовнішніх та внутрішніх змінних.
Rany Albeg Wein

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).
Xiong Chiamiov

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

Якщо ви використовуєте оболонку Bourne або ${PWD##*/}її немає.


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

1
Так, я використовував стару систему Solaris. Я оновив команду використовувати лапки.
аномальна

7

Ця нитка чудова! Ось ще один аромат:

pwd | awk -F / '{print $NF}'

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-альфа чи новіші.


напрочуд? просто жартую, але інших набагато коротше і простіше запам’ятати
codewandler

Це досить смішно = P До цього не чув про $ IFS (внутрішній роздільник поля). мій баш був занадто старий, але його можна перевірити тут: 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)"- хоча це дуже неефективно порівняно з справедливим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 ): xargsformultion неефективна і баггі , коли імена каталогів містять символи нового рядка або символи лапок, а друга формулювання викликає 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 <path-of-directory>

0

Я настійно вважаю за краще використовувати gbasename, що є частиною GNU coreutils.


FYI, він не поставляється з моїм дистрибутивом Ubuntu.
Катастичне подорож

@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.