[freebsd] test и пустые строки

Valentin Nechayev netch at netch.kiev.ua
Tue Nov 21 07:56:44 EET 2017


hi,

 Tue, Nov 21, 2017 at 01:34:30, spell wrote about "Re: [freebsd] test и пустые строки": 

> > Я бы перефразировал: всплыло некорректное использование команды test.
> 
> О, это всё объясняет :) Спасибо.
> Только ни разу не очевидно, что null string в случае "[ $str ]" допустим,
> а в случае "[ -n $str ]" - нет. И тем более что оно молча fallback-ает в
> treating an option as a regular string. 

А какой собственно у неё выход?
Вот запускается test. Неважно, в каком виде - как test или как [ (во
втором случае оно просто финальный ] не считает параметром). У неё на
входе argc с количеством параметров и argv с их строками. И теперь
надо неким святым духом опознать, что и как было задано на входе, что
автор скрипта подразумевал как параметр, а что как операция?
Когда видим какую-нибудь -n, это опция самой test, опция вызванного
скрипта, которую этот test проверяет, прочтённая из файла строка, имя
файла или что-то иное? Отличить их так нельзя, в отличие от
"полноценного" языка программирования, где чётко видно, где одно, а
где другое. И при этом неважно, у нас какого стиля язык. Например, для
C++

if (a != b && s1 != "!=" && s2 == "&&") { puts("ой всё"); }

или для LISP (неважно, какой диалект)

(if (and (= a b ) (not (streq (s1 "!=")) (streq s2 "&&"))) (puts "ой всё"))

синтаксис даёт их различать так, что спутать невозможно. А для test
такого нет. Уж такой интерфейс достался, извините.

Поэтому, для test есть формы гарантированно надёжного использования
проверок, которые не страдают от того, что где-то какое-нибудь "="
оказалось операцией самого test, а где-то - аргументом для этой
операции. Если у него два аргумента и первый равен "-n", это проверка
на непустоту. Если три аргумента и средний равен "=", это проверка на
текстовое равенство. Если три аргумента и средний равен "lt", это
проверка на числовое "меньше". И ещё несколько аналогичных операций.
Собственно, это всё, что гарантируется стандартом без побочных
эффектов (см. ниже - можете полный набор сами прочитать).

А теперь о случаях типа один аргумент, который проверяет на непустоту.
Тут одновременно и наследие (в смысле legacy), и простой сокращённый
случай - но это уже фактически та простота, которая не то хуже
воровства, не то "просто" требует очень внимательного отношения к
себе. Как и вообще многое в шелле и вокруг. Test тут далеко не
единственный пример. Возьмём grep. Мы можем десятки раз писать что-то
типа "grep vasya /var/log/auth.log", но если шаблон приходит снаружи,
мы обязаны написать:

grep -e "$pattern" "$file"

причём важны и -e - указать, что тут шаблон, и кавычки - чтобы шелл
сохранил это одним аргументом, независимо от количества параметров в
нём.

В случае test имеем несколько наслоений интерфейса - да, таки это
legacy. Первый, самый ранний, это тот самый одноаргументный вариант и
прочее связанное с ним. Когда осознали возможность конфликта с
опциями, добавили гарантированный вариант. Но из-за принципиальной
проблемы на уровне передачи аргументов оказалось, что есть
относительно немного надёжных форм. В частности, если не применяется
XSI расширение со скобками, то гарантия даётся на корректный парсинг
только одной проверки; если нужно две и больше - это делается на
уровне вызывающего шелла, а не самой test. Например, вы не можете
написать

if [ "$a" = 1 -a "$b" = 2 ]; then

надо писать вместо этого

if [ "$a" = 1 ] && [ "$b" = 2 ]; then

Зато при этом не надо ставить префиксы аргументам.

А вот если допустить проверку с объединителями стиля XSI (-a, -o,
скобки), можно писать несколько условий. Но тут начинается новая
проблема: а что, если одним из параметров придёт -a, -o, числовая
операция типа -eq, или скобка?
Вот тут уже для строковых операций можно исключить это проверками
вида:

if [ "x$a" = x1 -a "x$b" = x2 ]; then

а для числовых аналогично подставить ноль (по крайней мере для целых
неотрицательных... иначе, боюсь, тут вообще остаётся только делать
одну проверку за раз.

Стандарт можете почитать сами тут:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html

а дальше посмотреть на то, как более дружественные шеллы (bash,
zsh...) борются с последствиями этого. Как они расширяют сам test,
как эмпирически пытаются разбирать сложные случаи, как вместо этого
можно пользоваться конструкциями типа $((...)), и так далее.

(Если бы мне дали это всё исправить, я бы сделал вариант с только
префиксной записью всех вариантов (например, `= arg1 arg2' вместо `arg1
= arg2'), начальную опцию для него, и загнал бы это кувалдой в стандарт.
Примеры такого подхода есть - printf в стандарте вместо проблемного
echo. Проблемы неоднозначного парсинга были бы решены с гарантией и с
возможностью расширения на любые операции. Но сейчас имеем то, что
имеем.)


-netch-


More information about the freebsd mailing list