Remove a fixed prefix/suffix from a string in Bash
在我的bash脚本中,我有一个字符串及其前缀/后缀。 我需要从原始字符串中删除前缀/后缀。
例如,假设我有以下值:
1 2 3
| string="hello-world"
prefix="hell"
suffix="ld" |
我如何得到以下结果?
-
看一下Advanced Bash-Scripting Guide
-
在链接到所谓的Advanced Bash Scripting Guide时要非常小心; 它包含了很好的建议和可怕的混合物。
1 2 3 4
| $ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo"${foo}"
o-wor |
-
还有##和%%,如果$ prefix或$ suffix包含通配符,它??们会尽可能地删除。
-
有没有办法将两者合二为一?我试过${${string#prefix}%suffix}但它不起作用。
-
@static_rtti不,遗憾的是你不能像这样嵌套参数替换。我知道,这很遗憾。
-
@AdrianFrü hwirth:整个语言都很遗憾,但它非常有用:)
-
有什么东西叫,我在哪里可以找到它们的参考?
-
Nvm,Google中的"bash替代"找到了我想要的东西。
-
这在Advanced Bash-Scripting Guide的参数替换部分中有记录:tldp.org/LDP/abs/html/parameter-substitution.html。
-
@static_rtti,有一个解决方法:echo basename ${string/hell} ld(灰色部分在反引号之间)
-
@AdrianFrü hwirth:替换是区分大小写的。有什么办法让它不区分大小写?
-
@ccpizza参数替换不知道这样的修饰符,但如果它是一个固定的字符串,你可以随时做${foo#[Bb][Aa][Rr]}。不是很漂亮,但仍然可能比不必要的子shell / fork更好,具体取决于具体情况。
-
请注意,魔术字符是#和%,而不是#$和%$ - $是上面示例中$prefix的一部分 - 乍一看可能不太清楚
-
如果$suffix包含类似*][的模式字符,则结果可能是意外的。更好地引用它。见foo="abc*def"; suffix="*def"; echo"${foo%$suffix}"; echo"${foo%"$suffix"}"。它将打印abc* abc
-
有没有办法从命令输出中删除后缀/前缀而不先将其分配给临时变量?我尝试了类似这样的${$(cmd)#"$prex"},但不起作用。
-
OT:@static_rtti编写的内容确实在ZSH中使用了这种确切的语法:-)
-
参数扩展记录在Bash参考手册中。我们真的不想让人们认为ABS是一种高质量的参考。
使用sed:
1 2
| $ echo"$string" | sed -e"s/^$prefix//" -e"s/$suffix$//"
o-wor |
在sed命令中,^字符匹配以$prefix开头的文本,尾随$匹配以$suffix结尾的文本。
AdrianFrühwirth在下面的评论中提出了一些好处,但sed为此目的非常有用。 $ sed和$ suffix的内容由sed解释的事实可能是好的或坏的 - 只要你注意,你应该没事。美丽的是,你可以这样做:
1 2 3 4
| $ prefix='^.*ll'
$ suffix='ld$'
$ echo"$string" | sed -e"s/^$prefix//" -e"s/$suffix$//"
o-wor |
这可能是你想要的,并且比bash变量替换更有魅力和更强大。如果你记得有很大的力量就会有很大的责任(正如蜘蛛侠说的那样),你应该没事。
有关sed的快速介绍,请访问http://evc-cit.info/cit052/sed_tutorial.html
关于shell及其字符串使用的注释:
对于给出的特定示例,以下内容也可以使用:
1
| $ echo $string | sed -e s/^$prefix// -e s/$suffix$// |
......但仅仅是因为:
echo不关心它的参数列表中有多少个字符串,以及
$ prefix和$ suffix中没有空格
在命令行中引用字符串通常是一种好习惯,因为即使它包含空格,它也会作为单个参数显示给命令。我们引用$ prefix和$ suffix的原因相同:sed的每个edit命令都将作为一个字符串传递。我们使用双引号,因为它们允许变量插值;如果我们使用单引号,sed命令会得到一个文字$prefix和$suffix,这肯定不是我们想要的。
另请注意,在设置变量prefix和suffix时我使用单引号。我们当然不希望对字符串中的任何内容进行解释,因此我们单引引它们以便不进行插值。同样,在这个例子中可能没有必要,但这是一个非常好的习惯。
-
不幸的是,由于以下几个原因,这是一个糟糕的建议:1)不带引号,$string受到单词拆分和通配。 2)$prefix和$suffix可以包含sed将解释的表达式,例如正则表达式或用作分隔符的字符将破坏整个命令。 3)不需要调用sed两次(你可以-e 's///' -e '///'),也可以避免使用管道。例如,考虑string='./ *'和/或prefix='./',并且由于1)和2)而看到它可怕地破坏。
-
有趣的说明:sed几乎可以作为分隔符。在我的情况下,由于我正在解析路径中的前缀目录,我无法使用/,所以我使用了sed"s#^$prefix##。 (脆弱性:文件名不能包含#。因为我控制文件,所以我们很安全。)
-
@Olie文件名可以包含除斜杠和空字符之外的任何字符,因此除非您处于控制之中,否则您不能假定文件名不包含某些字符。
-
是的,不知道我在想什么。 iOS可能吗?不知道。文件名当然可以包含"#"。不知道我为什么这么说。 :)
-
@Olie:正如我理解您的原始评论,您说您选择使用#作为sed的分隔符意味着您无法处理包含该字符的文件。
你知道你的前缀和后缀的长度吗?在你的情况下:
1
| result=$(echo $string | cut -c5- | rev | cut -c3- | rev) |
或者更一般:
1
| result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev) |
但AdrianFrühwirth的解决方案很酷!我不知道那个!
我使用grep从路径中删除前缀(sed处理不好):
1
| echo"$input" | grep -oP"^$prefix\K.*" |
\K从匹配中删除之前的所有字符。
-
grep -P是非标准扩展名。如果您的平台支持更多功能,但如果您的代码需要合理的可移植性,这是一个可疑的建议。
-
@tripleee确实。但我认为安装了GNU Bash的系统也有一个支持PCRE的grep。
-
不,例如MacOS开箱即用Bash而不是GNU grep。早期的版本实际上有来自BSD grep的-P选项,但他们删除了它。
1 2 3 4 5 6 7 8 9 10 11
| $ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ #remove"hell" from"hello-world" if"hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}
$ #remove"ld" from"o-world" if"ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor |
笔记:
#$ prefix:adding#确保只有在开头找到子字符串"hell"才会被删除。
%$后缀:添加%确保只有在end中找到子字符串"ld"才会被删除。
如果没有这些,子串"地狱"和"ld"将在任何地方被移除,即使它位于中间。
-
谢谢你的笔记! qq:在你的代码示例中,你还在字符串后面有一个正斜杠/,那是为了什么?
-
/分隔当前字符串和子字符串。 这里的子字符串是发布问题的后缀。
小而通用的解决方案:
1
| expr"$string" :"$prefix\(.*\)$suffix" |
-
如果您使用的是Bash,则根本不应该使用expr。 在最初的Bourne shell的时代,它是一种方便的厨房水槽实用程序,但现在已经过了它最好的日期。
使用=~运算符:
1 2 3 4 5
| $ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ [["$string" =~ ^$prefix(.*)$suffix$ ]] && echo"${BASH_REMATCH[1]}"
o-wor |
使用@AdrianFrühwirth回答:
1 2 3 4
| function strip {
local STRING=${1#$"$2"}
echo ${STRING%$"$2"}
} |
像这样使用它
1 2 3
| HELLO=":hello:"
HELLO=$(strip"$HELLO"":")
echo $HELLO # hello |