我可以通过调用外部实用程序sed
(对于已知的非空$myvar
)来做到这一点,如下所示:
if [ "$(printf %s "$myvar" | sed -n '$=')" -eq 1 ]; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have only one line"
fi
有没有办法只使用bash
内置函数来做到这一点?
更好的是,有没有一种方法可以以 POSIX 指定的方式执行此操作,而无需调用外部实用程序?
(这个问题是好奇心和寻找更好的做事方法之一;上面的代码做功能。我想知道是否有更干净/更快的方法。)
答案1
POSIX方式:
NL='
'
case $myvar in
*"$NL"*) echo more than one line ;;
*) echo one line ;;
esac
这也适用于 POSIX 之前的类似 Bourne 的 shell。
答案2
有几个选项(首先是 bash,下面是 POSIX)。
每个函数内部的代码可以很容易地在外部使用。
#!/bin/bash
nl=$'\n'
aregex (){ [[ $a =~ $nl ]]; }
apattern (){ [[ $a == *$nl* ]]; }
acut (){ [[ $a != "${a%%"$nl"*}" ]]; }
areplace (){ [[ $a != "${a//"$nl"/}" ]]; }
acase (){ case $a in (*$nl*) true;; (*) false;; esac; }
aifs ()( IFS="$nl"
set -f; set -- x${a}x;
(( $# > 1 ));
)
aread (){ IFS="$nl" read -rd '' -a b <<<"x${a}x";
(( "${#b[@]}" > 1 )); }
每个功能都是一个选项。如果只有一行,则每个函数将退出并返回值 0;如果变量$a
有多个换行符(多个$'\n'
),则返回值 1。
执行函数后,将打印所需的行:
out=''; "$function" && out="more than "
printf "%9s = %sone line\n" "$1" "$out"
执行所有选项:
a='ab'"$nl"'cd' ; alltests
a='ab cd' ; alltests
给出这个输出:
aregex = more than one line
apattern = more than one line
acut = more than one line
areplace = more than one line
acase = more than one line
aifs = more than one line
aread = more than one line
aregex = one line
apattern = one line
acut = one line
areplace = one line
acase = one line
aifs = one line
aread = one line
POSIX
由于多种原因,以下选项在 POSIX 中失败:
=~
areregex : POSIX 中没有正则表达式运算符。==
apattern : POSIX 中没有运算符。- areplace :参数扩展没有
${ / / }
选项。
其中四种可以安全地转换为 POSIX:
- posixcut:可能是最好的解决方案。
- posixcase :POSIX shell 的常见解决方案。
- posixifs:在子 shell 内执行以便能够设置
set -f
。 - posixread : read 没有
-d
or-a
但可以修改。
#!/bin/dash
nl='
'
posixcut (){ [ "${a}" != "${a%%"$nl"*}" ] ; }
posixcase(){ case $a in (*$nl*) true;; (*) false;; esac; }
posixifs ()( IFS="$nl";
set -f; set -- x${a}x;
[ "$#" -gt 1 ];
)
posixread(){ local b=0;
while IFS=$nl read -r _; do
b=$((b+1));
[ $b -gt 1 ] && break;
done <<-_EOT_
x${a}x
_EOT_
[ $b -gt 1 ];
}
笔记:是的,local
严格来说不是 POSIX,但是得到很好的支持。
本地的b
, 可以安全地删除(检查全局$b
使用情况)。
谍影重重(1977 年外壳)。
如果您需要 1977 年原始 Bourne shell 的代码,请使用以下代码:
posixcase() { case $a in *$nl*) true;; *) false;; esac; }
posixifs () ( IFS="$nl"
set -f; set -- x${a}x;
[ "$#" -gt 1 ];
)
bourneread()( b=0 ### A helper function.
while :; do
if IFS=$nl read c && [ $b -le 1 ]; then
b=`expr $b + 1`
else
echo "$b"
break
fi
done <<-_EOT_
x${a}x
_EOT_
)
posixread (){ [ `bourneread` -gt 1 ]; }
posixcut 中使用的扩展${a%% }
在 Bourne 中不起作用。 posixif 需要分为两部分才能在 Bourne 中工作。
读取选项需要进行重大更改,但可以正常工作。
它的函数是posixread,bourneread是必需的辅助函数。
Bourne 的所有代码都经过测试可以正常工作传家宝贝壳。
答案3
以下代码片段适用于bash
(有或没有-posix
选项):
#!/bin/bash
#!/bin/bash -posix
version_1 () { [[ "$myvar" = *$'\n'* ]]; }
version_2 () {
local newlines="${myvar//[^$'\n']/}"
[[ "${#newlines}" -eq 1 ]]
}
for test in version_1 version_2; do
if $test; then echo many lines; else echo one line; fi
done
答案4
我建议使用 Bash 参数扩展,如下所示:
n="${myvar//[^\n]}"; if [ ${#n} -eq 1 ]; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have only one line"
fi
测试样品:
myvar="xxx"
myvar="xx\nxx"
myvar="xx\nx\nx"