如何测试变量是否为空或仅包含空格?


240

以下bash语法验证是否param不为空:

 [[ !  -z  $param  ]]

例如:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

没有输出,它的罚款。

但是,当param一个(或多个)空格字符为空时,情况就不同了:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

输出“我不为零”。

如何更改测试以将仅包含空格字符的变量视为空?


4
来自man test-z STRING - the length of STRING is zero。如果要删除中的所有空格$param,请使用${param// /}
dchirikov

6
WOW根本没有trim()任何* nix内置的简单函数吗?如此众多的hack来实现如此简单的功能……
ADTC

6
@ADTC $(sed's / ^ \ s + | \ s + $ // g'<<< $ string)对我来说似乎很简单。为什么要重新发明轮子?
jbowman '16

3
因为它可能更
明亮

1
请注意,这[[ ! -z $param ]]等同于test ! -z $param
诺亚·萨斯曼

Answers:


292

首先,请注意该-z测试明确用于:

字符串的长度为零

也就是说,只包含空格的字符串应该不会是下真-z,因为它有一个非零长度。

您想要使用模式替换参数扩展从变量中删除空格:

[[ -z "${param// }" ]]

这将扩展param变量,并将模式的所有匹配项(单个空格)都替换为空,因此仅包含空格的字符串将被扩展为空字符串。


中如何工作的基本事实是,${var/pattern/string}替换的第一最长比赛patternstring。当pattern/(如上所述)开头时,它将替换所有匹配项。因为替换为空,所以我们可以省略final /string值:

$ {参数/模式/字符串}

图案被膨胀,以产生一个模式,就像在文件名扩展。扩展参数,并将模式与其值的最长匹配项替换为string。如果模式以“/”开始,所有的比赛模式被替换为的字符串。通常只替换第一个比赛。...如果string为null,则删除模式匹配项,并且/后面的模式可以省略。

毕竟,我们最终${param// }要删除所有空格。

请注意,尽管语法存在于ksh(起源于)zsh和中bash,但该语法不是POSIX,因此不应在sh脚本中使用。


使用sed他们说的...只是他们说echo的变量...
mikezter

1
嗯 如果是这样的${var/pattern/string}话,不是应该[[ -z ${param/ /} ]]在斜线之间将空格作为模式,而将其作为字符串之后再没有?
Jesse Chisholm

@JesseChisholm“因为替换为空,所以我们可以省略最后的/和字符串值”;“如果字符串为null,则将删除模式匹配项,并且可以省略/以下模式。”
迈克尔·荷马,

6
@MichaelHomer答案[[ -z "${param// }" ]]肯定看起来像是pattern空的,replacement string是单个空格。啊!我现在看到了,if pattern begins with a slash我错过了那部分。所以模式是/ ,字符串被保留,因此为空。谢谢。
Jesse Chisholm

bash注意:如果在set -o名词集合(set -u)中运行,并且未设置param(而不是null或空格),则将生成未绑定的变量错误。(set + u; .....)将免除此测试。
布赖恩·克里斯曼

31

检查字符串仅包含授权集中的字符的简单方法是测试是否存在未授权字符。因此,不是测试字符串是否仅包含空格,而是测试字符串是否包含除空格之外的某些字符。在bash,ksh或zsh中:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

“仅由空格组成”包括变量为空(或未设置)的情况。

您可能需要测试任何空白字符。使用[[ $param = *[^[:space:]]* ]]使用区域设置,或任何明确的,你想测试,如空白字符表[[ $param = *[$' \t\n']* ]]来测试空间,制表符或换行符。

将字符串与=内部模式匹配[[ … ]]是ksh扩展名(也存在于bash和zsh中)。在任何Bourne / POSIX样式中,您都可以使用该case构造将字符串与模式进行匹配。请注意,标准外壳模式用于!否定字符集,而不是^像大多数正则表达式语法一样。

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

为了测试空格字符,$'…'语法特定于ksh / bash / zsh。您可以按原样在脚本中插入这些字符(注意,换行符必须在引号内,因为反斜杠+换行符扩展为空),或生成它们,例如

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac

几乎非常好的-但是,到目前为止,它还没有回答所问的问题。它当前可以告诉您未设置空的shell变量与仅包含空格的变量之间的区别。如果您接受我的建议,则将添加尚未通过其他任何明确测试外壳变量是否已设置的答案的参数扩展形式-我的意思是${var?not set}-通过该测试并幸存下来将确保与您匹配的变量*) case将产生确定的结果例如,答案。
mikeserv

更正-实际上,它可以回答最初提出的问题,但此后对其进行了编辑,从根本上改变了问题的含义,因此使您的回答无效。
mikeserv

你需要[[ $param = *[!\ ]* ]]mksh
斯特凡Chazelas

20

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

区分空白非空白,未设置

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]用于水平间距字符(ASCII中的空格和制表符,但您的语言环境中可能还有更多;某些系统将包含不间断空格(如果可用),有些则不会)。如果还需要垂直间距字符(例如换行符或换页符),请替换[:blank:][:space:]


POSIXly是理想的,因为它可以与(可能更好)的破折号一起使用。
肯·夏普

zsh似乎有问题[![:blank:]],它引发了:b无效的修饰符。在shksh模拟,它引发了未找到的事件[
cuonglm '16

@cuonglm,您尝试了什么?var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'对我来说还可以。未找到的事件仅适用于交互式外壳。
斯特凡Chazelas

@StéphaneChazelas:嗯,对,我尝试使用交互式shell。该:b无效的修饰符是有点怪。
cuonglm '16

1
@FranklinYu,在OS / X shbash建有--enable-xpg-echo-default--enable-strict-posix-default以能够兼容的Unix(其涉及echo '\t'输出标签)。
斯特凡Chazelas

4

编写shell脚本而不是使用良好的脚本语言编写脚本的唯一剩余原因是,极端的可移植性是否是首要考虑的问题。 遗留物/bin/sh是您唯一可以确定的东西,但是例如Perl 比Bash 可能跨平台使用。因此,切勿编写使用并非真正通用的功能的shell脚本-切记,一些专有的Unix供应商在POSIX.1-2001之前冻结了其shell环境。

有一种可移植的方法可以进行此测试,但是您必须使用tr

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(的默认值$IFS很方便,默认值为空格,制表符和换行符。)

printf内建函数本身是可移植的,但依靠它比找出echo您的哪个变体要麻烦得多。)


1
这有点令人费解。测试字符串是否包含某些字符的常用便携式方法是使用case
吉尔斯2014年

@Gilles我认为不可能编写一个caseglob来检测一个为空或包含空格字符的字符串。(使用正则表达式很容易,但是case不使用正则表达式。如果您关心换行符,则答案中的构造将无法正常工作。)
zwol

1
我以空格作为示例,因为这正是问题的所在。这同样容易测试空白字符:case $param in *[![:space:]]*) …。如果要测试IFS字符,可以使用模式*[$IFS]*
吉尔斯2014年

1
@mikeserv在一个非常认真的防御脚本中,您<space><tab><newline>在开始时将IFS显式设置为,然后再也不会碰它。我以为那会分散注意力。
zwol 2014年

1
关于模式中的变量,我认为这适用于所有Bourne版本和所有POSIX shell;这将需要额外的代码来检测案例模式并关闭变量扩展,我想不出这样做的理由。我有一些实例,它.profile已经被许多Bourne shell执行。[$IFS]如果IFS具有某些非默认值(空(或未设置),包含],以!或开头),则当然不会执行预期的操作^
吉尔斯2014年

4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi

这摆脱了任何空格字符,而不仅仅是一个空格,对吗?谢谢
Alexander Mills

0

为了测试变量是否为空或包含空格,您还可以使用以下代码:

${name:?variable is empty}

2
我认为您的答案被否决了,因为“或包含空格”是不正确的。
2014年

小心-这会杀死当前的shell。最好放在(:$ {subshel​​l?})中。另外-这将写入stderr-您可能需要重定向。而最重要的计算结果为变量的实际值如果不设置或者为空。如果其中有命令,则只需运行它即可。最好对进行测试:
mikeserv

它将如何杀死外壳。您也可以测试它,首先定义,name=aaaa然后运行此命令echo ${name:?variable is empty},它将打印变量名称的值,然后运行此命令echo ${name1:?variable is empty},它将打印-sh:name1:变量为空
pratik

是的- echo ${name:?variable is empty}会求值为$name的非null值,或者将杀死shell。因此,出于同样的原因,您prompt > $randomvar也不应该不执行任何操作${var?}-如果其中有一个命令,它可能会运行-而且您也不知道它是什么。交互式外壳不必退出-这就是为什么您不需要退出的原因。不过,如果您想查看一些测试,这里有很多测试这个时间不长...
mikeserv

0

要测试变量是否未设置,为空(具有空值)仅包含空格,请执行以下操作:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

说明:

  • ${param%%[! ]*}从第一个非空间到最后一个的扩展。
  • 那只剩下前导空间。
  • 下一个扩展从变量中删除前导空格。
  • 如果结果为空,则变量以空开头。
  • 或者,如果变量不为空,则删除前导空格使其为空。
  • 如果结果为空-z,则回显empty

以上是POSIX兼容的,可以在任何Bourne后代中运行。

如果要将其扩展到其他选项卡,请包括一个显式选项卡:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

或使用带有需要删除的字符的变量:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

或者您可以使用一些POSIX“字符类表达式”(空白表示空格和制表符):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

但这将在mksh,lksh和posh中失败。


-1

尝试这个:

$ [[ -z \`echo $n\` ]] && echo zero

zero
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.