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

时间:2014-12-26 15:05:16

标签: bash

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

echo $var
"some string.rtf"

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

echo $var2
"some string"

我该怎么做?

11 个答案:

答案 0 :(得分:157)

你可以这样做:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"

答案 1 :(得分:114)

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

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

答案 2 :(得分:47)

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

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

如果您确实要始终删除特定的数量个字符,则可以使用以下一些选项。

您专门标记了此bash,因此我们将从bash内置插件开始。工作时间最长的是我上面使用的相同的后缀删除语法:要删除四个字符,请使用var2=${var%????}。或仅在第一个为点的情况下删除四个字符,请使用var2=${var%.???},类似于var2=${var%.*},但仅在点后的部分恰好是三个字符时才删除后缀。如您所见,要以这种方式计算字符,您需要为每个未知字符删除一个问号,因此对于较大的子字符串长度,此方法变得很笨拙。

在较新的Shell版本中,一个选项是子字符串提取:var2=${var:0:${#var}-4}。您可以在此处用任何数字代替4来删除其他数量的字符。 ${#var}被替换为字符串的长度,因此这实际上是要求提取并保留索引0到(length-4)之间的所有字符。使用这种方法,您将失去仅在字符串与模式匹配时进行更改的选项;不管字符串的实际值是多少,副本都将包括除最后四个字符之外的所有字符。

Bash可让您忽略起始索引;它默认为0,因此您可以将其缩短为var2=${var::${#var}-4}。实际上,较新版本的bash(特别是4+,表示MacOS附带的版本不起作用)将负索引识别为从字符串末尾算起的数字,因此您也可以摆脱长度表达式: var2=${var::-4}

如果您实际上不是在使用bash,而是在使用其他POSIX类型的外壳,则使用%的基于模式的后缀删除仍然可以工作-即使在普通的破折号中,也不会使用基于索引的子字符串提取t。 Ksh和zsh都支持子字符串提取,但是需要显式的0开始索引。 zsh还支持负端索引,而ksh需要长度表达式。

您当然可以运行某些实用程序来修改字符串并使用命令替换捕获其输出,而不是使用内置的shell参数扩展。有很多方法可以使用,但是类似var2=$(cut -c -4 <<<"$var")的方法可能是最短的选择。

答案 3 :(得分:34)

使用Variable expansion/Substring replacement

  

$ {VAR /%图案/代用}

     

如果var的后缀与Pattern匹配,则替换Replacement for Pattern。

所以你可以这样做:

~$ echo ${var/%????/}
some string

可替换地,

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

~$ echo ${var/.rtf/}
some string

如果它始终以.xyz结尾:

~$ echo ${var%.*}
some string

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

~$ len=${#var}
~$ echo ${var::len-4}
some string

或只是echo ${var::-4}

答案 4 :(得分:28)

对我有用的是:

echo "hello world" | rev | cut -c5- | rev
# hello w

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

cat somefile | rev | cut -c5- | rev

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

答案 5 :(得分:22)

你可以使用sed,

sed 's/.\{4\}$//' <<< "$var"

示例:

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

答案 6 :(得分:2)

我尝试了以下内容,它对我有用:

#! /bin/bash

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

输出:hel

答案 7 :(得分:2)

在这种情况下,您可以使用基本名称,前提是您要删除的文件具有相同的后缀。

示例:

basename -s .rtf "some string.rtf"

这将返回“一些字符串”

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

f=file.whateverthisis
basename "${f%.*}"

输出“文件”

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

答案 8 :(得分:0)

通过计算字符串的大小,这对我有用 您需要回显返回所需的值,然后将其存储如下

removechars(){
        var="some string.rtf"
        size=${#var}
        echo ${var:0:size-4}  
    }
    removechars
    var2=$?
  

一些字符串

答案 9 :(得分:0)

希望以下示例会有所帮助,

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

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

示例:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}

输出:

    Enter:Siddharth Murugan
    Siddhar

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

答案 10 :(得分:0)

这也可以做到:

... | head -c -1
-c, --bytes=[-]NUM
              print the first NUM bytes of each file; with the leading '-', print all but the last NUM bytes of each file