Что такое разделение слов? Почему это важно в программировании оболочки?

16

Я запутался из-за роли, которую играет разделение слов zsh. Я не сталкивался с этой концепцией при программировании на C, Python или MATLAB, и это вызвало у меня интерес к тому, почему расщепление слов кажется чем-то специфичным для программирования оболочки.

Я читал о разделении слов на этом и других сайтах ранее, но не нашел четкого объяснения концепции. В Википедии есть определение разделения слов, но, похоже, нет ссылок на то, как оно применяется к оболочкам Unix.

Вот пример моей путаницы в zsh:

В FAQ по Z Shell я прочитал следующее:

3.1: Почему , $varкогда var="foo bar"не делать то , что я могу рассчитывать?

В большинстве производных от Bourne-shell переменные, состоящие из нескольких слов, например var="foo bar" , разделяются на слова, когда передаются команде или используются в for foo in $varцикле. По умолчанию у zsh такого поведения нет: переменная остается нетронутой. (Это не ошибка! См. Ниже.) Опция SH_WORD_SPLITсуществует для обеспечения совместимости.

Однако в Руководстве по Z Shell я прочитал следующее:

SH_WORD_SPLIT (-y) <K> <S>

Вызывает разделение полей при раскрытии параметров без кавычек. Обратите внимание, что эта опция не имеет ничего общего с разделением слов. (См. Расширение параметров.)

Почему говорится, что SH_WORD_SPLITэто не имеет ничего общего с разделением слов? Разве слово не разделяет именно то, что это все?

Амелио Васкес-Рейна
источник

Ответы:

22

Ранние оболочки имели только один тип данных: строки. Но это обычно манипулирует списками строк, как правило, при передаче нескольких имен файлов в качестве аргументов в программу. Другим распространенным вариантом использования для разделения является случай, когда команда выводит список результатов: выходные данные команды представляют собой строку, а требуемые данные - это список строк. Чтобы сохранить список имен файлов в переменной, вы должны поставить пробелы между ними. Тогда сценарий оболочки, как это

files="foo bar qux"
myprogram $files

вызывается myprogramс тремя аргументами, так как оболочка разбивает строку $filesна слова. В то время пробелы в именах файлов были либо запрещены, либо широко считались не выполненными.

В оболочке Korn появились массивы: вы можете хранить список строк в переменной. Оболочка Korn оставалась совместимой с установленной в то время оболочкой Bourne, поэтому при расширении открытых переменных происходило разделение слов, а использование массивов требовало некоторых синтаксических издержек. Вы написали бы фрагмент выше

files=(foo bar qux)
myprogram "${files[@]}"

У Zsh были массивы с самого начала, и его автор выбрал более разумный дизайн языка за счет обратной совместимости. В zsh (по правилам расширения по умолчанию) $varне выполняется разделение слов; если вы хотите сохранить список слов в переменной, вы должны использовать массив; и если вы действительно хотите разделить слова, вы можете написать $=var.

files=(foo bar qux)
myprogram $files

В наши дни пробелы в именах файлов - это то, с чем вам нужно справиться, поскольку многие пользователи ожидают, что они будут работать, а также потому, что многие сценарии выполняются в контекстах, чувствительных к безопасности, когда злоумышленник может контролировать имена файлов. Так что автоматическое разбиение слов часто создает неудобства; поэтому мой общий совет всегда использовать двойные кавычки, то есть писать "$foo", если вы не понимаете, почему вам нужно разделить слова в конкретном случае использования. (Обратите внимание на то, что расширения с открытыми переменными также подвергаются сглаживанию.)

Жиль "ТАК - перестань быть злым"
источник
Спасибо, Жиль, это действительно полезно! Правильно ли говорить, что грубо говоря, разделение слов преобразует строки формы "word1 word2 word3"в списки / массивы формы "word1" "word2" "word3"? Я также обновил ОП с конкретным источником путаницы в Zsh.
Амелио Васкес-Рейна
1
@intrpc "Разделение слов" - это не разделение на слова на естественном языке, а на $IFSсимволы. Следовательно, «разделение полей» является лучшим названием. Но «расщепление слов» часто используется для этой концепции в литературе по оболочкам. Документация по Zsh не соответствует словам.
Жиль "ТАК - перестань быть злым"
1
См. Также rc(оболочка plan9, также перенесенная в Unix), чтобы получить еще лучший дизайн, чем zsh, когда речь идет о переменных и массивах.
Стефан Шазелас
3

Разделение слов не является специфическим для оболочки.

Большинство программ, которые должны анализировать ввод текста, используют некоторую форму разделения слов в качестве первого шага. Это делается перед тем, как идентифицировать по этим «словам», числам, операторам, строкам, токенам и любым другим подобным объектам, которые им необходимо обработать.

Что характерно для оболочек, так это то, что они должны правильно составлять список аргументов команд, называемых (C argc / argv, python sys.argv), включая передачу аргументов со встроенными пробелами, пустыми аргументами, пользовательскими разделителями и так далее. Многие оболочки используют переменную IFS, чтобы обеспечить некоторую гибкость.

jlliagre
источник
3

В этом конкретном случае Zsh разделение слов определяется несколько иначе, чем разделение полей.

Учтите prog a b c, что он будет передаваться в трех аргументах независимо от того, как вы установите IFS. Это разделение слов .

Если вы это сделаете A="a b c"; prog $A, он передаст три аргумента, если IFSвключает пробел или один аргумент в противном случае. Это разделение полей .

Определения здесь неуловимы. Документ Zsh пытается сказать, что, даже если вы отключите эту опцию, prog a b cон все равно получит отдельные аргументы (это то, что люди всегда ожидают).

Hot.PxL
источник
1
Барт Шефер, давний разработчик Zsh, подтверждает, что это действительно подразумеваемый смысл этого текста .
Стефан Шазелас