bash:为路径名提取最后两个目录

use*_*256 16 string bash

在bash中,我似乎在一些非常简单的事情上失败了.我有一个字符串变量,它包含目录的完整路径.我想将其中的最后两个目录分配给另一个字符串.例如,如果我有:

DIRNAME = /a/b/c/d/e
Run Code Online (Sandbox Code Playgroud)

我想要:

DIRNAME2 = d/e
Run Code Online (Sandbox Code Playgroud)

我确信有一个简单的bash构造或sed命令可以做到,但它正在逃避我.我有点像一个通用版本,basename或者dirname不仅仅是返回名称的极端部分.

谢谢!戴夫

Jon*_*ler 14

DIRNAME="/a/b/c/d/e"
D2=$(dirname "$DIRNAME")
DIRNAME2=$(basename "$D2")/$(basename "$DIRNAME")
Run Code Online (Sandbox Code Playgroud)

或者,在一行中(但要小心所有双引号 - 分割时更容易):

DIRNAME2=$(basename "$(dirname "$DIRNAME")")/$(basename "$DIRNAME")
Run Code Online (Sandbox Code Playgroud)

除非你严重受虐狂,否则不要尝试使用反引号的游戏.如果路径中可能有空格,请在变量名称周围使用双引号.

这适用于几乎任何shell Korn Shell以及Bash.在bash,还有其他机制可用 - 其他答案说明了许多选项中的一些,但expr也是一个老派的解决方案(它也出现在第7版Unix中).这个使用反引号的代码也适用于Bash和Korn shell - 但不适用于Heirloom Shell(类似于Unix System V Release 2/3/4 shell,IIRC).

DIRNAME2=`basename "\`dirname \\"$DIRNAME\\"\`"`/`basename "$DIRNAME"`
Run Code Online (Sandbox Code Playgroud)

(嵌套的两个层次并不太糟糕,但它非常糟糕;三个非常棘手!)

测试

当测试路径名称操作应该在路径名中的空格中存在时,值得测试使用包含双空格(而不是单个空格)的名称.例如:

DIRNAME="/a b/ c d /  ee  ff  /  gg  hh  "
echo "DIRNAME=[[$DIRNAME]]"
echo "basename1=[[$(basename "$DIRNAME")]]"
echo "basename2=[[`basename \"$DIRNAME\"`]]"
echo
D2=$(dirname "$DIRNAME")
echo "D2=[[$D2]]"
DIRNAME2=$(basename "$D2")/$(basename "$DIRNAME")
echo "DIRNAME2=[[$DIRNAME2]]"
echo
DIRNAME3=$(basename "$(dirname "$DIRNAME")")/$(basename "$DIRNAME")
echo "DIRNAME3=[[$DIRNAME3]]"
DIRNAME4=`basename "\`dirname \\"$DIRNAME\\"\`"`/`basename "$DIRNAME"`
echo "DIRNAME4=[[$DIRNAME2]]"
Run Code Online (Sandbox Code Playgroud)

那个输出是:

DIRNAME=[[/a b/ c d /  ee  ff  /  gg  hh  ]]
basename1=[[  gg  hh  ]]
basename2=[[  gg  hh  ]]

D2=[[/a b/ c d /  ee  ff  ]]
DIRNAME2=[[  ee  ff  /  gg  hh  ]]

DIRNAME3=[[  ee  ff  /  gg  hh  ]]
DIRNAME4=[[  ee  ff  /  gg  hh  ]]
Run Code Online (Sandbox Code Playgroud)


Xiè*_*léi 10

我更喜欢尽可能多地使用内置函数,以避免创建不必要的进程.因为您的脚本可能在Cygwin或其他过程创建非常昂贵的操作系统下运行.

我想如果你只想提取两个目录,那就不那么冗长了:

base1="${DIRNAME##*/}"
dir1="${DIRNAME%/*}"
DIRNAME2="${dir1##*/}/$base1"
Run Code Online (Sandbox Code Playgroud)

这还可以避免执行其他命令时涉及的特殊字符问题.


Gor*_*son 5

我不知道专门用于修剪路径的方法,但您当然可以使用bash 的正则表达式匹配来完成

DIRNAME=/a/b/c/d/e
if [[ "$DIRNAME" =~ ([^/]+/+[^/]+)/*$ ]]; then
    echo "Last two: ${BASH_REMATCH[1]}"
else
    echo "No match"
fi
Run Code Online (Sandbox Code Playgroud)

注意:为了处理路径中一些允许但不常见的事情,我在此处制作的模式比您预期的要复杂一些:它修剪尾部斜杠,并在最后一个斜杠之间容忍多个(冗余)斜杠两个名字。例如,在“/a/b/c//d//”上运行它会匹配“c//d”。