在Bash中获取文件路径参数中的最后一个目录名/文件名

343
我正试图编写一个SVN的post-commit hook,这个hook运行在我们的开发服务器上。我的目标是尝试自动检出已提交的项目到它在服务器上托管的目录中。然而,我需要能够只读取传递给脚本的目录字符串中的最后一个目录,以便检出到与我们项目所在的子目录相同的地方。
例如,如果我对名为“example”的项目进行SVN提交,我的脚本将得到"/usr/local/svn/repos/example"作为其第一个参数。我需要从字符串末尾获取"example"并将其与另一个字符串连接起来,以便可以检出到"/server/root/example"并立即查看更改。
5个回答

503

basename命令用于去除路径中的目录前缀:

$ basename /usr/local/svn/repos/example
example
$ echo "/server/root/$(basename /usr/local/svn/repos/example)"
/server/root/example

2
basename 绝对是我要找的。但是如何获取存储在变量中的参数的 basename 呢?例如:SUBDIR="/path/to/whatever/$(basename $1)" - TJ L
6
听起来好像没有 $1,或者 $1 是空的。 - sth
1
不幸的是,如果你包装命令,basename 不是一个好主意。只是要记住这一点。 - dtc

149

可以使用以下方法来获取路径名中的任何路径:

some_path=a/b/c
echo $(basename $some_path)
echo $(basename $(dirname $some_path))
echo $(basename $(dirname $(dirname $some_path)))

输出:

c
b
a

5
不支持带空格的路径...您可以使用引号来解决这个问题...下面这个命令可以奇妙地解决这个问题:echo "$(basename "$(dirname "$pathname")")" - Ray Foss
如何只打印路径/a/b?谢谢。 - undefined

97

Bash可以获取路径的最后一部分,而无需调用外部的basename命令:

dir="/path/to/whatever/"
dir="${dir%/}"             # strip trailing slash (if any)
subdir="${dir##*/}"

这里使用了Bash的参数扩展来删除字符串中最后(剩余)斜杠之前的部分。


3
在我的Mac电脑上,对于每个数千个文件都要做一些微不足道的处理情况下,使用子字符串符号比使用dirname/basename快了一个数量级以上。 - George
1
d=/home/me/somefolder;subdir="/$d/${1##*/}" 我最终得到了类似于“//home/me/somefolder//”这样的东西,其中$d实际上来自循环“for d in $(find $SOMEFOLDER -maxdepth 1 -type d);”。使用“subdir=$(basename $d)”可以按预期工作。 - Buttle Butkus
2
@ButtleButkus:你应该使用while而不是for来迭代find的输出(find -print0 | xargs -0更好),或者使用globbing:for d in $SOMEFOLDER/*/(最后的斜杠就像-type d一样工作,如果你shopt -s globstar,则可以在Bash 4中使用**进行递归,但可能会出现“参数列表过长”的消息)。请注意,命令中的${1}部分表示脚本或函数的第一个参数。您可能需要使用${d##*/}或其他变量或参数规范,或确保在$1中传递了一个参数。 - Dennis Williamson
6
非常感谢你的分享。对于未来可能会开始想知道“为什么它不起作用”或者“我是唯一一个笨蛋”的读者们,需要注意的是,上面的答案假设$1包括“需要移除最后一个组件的路径”。而我忽略了这一点。我的应用场景是:target_path='/home/user/dir1/dir2/dir3/'; target_path="${target_path%/}"; last_component=${target_path##*/}; echo $last_component - 这个命令可以工作。 - Vinay Vissh
3
请点击此链接了解 ${1##*/}如何为什么 工作原理:https://unix.stackexchange.com/a/171786/15070 - Matt
显示剩余5条评论

2
为了不使用外部命令打印文件名,请运行:
```bash echo "$(basename /path/to/file)" ```
fileNameWithFullPath="${fileNameWithFullPath%/}";
echo "${fileNameWithFullPath##*/}" # print the file name

此命令必须比basenamedirname运行更快。


它与2010年https://dev59.com/i3A75IYBdhLWcg3wdIl0#3294514/中给出的有何不同? - ᄂ ᄀ

-1
如果文件在当前目录中,您可以使用以下简短的代码:
cd /dev/shm
basename $(pwd) # shm

我已经有了这个sed命令,尽管我承认它并不是很实用:

path="/dev/shm"
sed -r "s/^.*\/(.*)$/\1/" <<< $path # shm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接