Comments 63
Просто взять и сделать дистрибутив целиком без bash.
Но не прижилось. Вот тут уже интересно подумать почему.
до сих пор нет попыток прикрутить вместо этого ископаемого монстра нормальный современный интерпретируемый язык программирования
Shell изначально развивался как средство быстрой интеграции программ в командной строке, и в этом плане он до сих пор удобнее всех альтернатив. Задачи типа "взять последние 5000 строчек лога, отобрать с типом POST и http status 500, выбрать IP, отсортировать по частоте встречаемости" или "заменить в конфиге все example.com на example.net, протестировать корректность конфига, если ок — перезапустить сервис" на баше делаются почти не думая, быстро и лаконично.
Ну и про legacy забывать не стоит, "просто взять и сделать дистрибутив целиком без bash" — это огромное количество человеко-лет на переписывание всех скриптов, используемых всеми программами, и дальнейшее сопровождение.
З.Ы. Минусуют ИМХО зря, вопрос вполне нормальный.
Да, и bash прекрасен именнно как максимально простой и лаконичный клей между всеми этими программами: foo | bar | baz > result
. Представьте себе то же самое на питоне: import subprocess as sp; p=sp.Popen("foo", stdout=sp.PIPE); sp.communicate(...
. Ну это же просто застрелиться.
(ba)sh работает под любой *nix системой и достаточно хорош, чтобы альтернативы не развивались за ненадобностью. Не идеален, а именно достаточно хорош для 99% задач.
Хотя я бы, например, с удовольствием использовал что-нибудь лаконичное как bash, но при этом с нормальной работой с массивами и функциями, что-нибудь вроде специального диалекта питона.
foo | bar | baz > result
def clear(d):
pushd @(d)
rm `\w+\d{,3}\.log`
popd
print(d, "is cleared")
clear($HOME)
aliases["ll"] = "ls -Al"
ll src
sudo -H pip3 install PyYAML
import yaml
doc = yaml.load($(foo | bar | baz))
git commit -m @(doc["message"]) and git push
Почему вы решили, что альтернативы не развиваются? Сейчас самая популярная тройка — fish, zsh и bash (интерактивные); dash и bash — основные для скриптов. Всё, кроме dash, развивается. Zsh в ряде дистрибутивов вообще стоит по‐умолчанию. Ещё в список популярных оболочек хотят, как минимум, powershell и xonsh (xonsh мне в этом отношении нравится больше, но сейчас я использую zsh).
При этом, хотя все упомянутые оболочки явно испытывают(ли) влияние bash и POSIX shell, POSIX скрипты работают только в dash, zsh и bash, а остальные несовместимы. Сам zsh имеет огромное количество возможностей, отсутствующих (сложных в реализации/требующих много кода) во всех других оболочках.
PowerShell на мой взгляд недотягивает. Через консоль по-быстрому задеплоить солюшн — ок. Попытка сделать клон *sh — явно нет: слишком многого недостаёт.
Честно Вам скажу, с sh знаком очень поверхностно, не знаю всех тонкостей, но вот PowerShell, как мне кажется, очень хорош, если ты, к примеру, C# программист, ведь PoSh — это чистый .Net. Считаю это одним из преимуществ, как и то, что там можно оперировать объектами.
PS Для хейтеров — попрошу мой комментарий не воспринимать как негативную критику *sh. Всего лишь считаю, что преимущества есть у всего.
Дистрибутив-то вам зачем? Я вполне понимаю ваше отношение в целом, но поверьте, что любой интерпретируемый язык прикручивается одной строкой в начале кода.
Например, я в качестве такого языка широко использую groovy. А можно что-то более традиционное, типа перла или python. И все это вполне себе живет.
while IFS= read -r -d '' file; do
# Arbitrary operations on "$file" here
done < <(find /some/path -type f -print0)
http://stackoverflow.com/a/8677566/1398863
А вообще торт!
Ого. А вы всё продолжаете и продолжаете отмываться %)
Есть такой и для баша. Например, гугловский: https://google.github.io/styleguide/shell.xml
Используя его, большинство проблем исчезает. Язык как язык. Свои плюсы, свои минусы. Есть класс задач, который лучше него так никто и не решает.
А вообще автор клёвый чувак и у него очень полезные заметки по юниксовым утилитам.
Неплохо. Ещё есть некий "framework" для bash – думаю, продвинутым пользователям будет очень занятно почитать код =)
Дает очень полезные советы после анализа кода. Используя её, узнал все те «best practice», которые описаны в этой статье.
Статья очень полезная, спасибо за перевод, с удовольствием прочитал её всю до конца. Я не видел русскоязычном сегменте подобных статей, а пару раз очень надо было дать ссылку на руководство по Bash людям, которые плохо знают английский. Теперь у меня такая ссылка есть.
Хороший перевод. От себя могу только добавить, что большинство ошибок может предотвратить set -e
:
#!/bin/bash -e
…
Кроме того, слышал про gdb‐like дебаггер. bashdb, не он?
Конечно, можно гнать на тех, кто обрабатывает данные. А можно просто хорошо делать свою работу, и не беспокоиться об опасных форматах.
C-программисты уже знакомы с &&. Bash использует такое же упрощённое вычисление.
Насколько я в курсе, это называется «ленивое вычисление» (когда вторая часть булевского выражения вычисляется, только если нужно; если же из первой уже следует результат, то вторая не вычисляется).
Баш полезен, бесспорно, например для вызова всяких утилит и для работы с файловой системой. Что также удобно: его простые команды прекрасно вызываются через любой интерпретируемый язык более высокого уровня (php/python/ruby). На мой взгляд, правильно писать основную логику скрипта на чём-то другом, помимо баша. А единичные команды можно вызывать уже с помощью bash. Получается очень удобно в итоге.
… И при этом ваша команда выполнится не только для файлов непосредственно в текущей директории, но и рекурсивно для всех файлов внутри неё. Вряд ли человек, написавший (в случае с mp3) изначально команду с "*.mp3", этого хочет.
find. -type f -maxdepth 1
P.S. Статья интересная, но не осилил, положил в закладки, потом поизучаю. Правда, часть этих граблей уже испытал на себе раньше…
ls *.mp3 | while read curr_file
do
cp "${curr_file}" "./dst/${curr_file}"
done
Даже при условии не верного отображения имени, команда отрабатывает корректно
$ ls
copy.sh dst ?????? ????*.txt
$ copy.sh
$ cd dst/
$ cat Привет\ Хабр\*.txt
Hello Habr!
Причем, что интересно, то find вообще не находит файл по маске
$ find . -type f -name "*.txt" -print
Или есть таки какие то подводные камни?
for curr_file in *.mp3; do
:
done
?
Но если последнее имя файла в списке заканчивается новой строкой, то `...` или $() уберут и его в придачу.
вот здесь не очень понял, что значит имя файла заканчивается новой строкой. Можно привести пример?
Какой пример? В качестве имени файла может фигурировать любая строка, не содержащая нулевой байт и не являющаяся пустой (в зависимости от ФС, может быть ещё ограничение на длину имени файла). В том числе, строка вида $'foo\nbar\n'
. Конечно, используемые программы это несколько ограничивают (те же имена вида -t
лучше записывать как ./-t
, а file:///tmp/test
может значить как ./file:/tmp/test
, так и URL), но open()
в libc сожрёт практически всё, поэтому после open("foo\nbar\n", O_CREAT)
вы вполне можете наблюдать имя foo$'\n'bar$'\n'
. Сама оболочка и большинство используемых из неё программ ограничивают максимум возможные начала имени файла, а не его конец, поэтому вызывать open
самому не нужно: touch $'foo\nbar\n'
не хуже.
mkdir -v test
cd test
touch $'two\nlines.mp3' #demo for problem 1. bash specific
ls *.mp3 | while read curr_file; do echo "= $curr_file ="; done
Ещё обратите внимание на опцию --quoting-style= у ls. В каких-то версиях ls по-умолчанию окружает кавычками имена файлов с пробелами, так что read $curr_file съест лишние кавычки.
Сделать файл с именем, заканчивающимся new line:
touch $'myfile\n'
Не понял, что имелось ввиду под «read $curr_file съест лишние кавычки»? bash -c 'echo ''"abc def"'' | while read t ; do printf "<%s>\n" "$t"; done'
выдаёт <"abc def">
.
Код, который вы показали в предыдущем комментарии явно отличается от того, что вы пробовали выполнять.
код одинаковый, просто проверял на txt. Теперь понял. Но проблема с find и неправильной кодировкой остается в силе. Я бы сделал примечание.
Какой пример?
вот этот
$ touch $'two\nlines.mp3'
$ touch $'myfile\n'
К проблемам с именами файлов: попытайтесь использовать CMake, чтобы собрать что‐либо в каталоге bu;ld
. Узнаете, что *sh даже близко не стоит к CMake по «удобству» использования имён с «нестандартными» символами внутри.
Собственно, CMake я вспомнил, потому что
- Может быть и хуже.
- При этом между *sh и ©Make я не видел чего‐либо, что было лучше систем сборок, но хуже оболочек (хотя нет, если рассматривать tcsh отдельно от POSIX‐совместимых оболочек, то он как раз окажется где‐то между).
Например, хакер получил доступ (неважно как) к какой-либо папке, которую обрабатывает (например, по расписанию или по событию какому-то) какой-то скрипт. Может ли хакер создать такой набор (или даже 1 файл) файлов, чтобы при обработке тем скриптом выполнилась заранее заданная команда?
Предположим, что хакер не знает содержимое скрипта, но знает что этот скрипт делает (ну, к примеру, копирует содержимое папки куда-то, или наоборот, чистит папку, или конвертирует содержимое файлов в другой формат...) Теоретически, можно ведь предположить, что администратор ресурса допустил одну или несколько ошибок вроде описанных тут и использовать это.
Везде где только могу использую zsh, но когда пишу скрипты, всегда пишу #!/bin/bash (или иногда #!/bin/sh)...
Спасибо за отличный перевод! Узнал для себя несколько новых штук)
#!/bin/bash
cd /mnt/logs
rm -rf *
Угадайте что произошло, когда упала сеть и сервак ребутнули?
- Сеть не работает => NFS не монтируется;
- NFS должна монтироваться в /mnt => директории /mnt/logs не существует;
cd: /mnt/logs: No such file or directory
;- … =)
Этот пример скорее показывает важность правильной настройки инициализации.
cd /mnt/logs && rm -rf *
Ну или массу других вариантов можно придумать. Конечно, пока «петух не клюнет» — админ не пошевелится :)
43. somecmd 2>&1 >>logfileДля этого есть удобный башизм:
somecmd &>>logfile
или если с перезаписью:somecmd &>logfile
В последней ссылке об этом есть, но, думаю, полезно явно озвучить такую плюшку.
Подводные камни Bash