如何从Bash中的字符串中删除最后n个字符?

bec*_*cko 163 bash

var在一个包含字符串的Bash脚本中有一个变量,如:

echo $var
"some string.rtf"
Run Code Online (Sandbox Code Playgroud)

我想删除此字符串的最后4个字符并将结果分配给新变量var2,以便

echo $var2
"some string"
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

phe*_*phe 193

你可以这样做:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"
Run Code Online (Sandbox Code Playgroud)

  • 它是`bash` 4+,但在早期版本中你可以使用稍长的`$ {v :: $ {#v} -4}`. (58认同)
  • 出于某种原因,在zsh` $ {v :: - 4}`croaks"zsh:close brace expected".但@fredtantini在'$ {v:0:-4}下面的回答是正常的. (9认同)
  • bash 4+我想. (8认同)
  • 没有为我工作,bash说''糟糕的替代' (5认同)
  • 错误:-2:子串表达式<0 (4认同)
  • @Edward我也刚刚遇到这个问题,就我而言,这似乎是因为macOS不支持子字符串中的负速记(https://superuser.com/questions/1033273/bash-4-3-substring-negative- os-x 上的长度)。那里的解决方案有效,但很丑陋:`v2=${v:0:$((${#v} - 4))}` (4认同)
  • @PierreD day saver,我首先以错误的方向搜索右大括号问题,并试图逃避大括号。但 zsh 只需要一个起始索引。这也解释了两个双冒号。感谢 anishane 对 fredtantinis 答案的评论。 (3认同)

Eta*_*ner 146

要从字符串末尾删除四个字符使用${var%????}.

在最终.使用后删除所有内容${var%.*}.

  • + 1这就是问题*实际上*要求. (14认同)
  • 纯shell回答,并且将在BASH 3.x中工作,这些系统在许多因许可问题而拒绝实施BASH 4.x的系统中找到. (7认同)
  • @user2023370 查阅文档应该会发现有一个相应的参数替换 `${var#????}` 。 (3认同)
  • 这很酷,因为它可以抵抗变短的字符串;那么索引偏移变体就会失败。 (2认同)

Mar*_*eed 99

首先,通常最好明确说明您的意图。因此,如果您知道字符串以 结尾.rtf,并且想要删除它.rtf,则只需使用var2=${var%.rtf}. 这种方法的一个潜在有用的方面是,如果字符串不以结尾.rtf,则它根本不会改变;var2将包含var.

如果您想删除文件名后缀但不知道或不关心它到底是什么,您可以使用var2=${var%.*}删除以最后一个.. 或者,如果您只想保留所有内容但不包括第一个 .,则可以使用var2=${var%%.*}. 如果只有一个.,这些选项具有相同的结果,但如果可能有多个,则您可以选择从字符串的哪一端开始工作。另一方面,如果.字符串中根本没有,var2则将再次是var.

如果您真的想始终删除特定数量的字符,这里有一些选项。

bash专门标记了这一点,因此我们将从 bash 内置函数开始。最有效的一种是我在上面使用的相同的后缀删除语法:要删除四个字符,请使用var2=${var%????}. 或者仅当第一个字符是点时才删除四个字符,请使用var2=${var%.???},这就像var2=${var%.*}但仅当点后面的部分正好是三个字符时才删除后缀。如您所见,要以这种方式计算字符数,您需要删除每个未知字符一个问号,因此这种方法对于较大的子字符串长度来说变得笨拙。

较新的 shell 版本中的一个选项是子字符串提取:var2=${var:0:${#var}-4}. 在这里,您可以用任意数字代替4来删除不同数量的字符。在${#var}由字符串的长度更换,所以实际上,要求以提取和保持(长度- 4)字符开始与第一个(在索引0)。使用这种方法,只有当字符串与模式匹配时,您才无法进行更改;无论字符串的实际值是多少,副本都将包含除最后四个字符之外的所有字符。

你可以不考虑开始索引;它默认为 0,因此您可以将其缩短为var2=${var::${#var}-4}. 事实上,较新版本的 bash(特别是 4+,这意味着 MacOS 附带的那个版本将无法工作)将负长度识别为要停止的字符的索引,从字符串的末尾开始计算。因此,在这些版本中,您也可以去掉字符串长度表达式:var2=${var::-4}.

如果您实际上不是在使用 bash,而是在使用其他一些 POSIX 类型的 shell,那么基于模式的后缀删除 with%仍然有效——即使在普通的旧破折号中,基于索引的子字符串提取不会。Ksh 和 zsh 都支持子串提取,但需要显式的 0 起始索引;zsh 也支持负端索引,而 ksh 需要长度表达式。请注意,如果您使用这种与 bash 兼容的语法,则 zsh从 1 开始索引数组,但仍然从 0 开始索引字符串。但是 zsh 还允许您将标量参数视为字符数组,在这种情况下,子字符串语法使用基于 1 的计数并将开始和(包括)结束位置放在括号中,以逗号分隔:。var2=$var[1,-5]

您当然可以运行一些实用程序来修改字符串并使用命令替换捕获其输出,而不是使用内置的 shell 参数扩展。有几个命令可以工作;一个是var2=$(sed 's/.\{4\}$//' <<<"$var")


Reu*_*ani 39

对我有用的是:

echo "hello world" | rev | cut -c5- | rev
# hello w
Run Code Online (Sandbox Code Playgroud)

但我用它来修剪文件中的线条,这就是为什么它看起来很尴尬.真正的用途是:

cat somefile | rev | cut -c5- | rev
Run Code Online (Sandbox Code Playgroud)

cut只能从一些起始位置进行修剪,如果你需要可变长度的行,那就不好了.因此,这个解决方案反转(rev)字符串,现在我们将其与结束位置相关联,然后cut如上所述使用,并将其(再次rev)反转回原始顺序.

  • @Mohammad因为[损坏的引用,](/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) *是*一行。 (2认同)

fre*_*ini 35

使用可变扩展/子串替换:

$ {VAR /%图案/换货}

如果var的后缀与Pattern匹配,则替换Pattern替换.

所以你可以这样做:

~$ echo ${var/%????/}
some string
Run Code Online (Sandbox Code Playgroud)

或者,

如果你总是有相同的4个字母

~$ echo ${var/.rtf/}
some string
Run Code Online (Sandbox Code Playgroud)

如果它总是以.xyz:

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

您还可以使用字符串的长度:

~$ len=${#var}
~$ echo ${var::len-4}
some string
Run Code Online (Sandbox Code Playgroud)

或者干脆 echo ${var::-4}


Avi*_*Raj 25

你可以使用sed,

sed 's/.\{4\}$//' <<< "$var"
Run Code Online (Sandbox Code Playgroud)

例:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string
Run Code Online (Sandbox Code Playgroud)

  • 这很好,因为它可以像这样在一行上工作... | sed 's/.\{4\}$//'. 可以在不使用变量的情况下使用 (3认同)

小智 15

这也可以完成这项工作:

... | head -c -1
Run Code Online (Sandbox Code Playgroud)
-c, --bytes=[-]NUM
              print the first NUM bytes of each file; with the leading '-', print all but the last NUM bytes of each file
Run Code Online (Sandbox Code Playgroud)


Vic*_*box 8

最上面的答案对我不起作用,因为 mac os x 附带了不同版本的 bash。

我像这样使用 sed:

var2=`echo $var2 | sed 's/.$//'`
Run Code Online (Sandbox Code Playgroud)

删除最后一个字符

var2=`echo $var2 | sed 's/..$//'`
Run Code Online (Sandbox Code Playgroud)

删除最后 2 个字符。


Pri*_*ain 6

我尝试了以下方法,它对我有用:

#! /bin/bash

var="hello.c"
length=${#var}
endindex=$(expr $length - 4)
echo ${var:0:$endindex}
Run Code Online (Sandbox Code Playgroud)

输出: hel


小智 6

希望下面的例子会有所帮助,

echo ${name:0:$((${#name}-10))} --> ${name:start:len}

  • 在上面的命令中,name 是变量。
  • start 是字符串的起点
  • len 是必须删除的字符串的长度。

例子:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}
Run Code Online (Sandbox Code Playgroud)

输出:

    Enter:Siddharth Murugan
    Siddhar
Run Code Online (Sandbox Code Playgroud)

注意:Bash 4.2 添加了对负子字符串的支持


Gig*_*igi 5

在这种情况下,您可以使用 basename 假设您要删除的文件具有相同的后缀。

例子:

basename -s .rtf "some string.rtf"
Run Code Online (Sandbox Code Playgroud)

这将返回“一些字符串”

如果您不知道后缀,并希望它删除最后一个点之后的所有内容:

f=file.whateverthisis
basename "${f%.*}"
Run Code Online (Sandbox Code Playgroud)

输出“文件”

% 表示砍,。是你要砍的,* 是通配符