Bash Days | Linux | DevOps
23.3K subscribers
153 photos
25 videos
667 links
Авторский канал от действующего девопса

Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.

Автор: Роман Шубин
Реклама: @maxgrue

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
Вот всё трём мы с тобой за бест-практики, но практически ничего не разбираем по бэд-практикам.

Давай это исправлять в контексте Bash скриптов.

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

Естественно будет серебряная пуля — как сделать правильно.


Ну чё, готов? Тогда поехали!

Почему при копировании файлов, необходимо использовать кавычки?

Допустим, у тебя есть переменная $file, в которой как ни странно хранится имя файла. И есть $target, в ней мы указываем путь куда скопировать файл.

Если мы пишем:

cp $file $target


То возникает проблема, Если имя файла содержит пробелы, то Bash ясен хуй разобьет эту строку на пробелы:

file="You can suck my dick.avi"


Команда превращается в такое:

cp You can suck my dick.avi /tmp


И это воспримется интерпретатором как копирование нескольких файлов:

You
can
suck
my
dick.avi


Дальше. Если в имени файла ($file) будут символы *, ?, [ ], то это интерпретируется как шаблон для поиска файла.

Так называемое — Pathname Expansion.


Ага. Теперь если в имени файла содержится символ -, к примеру твой файл называется: -bashdays.txt, то команда cp расценит, что ты указал флаг и выебет тебя в глаз.

cp -bashdays.txt /tmp

cp: ну ты ебать инвалид -- 'h'
Try 'cp --help' for more information.


Как правильно?

Вот так:

cp -- "$file" "$target"


Кавычки в данном контексте предотвратят разделение на пробелы и ошибочное расширение подстановок.

Но если сделать так:

cp "-bashdays.txt" "/tmp"

cp: еще и баран -- 'h'
Try 'cp --help' for more information.


Чтобы не быть «инвалидом и бараном» нужно после cp вхерачить --.

Эти два тире напомнят cp что дальше идут только файлы, а никакие-то флаги.

Каков итог?

Думаю ты и сам выводы сделал. Всегда пиздярь кавычки вокруг переменных в Bash.

Даже если работает без кавычек это ничего не значит, не ровен час получишь по ебалу.

Например твой скрипт будет выполняться в другом окружении, где $IFS (разделитель слов) изменён или файлы содержат пробелы и спецсимволы.


В общем сразу мотай на ус эту тему, подстели соломку и пиши скрипты как профи.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
109
Продолжаем погружаться в Бэд-Практики!

предыдущие посты о том, что это такое ищи по тегу #badpractices


В прошлом посте мы рассмотрели файлы, которые начинаются с дефиса и то, что команда cp их может воспринимать как флаг. Ну дак вот.

Как это победить мы с тобой уже знаем, завернуть в кавычки и кинуть два дефиса:

cp -- "$file" "$target"


Что означают 2 дефиса опять же смотри предыдущие посты по тэгу #badpractices


Ок. Минус этого метода — его нужно сука запомнить и не забывать использовать каждый раз при передачах файлов. Вообще не интуитивно.

Второй способ разруливания этой ситуации — указать путь к файлу.

Смотри. Если перед именем файла добавить ./ (означает текущую папку), то даже если файл называется -bashdays.txt он будет передан в команду правильно.

for i in ./*.txt; do
cp "$i" /tmp
done


В этом случае cp не затроит и выполнит задуманное.

Ну и еще вариант на закуску.

Можно просто ебануть ./ перед именем файла при передачах в команду:

for i in *.txt; do
cp "./$i" /tmp
done


Этот варик немного сэкономит ресурсы, тут не требуется дополнительных вычислений для обработки имен файлов. Но при условии, что у тебя немного файлов. Если их будет дохуя, то увы, будет тормозить.

Выводы

Лучший вариант — всегда указывать путь к файлу (относительный ./ или полный /home/user/...). Это избавит тебя от большинства проблем с файлами которые начинаются с дефиса.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
40
Когда в Bash ты сравниваешь две переменные, важно не проебаться с кавычками.

[ $foo = "bar" ]


В этом примере если переменная $foo будет пустой, то по итогу ты попадешь в просак:

[ = "bar" ]

bash: [: =: unary operator expected


Логично вылезет ошибка, потому что «=» ожидает два значения. Чтобы избежать этой ситуации, на помощь приходят — кавычки.

[ "$foo" = "bar" ]


Теперь всё в поряде. Ошибки никакой нет.

Но Bash не пальцем деланный, поэтом сравнить две переменные можно иначе.

[[ $foo == bar ]]


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

Есть еще легаси способ:

[ x"$foo" = x"bar" ]


В современном мире ты вряд ли с ним столкнешься, но в каких-то допотопных скриптах вполне можешь найти.

Если $foo пустая, то без x получится:

[ = bar ]


А с x будет:

[ x = xbar ]


В [[ ... ]] переменные не разделяются на слова, даже если содержат пробелы.


foo="hello bashdays"
[[ $foo = "hello bashdays" ]]


А если сделать так:

foo="hello bashdays"
[ $foo = "hello bashdays" ]


Получишь ошибку: bash: [: too many arguments

Все это справедливо для Bash. Если пишешь под sh, то твой путь это одинарные скобки [...].

А еще в двойных скобках можно использовать шаблоны:

foo="hello bashdays"
[[ $foo == h* ]]


Вернёт true, потому что foo начинается с «h».

Либо написать сложное условие:

[[ $foo = "bar" || $bar = "baz" ]]


В одинарных кавычках это выглядело бы так:

[ "$foo" = "bar" -o "$bar" = "baz" ]


Выводы:

Всё что тебе нужно знать это первые два способа:

[ "$foo" = "bar" ]
[[ $foo == bar ]]


Это трувей, бестпрактика и мастхев.

Изучай.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
86
Представь что у тебя есть переменная:

f="My Documents/file.txt"


И в скрипте мы делаем так:

cd $(dirname "$f")


Это ошибочный вариант, бэд мать его практика.

Команда cd $(dirname "$f") должна вернуть путь к папке, где лежит файл.

НО! Результат этой команды разбивается на части, если в нём есть пробелы.

Например:

dirname "My Documents/file.txt"


Выдаст: My Documents

А если так:

cd My Documents


Логично, получаем ошибку:

cd: No such file or directory: My


Bash думает что это два отдельных слова, а не один путь.

➡️ Бест-практика

cd -P -- "$(dirname -- "$f")"


1. Кавычки защищают результат команды от разбиения.
2. И cd получит целый путь, даже если в нём есть пробелы.

Как работают кавычки

- Когда Bash видит $(...), он воспринимает это как отдельную область, некий «уровень».

- Кавычки внутри $(...) работают только внутри.

- Кавычки снаружи не объединяются с внутренними.

Наглядно, можно представить так:

cd "$( dirname "$f" )"


Внутренние кавычки "$f" защищают переменную f

Внешние кавычки "" защищают результат dirname "$f"

Теперь даже если переменная будет содержать пробелы команда не разобьётся на части.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
75
Представь, что у тебя есть две коробки. На одной написано «хуи дрочёны», а на другой «пики точены».

Ты хочешь проверить:

— В первой коробке хуи?
— Во второй коробке пики?

Если оба условия верны, ты говоришь — заебись!.

Бэд практика:

[ "хуи" = "коробка1" -a "пики" = "коробка2" ]


Тут -a (И) считается устаревшим и в некоторых случаях это работать не будет. Так что если такое видишь или пишешь, сразу сноси, это хуйня!

Одна из проблем с [ A = B -a C = D ] (или -o) в том, что POSIX не определяет, как должна работать команда [ ... ], если у неё больше 4 аргументов.


Бест-практика:

Разделяем на две проверки:

[ "коробка1" = "хуи" ] && [ "коробка2" = "пики" ]


Сначала проверяется первое условие, затем второе.

Если оба верны — команда выполнится.

Либо делаем конкретно под Bash:

[[ "коробка1" = "хуи" && "коробка2" = "пики" ]]


Здесь можно использовать &&, всё будет работать правильно.

Выводы:

Если используешь [ ... ], то делай две отдельные проверки.

Если [[ ... ]], то можно писать всё внутри.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
83
Если написать так [[ $foo > 7 ]], то далеко не факт что это правильно отработает.

Двойные скобки [[ ... ]] в Bash предназначен для проверки условий, но не для работы с числами. Для чисел лучше хуячить (( ... )).

➡️ Бест-практика

(( foo > 7 ))


А если хочется прям строго соответствовать POSIX, делай так:

[ "$foo" -gt 7 ]


Теперь давай разберемся почему с [[ $foo > 7 ]] словишь ошибку.

Символ > в [[ ... ]] сравнивает строки, а не числа. Например, "10" < "7", потому что 1 идёт раньше 7 в алфавите.

В [...] символ > вообще означает «перенаправление вывода», и создаст файл с именем 7 в текущей папке.

Еще пример:

case $foo in
("" | *[!0123456789]*) echo "Ошибка: foo не число!" && exit 1 ;;
*) [ "$foo" -gt 7 ] ;;
esac


Если $foo содержит что-то вроде $(rm -rf /), то при определённых условиях это может привести к пиздецу. Поэтому перед проверкой лучше убедиться, что $foo — это число.

Код выше проверяет, является ли переменная $foo числом, и если да, сравнивает её с 7.

case $foo in — конструкция для проверки значений переменной $foo по шаблонам.

"" — пустая строка (если $foo пустое).

("" | *[!0123456789]*) — строка, содержащая хотя бы один символ, который не цифра (например, abc, 12a3).

Если условие выполняется, выводится сообщение "Ошибка: foo не число!", и скрипт завершает работу с кодом 1 (exit 1).

* — означает «всё остальное» (то есть, если $foo не попал под первый шаблон).

[ "$foo" -gt 7 ] — проверяет, больше ли $foo чем 7.


Выводы: для работы с числами используем (( ... )) или [ "$foo" -gt 7 ], а переменные перед проверкой лучше очищать от лишних символов.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
39
Продолжаем делать правильно.

Этот код выглядит вполне нормально:

grep foo bar.txt | while read -r; do ((count++)); done


Он считает, сколько строк в файле bar.txt содержат слово foo.

Здесь главная проблема — переменная count не изменится вне цикла while, потому что в Bash каждая команда в пайплайне (|) запускается в отдельной оболочке (subshell). То есть count++ происходит «внутри», и снаружи этого не видно.

Если простым языком: Каждая часть, разделённая |, запускается в отдельной «коробке» (subshell). То есть while работает внутри своей коробки. B всё что там происходит, не видно снаружи.

Некоторые оболочки ksh93 или Bash с включённой настройкой shopt -s lastpipe работают по-другому — цикл выполняется в той же оболочке, и тогда count изменится.


count=0
echo -e "one\ntwo\nthree" | while read line; do ((count++)); done
echo $count


Этот код вернет 0, несмотря на count++, а вот например в zsh вернется 3.

Как быть?

Первый вариант:

shopt -s lastpipe


Эта штука говорит интерпретатору — что последний элемент конвейера будет выполнен в окружении текущей оболочки.

Второй вариант:

Вообще избавиться от while и всё сделать через grep:

count=$(grep -c foo bar)
echo $count


Можно еще наколхозить и передавать значения через временный файл, но это прям пиздец шляпа и костыль.

Выводы

Нужно просто посчитать строки:

count=$(grep -c foo bar)


Нужно обрабатывать строки:

while read line; do ...; done < bar


Хочешь использовать pipe:

shopt -s lastpipe


Вот и вся наука.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
52
Вот те на!

if [ false ]; then echo "HELP"; fi


Большинство думает, что [ — это часть команды if как скобки в других языках программирования. Но нихуя!

В Bash if просто запускает команду. Команда [ ... ] — это обычный бинарник, аналогично команде test, а не специальный синтаксис.

Скобка ] нужна только для красоты и завершения команды [.

Команда выше напечатает HELP, потому что строка "false" — непустая, а значит, условие считается истинным.

Теперь о главном

Если хочешь использовать grep в if, не надо писать скобки!

Плохая практика:

if [ grep -q "foo" myfile ]; then
echo "Найдено!"
fi


[... ] — ожидает условие, а не команду

grep — это команда, а не логическое выражение

Внутри [ запускать команды нельзя — это приведёт к ошибке

Хорошая практика:

if grep -q "foo" myfile; then
echo "Найдено!"
fi


if просто запускает grep

Если grep нашёл совпадение, он вернёт 0 (успех), и выполнится then

И Всё работает как надо, без лишних скобок!

[ — это как калькулятор, а grep — это поиск. В калькуляторе искать бесполезно!

Выводы

Никогда не пиши if [ grep ... ] — это ошибка!

Пиши просто if grep ..., чтобы проверить результат команды.

if работает с командами. [ — это тоже команда, но не синтаксис.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
283
Позволил себе пару дней ничего не делать, кроме конечно проверки LF домашек. Лежал, исследовал дырки в axiom verge, гулял. Мне понравилось!

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

Ладно, лирика. Давай дальше бест-практики тыкать.

Команда if [ bar = "$foo" ]; проверяет, равны ли два значения.

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

Неправильно:

[bar="$foo"] 
[ bar="$foo" ]
[[bar="$foo"]]


1. Нельзя слеплять всё вместе. Это хуёва даже в плане кодстайла.
2. Нет пробела вокруг знака «=».
3. Аналогично, всё слеплено.

Правильный синтаксис:

if [ bar = "$foo" ]; then


или

if [[ bar = "$foo" ]]; then


Почему?

Потому что [, =, ] это отдельные символы, первая скобка вообще команда test. А если ты все слепляешь, Bash посчитает тебя долбаёбом и отправит в пешее эротическое.

Главное правило — всегда ставь пробелы между каждым элементом в условии.

tags: #bash #badpractices #bestpractices

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
41
Бебебед практики bash, продолжаем.

Нельзя просто так взять условие из языка «Сиськи» и заменить его на квадратные скобки.

Сколько не повторяй, один хер делают неправильно.

Команда [ — это не просто скобка, а отдельная команда. Она проверяет, пиздеж ли что-то или нет.

Хуёвая практика:

if [ a = b ] && [ c = d ]; then
...
fi


В этом примере сначала проверяется a = b, если это пиздёж — дальше ничего не выполняется.

Если же a = b НЕ пиздёж — тогда проверяется c = d. И если c = d НЕ пиздёж, то выполняется, то что внутри then.

Проще по моему объяснить уже нереально. Сложно перепутать пизду с розеткой. Хотя… вечно путают.


Бест-практика! Зырь!

if [[ a = b && c = d ]]; then
...
fi


Делаем двойные скобки. Двойные скобки умеют работать с && внутри.

[[ ... ]] — это специальная команда Bash, которая используется для проверки условий. Она умнее и безопаснее, чем обычная [ ... ].

Внутри неё можно использовать логические операторы && (и) и || (или) без дополнительных скобок или дополнительных [.

Рассмотрим пример:

a="hello"
b="hello"
c="bashdays"
d="bashdays"

if [[ $a = $b && $c = $d ]]; then
echo "Условия совпали!"
fi


1. Проверяет $a = $b → "hello" = "hello"? → Да.

2. Дальше && говорит: Если первое условие — НЕ пиздёж, идём ко второму.

3. Проверяет $c = $d → "bashdays" = "bashdays"? → Да.

4. Оба условия верны → выполняется команда echo.


[ — работает с одним условием за раз

[[ — может работать с комбинированными условиями прямо внутри себя.

Для тупых Кто до сих пор не понял:

Представь, что ты говоришь своей бабушке: Если я убрал в комнате И сделал уроки, я могу поиграть в девопс-инженера.

Ну так вот:

[[ ... ]] — это бабушка, которая за всю свою жизнь повидала много дерьма и вполне может понять твою сложную фразу: убрал И сделал уроки.

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

«Сначала убрал?» — (ДА) Не пиздёж.

«Сделал уроки?» — (ДА) Не пиздеж.

«Ок, теперь я могу поиграть в девопс-инженера»


Вот и вся наука. Изучай!


🛠 #bash #badpractices #bestpractices

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
93