关于shell:在bash脚本中获取当前目录名(不带完整路径)

Get current directory name (without full path) in a Bash script

如何在bash脚本中获取当前的工作目录名,或者更好的方法是只获取一个终端命令。

pwd给出了当前工作目录的完整路径,如/opt/local/bin但我只想要bin


不需要basename,尤其不需要运行pwd的子shell(它a href="http://mywiki.wooledge.org/subshell"rel="noreferrer">添加了额外的、昂贵的fork操作/a a);shell可以使用a href="http://www.gnu.org/software/bash/manual/html_node/shell-parameter-expansion.html"rel="noreferrer">参数在内部执行此操作。R扩展/AA:

1
2
3
4
5
6
7
8
9
10
result=${PWD##*/}          # to assign to a variable

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

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

请注意,如果您在其他情况下(不是PWD,而是其他保存目录名的变量)应用此技术,则可能需要修剪任何尾部斜杠。下面使用bash的extglob支持来处理多个尾随斜杠:

1
2
3
4
5
6
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
'
"$result"

或者,不使用extglob

1
2
3
4
5
6
dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}
<div class="
suo-content">[collapse title=""]<ul><li>$pwd$pwd有什么区别?</li><li>@前者是一个参数扩展操作,它修剪目录的其余部分,只提供basename。我的答案中有一个关于参数扩展的文档链接;如果您有任何问题,请随时关注。</li><li>@黑猩猩先生-我试图用最初的<wyn>${pwd}</wyn>和所有的全路径变体来设置变量(失败)。朋友不急,但如果你可以补充或评论,告诉我为什么需要<wyn>##*&#47;</wyn>来工作?还有大写的印刷电路板,真让我吃惊。为什么<wyn>result=${PWD#*&#47;}</wyn>评估为/full/path/to/script,<wyn>result=${PWD##*&#47;}</wyn>评估为script?再次感谢你的回答。</li><li>@stefgosselin <wyn>$PWD</wyn>是一个内置bash变量的名称;所有内置变量的名称都是大写的(以区别于本地变量,本地变量应该至少有一个小写字符)。<wyn>result=${PWD#*&#47;}</wyn>不对<wyn>&#47;full&#47;path&#47;to&#47;directory</wyn>进行计算;相反,它只去掉第一个元素,使其成为<wyn>path&#47;to&#47;directory</wyn>;使用两个<wyn>#</wyn>字符使模式匹配贪婪,尽可能多地匹配字符。有关更多信息,请阅读链接在答案中的"参数扩展"页。</li><li>注意,由于<wyn>cd</wyn>通过符号链接产生的复杂性,以及bash是否有<wyn>-o physical</wyn>选项集等,<wyn>pwd</wyn>的输出并不总是与<wyn>$PWD</wyn>的值相同。这在处理自动挂载的目录时尤其令人讨厌,在处理时,记录物理路径而不是逻辑路径将生成一个路径,如果使用该路径,则允许自动挂载程序自动卸载正在使用的目录。</li><li>好极了!有人应该为像我这样的老壳牌人写一本书。也许外面有一个?它可以让一个老掉牙的系统管理员说"回到我的时代,我们只有<wyn>sh</wyn><wyn>csh</wyn>",如果你想要退格键工作,你必须阅读整个<wyn>stty</wyn>手册页,我们喜欢它!"</li><li>如何只打印最后一个目录名?例如:<wyn>&#47;etc&#47;usr&#47;abc&#47;xyz.txt     printf '%s
'"${PWD##*&#47;}" #prints abc</wyn>如何打印USR?</li><li>@H.APP.Y:<wyn>IFS=&#47; read -r -a dirs <<<"$PWD"; printf '%s
'"${dirs[${#dirs[@]} - 2]}"</wyn>是第一个想到的方法。如果你想做得更好,我建议你问个问题。</li><li>-1所节省的微不足道的性能是使用字符串操作而不是重新使用标准函数<wyn>basename</wyn>的不良借口。它不太干净,如果你那么关心性能,你不应该首先使用bash。</li><li>@dbedrenko,如果<wyn>basename</wyn>是一个函数,而不是一个外部应用程序(并且有一个调用约定,不需要<wyn>fork()</wyn>和<wyn>mkfifo()</wyn>来调用并将输出检索到变量中),那么我对它没有问题。然而,这是后者。此外,与<wyn>dirname</wyn>不同,<wyn>basename</wyn>没有实现处理的角情况:您实际上是在启动一个外部程序来执行一些简单的字符串操作,而不是在进程中执行,没有任何好处。</li><li>…至于"可忽略不计",这要视情况而定。如果您在超过10000个文件名的循环中调用它,那么它一点也不能忽略。</li><li>@dbedrenko…好吧,我需要稍微回顾一下相同结果的声明:<wyn>basename foo&#47;bar&#47;baz&#47;</wyn>(带尾随斜线)是<wyn>baz</wyn>,而参数扩展结果是一个空字符串。但是,我倾向于认为,在处理文件名的常见情况下,pe空字符串的结果实际上更为正确:在<wyn>basename</wyn>情况下,您得到的是目录组件,而在字符串操作情况下,您得到的是结果通知您(正确!)该路径中没有文件名。</li><li>(…和<wyn>PWD</wyn>保证永远不会有斜线,使得这种区别在当前情况下毫无意义)。</li><li>我使用这个答案来标记(在顶部添加一行)爪形邮件文件,使用这个线程中描述的父目录basename(我的):lists.claus mail.org/pipermail/users/2017 march/&hellip;。时间安排很有趣,如gist.github.com/victoriastuart/7457b277d21a37b7a79eb9a957b9&zwnj;&8203;88d所示。tldr:[basename"$pwd"]比[printf"$pwd*/"]慢约11-31倍。</li><li>您也可以使用任何变量,不必是<wyn>$PWD</wyn>:<wyn>${SOME_VARIABLE##*&#47;}</wyn>。</li><li>@尼克布尔,埃多克斯1〔3〕永远不会以一刀切结束。这个问题是关于"当前目录名"的,而不是有意的一般性问题。</li><li>不用担心,这个问题是在搜索Google中任何目录的名称时出现的,因为这个原因我补充说。但是,正确的做法是,问题只指定了当前目录。</li><li>有意义的是,人们希望更广泛地应用它——我重写了附录,使其更加以bash为中心(因此,适合于我们标记的shell),并明确表示在<wyn>PWD</wyn>情况下不需要它。</li><li>好极了!我认为你不一定需要<wyn>extglob</wyn>。</li><li>用底片检查我的编辑!我认为它也符合POSIX,但我不是100%的。</li><li>两者都不能在ideone.com/gmmyln中使用。坦率地说,这些编辑有点笨手笨脚——也许你可以添加你自己的答案?</li><li>@查尔斯达菲,你确定吗?它在ideone.com上对我有效-如果是的话,很抱歉!我不确定它是否值得一个完整的答案</li><li>参见我提供的链接——两个变体都发射<wyn>&#47;path&#47;to&#47;somewhere</wyn>,而不是<wyn>somewhere</wyn>。或者…哦,你只是在演示修剪,没有basename功能?我误读了附录;它可能需要澄清。</li><li>错过了!是的:)我知道这比你最初的优雅解决方案更糟糕,但它也适用于路径中的尾随斜杠和非尾随斜杠,这很酷。</li><li>我觉得这个答案很痛苦。下面的答案很简单:arkady's(使用basename)和digitalross(使用pure bash,只给出一行答案而不使用blah)</li><li>如果你不想了解细节,bash是一种错误的语言——它充满了陷阱和警告,如果你拒绝学习它们,你最终会得到一个脚本,看起来它工作得很好,当它遇到一个不寻常的文件名(或者仅仅是一个只需几秒钟就可以运行几分钟的脚本,因为你会得到如果在紧密的内环中使用<wyn>basename</wyn>。愿意花些时间来学习细节是值得的——或者选择一种不同的、不那么挑剔的语言。</li><li>(是的,即使是像<wyn>echo"$foo"</wyn>这样简单的东西本身也会发生不可预测的行为,特别是在非bash posix shell上运行时——请参见pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html&zwnj;&8203;请注意,在bash以及<wyn>xpg_echo</wyn><wyn>baz</wyn>中也会发生同样的行为。4〕标志都设置好了,这两个标志都可以在编译时和运行时设置…再看看我所说的"挑剔"是什么意思。</li><li>我大约一周回来一次这个答案-谢谢查尔斯!</li></ul>[/collapse]</div><hr><P>使用<wyn>basename</wyn>程序。对于你的情况:</P>[cc lang="bash"]% basename"$PWD"
bin


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

​;&8203;&8203;&8203;&8203;


您可以使用pwd和basename的组合。例如。

1
2
3
4
5
6
7
8
#!/bin/bash

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

echo"$BASENAME"

exit;


GRP:

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


我喜欢选择的答案(Charles Duffy),但是如果你在symlinked目录中并且你想要目标目录的名称,就要小心了。不幸的是,我认为它不能用单参数扩展表达式来实现,也许我错了。这应该有效:

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

为了看到这一点,一个实验:

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

报告"酒吧"

名字

要显示路径的前导目录(而不产生/usr/bin/dirname的fork exec),请执行以下操作:

1
echo ${target_PWD%/*}

例如,转换foo/bar/baz->foo/bar


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

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


这条线棒极了!这里还有一种口味:

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

令人惊讶的是,没有人提到这种只使用内置bash命令的替代方法:

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

作为额外的奖励,您可以通过以下方式轻松获得父目录的名称:

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

这些将在bash 4.3-alpha或更新版本上工作。


1
basename $(pwd)

1
echo"$(basename $(pwd))"


我通常在sh脚本中使用这个

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

你可以用它来实现自动化…


对于像我这样的寻找骑师:

1
2
find $PWD -maxdepth 0 -printf"%f
"

下面的grep和regex也在工作,

1
>pwd | grep -o"\w*-*$"


1
basename"$PWD"

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

将内部文件名分隔符(IFS)转回一个空格。

1
IFS=

ifs=后面有一个空格。


只需使用:

1
pwd | xargs basename

1
basename"`pwd`"


我非常喜欢使用gbasename,它是GNU coreutils的一部分。


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

1
$basename <path-of-directory>

以下命令将导致在bash脚本中打印当前工作目录。

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


我使用这个命令:


dirname"$0"