LINUX.ORG.RU

Баг или фича: expr $456 + $457 = expr 56 + 57

 , ,


0

1

Случайно обнаружил интересную особенность в Bash с переменными. Выглядит это так:

expr $456 + $457

Ответ: 113.

Причём даже если брать любые цифры от 1 до 9: сумма ${1..9}56 и ${1..9}57 неизменно 113. Это работает с любыми цифрами — сумма, конечно же, другая, но первый разряд числа с долларом будто исчезает при вычислении. А куда исчезает? Кто знает?

Однако, с нулём такой фокус не проходит:

expr $056 + $057

Выводит ошибку: expr: non-integer argument

Почему так происходит?

Попутно выяснил, что умножение при помощи expr делается именно через обратный слэш:

expr 5 * 3 

— выдаст ошибку. А вот так:

expr 5 \* 3

— посчитает нормально.

Кто столкнулся с непонятным поведением expr при вычислениях, вот тут немного прояснили, откуда чего берётся, и что нужно делать, чтобы вычислялось нормально. Вкратце: нужно экранировать арифметические операторы.

★★★★★

Последнее исправление: Desmond_Hume (всего исправлений: 8)
Ответ на: комментарий от Desmond_Hume

Приведи пример явного указания переменных, а не через специальные значения аргументов.

Почему я его должен приводить, если я тебе сразу сказал, что явно им присваивать значения нельзя? «Нельзя говоришь? Приведи пример, как можно» такая логика? Эти переменные (с числами) — особые, в них хранится (грубо говоря) строка запуска. В $0 — сама команда, в $1 и далее — аргументы по порядку, начиная с первого. Что ещё тебе непонятно?

Пример явного указания переменных (естественно, без использования этих специальных), раз он тебе нужен:

bm="Банановый мастер"
echo $bm

Устроит? Или тебе надо именно $1–$9 переназначить? Зачем? Для чего? Объясни непосредственно задачу, или какой момент до сих пор вызывает непонятнки.

Ну или вот самое близкое к тому, что ты просил: (){echo $5} 'апельсиновый подаван' 'абрикосовый адепт' '' 'персиковый слейв' 'банановый мастер'

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 2)
Ответ на: комментарий от Evenik
set --help
set: set [-abefhkmnptuvxBCEHPT] [-o option-name] [--] [-] [arg ...]
    Set or unset values of shell options and positional parameters.

Где тут variables? Ещё раз … нужны переменные, в обычном понимании. Вот как с expr: expr $4 \* $5, 4 присвоено значение 20, а 5 присвоено 7, к примеру. Вот так. Пробуем.

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

Ещё раз … нужны переменные, в обычном понимании.

Ну так используй буквенные. В чём проблема-то? Что ты к $1–$9 привязался? Они не для того, чтобы их присваивать как обычные переменные.

CrX ★★★★★
()
Ответ на: комментарий от Desmond_Hume

Где тут variables?

Вот:

4.3.1 The Set Builtin

This builtin is so complicated that it deserves its own section. set allows you to change the values of shell options and set the positional parameters, or to display the names and values of shell variables.

https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#The-Set-Builtin

нужны переменные, в обычном понимании

Зачем нужны и какое понимание называется «обычным»?

Вот так. Пробуем.

$ set '' '' '' '20' '7'
$ expr $4 \* $5
140
Evenik ★★
()
Последнее исправление: Evenik (всего исправлений: 2)
Ответ на: комментарий от CrX

Ну надо же! Оказывается, шелл раскрывает звёздочку в файлы с любым именем.

Так, а теперь поведай нам, как шелл раскрывает слэш … :))

Не, кормить бананами тебя больше не буду. Сорри, в игнор тебя отправляю. Экранировать надо арифметические операторы, о чём ни слова в man-странице по expr. Всё-таки, ещё раз убеждаюсь в правоте слов Ричарда Столлмана - про плохую документацию, над которой работать и работать ещё. Увы. Мне, кстати, тоже неохота писать документацию к своей проге. Это, наверное, самое утомительное занятие - ведь ты знаешь, как оно работает, тебе лично это не нужно … писать для Пети? Да пускай ищет сам! Всё по банановому сценарию - отправить на пальму. А залезет или не залезет - его проблемы!

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от Desmond_Hume

Так, а теперь поведай нам, как шелл раскрывает слэш … :))

Прямой — никак не раскрывает. Обратный используется для «экранирования» символа, следующего за ним.

Если нужен literally обратный слеш, то его тоже надо экранировать, да. То есть, писать \\.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

Слеш никак экранировать не требуется — шелл его никаким особым способом не «раскрывает», это точно такой же обычный символ для него, как и все остальные.

Не притворяемся банановыми чемпионами

Вообще-то я первым делом ответил на твой вопрос про прямой. Потом добавил про обратный, потому что это тоже надо знать.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)

Те тебя такое не удивляло $'\n'? Наверное, кода мало видел. В zsh оно работать не будет, так как тот возвратить пустую строку, а интерпретатор баша примитивен и только и переменные числовые могт быть только $0-$9, если они не установлены, то вернется все что после них

rtxtxtrx ★★
()
Ответ на: комментарий от Desmond_Hume

Экранировать надо арифметические операторы, о чём ни слова в man-странице по expr.

Потому что это не имеет никакого отношения к expr как таковому. Это то, как работает сам шелл. Прежде, чем выполнять команду, он «раскрывает» вайлдкарды и переменные. Независимо от того, какая это команда. Ты предлагаешь в мануале по каждой команде дублировать мануал собственно шелла? Это маразм.

CrX ★★★★★
()
Ответ на: комментарий от intelfx

Дальше ${9} ${10} ${11} и т.д. Синтаксический сахар «без скобок» — для ленивых, естественно, он несколько ограничен.

Многие писатели на баше вообще по рукам бьют за переменные без скобок. Даже если не внутри строки. Я лично с ними не согласен, но такое явление есть, да.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от Evenik

display the names and values of shell variables.

Display - это не assign. Почувствуй разницу. Да, «систему ты обманул», а теперь попробуй назначить новое значение переменной $4. Только не говори, что для тебя способ назначать переменные через set - обычное дело, а var=val не для тебя, а для таких слабаков, как я …

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от CrX

Да, я отлично понимаю, что с фигурными скобками всё парсится правильно.

Синтаксический сахар «без скобок» — для ленивых, естественно, он несколько ограничен.

Кто сказал и почему «естественно»? Во-первых, это не синтаксический сахар. Во-вторых, форма записи $foo полностью эквивалентна ${foo}, вне зависимости от того, сколько в имени переменной символов. Во всех случаях, кроме этого.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 3)
Ответ на: комментарий от rtxtxtrx

А разве массивов мало? Зачем вообще такое делать, не понимаю, позиционные параметры отнимать под переменные … господи, мне даже в страшном сне такое бы в голову не пришло)).

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от intelfx

Форма записи $foo полностью эквивалента ${foo}, вне зависимости от того, сколько в имени переменной символов. Во всех случаях, кроме этого.

Не полностью. ${foo}_bar и $foo_bar — разные вещи.

Со скобками всегда «безопаснее» и однозначнее, без скобок приходится учитывать контекст, в котором юзается (не везде можно), зато писать меньше и читать чуть поудобнее.

CrX ★★★★★
()
Ответ на: комментарий от CrX

Не вижу трудности, в написании правильного синтаксиса. Там всего лишь один символ написать. Не надо всю документацию приводить. Постоянно в какие-то крайности идёшь. Слезть с пальмы надо, а то свысока не видно, что внизу.

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от CrX

Ты пытаешься подогнать своё объяснение под ответ.

${foo}_bar и $foo_bar — разные вещи.

Это нерелевантно. Естественно, подразумевается, что границы токена не двусмысленны.

intelfx ★★★★★
()
Ответ на: комментарий от intelfx

Естественно, подразумевается, что границы токена не двусмысленны.

Ну собственно, я об этом же. Синтаксис скобками подходит для всех случаев, а синтаксис без скобок — только при определённых обстоятельствах. Ну вот и с циферными примерно то же самое. Они жёстко заданы, как односимвольные (если без скобок), потому что если парсить дальше, то и $9a уже не столь однозначно, вдруг это именно ${9a} (и ошибка). Наверное, можно было сделать шелл несколько «умнее» и разрешить парсить переменные с именем, начинающимся с цифры дальше, но ограничить первым нецифровым символом, но видимо, решили так не делать. Чтоб и парсить проще и уж точно неоднозначностей не возникало. Ну типа есть же синтаксис со скобками, вот и юзайте, а без скобок — это для тех случаев (коих большинство), где хватает и так.

CrX ★★★★★
()
Ответ на: комментарий от Desmond_Hume

теперь попробуй назначить новое значение переменной $4

Это позиционный параметр. Синтаксис 4=foo здесь не работает. Присвоить значение позиционному параметру можно только через set. Пример смотри выше.

Evenik ★★
()
Ответ на: комментарий от CrX

Быть человеком легче и проще, чем обезьяной. Можно, конечно, через set всё время извращаться, назначать переменные, а не через стандартный оператор равенства и проч… но будет ли это нормальным? Нет. Возможным? Да.

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

Это вообще к чему? В сообщении, на которое ты отвечаешь, речь вообще о другом. Тебе пока о таком думать рано.

Можно, конечно, через set всё время извращаться, назначать переменные, а не через стандартные оператор равенства…

Зачем?

Этого вообще не нужно делать. Переменный с «числовыми именами» зарезервированы под позиционные параметры. Они не предназначены для того, чтобы ты их переназначал. Тебе об этом уже и я и ещё несколько людей повторили несколько раз.

Но ты упорно хочешь им присваивать другие значения. При этом ни разу не объяснил, зачем оно тебе надно, и почему не использовать нормальные буквенные переменные. Но ладно, надо так надо — тебе показали, как при большом желании это сделать (например через тот же set).

Что тебе опять не нравится, или какой момент до сих пор не понятен? И причём тут вообще люди и обезьяны?

CrX ★★★★★
()
Ответ на: комментарий от intelfx

Ничего я не пытаюсь подогнать, я просто развернул, о чём сразу и сказал. Я вообще не вижу, в чём конкретно ты не согласен со сказанным мной. Да и у меня ни с чем из сказанного с тобой по существу (а не о моих попытках что-то там сделать), тоже. Просто что-то агришься и всё, а в чём предмет спора — непонятно. Думаю, на этом можно закончить данную ветку диалога — можешь считать, что я пытаюсь что-то там куда-то подогнать, мне не жалко.

CrX ★★★★★
()
Ответ на: комментарий от Desmond_Hume

Не вижу трудности, в написании правильного синтаксиса.

Вообще-то в man expr об этом есть:

       Beware that many operators need to be escaped or quoted for shells.
       Comparisons are arithmetic if both ARGs are numbers, else
       lexicographical.  Pattern matches return the string matched between \(
       and \) or null; if \( and \) are not used, they return the number of
       characters matched or 0.

Ну да, возможно в пример стоило бы добавить помимо скобок ещё астериск, раз уж он используется для умножения. Тут согласен.

Но не то чтобы об этом ничего не сказано — сказано, про «many operators». Ну и предполагается, что читающий, наверное, знает, как работает шелл…

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от CrX

И причём тут вообще люди и обезьяны?

Ой, кто тут?

С назначением других значений зарезервированным позиционным параметрам, выдавая это под обычное присваивание переменных в обычном повседневном программировании, не я начал, заметь… почитай портянку, что ты писал, и что я писал. Ты поймёшь, что про set начал не я.

Тебе пока о таком думать рано.

Тебе зато уже поздно)))

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

С назначением других значений зарезервированным позиционным параметрам, выдавая это под обычное присваивание переменных в обычном повседневном программировании, не я начал, заметь…

Именно ты. Я тебе просто проиллюстрировал, что переменные $0–$9 зарезервированы под позиционные параметры, сообщив, что присвоить их нельзя, продемонстрировав, как их юзать можно. «Выдавая это под обычное присваивание переменных в обычном повседневном программировании» — это уже в твоём воображаемом мире где-то. Требовать назначить им другие значения стал ты, а не я или кто-то другой.

почитай портянку, что ты писал, и что я писал.

Без проблем. Только давай ты тоже прочитаешь. И не через слово, а нормально. Может тогда поймёшь, что тебе всё объяснили об этом уже хрен знает сколько постов назад.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от CrX
Beware that many operators need to be escaped or quoted for shells.
       Comparisons are arithmetic if both ARGs are numbers, else
       lexicographical.  Pattern matches return the string matched between \(
       and \) or null; if \( and \) are not used, they return the number of
       characters matched or 0.

Вот такую инфу надо писать в самом начале, а не в самом конце man-странички. Это существенная информация, а не просто так, замечание к слову. Ещё раз отсылка к Ричарду Столлману, прав был про плохую документацию. Поначалу, когда обращался частенько к документации, думал, что он наговаривает - «как же так? тут так всё подробно написано!». И это не самый репрезентативный случай. Бывает ещё похлеще … встречался не раз. Поэтому, лезть в man, порой, как наказать себя. Проще на разжёванные странички зайти и сделать всё, что нужно.

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от CrX
Beware that many operators need to be escaped or quoted for shells.
       Comparisons are arithmetic if both ARGs are numbers, else
       lexicographical.  Pattern matches return the string matched between \(
       and \) or null; if \( and \) are not used, they return the number of
       characters matched or 0.

Я бы даже так написал:

Beware!
Many operators need...
OR
Warning!
Without screening/escape ... will not work

Человек же приходит к документации не просто так, а в случае проблемы. Других случаев не обнаружено. Просто так люди документацию не читают, это не художественный роман.

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от Desmond_Hume

Вот такую инфу надо писать в самом начале, а не в самом конце man-странички. Это существенная информация, а не просто так, замечание к слову.

Не согласен. Я хочу в самом начале видеть информацию по существу, а не попытки обучить тех, кто не знает основ. Если в начале каждого мануала все подобные штуки писать, то они станут похожи на юридические документы, начинающиеся с определения, кто такой подрядчик, кто такой исполнитель, что такое исполнение, и прочего такого, а суть где-то в середине.

Вот открывая мануал, я не хочу видеть подобную юредическую лабуду или попытки впихнуть в мануал по одной программе кучу всего о других — как работает шелл, в котором ты все команды запускаешь, например. Эдак можно до маразма дойти, в начале мануалов писать, что отправка команды осуществляется клавишей Enter, и всё такое прочее.

Ещё раз отсылка к Ричарду Столлману

Этот мануал от GNU, кстати. Просто к слову.

Поэтому, лезть в man, порой, как наказать себя. Проще на разжёванные странички зайти и сделать всё, что нужно.

Проще один раз выучить основы шелла. Серьёзно. Это ведь базовый инструмент для всех твоих команд, а не одной.

CrX ★★★★★
()
Ответ на: комментарий от CrX

информацию по существу,

Ага, эта «информация по существу» выглядит так: expr arg1 * arg2 А на деле - * превращается в экранированный * и проблемой с интерпретацией арифметического выражения, которое пытается обработать программа expr. Вот только она не отрабатывает, потому что передано ей ничего в удобоваримом виде не было.

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от CrX

Проще один раз выучить основы шелла.

Не бывает «основ шелла». Всё важно. Всё - составляет шелл. Назови то, что относится к шеллу, но не является основой. Нет такого. Это как блокчейн, одно звено, второе … ни одно звено нельзя выбросить или проигнорировать. Ты просто не хочешь признать, что такую простую операцию можно было легко представить в man-руководстве как expr arg1 \* arg2 - хуже бы не было. Опытный человек понял бы, что идёт экранирование спецсимвола. Неопытный не понял бы, но на результате его вычислений это бы не сказалось. Писать надо инструкции и руководства так, что даже несведущий человек бы понял. Кому нужны дебри - ради бога, навалом везде всего. Man-руководства должны быть понятными, краткими и с множеством примеров. Иногда даже просто примеры достаточно указать, ничего не объясняя.

P.S. А вообще, тихий ужас. Мне нужно было просто умножить числа или сложить, предположим, и … нет, я не вычислял матрицы, уравнения Гаусса и проч. Обычная арифметическая операция.

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

Назови то, что относится к шеллу, но не является основой.

Башизмы, особые фичи zsh вроде синтаксического сахара для короткой записи for, массивы.

Скажем так, основы — это то, что есть в POSIX Shell. Осталные наслояния знать для избежание ошибок не обязательно, но может быть полезно для повышения удобства работы.

Ты просто не хочешь признать, что такую простую операцию можно было легко представить в man-руководстве как expr arg1 * arg2 - хуже бы не было.

Почему не хочу? Я такого не говорил. Да, можно было бы написать и так. Против этого (в отличие от курса ликбеза по шеллу в начале вместо перехода сразу к конкретике, а не в конце) я ничего не имею. Можно было примеры написать именно так — с самой командой expr и экранированными спецсимволами (кстати, имхо expr arg1 '*' arg2 красивее). Но писался мануал явно без мысли, что кто-то может не знать, как работает шелл. И я его плохим из-за этого не считаю. Но да, можно было и так — я не против.

P.S. А вообще, тихий ужас. Мне нужно было просто умножить числа или сложить, предположим, и … нет, я не вычислял матрицы, уравнения Гаусса и проч. Обычная арифметическая операция.

Обычно bc для этого используют, если надо ограничиться тем, что есть в POSIX. Ему одной строкой можно всё выражение сразу передать (не отдельными аргументами, а одним, в котором выражение), что удобнее: echo '4*(2+3)' | bc. Ну а так вообще если не для скрипта надо, а просто калькулятор в терминале, имхо, удобнее всего использовать python вообще.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от CrX

Башизмы, особые фичи zsh вроде синтаксического сахара для короткой записи for, массивы.

Я уж было приготовился, думал, сейчас что-то, может быть, дельное напишешь, но нет. Про «башизмы» не знаю, что имеешь в виду. Знаю только то, что в Ubuntu, да и во многих других популярных дистрибутивах, чаще всего используется bash. А массивы это вообще суперфича, я и не подозревал раньше, что они есть в bash. Сильно обрадовался, когда узнал, что они есть и представлены в более или менее вменяемом виде, с внятным синтаксисом.

Скажем так, основы — это то, что есть в POSIX Shell.

Вот никогда этого не понимал, зачем нужна эта жёсткая привязка. Попытка сделать из Ubuntu Unix? Обеспечить их совместимость? А чтобы что? Зачем? Наоборот если - да, согласен, будучи фришником неплохо иметь под рукой ещё и линуксовые инструменты, благо что в линуксе побогаче с этим всем …

Desmond_Hume ★★★★★
() автор топика
Ответ на: комментарий от Desmond_Hume

Вот такую инфу надо писать в самом начале, а не в самом конце man-странички.

Сфигали начинать ман с описания очевидных вещей, которые вообще не относятся к программе expr??

Шеллов — десятки, среди них есть некоторое количество похожих друг на друга, но в общем случае в каждом шелле — свои правила. Прикажешь в мане к каждой программе описывать особенности шеллов? Кроме того, программу expr можно запускать не из шелла, а из другой программы, написанной на каком-либо языке программирования, каждый со своим синтаксисом и семантикой. Может, ещё их описать?

Ещё раз отсылка к Ричарду Столлману, прав был про плохую документацию.

Программа expr — из пакета GNU coreutils, который есть детище Столлмана, поэтому отсылка твоя некорректна. man expr, а лучше info expr — это пример хорошей документации.

Поэтому, лезть в man, порой, как наказать себя.

Гляжу в книгу, вижу фигу?

debugger ★★★★★
()
Ответ на: комментарий от sigurd

Ты все еще говоришь про bash? В нем невозможно определить переменные пользователя с именами $N - они зарезервированы за башем и имеют специальное значение.

Возможно, для этого есть команда set. И выше уже был пример.

debugger ★★★★★
()
Ответ на: комментарий от Desmond_Hume

Я подозревал, что не сработает.

$ Desmond_Hume() {echo $5 $4 $3 $2 $1} && Desmond_Hume arg1 arg2 arg3 arg4 arg5
bash: syntax error near unexpected token `{echo'

Исправить опечатку несложно, если слегка покурить синтаксис баша. Во-первых, фигурные скобки являются зарезервированными словами (reverved words), поэтому должны отделяться пробелами. Во-вторых, список команд в фигурных скобках должен заканчиваться ;. Вуаля:

$ Desmond_Hume() { echo $5 $4 $3 $2 $1; } && Desmond_Hume arg1 arg2 arg3 arg4 arg5
arg5 arg4 arg3 arg2 arg1
debugger ★★★★★
()
Ответ на: комментарий от Desmond_Hume
set --help

Где тут variables?

Попробуй man set или man bash:

$ man set
   set [-abefhkmnptuvxBCEHPT] [-o option-name] [--] [-] [arg ...]
   set [+abefhkmnptuvxBCEHPT] [+o option-name] [--] [-] [arg ...]
          ...
          Any arguments remaining after option processing are treated as values for 
          the positional parameters and are assigned, in order, to $1, $2, ...  $n.  
          ...
debugger ★★★★★
()
Ответ на: комментарий от debugger

свои правила

То есть, ты хочешь сказать, что команда expr arg1 \* arg2 не сработает в другом shell’e, отличном от bash?

Desmond_Hume ★★★★★
() автор топика
Последнее исправление: Desmond_Hume (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

То есть, ты хочешь сказать, что команда expr arg1 * arg2 не сработает в другом shell’e, отличном от bash?

Попробуй исполнить свою команду в pwsh (powershell).

debugger ★★★★★
()
Последнее исправление: debugger (всего исправлений: 1)
Ответ на: комментарий от Desmond_Hume

Можно прыгнуть со скалы или микроскопом забивать гвозди. Да, и то, и то возможно и реализуемо. Но зачем?

Идиотские аналогии. Исполнение команды set не приводит к деструктивным последствиям. Если эта команда не нравится тараканам в твоей голове, то команда в этом не виновата.

debugger ★★★★★
()
Ответ на: комментарий от Desmond_Hume

Вторая команда какая?

При чём тут вторая команда? Если бы ты заглянул в ман, то таких идиотских вопросов бы не задавал:

Compound Commands

...

{ list; }
    list is simply executed in the current shell environment.  list must be terminated with a newline or semicolon.
    This is known as a group command.  The return status is the exit status of list.  Note that unlike the
    metacharacters ( and ), { and } are reserved words and must occur where a reserved word is permitted to be
    recognized.  Since they do not cause a word break, they must be separated from list by whitespace or another
    shell metacharacter.
debugger ★★★★★
()
Ответ на: комментарий от debugger

Приводит, если в коде будут использоваться конструкции, которые используют $1, $2 в качестве позиционных параметров аргументов, а не в качестве чисел.

Desmond_Hume ★★★★★
() автор топика