UNIX 'basename' 命令的奇怪行为

UNIX 'basename' 命令的奇怪行为

我想使用basename来获取以换行符分隔的文件路径列表的基本名称。当 n > 2 时,它似乎有效:

$ basename `echo -e '/foo/bar \n /food/baz \n /oof/rab'`
bar
baz
rab

但是当 n = 2 时,它只输出第一行。

$ basename `echo -e '/foo/bar \n /food/baz'`
bar

为什么没有打印baz?这是 中的错误吗basename?这是我的平台信息:

$ uname -a
Darwin MacBook-Pro-4.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun  4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64

答案1

basename不接受路径列表(更不用说以换行符分隔的路径列表),它接受单个路径(可选择后跟要删除的后缀)。

此外,当您echo在反引号中使用命令(如 )时,shell 会获取该命令的输出,将其拆分为由空格分隔的“单词”(通常是空格、制表符和换行符,但您可以使用 进行更改$IFS),扩展它找到的任何通配符,并将所有这些的结果传递给另一个命令(basename在本例中)。因此,echo输出中的换行符被视为单词之间的更多空格,basename根本不传递给。所以这个命令:

basename `echo -e '/foo/bar \n /food/baz \n /oof/rab'`

相当于:

basename "/foo/bar" "/food/baz" "/oof/rab"

...显然,当basename获得三个这样的参数时,它会分别打印每个参数的基本名称。手册页basename没有记录这种行为,所以如果有什么问题的话,那就是在这种情况下它没有给出错误。

类似地,此命令:

basename `echo -e '/foo/bar \n /food/baz'`

相当于

basename "/foo/bar" "/food/baz"

...但在这种情况下,basename的行为是定义的并且完全不同 - 它采用第一个参数的基本名称,从其末尾删除第二个参数(如果匹配),并打印结果。“bar”不以“/food/baz”结尾,所以它只打印“bar”。

此外,echo -e奇怪的是,它不可移植(即使在不同版本的 OS X 之间!)。下面,我使用 bash 的$' '字符串格式来翻译转义序列。

如果要打印以换行符分隔的路径列表的基本名称,请使用循环basename分别对每个路径运行:

while read -r filepath; do
    basename "$filepath"
done <<< $'/foo/bar \n /food/baz'

或者直接使用sed删除每行第一个“/”:

echo $'/foo/bar \n /food/baz \n /oof/rab' | sed 's@^.*/@@'

答案2

basename 有一个可选参数后缀 例如

basename include/stdio.h .h

返回"stdio"

您的第二个参数"/food/baz"以空格分隔," \n "被视为后缀。由于不匹配,因此"bar"仅返回。

你可以试试

basename `echo -e '/foo/bar \n r'`

应该返回"ba"

答案3

不确定操作系统版本,但可能有一些不同的基本名称实用程序。我的(Mac OS 10.12.6 上的 /usr/bin/basename)在 echo 子命令中给出有关“-e”的错误,但这可能是我的 shell(tcsh)。

这对我有用:

basename /foo/bar /food/baz /oof/rab

答案4

为了补充@Gordon 的答案,basename(和它的朋友dirname)有时用来删除后缀。

$ basename /foo/bar.c .c
bar

这在编译或处理文件时很方便。例如:

SRC=$1
DEST=$(dirname $SRC)/$(basename $SRC .txt).csv

process ${SRC} > ${DEST}

(请注意,以上代码片段不适用于带有奇怪字符(空格、制表符或换行符)的文件)

相关内容