String 删除前导空格在Bash中不起作用

String 删除前导空格在Bash中不起作用,string,bash,removing-whitespace,parameter-expansion,String,Bash,Removing Whitespace,Parameter Expansion,我在Bash中有一个字符串,它可能以任何数量的前导空格开头,也可能不以任何数量的前导空格开头,例如 " foo bar baz" " foo bar baz" "foo bar baz" 我想从字符串中删除“foo”的第一个实例,以及任何前导空格(可能没有) 根据来自的建议,我尝试了以下方法: str=" foo bar baz" regex="[[:space:]]*foo" echo &q

我在Bash中有一个字符串,它可能以任何数量的前导空格开头,也可能不以任何数量的前导空格开头,例如

"  foo bar baz"
" foo bar baz"
"foo bar baz"
我想从字符串中删除“foo”的第一个实例,以及任何前导空格(可能没有)

根据来自的建议,我尝试了以下方法:

str=" foo bar baz"
regex="[[:space:]]*foo"
echo "${str#$regex}"
echo "${str#[[:space:]]*foo}"
如果str有一个或多个前导空格,那么它将返回我想要的结果,即
\u bar baz
(下划线=前导空格)。如果字符串没有前导空格,它将不会执行任何操作并返回
foobarbaz
。这两个“回声”在这里返回相同的结果

我的理解是,在
[:space:]
之后使用
*
应该匹配[[:space:]]的零个或多个实例,而不是一个或多个。我在这里错过了什么或做错了什么

编辑

@拉曼-我尝试了以下方法,但它们也不起作用:

echo "${str#[[:space:]]?foo}"
echo "${str#?([[:space:]])foo}"
echo "${str#*([[:space:]])foo}"

无论是否有尾随空格,这三种解决方案都不会删除“foo”。唯一有效的解决方案是我发布的带有星号的解决方案——当有尾随空格时,它将删除“foo”,但当没有尾随空格时,它不会删除。“最好的做法是使用参数展开(使用扩展全局变量),如下所示:

# Make sure extglob is enabled
shopt -s extglob

str=" foo bar baz"
echo "${str##*([[:space:]])}"
这使用了扩展的glob
*([[:space:]])
,以及
参数扩展(贪婪匹配)

编辑。由于您的模式具有后缀
foo
,因此不需要使用贪婪匹配:

echo "${str#*([[:space:]])foo}"
够了

注意。您也可以将
foo
放入变量中,但要小心,您必须引用它:

pattern=foo
echo "${str#*([[:space:]])"$pattern"}"
会有用的。如果
模式
的扩展包含glob字符,则必须引用它。例如,当
pattern=“foo[1]”

我的理解是,在[[:space:]之后使用*应该匹配[[:space:]的零个或多个实例,而不是一个或多个

那是错误的

我错过了什么

事实并非如此。在正则表达式中,
*
匹配零个或多个前面的字符或组。在glob
*
中,匹配任何内容。这与文件名扩展相同,请考虑
ls[[:space:]*foo

您可以使用扩展bash glob并执行以下操作:

shopt -s extglob
str=' foo bar baz'
echo "${str#*([[:space:]])foo}"
要做任何更复杂的事情,实际上使用正则表达式

str=' foo bar baz';
[[ $str =~ ^[[:space:]]*foo(.*) ]];
echo "${BASH_REMATCH[1]}"

如果您想要的是真正的正则表达式匹配,则应使用真正的正则表达式匹配:

$: [[ "$str" =~ [[:space:]]*(.*) ]]
$: echo "[${BASH_REMATCH[1]}]"
[foo  bar       baz]
一个更普通的方法是跳过引用

$: echo "[$str]"
[ foo bar baz]
$: new=$( echo $str )
$: echo "[$new]"
[foo bar baz]
请注意,在任何更复杂的情况下,这会让你面临各种各样的麻烦。 如果您想在值之间保留多个连续的空格,或者只保留一个选项卡而不只是一个引号等,则会中断

$: str=' foo  bar'$'\t''baz';
$: echo "[$str]"
[ foo  bar      baz]
$: new=$( echo $str )
$: echo "[$new]"
[foo bar baz]

它也可能造成其他类型的破坏,但在适当的时候知道这种情况的诀窍是很好的。

@RamanSailopal说
匹配零次或一次,而
*
匹配零次或多次。无论如何我都试过了,但没用-会更新问题。
而且也没用:
启用extglob…只是
${str#foo}
有什么问题?@oguzismail在
str=oguzfoo
的情况下,我想op不需要匹配。@Lou:只是为了确保这不是XY问题:你想在空格处拆分字符串吗?如果是这样的话,您应该改为使用:
read-ra-ary-d'#
也有效。啊,我想你也想删除
foo
后面的空格。谢谢,这很有效!出于兴趣,为什么要用最长的匹配而不是最短的匹配?@Lou:否则只会删除第一个空格。但是现在我意识到你的模式中也有
foo
,所以
echo“${str#*([[:space:]])foo}”
就足够了。我已经编辑了答案(还添加了关于将模式放入变量的备注)。@Lou:1。您不需要在
re
中引用标记
foo
:这就足够了:
re=“*([[:space:])foo*([[:space:])”
。2.不得引用该模式!实际上,这里的引号是为了防止将模式解释为模式!因此:
echo“${str#$re}”
(不带引号的
$re
)是正确的。@Lou:不完全:)只有当您不想将模式变量解释为模式时,才应该在参数展开中引用它。这里有一个非常简单的例子,你可以试试:
str=“foo-bar”;pattern=“*”
。(事实上,我们在这里使用引号是不相关的)。没有引号:
echo“${str#$pattern}”
您将获得
oo条
,因为
str
与模式
f*
匹配。但是有了引号:
echo“${str#“$pattern”}”
,你会得到
foobar
,因为
str
pattern
的逐字内容不匹配,这是
f*
。啊,这很有道理!谢谢你解释得这么清楚:)这不是我想做的-我只想从字符串前面加上一个给定的单词来修剪前导空格,而不是从字符串中的所有单词来修剪前导空格。这就是为什么我要警告它。第一个匹配解决方案是更好的方法