提取Bash中最后两个斜杠之间的字符串

提取Bash中最后两个斜杠之间的字符串,bash,Bash,我知道这可以很容易地用正则表达式完成,就像我在上面回答的那样,但是我需要在bash中完成 所以我发现的关于Stackoverflow的最接近的问题是这个问题,但是区别在于 DIRNAME = /a/b/c/d/e 然后我需要提取 d 天哪,也许这是显而易见的,但一开始对我来说不是。我得到了正确的结果: dir=$(basename -- "$(dirname -- "$str")") echo "$dir" 使用zsh参数替换也很酷 echo ${${DIRNAME%/*}##*/} 我

我知道这可以很容易地用正则表达式完成,就像我在上面回答的那样,但是我需要在bash中完成

所以我发现的关于Stackoverflow的最接近的问题是这个问题,但是区别在于

DIRNAME = /a/b/c/d/e
然后我需要提取

d

天哪,也许这是显而易见的,但一开始对我来说不是。我得到了正确的结果:

dir=$(basename -- "$(dirname -- "$str")")
echo "$dir"

使用zsh参数替换也很酷

echo ${${DIRNAME%/*}##*/}
我认为它也比double$()快,因为它不需要任何子流程


基本上,它先从右侧切掉,然后再从左侧切掉所有剩余的部分。

使用
awk

echo "/a/b/c/d/e" | awk -F / '{ print $(NF-1) }' # d
编辑:当路径包含换行符时,此选项不起作用,当斜杠少于两个时,仍会给出输出,请参见下面的注释。

使用
sed

如果你想得到第四个元素

DIRNAME="/a/b/c/d/e"
echo "$DIRNAME" | sed -r 's_^(/[^/]*){3}/([^/]*)/.*$_\2_g'
如果要获取前一个元素

DIRNAME="/a/b/c/d/e"
echo "$DIRNAME" | sed -r 's_^.*/([^/]*)/[^/]*$_\1_g'

这可能相对较长,但它的执行速度也比前面的大多数答案快得多(除了zsh only one和j.a.的答案),因为它只使用bash中内置的字符串操作,并且不使用子shell扩展:

string='/a/b/c/d/e'  # initial data
dir=${string%/*}     # trim everything past the last /
dir=${dir##*/}       # ...then remove everything before the last / remaining
printf '%s\n' "$dir" # demonstrate output

上面使用了
printf
,因为
echo
不能可靠地处理所有值(想想它在带有
/a/b/c/-n/e
的GNU系统上会做什么。)

这里是一个纯
bash
解决方案:

[[ $DIRNAME =~ /([^/]+)/[^/]*$ ]] && printf '%s\n' "${BASH_REMATCH[1]}"
与其他一些答案相比:

  • 它匹配最后两个斜杠之间的字符串。例如,如果
    DIRNAME=d/e
    ,则它与
    d
    不匹配
  • 它更短更快(只使用内置程序,不创建子流程)
  • 支持最后两个斜杠之间的任何字符(有关详细信息,请参阅)

还要注意,在
bash
中,这不是分配变量的方式:

DIRNAME = /a/b/c/d/e
       ^ ^
这些空格是错误的,请删除它们:

DIRNAME=/a/b/c/d/e


$(basename$(dirname“$dirname”)
是否满足您的要求?否则,does
x=${DIRNAME%/*};echo${x##*/}
做你需要的工作吗?当心堕落的案例。在我把答案贴在下面之后,我看到了你的评论。是的。我真的不明白这个命令是如何/为什么提取最后两个斜杠之间的字符串的,但我不在乎它对我有多大作用。
dirname
命令删除文件名的最后一个部分,删除
/e
basename
命令除去文件名的最后一个组件,删除
/a/b/c
,只留下
d
@Jonathan,注意解决方案
x=${DIRNAME%/*};echo${x##*/}
在这里也匹配
d
DIRNAME=a/b
,我认为这不是OP想要的。@JonathanLeffler,这有点错误——在DIRNAME扩展周围也需要引号。哪个版本的Bash支持嵌套替换
zsh
有,但不是Bash AFAIK-但我也是来学习的。正确的编写方法应该是
dir=$(basename“$(dirname“$str”))”
…否则,当字符串包含空格、匹配文件的全局表达式等时,您会遇到错误,这是足够慢,你不会想把它放在一个内部循环,运行数百或数千次;命令替换(即,
$(…)
语法)会带来很大的性能损失。与使用内置字符串操作相比,效率非常低。(虽然awk答案和basename/dirname答案也是如此)。此外,由于缺少引号而导致的错误——看看如果
dirname
包含实际匹配任何内容的glob表达式或运行空格会发生什么情况。是的,字符串操作更好,只是,
sed
是另一种解决方案。。。。。在哪一个测试用例失败中,例如一个例子?考虑代码< > Drime= '/A/B/C/*D*/E '< /代码>其中,空格和文字星号是目录名的一部分。如果
DIRNAME
中有换行符,则其行为也会异常。这可能是最好的答案,因为它也是严格符合POSIX的。此@gniourf的唯一问题是它还匹配最后一个斜杠之前的任何字符串(例如
string=“d/e”
匹配
d
),OP要求最后两个斜杠之间的字符串。如果没有斜杠,它甚至可以打印任何字符串(例如:
string=hello
->打印
hello
)。@j.a.,我认为这两种情况都不是错误——都不属于规范中隐含的假设范围。如果你想输入一个
If[[$string=*/*/*]];然后
fi
包装此代码以避免这两种情况,这是一项微不足道的工作。应该注意的是,此解决方案在规模上的性能明显优于@j.a。即使进行了上述检查,我仍然看到路径
/home/a/b/c/d/e/f/g.txt
,1000000次迭代大约1.07秒,regex替换解决方案的时间为7.57秒。这也与这里的
d
相匹配:
DIRNAME=a/b
,OP要求最后两个斜杠之间的字符串。如果没有斜杠,这甚至可以打印任何字符串(例如:
echo hello | awk-F/{print$(NF-1)}
->打印
hello
)..并在文件名包含文字换行符时给出不正确的结果。