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

Der*_*mer 765 bash shell

如何在bash脚本中获取当前工作目录名称,甚至更好,只需一个终端命令.

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

Cha*_*ffy 1033

不需要basename,特别是不需要运行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.
Run Code Online (Sandbox Code Playgroud)

请注意,如果您在其他情况下应用此技术(不是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"
Run Code Online (Sandbox Code Playgroud)

或者,没有extglob:

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

  • $ {PWD ##*/}和$ PWD有什么区别? (28认同)
  • @Mr_Chimp前者是一个参数扩展操作,它修剪目录的其余部分以仅提供基本名称.我在关于参数扩展的文档的答案中有一个链接; 如果您有任何疑问,请随时关注. (23认同)
  • @stefgosselin` $ PWD`是内置bash变量的名称; 所有内置变量名都是全部大写(以区别于局部变量,局部变量应始终至少有一个小写字符).`result = $ {PWD#*/}`确实_not_评估为`/ full/path/to/directory`; 相反,它只剥离第一个元素,使其成为`path/to/directory`; 使用两个`#`字符使模式匹配贪婪,匹配尽可能多的字符.阅读答案中链接的参数扩展页面以获取更多信息. (23认同)
  • 请注意,`pwd`的输出并不总是与`$ PWD`的值相同,因为`cd`通过符号链接引起的复杂化,bash是否设置了`-o physical`选项,依此类推.这曾经特别讨厌处理自动挂载的目录,其中记录物理路径而不是逻辑路径将产生一个路径,如果使用,将允许自动挂载程序自动卸载正在使用的目录. (5认同)
  • 如果你不想学习细节,Bash 是错误的语言——它充满了陷阱和警告,如果你拒绝学习它们,你最终会得到一个*看起来*运行良好的脚本删除遇到不寻常的文件名时的备份(或者只是一个可以在几秒钟内运行几分钟的脚本,如果您在紧密的内部循环中使用 `basename` 就会得到)。愿意花一些时间来了解细节是值得的——或者选择一种不同的、不那么挑剔的语言。 (3认同)
  • @Mr_Chimp - 我试图用初始的`${pwd}` 和所有完整路径变体来设置变量(但失败了)。朋友不要急,但如果你可以*也许*添加或评论,请告诉我为什么需要`##*/`才能使其工作?还有大写的密码,这真的让我感到惊讶。为什么`result=${PWD#*/}` 评估为/full/path/to/script 而`result=${PWD##*/}` 评估为script?再次感谢您的出色回答。 (2认同)
  • 太好了!有人应该为像我这样的老Borne shell人写一本书.也许那里有一个?它可能有一个旧的系统管理员说的东西,"回到我的日子,我们只有`sh`和`csh`,如果你想让退格键工作,你必须阅读整个`stty`手册页,我们喜欢它!" (2认同)
  • @h.APP.y: `IFS=/ read -r -a dirs <<<"$PWD"; printf '%s\n' "${dirs[${#dirs[@]} - 2]}"` 是我想到的第一个方法。如果您想要更好的东西,我建议您作为自己的问题来问。 (2认同)
  • @DBedrenko,如果`basename` 是一个**函数**,而不是**外部应用程序**(并且具有不需要`fork()` 和`mkfifo()` 来调用的调用约定将输出检索到变量中),我对此没有问题。然而,它是后者。此外,与 `dirname` 不同的是,`basename` 没有实现以下处理的极端情况: 您实际上是在启动一个外部程序来执行一些简单的字符串操作,而不是在进程中进行操作,没有任何好处。 (2认同)
  • ......至于"微不足道",这取决于.如果你在一个超过10,000个文件名的循环中调用它,它根本不可忽略. (2认同)
  • @DBedrenko ...好吧,我确实需要回溯相同结果的声明*一点*:`basename foo/bar/baz/`(带有尾部斜杠)是`baz`,而参数扩展结果是一个空细绳。然而,我倾向于认为,在使用文件名的常见情况下,PE 空字符串结果实际上*更正确*:在 `basename` 情况下,您将获得一个目录组件,而在字符串中- 操作情况下,您得到的结果通知您(正确!)该路径中**没有**没有文件名。 (2认同)
  • (...并且“PWD”保证永远不会有尾部斜杠,使得这种区别在当前情况下毫无意义)。 (2认同)
  • 我使用这个答案来标记(在顶部添加一行)Claws Mail 文件,使用父目录基名,如该线程(我的)中所述:http://lists.claws-mail.org/pipermail/users/2017 -March/thread.html#18865。时间很有趣,如我的要点所示,https://gist.github.com/victoriastuart/7457b2727d21a37b7a79eb9a957b988d。TLDR:[basename "$PWD"] 比 [printf "${PWD##*/}\n"] 慢约 11-31 倍... (2认同)
  • 请参阅我提供的链接 - 两种变体都会发出“/path/to/somewhere”,而不是“somewhere”。或者...*哦*,您只是在演示修剪,没有基本名称功能?我误读了附录;也许有必要对其进行澄清。 (2认同)
  • (是的,即使像 `echo "$foo"` 这样简单的东西本身也可能表现得不可预测,特别是在非 bash POSIX shell 上运行时——请参阅 http://pubs.opengroup.org/onlinepubs/ 的应用程序使用和基本原理部分9699919799/utilities/echo.html;请注意,当同时设置“xpg_echo”和“posix”标志时,bash 中也会发生相同的行为,并且这两个标志都可以在编译时和运行时设置。 ..再次看看我所说的“挑剔”是什么意思)。 (2认同)
  • @Jas,请参阅答案中包含的参数扩展链接。 (2认同)
  • @pjh,好点;进行相应调整。 (2认同)

Ark*_*ady 290

使用该basename程序.对于你的情况:

% basename "$PWD"
bin
Run Code Online (Sandbox Code Playgroud)

  • 这不是`basename`程序的目的吗?除了错过报价之外,这个答案有什么问题? (18认同)
  • 当然使用`basename`不那么"有效".但就开发人员的工作效率而言,它可能*更高效*,因为与丑陋的bash语法相比,它很容易记忆.所以,如果你记得它,那就去吧.否则,`basename`也可以.有没有人曾经不得不提高bash脚本的"性能"并使其更高效? (17认同)
  • @Nacht`basename`确实是一个做正确事情的外部程序,但运行_any_外部程序的东西bash可以开箱即用只使用内置功能是愚蠢的,会产生性能影响(`fork( )`,`execve()`,`wait()`等等,无缘无故. (11认同)
  • @LouisMaddox,有点kibitzing:`function`关键字是POSIX不兼容的,没有在标准化语法上添加任何值,缺少引号会在带有空格的目录名中产生错误.`wdexec(){"./ $(basename"$ PWD")"; 可能是一个更好的选择. (4认同)
  • ...另外,我对'basename`这里*的反对*不适用于`dirname`,因为`dirname`具有`$ {PWD ##*/}`不具备的功能 - 转换字符串例如,对.``没有斜线.因此,虽然使用`dirname`作为外部工具会产生性能开销,但它还具有有助于补偿相同功能的功能. (3认同)
  • @usr,...作为编写通过 maildirs 操作的 bash 脚本的人(一种邮箱存储格式,每个电子邮件一个文件,如 qmail 所用),是的,*绝对*,我有现实世界的理由支付注意脚本编写的效率。我们讨论的是在非嵌入式硬件上每个命令替换 20 毫秒的规模——循环超过 100,000 个项目,即使您的代码只有其中一个,您也是在实时讨论。(当然,在某一点上,shell 不再是正确的工具,但是如果您不注意代码编写的好坏,您会更快地达到这一点)。 (3认同)
  • 这正是我所需要的,优雅和简洁的+1. (2认同)
  • 面对面的开发人员正在争论最好的答案。我支持,请将此答案标记为已接受。 (2认同)

Dig*_*oss 134

$ echo "${PWD##*/}"
Run Code Online (Sandbox Code Playgroud)

​​​​​

  • @jocap ...另外,我不确定"echo"实际上是答案的合法部分.问题是如何__答案,而不是如何_print_答案; 如果目标是将其分配给变量,例如,`name = $ {PWD ##*/}`是正确的,`name = $(echo"$ {PWD ##*/}")`是错的(在不必要的低效率意义上). (9认同)
  • 这个家伙更清楚"回声",查理;) (8认同)
  • @jocap ...除了在没有引用参数的情况下使用echo之外是_wrong_.如果目录名称有多个空格或制表符,则它将折叠为单个空格; 如果它有一个通配符,它​​将被扩展.正确的用法是`echo"$ {PWD ##*/}"`. (2认同)

小智 23

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

#!/bin/bash

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

echo "$BASENAME"

exit;
Run Code Online (Sandbox Code Playgroud)

  • 请不.反引号创建一个子shell(因此,一个fork操作) - 在这种情况下,你使用它们*两次*![作为一个额外的,虽然风格狡辩 - 变量名称应该是小写的,除非你将它们输出到环境或它们是一个特殊的,保留的情况]. (17认同)
  • 而作为第二种风格的狡辩背景应该用$()代替.仍然分叉,但更可读,需要更少的兴奋. (11认同)
  • 按照惯例,环境变量(PATH、EDITOR、SHELL...)和内部 shell 变量(BASH_VERSION、RANDOM...)都是完全大写的。所有其他变量名称都应小写。由于变量名称区分大小写,因此此约定可以避免意外覆盖环境变量和内部变量。 (3认同)
  • 取决于约定。有人可能会争辩说,所有的shell变量都是环境变量(取决于shell),因此所有的shell变量都应该全大写。其他人可能会根据范围争论—全局变量是全大写,而局部变量是小写,并且都是全局变量。另一个人可能会争辩说,使变量全都大写,这与乱扔外壳脚本的命令又增加了一层对比,这些命令都是小写的简短但有意义的单词。我可能会/可能不会/同意/其中任何一个参数,但是我已经看到所有三个参数都在使用中。:) (2认同)

小智 15

grep怎么样:

pwd | grep -o '[^/]*$'
Run Code Online (Sandbox Code Playgroud)

  • 不会推荐,因为它似乎获得了一些颜色信息,而`pwd | xargs basename` 不..可能没那么重要,但另一个答案更简单,跨环境更一致 (2认同)

Fir*_*Sky 15

用:

basename "$PWD"
Run Code Online (Sandbox Code Playgroud)

或者

IFS=/ 
var=($PWD)
echo ${var[-1]} 
Run Code Online (Sandbox Code Playgroud)

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

IFS= 
Run Code Online (Sandbox Code Playgroud)

IFS 后面有一个空格。


FDS*_*FDS 8

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

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}
Run Code Online (Sandbox Code Playgroud)

要看到这个,一个实验:

cd foo
ln -s . bar
echo ${PWD##*/}
Run Code Online (Sandbox Code Playgroud)

报告"酒吧"

DIRNAME

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

echo ${target_PWD%/*}
Run Code Online (Sandbox Code Playgroud)

这将改变foo/bar/baz - > foo/bar

  • 不幸的是,`readlink -f`是一个GNU扩展,因此在BSD(包括OS X)上不可用. (5认同)

ano*_*mal 8

echo "$PWD" | sed 's!.*/!!'
Run Code Online (Sandbox Code Playgroud)

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

  • 仅供参考,`$ {PWD ##*/}`是POSIX sh - 每个现代/ bin/sh(包括破折号,灰烬等)都支持它; 要在最近的一个盒子上击中实际的Bourne,你需要使用一个稍老的Solaris系统.除此之外 - "echo"$ PWD"`; 省略引号会导致错误(如果目录名称包含空格,通配符等). (4认同)
  • 是的,我使用的是旧式 Solaris 系统。我已更新命令以使用引号。 (2认同)

rod*_*pes 7

这个帖子很棒!这是另一种味道:

pwd | awk -F / '{print $NF}'
Run Code Online (Sandbox Code Playgroud)


小智 6

basename $(pwd)
Run Code Online (Sandbox Code Playgroud)

或者

echo "$(basename $(pwd))"
Run Code Online (Sandbox Code Playgroud)

  • 需要更多的引号才能正确 - 事实上,`pwd` 的输出在传递给 `basename` 之前是字符串拆分和全局扩展的。 (2认同)

gec*_*kos 6

有很多方法可以做到这一点,我特别喜欢Charles方式,因为它避免了新的进程,但在知道这一点之前,我用 awk 解决了它

pwd | awk -F/ '{print $NF}'
Run Code Online (Sandbox Code Playgroud)


ahm*_*ira 6

只需运行以下命令行:

basename $(pwd)
Run Code Online (Sandbox Code Playgroud)

如果您想复制该名称:

basename $(pwd) | xclip -selection clipboard
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Art*_*les 5

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

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

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

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"
Run Code Online (Sandbox Code Playgroud)

这些将适用于 Bash 4.3-alpha 或更新版本。


小智 5

我通常在 sh 脚本中使用它

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder
Run Code Online (Sandbox Code Playgroud)

你可以用它来自动化事情......


小智 5

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

find $PWD -maxdepth 0 -printf "%f\n"
Run Code Online (Sandbox Code Playgroud)

  • 将 `$PWD` 更改为 `"$PWD"` 以正确处理不寻常的目录名称。 (2认同)