Bash 中的字符串裁剪

gib*_*ies 3 bash string

假设我的变量中有一个字符串,如下所示。

var="/fnxn/ngfdg/abc.ext"
Run Code Online (Sandbox Code Playgroud)

我可以按如下方式获取此变量的根(删除扩展名 .*)

echo ${var%.*}
Run Code Online (Sandbox Code Playgroud)

我可以得到这个变量的尾部(删除路径*/)如下

echo ${var##*/}
Run Code Online (Sandbox Code Playgroud)

但是现在我必须同时删除路径和扩展名。我可以使用另一个变量分两步完成,如下所示。

var2=${var##*/}
var3=${var2%.*}
Run Code Online (Sandbox Code Playgroud)

但是单步选项(类似于下面的 zsh 语法)在 bash 中显示了一条错误消息“坏替换”。

echo ${${var##*/}%.*}
Run Code Online (Sandbox Code Playgroud)

如果我得到一个单步可理解的选项来减少代码长度并避免额外的环境变量,那将会很有用。

Mic*_*mer 5

如果您有固定的扩展名,POSIX 将basename完全支持您尝试的裁剪:

basename "$var" .ext
Run Code Online (Sandbox Code Playgroud)

如果不这样做,zsh 扩展完全支持您正在尝试的内容:

如果使用 ${...} 类型的参数表达式或 $(...) 类型的命令替换代替上面的 name,则首先将其扩展并将结果用作 name 的值。因此可以执行嵌套操作: ${${foo#head}%tail} 用删除了 'head' 和 'tail' 的值来替换 $foo 的值。

所以在 zsh

echo ${${var##*/}%.*}
Run Code Online (Sandbox Code Playgroud)

会做你所期望的。


如果您致力于 Bash,并且想要保存行,则可以使用 sed:

sed -e 's/\.[^.]*$//' <<<"${var##*/}"
Run Code Online (Sandbox Code Playgroud)

这只是用正则表达式替换最后一个之后的所有.内容,就像现在一样丢失前缀。然而,我认为这并没有特别节省两行版本的内存,而且它也可能不太容易理解。

请注意,您没有理由不能在整个过程中使用相同的变量:

var2=${var##*/}
var2=${var2%.*}
Run Code Online (Sandbox Code Playgroud)

扩展发生在赋值之前,所以这是安全的。如果您担心的是一个非常长的文件名被存储了两次,现在不会发生这种情况,但我认为这不是一个现实问题。

  • @gibies,我的意思是 `echo ${var%.*}` 是 `zsh` 语法。[在`bash`中,你需要:`echo "${var%.*}"`](/q/171346/)。(好吧,严格来说,这将是 [`printf '%s\n' "${var%.*}"`](/q/65803) (在 `zsh` 中也是如此))。 (2认同)
  • @gibies,省略引号是在 bash 和大多数其他类似 Bourne 的 shell 中调用 split+glob 运算符。大括号通常只是装饰性的,引号是必要的。请参阅 http://mywiki.wooledge.org/BashPitfalls,或此处的任何问答:[1](/q/171346)、[2](/q/68694)、[3](/q/131766)、 [4](/q/131766), [5](/q/78914)... 尝试例如使用 `var='foo/bar * baz'` 和 `$IFS` 的默认值。与`zsh`比较。 (2认同)