Проверка необходимых команд в bash и разговор об интерфейсах
Здравствуйте, я некомпетентен.
Я закончил свой последний рабочий день, получил шоколад от девушки с работы, меня угостили якинику незадолго до ухода, и я весело провел время, разговаривая с человеком, который 25 лет работал сетевым инженером, о его прошлом и текущем домашнем сетевом окружении, а также о моем собственном домашнем сервере.
Я думаю, что мне удалось сделать все, что было в моих силах, насколько это возможно для моего неопытного уровня, но поскольку я человек, который часто ошибается, мне очень жаль.
Я ежедневно убеждаюсь, что скрипты оболочки очень мощные.
Конечно, их низкая переносимость, например, невозможность функционального описания, как в csh, который сейчас является оболочкой по умолчанию в FreeBSD, неоспорима.
Однако я считаю, что на практике сила скриптов оболочки заключается в простоте работы с интерфейсами.
Интерфейсы C и скриптов оболочки
Например, предположим, что вы используете echo в скрипте оболочки следующим образом.
echo "test"
Это соответствует стандартному выводу в языке C.
#include <stdio.h>
int main() {
const char *message = "test";
// Вывод сообщения в стандартный вывод
fprintf(stdout, "%s\n", message);
return 0;
}
Если вы хотите вывести в стандартный вывод ошибок, вы можете перенаправить его следующим образом:
echo "testerr" 1>&2
А в случае языка C это соответствует следующему:
#include <stdio.h>
int main() {
const char *message = "testerr";
// Вывод сообщения в стандартный вывод ошибок
fprintf(stderr, "%s\n", message);
return 0;
}
Кроме того, возвращаемые значения при сбоях выполнения большинства команд GNU/Linux искусно спроектированы благодаря прошлым пионерам, что облегчает тестирование условий и делает их весьма эффективными при использовании в качестве скриптового языка.
Однако проблема в том, что это преимущество можно получить только в среде, где доступны скрипты оболочки.
Даже если написать аналогичный код на другом языке, его универсальность будет работать, но команды функционируют как своего рода библиотеки в языке, и я чувствую, что это одна из причин, по которой говорят о недостаточной универсальности.
Тем не менее, я считаю, что возможность так просто манипулировать файловыми дескрипторами очень мощна.
Для else достаточно соединить две пайпы.
※Подумал после написания, строго говоря, это не else ^^;
Это просто действует как else, когда возвращаемое значение не равно 0.
$ curl aa || echo "wtf"
curl: (6) Could not resolve host: aa
wtf
Как было бы здорово, если бы я знал о такой полезной вещи еще в средней школе.
Я бы хотел, чтобы любой сайт предоставлял больше возможностей для изучения скриптов оболочки.
На самом деле, если вспомнить сейчас, японская среда в GNU/Linux в то время была довольно сложной, и я помню, что предиктивный ввод японского языка был непрактичным. Возможно, нынешняя простота обусловлена распространением Chrome OS, благодаря которому расцвел OSS японский ввод.
В то время, когда я был в средней школе, я помню, как настраивал ThinkPad с Ubuntu для своего эксцентричного друга, который хотел его...
Итак, как проверить команды?
Я думал, нет ли более умного и читаемого способа написания проверки команд в скриптах Bash.
Тем не менее, передача элементов из массива в цикл for, что само по себе умно, конечно, удобна, но если посмотреть на описание при хранении в массиве...
#!/bin/bash
cmds=(aa bb cc)
for i in ${cmds[@]}; do
command -v $i 1> /dev/null || echo "Please install $i"
done
Мы сохраняем их в cmds как массив с помощью () и разворачиваем массив с помощью ${cmds[@]}.
Но если команд становится больше, и мы добавляем их в cmds=(), возникает вопрос, а нужно ли вообще писать их здесь?
В других языках информация о зависимостях содержится в таких файлах, как Cargo.toml, deno.json, go.mod, Gemfile, или в часто нелюбимом requirements.txt.
Было бы удобно иметь файл, где все это собрано таким образом.
Поэтому я сделал это так:
cat ./cmd | xargs which 1> /dev/null || echo "Please install cmd"
И как выглядит файл cmd?
$ cat cmd
ls
pwd
cargo
На самом деле, я хотел проверить с помощью команды command, но command работает как внутренняя команда оболочки и не может быть передана в xargs.
$ which command
which: no command in (/home/haturatu/.rbenv/bin:/home/haturatu/.rbenv/shims:/home/haturatu/.deno/bin:/home/haturatu/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/opt/android-sdk/platform-tools:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/lib/plan9/bin:/home/haturatu/.local/bin:/home/haturatu/.local/bin:/home/haturatu/Android/Sdk/cmdline-tools/tools/bin:/home/haturatu/Android/Sdk/platform-tools:/home/haturatu/Android/Sdk/emulator:/home/haturatu/Android/Sdk/tools:/home/haturatu/Android/Sdk/tools/bin:/home/haturatu/Android/Sdk/cmdline-tools/tools/bin:/home/haturatu/Android/Sdk/platform-tools:/home/haturatu/Android/Sdk/emulator:/home/haturatu/Android/Sdk/tools:/home/haturatu/Android/Sdk/tools/bin:/home/haturatu/.npm-global/bin:/home/haturatu/go/bin)
Извините, если ошибаюсь, но я думаю, что при передаче в xargs, он работает как отдельный процесс xargs, отличный от запущенного процесса bash, поэтому он не может быть распознан.
Поэтому я не могу передать команду, которая не прошла проверку which, в сообщение Please install cmd, но поскольку вывод ошибки which появляется до этого, я подумал, что это может быть излишним.
※Подумал после написания, а что если xargs /bin/bash command -v... это может сработать.
Есть еще одно преимущество хранения этой группы команд в отдельном файле.
Вы можете установить их все сразу из этого файла.
cat cmd | xargs -I {} sudo pacman -S --noconfirm {}
В случае apt это также возможно с опцией -y.
Итак, независимо от того, буду ли я использовать это на самом деле, я оставляю это как идею.
До свидания. Всего наилучшего. (Мне кажется, что я написал несколько статей с момента последней статьи в конце года, но это, наверное, просто кажется.)