Как стать автором
Обновить

Комментарии 30

одни люди борются с башизмами, другие пишут о них статьи

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

из 6 систем с linux у меня на столе bash есть на 2 (на 3 из 7, если считать android за linux-систему).
за freebsd не слежу, там уже появился bash в дефолтной установке?


с кучей лишних действий и абсолютно нечитаемыми конструкциями

можно подумать, что массивы bash — верх читаемости.


и что, кроме классического bourne shell и bash больше скриптовых языков нет?
ИМХО если потребовались массивы, то это уже повод задуматься о другом языке программирования.


P. S. я не говорю, что башизмы — вселенское зло и их никогда нельзя использовать; это как goto и глобальные переменные — иногда делают жизнь лучше, но в целом нужно стремиться избегать.

из 6 систем с linux у меня на столе bash есть на 2
Расскажите, пожалуйста, что за дистрибутивы такие, в которых нет bash. Только не надо про всякое embedded с busybox.

и что, кроме классического bourne shell и bash больше скриптовых языков нет?
Замените башизмы на zsh-измы, fish-измы или что вам больше нравится.

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

это как goto и глобальные переменные
Сравнивать массивы с goto это, конечно, мощно. Ну, можете избегать массивов и дальше, удачи вам с обработкой сотни тысяч строк в каком-нибудь однострочнике, который надо написать за пару минут.
удачи вам с обработкой сотни тысяч строк в каком-нибудь однострочнике, который надо написать за пару минут.


Например? Где необходим массив, там будет его заполнение и какая-то обработка (как минимум 2 конструкции, похожие на цикл). Однострочник такого размера выглядит достаточно неприятно (для меня), чтобы решать эту задачу другим способом.
Как по мне, так подавляющее большинство однострочников выглядит отвратительно :) Но они и не для красоты обычно пишутся, а чтобы решить какую-то конкретную проблему. Я не совсем корректно выразился, под «однострочниками» я имел в виду все, что пишется в терминале (даже если с переносами) как быстрый метод что-то сделать.

Пример, возьмем стандартную итерацию по куче строк (тут не особо важно из файла или stdin, но я для простоты беру миллион строк из файла):
while read -r line; do
    echo "$line changed" >> output.txt
done < 1m_lines.txt

Это на моей машине выполняется за 44 секунды.

Теперь используем массивы из баша:
readarray -t lines < 1m_lines.txt
for i in "${!lines[@]}"; do
    lines[$i]="${lines[$i]} changed"
done
printf "%s\n" "${lines[@]}" > output.txt

Больше писать, но выполнение за 10 сек, выигрыш в 4 раза.

Понятно, что такой пример это сферический конь в вакууме, который меняется на банальный sed, но в реальных задачах логика бывает куда сложнее и этим уже не обойтись, массивы сильно улучшают как удобство работы с кучей данных, так и скорость. То же самое справедливо и для других башизмов.
Больше писать, но выполнение за 10 сек, выигрыш в 4 раза.
Хм, есть же вариант, когда и меньше писать, на один символ, и без всяких этих ваших «башизмов», чистый POSIX.
while read -r line; do
    echo "$line changed"
done < /tmp/1m_lines.txt > /tmp/output-out.txt

Общее время: 0m17.949s против 1m46.314s, 6 раз. Кстати, хотя общее время этого варианта такое же, как у башизнутого варианта, но по процессорному времени, POSIX вариант в 1,24 раза быстрее башизнутого.
Во-первых, эти варианты при практическом использовании не равнозначны, перенаправление всего stdout в файл ведет не только к неудобствам с диагностическими сообщениями, которые теперь придется перенаправлять вручную, но и к ненулевой возможности поиметь среди данных вывод какой-нибудь тулзы, использованной при процессинге. Я лично сталкивался с ситуацией, когда curl, используемый чтобы пнуть нужный вебхук, после истечения токена вместо пустой строки при коде 200 начинал отдавать сообщение об ошибке с другой стороны (что вполне логично, но не было предусмотрено автором скрипта). Думаю про тонны всевозможного стороннего софта, который зачастую даже не знает, что ошибки надо выводить в stderr, и упоминать не нужно.

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

P.S. Не смог повторить ситуацию, когда баш вариант сожрал больше процессора, в обоих случаях плюс-минус одно и то же, POSIX вариант незначительно медленнее.
image
Во-первых, эти варианты при практическом использовании не равнозначны


Равнозначны. Медленный вариант 1 000 000 раз открывает/закрывает файловый дескриптор — вот и весь секрет медленности.

Во-вторых, массивы это не просто красивая обертка для чтения кучи строк


Не возражаю. Я и раньше знал, что массивы в bash есть, но зачем они там нужны — неизвестно. Просто как только у меня возникает задача, в которой нужно что-то делать с массивами — желание сделать это в bash сразу пропадает;) На десктопе — есть oocalc и python, там всё это значительно легче.
Равнозначны. Медленный вариант 1 000 000 раз открывает/закрывает файловый дескриптор — вот и весь секрет медленности.
Попробуйте еще раз прочитать первый абзац моего комментария, в нем нет ни слова про производительность.
Не смог повторить ситуацию, когда баш вариант сожрал больше процессора, в обоих случаях плюс-минус одно и то же, POSIX вариант незначительно медленнее.
Я Вас умоляю! Очевидно ж, это ж зависит от длины строк. Для понимания, что массивы bash это только синтаксический сахар и ничего больше, в деле «башизмов» можете опробовать следующий код:
readarray -t lines < /tmp/1m_lines.txt
printf "%s changed\n" "${lines[@]}" > /tmp/output-bash1.txt
Который в 2...3 раза короче и шустрее вашего, поскольку не имеет хотя бы цикла.

А заодно, и подумать над вопросом: «Почему нормальные реализации массивов в POSIX awk, а так же в perl или python, входящие в стандарт Linux, выполняют эту же задачу на два порядка быстрее?»
Я же написал что пример это сферический конь в вакууме, а вы мне что пытаетесь показать, что я не додумался обойтись без цикла? Любому идиоту понятно, что для задачи «дописать в каждую строку файла текст» не нужны никакие массивы. Но вы можете продолжать и дальше спорить сами с собой.
Извиняйте, но странный код глаз режет. Да ещё ж и со странным примечанием: «Больше писать, но выполнение за 10 сек, выигрыш в 4 раза.»

Впрочем, задача «дописать в каждую строку файла текст» средствами только shell имеет свой интерес. И похоже башизмы для неё совсем не полезны.
P.S.
Да, забытый POSIX вариант, вероятной самый шустрый из всех чисто shell/bash вариантов:
IFS='
' 
printf "%s changed\n" $(cat /tmp/1m_lines.txt) > /tmp/output-pos.txt
Писали бы лучше про именованные массивы, кому обычные в баше нужны)

Да хотя бы джавистам, которые до сих пор свои JAVA_OPTS собирают конкатенацией строк, вместо того, чтобы пользоваться массивом.

Как бы, даже под macOS/Linux их пугает:
$ man sh
...
       If  bash  is  invoked  with  the name sh, it tries to mimic the startup
       behavior of historical versions of sh as  closely  as  possible,  while
       conforming  to the POSIX standard as well... 
       ... posix mode...

И не говоря уж за Solaris/AIX/FreeBSD/… Говорят, Java многоплатформенная ж…

Это из разряда "ну, бороду я, допустим, сбрею… Но умище, УМИЩЕ то куда деть?.."

Массивы в баше очень полезны, весь piu-piu на них держится)
Небольшой хинт, используя printf, можно обойтись без for циклов:
printf без for
$ arr=(a b c d)
$ printf "%s\n" ${arr[@]}
a
b
c
d

В статье Вы не упомянули о различии в обработке
Упомянули, но одним коротким абзацем без примеров.


  • ${arr[*]}
  • ${arr[@]}
  • "${arr[*]}"
  • "${arr[@]}"

Например:


x=( "hello world" "I am" bash )

# каждое слово в отдельной строке
printf "%s\n" ${x[*]}
printf "%s\n" ${x[@]}

# все слова в одной строке
printf "%s\n" "${x[*]}"

# каждый элемент массива в отдельной строке
printf "%s\n" "${x[@]}"

Дополнение. На самом деле автор упомянул об этом — я не заметил этот абзац в тексте:


Обратите внимание, что символ "@" может быть использован вместо "" в конструкциях типа {arr[]}, результат будет одинаковым за исключением разворачивания записи в кавычках. "$" и "$@" выведут записи в кавычках, "${arr[]}" вернет каждую запись как одно слово, "${arr[@]}" вернет каждую запись в виде отдельных слов.
Вот за что нетривиальные скрипты баш не люблю, ну как такое читать.
${!arr[*]} против например arr.keys()
${!arr[*]} против например arr.keys()


bash vs python?

Эм, вроде, полстатьи про expansion, или как?

я не автор, я переводчик
-пациент, вы курите?
-нет
-пьете?
-нет
-на bash пишиете?
-нет
— ну тогда это очень усложняет диагностику заболевания )))))
И не слова не сказано про именованные(associative) массивы, которые позволяют использовать вместо индексов(0,1,2...) свои произвольные идентификаторы.
Пример associative array
#initialising
declare -A arr
arr[some_string]=test1
arr[some_string2]=test2
arr[1234]=test3
#values out
$ echo ${arr[@]}
test2 test3 test1
#keys out
$ echo ${!arr[@]}
some_string2 1234 some_string

возможно, вы не знакомы с такой полезной особенностью bash как массивы
Извините, но что у Вас с русским языком? Вот не удержался, глаза режет.

Особенность — характерное, отличительное свойство кого-/чего-нибудь.
С каких пор массивы — это «отличительная» черта Bash? Массивы есть во многих языках, и Bash тут совсем не исключение. Их наличие нисколько не делает Bash особенным.

… you may not be familiar with bash's array feature.
В данном контексте «bash's feature» — это, скорее, «часть языка Bash» [1].

[1] dictionary.cambridge.org/ru/словарь/английский/feature
С каких пор массивы — это «отличительная» черта Bash?


По сравнению с sh. Сравнивать bash с любым другим языком вряд ли имеет смысл.
bash по сравнению с чуть более старой версией баш можно сравнить.
В bash ассоциативные массивы появились недавно (по меркам линукса). Но гораздо гораздо раньше они уже активно и удобно использовались в перл, а потом в питон.

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

Круто! Не знал что так можно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории