仅使用bash子字符串移除来剥离前导零

仅使用bash子字符串移除来剥离前导零,bash,Bash,我知道还有其他方法可以解决这个问题(我目前使用的是“expr”方法),但我不知道如何只使用bash内置函数 当我试图使用${variable###remove}构造从变量中去掉前导零时,我只能让它删除一个零或所有数字 string="00123456" echo "${string##0}" // Only the first 0 is removed echo "${string##0*0}" // The whole string is removed echo "${string#0*0}

我知道还有其他方法可以解决这个问题(我目前使用的是“expr”方法),但我不知道如何只使用bash内置函数

当我试图使用${variable###remove}构造从变量中去掉前导零时,我只能让它删除一个零或所有数字

string="00123456"
echo "${string##0}" // Only the first 0 is removed
echo "${string##0*0}" // The whole string is removed
echo "${string#0*0}" // Works

string="01230"
echo "${string##0}" // Works
echo "${string##0*0}" // The whole string is removed
echo "${string#0*0}" // Again, the whole string is removed
我读了两遍bash手册,看看我是否做得正确,但官方文档充其量也很稀少。我知道启用extglob也可以解决这个问题,但对于这样一个简单的问题来说,这似乎有些过头了


我是否遗漏了一些明显的东西,或者仅仅使用bash函数就很难从字符串中删除一个或多个前导零?

这也是一种在一行程序中剥离前导零的纯bash方法:

string="00123456"
[[ "$string" =~ ^0*(.+)$ ]] && echo "${BASH_REMATCH[1]}"
123456

注意:regexp包含将“00000”输入转换为“0”的边缘大小写。以下内容将从字符串中删除所有前导的
0
s:

$ string="000123456000"
$ echo "${string#"${string%%[!0]*}"}"
123456000
“${string%%[!0]*}”
将在删除字符串中最长的尾随部分后返回匹配,该部分满足
[!0]*
——基本上返回开头的零

“${string#”${string%%[!0]*}”}”
将从字符串开头删除上面返回的部分


或者,您可以使用shell算法:

$ string="0000123456000"
$ echo $((10#$string))
123456000

还有另一种方式,尽管这需要启用extglob功能:

echo ${string/#+(0)/}

在cron作业生成器脚本中处理分钟和小时时,我发现08&09前面的前导零会导致bash认为该值是无效的八进制值。由于单个零仍然有效,脚本无法仅剥离前导零。解决办法是通过管道输送到不列颠哥伦比亚省

HOUR=09
echo $HOUR | bc
9

HOUR=0
echo $HOUR | bc
0

HOUR=14
echo $HOUR | bc
14

删除与扩展全局模式匹配的字符串开头最长的子字符串
+(0)
,即一个或多个零

shopt -s extglob
string="0000123456000"
echo ${string##+(0)}
印刷品:

注意:这将把只包含零的字符串减少为空字符串

shopt -s extglob
string="0000000000000"
echo ${string##+(0)}
打印一个空字符串

shopt -s extglob
string="0000000000000"
echo ${string##+(0)}
如果要将一串零减少为单个零,可以使用算术展开和[base#]n形式强制算术基数为10,以防止八进制解释:

string="0000000000000"
echo $((10#$string))
印刷品:


+你能详细说明一下它是如何工作的吗?“这似乎是一个非常棒的解决方案,但我不能完全相信它。”helpermethod补充了一个解释。似乎就是这个。尽管如此,我的头脑还是不断地对我喊叫,说应该有一种没有“双重否定”的方法。我也很想知道为什么所有的单一语句都失败了,为什么会这样。第一个解决方案很吸引人,但很难记住。这是一个函数包装器(用于多个输入):
stripLeadingZeros(){local s;对于“$@”中的s;do printf'%s\n'${s}“${s%[!0]*}”done;}
。从11123717开始:$(())设置一个算术上下文,10将数字从基数10转换为基数10,从而导致任何前导零被删除。这是用于
+()
语法。。。至少在
bash
3.2.x中。我想,新版本中的默认设置可能已经改变了。但是,正如其他答案所指出的,还有其他方法可以不依赖extglob来实现这一点……bash4.1的发行说明中说“有一个新的confgure选项,它强制默认启用extglob选项。”——。不确定“configure option”指的是什么,但在两个bash 4.2系统上的快速测试告诉我,
extglob
在默认情况下处于启用状态。正如您所说,它在3.2.x上默认是关闭的,这适用于今天的OSX(OSX10.9)。底线是:为了在多个bash版本之间安全,您需要确保该选项已打开,这会使本方便的解决方案不那么理想。@mklement0。。。除非您习惯于确保它始终处于打开状态(.profile/.bashrc)…如果您知道脚本只在您控制的环境中运行,那就太好了;如果需要可移植性,则必须采取额外的步骤确保
extglob
处于打开状态(如果必须打开它,最好以后再关闭)。这些全局选项使得编写可移植shell脚本变得棘手。为了完整性,答案应该说明如何在shell中启用此功能。我喜欢它:+1。。。但是我不认为有必要使用
&&
--
[[…]]
表达式是防弹的;它将始终导致
${BASH_REMATCH[1]}
保持正确的值。不是一个
够了,或者我遗漏了什么。谢谢,我想是性能方面的
的行为与
&
ps相同。此外,此方法可以轻松地允许一个剩余的
0
——这就是我要寻找的-
echo${BASH_REMATCH[1]:-0}
(当然,这可以通过其他答案完成,但需要另一个var分配步骤)。是的,当然。您的回答解决了这个问题,但出于我的特殊要求,如果字符串是
000
,我只想留下
0
。(顺便说一句,它的速度比其他的慢,但它很有效,并且显示了BASH_REMATCH的一个有趣用途——他们当然不是用这个名字打高尔夫的:)
0