UNIX `basename` 命令的奇怪行为

And*_*son 2 unix bash shell macos

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

$ basename `echo -e '/foo/bar \n /food/baz \n /oof/rab'`
bar
baz
rab
Run Code Online (Sandbox Code Playgroud)

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

$ basename `echo -e '/foo/bar \n /food/baz'`
bar
Run Code Online (Sandbox Code Playgroud)

为什么不打印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
Run Code Online (Sandbox Code Playgroud)

Gor*_*son 6

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

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

basename `echo -e '/foo/bar \n /food/baz \n /oof/rab'`
Run Code Online (Sandbox Code Playgroud)

相当于:

basename "/foo/bar" "/food/baz" "/oof/rab"
Run Code Online (Sandbox Code Playgroud)

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

同样,这个命令:

basename `echo -e '/foo/bar \n /food/baz'`
Run Code Online (Sandbox Code Playgroud)

相当于

basename "/foo/bar" "/food/baz"
Run Code Online (Sandbox Code Playgroud)

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

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

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

while read -r filepath; do
    basename "$filepath"
done <<< $'/foo/bar \n /food/baz'
Run Code Online (Sandbox Code Playgroud)

或者只是sed用来删除每行中的第一个“/”:

echo $'/foo/bar \n /food/baz \n /oof/rab' | sed 's@^.*/@@'
Run Code Online (Sandbox Code Playgroud)