Проверить, содержит ли переменная символ новой строки (POSIX)

8

Я знаю, что некоторые оболочки выдерживают такой тест:

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
    
Исаак
источник

Ответы:

12

Вы можете поместить жесткую новую строку в переменную и сопоставить шаблон с 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}

Очевидная подстановка команд не работает, потому что завершающие символы новой строки удаляются подстановкой.

ilkkachu
источник
5

Да,

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

(В принципе, мне нравится caseзаключать в кавычки все расширения переменных внутри шаблона, если я не хочу, чтобы они рассматривались как шаблон, хотя здесь это не имеет значения, поскольку $nlне содержит подстановочных знаков).

или

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

Должно ли все работать и быть POSIX-совместимым, и что я буду использовать для этого. Если вы удалите (s, он будет работать даже в старой оболочке Bourne.

Для другого способа установить $nlпеременную:

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

Обратите внимание, что $'\n'планируется включить в следующую версию стандарта POSIX . Это уже поддерживается ksh93, zsh, bash, mksh, BusyBox и FreeBSD , по shкрайней мере (по состоянию на февраль 2018 года).

Что касается того, достаточно ли у вас теста, у вас есть тест для обоих случаев, поэтому будет тестировать все пути кода.

В настоящее время есть кое-что, что не определено явно в спецификации POSIX: соответствует ли *строка, содержащая последовательности байтов, которые не образуют допустимые символы, или переменные оболочки могут содержать эти строки.

На практике, кроме тех, yashчьи переменные могут содержать только символы, и кроме байтов NUL (которые никакая оболочка не zshможет хранить в своих переменных), они *$nl*должны совпадать с любой строкой, которая содержит, $nlдаже если они содержат последовательности байтов, которые не формируют действительные символы (как $'\x80'в UTF-8).

findНапример, некоторые реализации не будут соответствовать -name "*$nl*"на них, поэтому, если вы тестируете новую оболочку, и если вы намереваетесь иметь дело с вещами, которые не гарантированно являются текстовыми (например, имена файлов), вы можете добавить тестовый пример для него. Как с с:

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

в локали UTF-8.

Стефан Шазелас
источник