ChatGPT解决这个技术问题 Extra ChatGPT

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

我如何在 bash 脚本中获取当前工作目录名称,或者更好的是,仅获取终端命令。

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

刚刚获得第 1000 次投票 :)

C
Charles Duffy

不需要基本名称,尤其不需要运行 pwd 的子 shell(其中 adds an extra, and expensive, fork operation); shell 可以使用 parameter expansion 在内部执行此操作:

result=${PWD##*/}          # to assign to a variable
result=${result:-/}        # to correct for the case where PWD=/

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 support 来处理多个斜杠:

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
result=${result:-/}        # correct for dirname=/ case
printf '%s\n' "$result"

或者,没有 extglob

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

${PWD##*/} 和 $PWD 有什么区别?
@Mr_Chimp 前者是一个参数扩展操作,它修剪目录的其余部分以仅提供基本名称。我对参数扩展文档的回答中有一个链接;如果您有任何问题,请随时关注。
@stefgosselin $PWD 是内置 bash 变量的名称;所有内置变量名都大写(为了与局部变量区分开来,局部变量应始终至少有一个小写字符)。 result=${PWD#*/}评估为 /full/path/to/directory;相反,它只去除第一个元素,使其成为 path/to/directory;使用两个 # 字符使模式匹配变得贪婪,匹配尽可能多的字符。阅读答案中链接的参数扩展页面以获取更多信息。
请注意,pwd 的输出并不总是与 $PWD 的值相同,这是由于 cd 通过符号链接、bash 是否设置了 -o physical 选项等引起的复杂情况。这过去在处理自动挂载的目录时变得特别讨厌,其中记录物理路径而不是逻辑路径会产生一个路径,如果使用该路径,将允许自动挂载程序自发卸载正在使用的目录。
好的!有人应该为像我这样的博恩贝壳老家伙写一本书。也许那里有一个?它可能有一个古怪的老系统管理员说,“回到我的时代,我们只有 shcsh,如果你想要退格键工作,你必须阅读整个 stty 手册页,我们喜欢它!”
A
Arkady

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

% basename "$PWD"
bin

这不是 basename 程序的目的吗?除了缺少引号之外,这个答案有什么问题?
@Nacht basename 确实是一个做正确事情的外部程序,但是运行 any 外部程序来完成 bash 可以仅使用内置功能开箱即用的事情是愚蠢的,无故产生性能影响(fork()execve()wait() 等)。
...顺便说一句,我在此处对 basename 的反对意见适用于 dirname,因为 dirname 具有 ${PWD##*/} 不具备的功能 - 转换不带斜杠的字符串例如,.。因此,虽然使用 dirname 作为外部工具会产生性能开销,但它还具有有助于弥补相同开销的功能。
当然使用 basename 的“效率”较低。但就开发人员的生产力而言,它可能高效,因为与丑陋的 bash 语法相比,它更容易记住。所以,如果你还记得它,那就去吧。否则,basename 也同样有效。有没有人不得不提高 bash 脚本的“性能”并使其更有效率?
@usr,...作为编写通过 maildirs 操作的 bash 脚本的人(一种邮箱存储格式,每封电子邮件一个文件,如 qmail 使用的那样),是的,绝对,我有真正的理由要关注脚本的效率。我们讨论的是非嵌入式硬件上每个命令替换 20 毫秒的规模——循环超过 100,000 个项目,即使您的代码只有其中一个项目,您也是在实时讨论。 (当然,shell 有时不再是正确的工具,但如果您不注意编写代码的好坏,您会更快地到达那个点)。
g
gniourf_gniourf
$ echo "${PWD##*/}"


@jocap ...除了在没有引用参数的情况下使用 echo 是错误。如果目录名有多个空格,或者一个制表符,它会被折叠成一个空格;如果它有一个通配符,它将被扩展。正确的用法是 echo "${PWD##*/}"
@jocap ...另外,我不确定“回声”实际上是答案的合法部分。问题是如何得到答案,而不是如何打印答案;例如,如果目标是将其分配给变量,则 name=${PWD##*/} 是正确的,而 name=$(echo "${PWD##*/}") 是错误的(在不必要的低效率意义上)。
A
Arsen Khachaturyan

利用:

basename "$PWD"

或者

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

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

IFS= 

IFS 后面有一个空格。


g
gniourf_gniourf

您可以使用密码和基本名称的组合。例如

#!/bin/bash

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

echo "$BASENAME"

exit;

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

grep怎么样:

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

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

或者

echo "$(basename $(pwd))"

需要更多引号才能正确 - 事实上,pwd 的输出在传递给 basename 之前是字符串拆分和全局扩展的。
因此,basename "$(pwd)" - 虽然与仅使用 basename "$PWD" 相比效率非常低,与使用参数扩展而不是调用 basename 相比,它本身效率低下。
r
rodvlopes

这个线程很棒!这里还有一种味道:

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

F
FDS

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

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

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

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

报告“酒吧”

目录名

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

echo ${target_PWD%/*}

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


不幸的是,readlink -f 是 GNU 扩展,因此在 BSD(包括 OS X)上不可用。
a
anomal
echo "$PWD" | sed 's!.*/!!'

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


仅供参考,${PWD##*/} 是 POSIX sh - 每个现代 /bin/sh(包括 dash、ash 等)都支持它;要在最近的机器上击中真正的 Bourne,您需要使用稍微有点旧的 Solaris 系统。除此之外——echo "$PWD";省略引号会导致错误(如果目录名称包含空格、通配符等)。
是的,我使用的是旧的 Solaris 系统。我已经更新了使用引号的命令。
A
Arton Dorneles

令人惊讶的是,没有人提到这种只使用内置 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
g
geckos

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

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

u
user2208522

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

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

$PWD 更改为 "$PWD" 以正确处理不寻常的目录名称。
D
Dutch Glory

我通常在 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 ...]
f
fedorqui

只需使用:

pwd | xargs basename

或者

basename "`pwd`"

这在所有方面都比 Arkady (stackoverflow.com/a/1371267/14122) 先前给出的答案更糟糕:当目录名称包含文字换行符或引号字符时,xargs formultion 效率低下且有错误,并且第二种公式调用 pwd 命令在子外壳中,而不是通过内置变量扩展检索相同的结果。
@CharlesDuffy 尽管如此,它是否是对该问题的有效且实用的答案。
@bachph,我不同意:根本不应该将错误的答案(f / e,当目录名称包含空格时不起作用的答案)视为答案。
A
Abhishek Gurjar

在带有正则表达式的 grep 下也可以正常工作,

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

如果目录名称包含“-”字符,则它不起作用。
G
Gautam Sreekumar

如果您只想查看 bash 提示区域中的当前目录,您可以编辑 ~ 中的 .bashrc 文件。将行中的 \w 更改为 \W

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

运行source ~/.bashrc,它只会在提示区域显示目录名称。

参考:https://superuser.com/questions/60555/show-only-current-directory-name-not-full-path-on-bash-prompt


S
Steve Benner

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


仅供参考,它没有随我的 Ubuntu 发行版一起提供。
@KatasticVoyage,它在 Ubuntu 上,它只是在那里被称为 basename 。它通常仅在 MacOS 和其他带有非 GNU 基本名称的平台上称为 gbasename
n
niks_4143

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

$basename <path-of-directory>

o
octopusgrabbus

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

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

(1) 我不确定我们在哪里确保参数列表使得 $1 将包含当前工作目录。 (2) pushdpopd 在这里没有任何作用,因为反引号内的任何内容都是在子 shell 中完成的——因此它不会影响父 shell 的开始目录。 (3) 使用 "$(cd "$1"; pwd)" 对带有空格的目录名称更具可读性和弹性。
B
Benjamin Zach

只需删除 / 之前的任何字符(或 \,如果您在 Windows 上)。由于比赛将变得贪婪,它将删除最后一个 / 之前的所有内容:

pwd | sed 's/.*\///g'

在您的情况下,结果符合预期:

λ a='/opt/local/bin'

λ echo $a | sed 's/.*\///g'
bin