在Bash脚本中获取当前目录名称(无完整路径)


Answers:


1113

不需要基名,特别是不需要运行pwd的子shell(它增加了额外且昂贵的fork操作);Shell可以使用参数扩展在内部执行此操作:

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,而是使用其他持有目录名称的变量)应用此技术,则可能需要修剪所有斜杠。下面使用了bash的extglob支持,即使有多个斜杠也可以使用:

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前者是参数扩展操作,它会修剪目录的其余部分以仅提供基本名称。我在答案中有一个指向参数扩展文档的链接。如有任何疑问,请随时关注。
Charles Duffy

24
@stefgosselin $PWD是内置bash变量的名称;所有内置变量名都用大写字母(以将其与局部变量区分开,局部变量应始终至少具有一个小写字符)。result=${PWD#*/}确实评估为/full/path/to/directory; 相反,它仅剥离第一个元素,使其成为path/to/directory;使用两个#字符会使模式与贪婪匹配,并尽可能匹配多个字符。阅读答案中链接的参数扩展页面以获取更多信息。
查尔斯·达菲

5
请注意,由于符号链接中ing 引起的复杂性,bash是否设置了选项等等,因此from的输出pwd并不总是与的值相同。这过去在处理自动挂载的目录时特别令人讨厌,在这种情况下,记录物理路径而不是逻辑路径会生成一条路径,如果使用该路径,则该路径将允许自动挂载器自发卸除正在使用的目录。$PWDcd-o physical
Alex North-Keys

3
真好!有人应该为像我这样的Borne贝壳老家伙写一本书。也许那里有一个?它可以有一个crotchity老系统管理员说这样的东西,“早在我的一天,我们只有shcsh,如果你想退格键工作,你必须阅读整个stty手册页,我们喜欢它!”
Red Cricket

333

使用该basename程序。对于您的情况:

% basename "$PWD"
bin

19
这不是basename程序的目的吗?除了缺少引号外,这个答案还有什么问题?
Nacht-恢复莫妮卡

11
@Nacht basename的确是一个外部程序,做正确的事情,但运行任何外部程序的一件事Bash可以只使用内置的功能是愚蠢的做外的开箱,招致性能的影响(fork()execve()wait()等)为没有理由。
Charles Duffy 2013年

3
......顺便说一句,我反对basename在这里适用dirname,因为dirname具有的功能,${PWD##*/}不-转化没有斜线为字符串.,例如。因此,尽管dirname用作外部工具会增加性能开销,但它也具有有助于补偿这些功能的功能。
查尔斯·达菲

4
@LouisMaddox,有点令人费解:function关键字与POSIX不兼容,没有在标准语法上添加任何值,并且缺少引号会在带有空格的目录名称中产生错误。wdexec() { "./$(basename "$PWD")"; }可能是一个更好的选择。
查尔斯·达菲

27
确保使用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
请不。反引号会创建一个子shell(因此是一个fork操作),在这种情况下,您要使用它们两次![另外,尽管有样式上的怪癖-变量名应该是小写的,除非您将它们导出到环境中或者它们是特殊的,保留的情况]。
查尔斯·达菲

11
作为第二种形式的古怪的backtic应该用$()代替。仍然是分叉的,但更具可读性,且所需的摘要较少。
杰里米·沃尔

1
按照约定,环境变量(PATH,EDITOR,SHELL等)和内部shell变量(BASH_VERSION,RANDOM等)是完全大写的。所有其他变量名应小写。由于变量名称区分大小写,因此该约定避免了意外覆盖环境变量和内部变量。
Rany Albeg Wein

2
取决于约定。有人可能会争辩说,所有的shell变量都是环境变量(取决于shell),因此所有的shell变量都应该全大写。其他人可能会根据范围争论-全局变量是全大写,而局部变量是小写,并且都是全局变量。另一个人可能会争辩说,使变量全部变为大写,这与乱扔外壳脚本的命令形成了另一层对比,这些脚本都是小写的简短但有意义的单词。我可能会/可能不会/同意/其中任何一个参数,但是我已经看到所有三个参数都在使用中。:)
dannysauer

17

grep怎么样:

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

不会推荐,因为它似乎获得了一些颜色信息pwd | xargs basename,但并不是那么重要。但是其他答案在整个环境中都更简单,更一致
davidhq 18/09/13

8

我喜欢选定的答案(Charles Duffy),但是如果您在符号链接目录中,并且想要目标目录的名称,请小心。不幸的是,我认为不能在单个参数扩展表达式中完成此操作,也许我误会了。这应该工作:

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

要看到这一点,可以进行一个实验:

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

报告“栏”

DIRNAME

要显示路径的前导目录(不产生/ usr / bin / dirname的fork-exec):

echo ${target_PWD%/*}

这将例如转换foo / bar / baz-> foo / bar


5
不幸的是,readlink -f它是GNU扩展,因此在BSD(包括OS X)上不可用。
熊加米奥夫

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

如果您使用的是Bourne shell或${PWD##*/}不可用。


4
仅供参考,${PWD##*/}是POSIX sh-每个现代的/ bin / sh(包括破折号,灰烬等)都支持它;要在最近的机器上运行实际的Bourne,您需要安装在较旧的Solaris系统上。除此之外- echo "$PWD"; 省略引号会导致错误(如果目录名称包含空格,通配符等)。
Charles Duffy

1
是的,我使用的是较旧的Solaris系统。我已经更新了使用引号的命令。
2014年

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-alpha或更高版本上运行。


令人惊讶地?只是在开玩笑,但其他人
则更

这很有趣= P在此之前没有听说过$ IFS(内部字段分隔符)。我的bash太旧了,但是可以在这里进行测试:jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

采用:

basename "$PWD"

要么

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

将内部文件名分隔符(IFS)回到空间。

IFS= 

IFS后有一个空格。


4
basename $(pwd)

要么

echo "$(basename $(pwd))"

2
需要更多的引号才是正确的-实际上,在将输出pwd传递给之前先对其进行字符串分割和glob扩展basename
Charles Duffy 2014年

因此,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
从各个方面来说,这都是Arkady先前给出的答案的较差版本(stackoverflow.com/a/1371267/14122):xargs当目录名包含文字换行符或引号字符,并且第二个公式调用pwd命令时,formultion效率低下且有错误而不是通过内置的变量扩展检索相同的结果。
Charles Duffy 2015年

@CharlesDuffy不过,这是对这个问题的有效和实际答案。
bachph


0

您可以使用basename实用程序,该实用程序从字符串中删除任何以/结尾的前缀和后缀(如果存在于字符串中),并将结果打印在标准输出上。

$basename <path-of-directory>

0

我非常喜欢使用gbasenameGNU 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)pushdpopd在这里毫无用处,因为反引号内部的所有操作都在子shell中完成-因此它不会影响父shell的目录。(3)使用"$(cd "$1"; pwd)"带有空格的目录名称将更具可读性和弹性。
Charles Duffy 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.