POSIX

POSIX

我可以通过调用外部实用程序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 没有-dor-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"

相关内容