测试变量是否包含换行符 (POSIX)

测试变量是否包含换行符 (POSIX)

我知道有些 shell 接受这种测试:

t() { [[ $var == *$'\n'* ]] && res=yes || res=no
      printf '%s ' "$res";
    }

var='ab
cd'
t
var='abcd'
t
echo

执行时:

$ bash ./script
yes no
  1. POSIX(破折号)的等效工作是什么

  2. 以下测试方法可靠吗?

    nl='
    '
    
    t() {  case "$var" in
               *$nl* ) res=yes ;;
               * ) res=no ;;
           esac
           printf '%s ' "$res"
         }
    
    var='ab
    cd'
    t
    var='abcd'
    t
    echo
    

答案1

您可以在变量中放置硬换行符,并与 进行模式匹配case

$ cat nl.sh
#!/bin/sh
nl='
'
case "$1" in
    *$nl*)  echo has newline ;;
    *)      echo no newline  ;;
esac

$ dash nl.sh $'foo\nbar'
has newline
$ dash nl.sh $'foobar'
no newline

制作换行符的另一种方法是这样的:

nl=$(printf "\nx"); nl=${nl%x}

明显的命令替换不起作用,因为替换会删除尾随换行符。

答案2

是的,

nl='
'
case $var in
  (*"$nl"*) echo yes;;
  (*)       echo no;;
esac

(原则上,我喜欢引用case模式内的所有变量扩展,除非我希望将它们视为模式,尽管在这里它没有什么区别,因为$nl不包含通配符)。

或者

case $var in
  (*'
'*) echo yes;;
  (*) echo no;;
esac

应该一切正常并且符合 POSIX 标准,以及我将使用什么。如果删除(s,它甚至可以在旧的 Bourne shell 中工作。

设置$nl变量的另一种方法:

eval "$(printf 'nl="\n"')"

注意$'\n'计划包含在下一版本的 POSIX 标准中。至少已经被ksh93zshbashmkshbusybox 和 FreeBSD支持(截至 2018 年 2 月)。sh

至于您拥有的测试是否足够,您已经对两种情况进行了测试,因此将测试所有代码路径。

目前 POSIX 规范中还没有明确指定一些内容:是否*匹配包含不形成有效字符的字节序列的字符串,或者 shell 变量是否可以包含这些字符串。

实际上,除了yash变量只能包含字符之外,除了 NUL 字节(没有 shell 但zsh可以存储在变量中)之外,*$nl*应该匹配任何包含的字符串,$nl即使它们包含不形成有效的字节序列字符(如$'\x80'UTF-8)。

例如,某些find实现将无法与-name "*$nl*"它们匹配,因此如果测试新的 shell,并且您打算处理不能保证是文本的内容(例如文件名),您可能需要为其添加一个测试用例。喜欢与:

test=$(printf '\200\n\200')

在 UTF-8 语言环境中。

相关内容