es
Feedback
LinuxCamp | DevOps

LinuxCamp | DevOps

Ir al canal en Telegram

Обо мне: C/C++/Linux эксперт. Говорим про разработку, Linux, DevOps, сети и администрирование. Админ (реклама): @XoDefender Чат: @linuxcamp_chat Менеджер: @Spiral_Yuri Биржа: https://telega.in/c/linuxcamp_tg РКН: https://clck.ru/3RWA3C

Mostrar más

📈 Análisis del canal de Telegram LinuxCamp | DevOps

El canal LinuxCamp | DevOps (@linuxcamp_tg) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 13 932 suscriptores, ocupando la posición 9 149 en la categoría Tecnologías y Aplicaciones y el puesto 47 289 en la región Rusia.

📊 Métricas de audiencia y dinámica

Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 13 932 suscriptores.

Según los últimos datos del 08 julio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -100, y en las últimas 24 horas de -6, conservando un alto alcance.

  • Estado de verificación: No verificado
  • Tasa de interacción (ER): El promedio de interacción de la audiencia es 28.83%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 11.52% de reacciones respecto al total de suscriptores.
  • Alcance de las publicaciones: Cada publicación recibe en promedio 4 023 visualizaciones. En el primer día suele acumular 1 608 visualizaciones.
  • Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 23.
  • Intereses temáticos: El contenido se centra en temas clave como linuxcamp, ядро, linux, диск, docker.

📝 Descripción y política de contenido

El autor describe el recurso como un espacio para expresar opiniones subjetivas:
Обо мне: C/C++/Linux эксперт. Говорим про разработку, Linux, DevOps, сети и администрирование. Админ (реклама): @XoDefender Чат: @linuxcamp_chat Менеджер: @Spiral_Yuri Биржа: https://telega.in/c/linuxcamp_tg РКН: https://clck.ru/3RWA3C

Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 09 julio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.

13 932
Suscriptores
-624 horas
-297 días
-10030 días
Archivo de publicaciones
Команды управления заданиями В прошлый раз мы узнали о фоновом выполнении команд и дали определение термину "задание". Сегодн
Команды управления заданиями В прошлый раз мы узнали о фоновом выполнении команд и дали определение термину "задание". Сегодня рассмотрим, встроенные в оболочку средства, которые позволяют управлять заданиями: bg, fg, jobs, kill. Давайте для начала запустим в фоне несколько команд sleep, которые ничего не будет делать в течение заданного времени:

$ sleep 10 & sleep 20 & sleep 30 &
Чтобы выяснить, за какими заданиями следит оболочка, используется команда jobs:

$ jobs  
[1]  Running sleep 10 &  
[2]- Running sleep 20 &  
[3]+ Running sleep 30 &
[1] - номер задания, который используется для идентификации. Полезен, когда необходимо "ткнуть пальцем" в определенный элемент, используя: fg, bg, kill; "" (отсутствие знака) - обозначает то, что задание не является ни текущим ни предыдущим, но находится в списке. "+" указывает на текущее задание, по отношению к которому будут выполнены команды, если не указывать номер. "-" указывает на предыдущее задание; Running - статус задачи. Также возможны: Stopped, Done, Terminated, Killed. Статусы могут меняться в ответ на отправленный сигнал (SIGKILL, SIGSTOP и т.д.); Теперь выведем второе задание на передний план. Для этого будем использовать команду fg с указанием номера:

$ fg %2  
sleep 20
Если в параметры ничего не вносить, команда выполнится для последнего задания (со знаком +). Для того, чтобы перенести выполнение в фон и не блокировать терминал, используется bg. Для примера запустим задание переднего плана, остановим его выполнение "Ctrl + Z" и отправим его в фоновый режим:

$ nm-applet
^Z  
[1]+ Stopped nm-applet

$ bg  
[1]+ nm-applet & 

$ jobs  
[1]+ Running nm-applet
К целевому заданию также можно обратиться по номеру:

$ jobs  
[1]- Stopped sleep 200  
[2]+ Running sleep 100 &

$ bg %1  
[1]+ sleep 200 &
С заданиями "kill %n" можно использовать для принудительного завершения:

$ kill %1  
[1]+ Terminated sleep 200
По умолчанию отправляется сигнал SIGTERM, при желании, можно указать любой другой:

$ kill -9 %1
[1]+ Killed sleep 200
🐧 LinuxCamp

Введение в фоновые команды и задания Стандартное выполнение команды в оболочке происходит так, что вам требуется ожидать ее з
Введение в фоновые команды и задания Стандартное выполнение команды в оболочке происходит так, что вам требуется ожидать ее завершения для дальнейшего взаимодействия с командной строкой. Это обычно неэффективно, когда выполнение занимает время и вам не требуется общаться с утилитой через CLI либо отлаживать ее логи. Не беда, вы можете запускать команды особым образом, чтобы они исчезали из поля зрения, но продолжали выполняться, освобождая текущую оболочку. Этот метод называется фоновым выполнением команды (backgrounding). Если же команда занимает оболочку, ее называют командой переднего плана (foreground). Запуск команды в фоновом режиме Чтобы запуститься в фоновом режиме, просто добавьте амперсанд "&" после команды:

$ wc -c file.txt & 
[1] 74931
$
[1] - номер, под которым закрепилось задание (job); 74931 - PID процесса; Если команда завершится успешно, оболочка выведет сообщение:

[1]+  Done                    wc -c file.txt
Если где-то произошла ошибка и команда завершилась аварийно, вы увидите сообщение о выходе с кодом возврата:

[1]+ Exit 1 wc -c file.txt
Амперсанд "&" является оператором списка - вы можете вывести в фон сразу ряд команд:

$ nm-applet & wc -c file.txt &
[1] 28839
[2] 28840
Чем является задание? Вы можете подумать, что под заданием подразумевается 1 процесс - немного не так. Задание — это единица работы оболочки. Простые команды, конвейеры и условные списки — примеры заданий, которые можно запускать из командной строки. Задание — это больше, чем просто процесс Linux. Задание может состоять из одного или нескольких процессов:

[4]+  Running  nm-applet && wc -c file.txt &
Например, конвейер из шести программ представляет собой одно задание, включающее 6 процессов:

$ cat file.txt | grep "word" | sort | uniq | tr 'a-z' 'A-Z' | wc -l > result.txt &

[5]+  Running  cat file.txt | grep --color=auto "word" ... &
В оболочке может быть запущено одновременно несколько заданий, каждый экземпляр имеет свой ID. 🐧 LinuxCamp

Я долго не мог найти, где собираются топовые IT-спецы и делятся своим опытом Спасибо коллеге, что подсказал канал IT-мероприя
Я долго не мог найти, где собираются топовые IT-спецы и делятся своим опытом Спасибо коллеге, что подсказал канал IT-мероприятия России. Теперь знаю, как провести свободное время и найти комьюнити по интересам! Это канал для тех, кто хочет не только просиживать за работой, в свободное время залипая на Ютубе. Там собрано огромное количество самых интересных событий в сфере IT для тех, кто реально хочет развиваться и стать лучшим. 📍 Анонсы форумов, конференций, митапов, вебинаров, хакатонов, олимпиад - рекомендую подписаться сразу, чтобы не потерять! IT-мероприятия России / ITMeeting / IT events

Подстановка вывода команды: $() Оператор, который мы сегодня рассмотрим является супер полезной фичей оболочки. Мы буквально
Подстановка вывода команды: $() Оператор, который мы сегодня рассмотрим является супер полезной фичей оболочки. Мы буквально можем подставить вывод команды либо целой цепочки посреди выражения. Предположим, у вас есть несколько тысяч текстовых файлов c песнями. Каждый файл содержит название песни, ее текст и имя исполнителя:
Название: Зеркала
Исполнитель: Лепс
Острые углы, нервы, суета
Стоит задача распределить файлы в подкаталоги по исполнителям. Ну, для начала, можно найти все файлы песен исполнителя с помощью grep:

$ grep -l "Лепс" *.txt
song1.txt
song2.txt
Затем переместить каждый файл в необходимый каталог:

$ mkdir leps
$ mv song1.txt leps
$ mv song2.txt leps
Чет неудобно, так? Было бы неплохо сказать оболочке: «Перемести все файлы, содержащие строку Лепс, в каталог leps». Для этого нужно забрать то, что нам выдала команда "grep -l" и передать результат в качестве списка аргументов для mv:

$ mv $(grep -l "Лепс" *.txt) leps
Синтаксис "$(команда)" выполняет выражение в круглых скобках и заменяет вставку его выводом. Таким образом, "grep -l" подменяется, подходящими под условие именнами файлов. Результат использования $() в примере выше аналогичен:

$ mv song1.txt song2.txt leps
В сценариях оболочки подстановка бывает полезна для сохранения вывода команды в переменной:
переменная=$(команда)
Например, напишем простенький скрипт, который поможет нам узнать, содержит ли текущая директория файлы:

status=$(ls ./ 2>/dev/null | wc -l)

if [ "$status" -gt 0 ]; then
    echo "В директории есть $status файлов"
else
    echo "Директория пуста или не существует"
fi
Выдаем права на выполнение, запускаем и проверяем:

$ chmod +x script.sh
$ ./script.sh
В директории есть 66 файлов
Для чего бывает полезно брать "$()" в кавычки? Есть несколько основных кейсов, когда следует использовать "$()" вместо $(). Во-первых, если вывод команды содержит пробелы, он будет разбит на отдельные слова. Представим, что стоит задача определить для текущей директории специальные права. Есть момент - имя каталога состоит из нескольких слов, разделенных пробелами "photo and video":

$ pwd
$ /home/xoadmin/photo and video
$ chmod 777 $(pwd)
После подстановки оболочка интерпретирует это как попытку применить chmod к трём разным объектам: "/home/xoadmin/photo", "and", и "video". Это, вероятно, приведёт к ошибке:

chmod: cannot access '/home/xoadmin/photo': No such file or directory

chmod: cannot access 'and': No such file or directory

chmod: cannot access 'video': No such file or directory
Чтобы вывод pwd воспринимался, как единое целое, следует заключить оператор в кавычки:

$ chmod 777 "$(pwd)"
Во-вторых, без кавычек, символы перевода строки могут быть удалены при выводе через echo:

$ echo $(ls)
adduser.conf alsa alternatives apache2 apg.conf apparmor
Если требуется сохранить четкую структуру текста, содержащего спец символы, следует использовать "$()":

$ echo "$(ls)"
adduser.conf
alsa
alternatives
🐧 LinuxCamp

Что за праздник на пороге?) Новый год 🔴 Ну вот, пишу завершающий пост в 2024 году, давайте подведем небольшие итоги и чуть побеседуем. Много было проделано работы, мы набрали больше 7к человек, среди которых как устоявшиеся спецы, так и новички, которым только предстоит окунуться в мир разработки и администрирования. Представляете, ! Я, честно говоря, думал, и до 5 с трудом доковыляем. Но как-то все пошло-поехало и получилось зафиксировать хороший результат. Очень рад, что в этом году все-таки получилось начать движение в сторону публичной деятельности. Мы держим в уме - все только начинается! Хочу каждому выразить огромную благодарность за то, что остаетесь на связи, читаете и комментируете публикации. Радует, что держатся и олды, которые на канале с самого его зачатия. Они помнят те бесконечные посты про динамические библиотеки)) Вам хочу пожелать крепкого здоровья, профессиональной реализации, огня в глазах, успехов в достижении поставленных целей и положительных перемен. Все у вас получится! Также искренне желаю, чтобы вас окружали правильные люди, которые будут помогать держать верный курс. Наше окружение оказывает значительное влияние на то, кто мы есть и кем будем. Работайте над ним, иногда рефлексируйте и, при необходимости, вносите коррективы) На этом прощаюсь, с наступающим праздником!

Выполняй только то, что нужно! Условные списки Bash Скорее всего, вы каждый день запускаете команды, которые зависят от выпо
Выполняй только то, что нужно! Условные списки Bash Скорее всего, вы каждый день запускаете команды, которые зависят от выполнения предыдущих. Механизм списков позволяет не только разом выполнить группу команд, но и выстроить условную последовательность через операторы "&& (И)" и "|| (ИЛИ)". Условные списки Предположим, вы хотите создать файл new.txt в каталоге dir. Типичная последовательность может быть следующей:

$ cd dir
$ touch new.txt
Запуск второй команды зависит от успешного выполнения первой. Если каталог не существует, то нет смысла и команду запускать. Оболочка позволяет сделать эту зависимость явной, объединив команды оператором && (И):

$ cd dir && touch new.txt
Если вы используете систему контроля версий Git, то, вероятно, знакомы со следующей последовательностью команд фиксации и внесения изменений:

$ git add . && git commit -m"fixed a bug" && git push
Если какая-либо из команд завершится ошибкой, остальные вы не запустите. Поэтому эта цепочка хорошо работает в формате условного списка. Так же, как оператор && запускает вторую команду только в случае успеха первой, оператор || (ИЛИ) запускает вторую команду только в случае сбоя первой. Например, следующая команда пытается войти в каталог и, если это не удается, создает его:

$ cd dir || mkdir dir
Вы часто будете видеть этот оператор в сценариях оболочки. В частности, для выхода в случае возникновения ошибки:

cd dir || exit 1
Операторы && и || можно использовать вместе, чтобы настраивать более сложные действия:

$ cd dir || mkdir dir && cd dir || echo "I failed"
Последовательность предполагает попытку войти в каталог dir, если это не удается, создать каталог и войти в него, а если и это не удается, вывести ошибку. Безусловные списки Команды в списке не обязательно должны зависеть друг от друга. Если вы разделяете команды точкой с запятой, они просто выполняются по порядку. Вот пример команды, которая спит течение двух часов, а затем создает резервную копию важных файлов:

$ sleep 7200; cp -a ~/files /mnt/backup_drive
Безусловные списки дают те же результаты, что и ввод команд по отдельности. Единственное существенное различие - коды возврата. В таком списке коды отдельных команд отбрасываются, кроме последней. Только код возврата последней команды в списке присваивается переменной оболочки "?":

$ mv file1 file2; mv file2 file3; mv file3 file4

# Код возврата команды «mv file3 file4»
$ echo $?
0
LinuxCamp 🐧

Вычисление переменных и развеивание заблуждений Мы с вами уже как-то говорили про суть переменных оболочки и окружения. Сегод
Вычисление переменных и развеивание заблуждений Мы с вами уже как-то говорили про суть переменных оболочки и окружения. Сегодня рассмотрим оператор $, который позволяет определить значение переменной. Когда командная оболочка вычисляет переменную, определяет ее значение и подставляет его вместо имени. Чтобы выполнить задачу, нужно просто поставить знак $ перед именем. Самый простой способ увидеть, как оболочка обрабатывает символ — запустить команду echo, которая выводит свои аргументы (после того, как оболочка завершит их вычисление):

$ echo My name is $USER and my files are in $HOME

My name is xoadmin and my files are in /home/xoadmin

$ echo ch*ter9
chapter9
Когда вы выводите на экран значение переменной с помощью команды echo, вы можете подумать, что сама команда проверяет переменную и выводит ее значение. На самом деле это не так, команда ничего не знает о переменных. Она просто выводит на экран любые аргументы, которые вы ей передаете. Значения для HOME и USER вычисляет оболочка перед запуском команды. Вы можете определить или изменить переменную в любое время, используя следующий синтаксис:

name=value
Например, если вы часто работаете с каталогом "Projects" внутри домашней директории, вы можете присвоить его имя переменной:

$ work=$HOME/Projects
И использовать его как удобное сокращение при работе с cd:

$ cd $work
$ pwd 
/home/xoadmin/Projects
Вы можете передавать $work любой команде, ожидающей имя каталога, в качестве аргумента:

$ cp myfile $work 
$ ls $work
Myfile
Вообще, этот принцип важно понять: оболочка вычисляет переменные, шаблоны и другие конструкции перед выполнением команды. Шаблоны vs переменные Предположим, вы хотите перенести ряд файлов с расширением ".txt" из одного каталога в другой. Вот два способа сделать это, но один работает, а другой нет:

# Метод 1
$ mv dir1/*.txt dir2 

# Метод 2
$ FILES="file1.txt file1.txt"
$ mv dir1/$FILES dir2
Метод 1 работает, потому что шаблон соответствует всему пути к файлу — имя каталога dir1 является частью совпадений:

$ echo dir1/*.txt
dir1/file1.txt dir1/file2.txt
Таким образом, метод 1 работает так, как если бы вы набрали следующую команду:

$ mv dir1/file1.txt dir1/file2.txt dir2
В методе 2 используются переменные, имеющие только свои буквальные значения, и нет специального инструмента для вычисления путей к файлам:

$ echo dir1/$FILES
dir1/file1.txt file2.txt
Следовательно, метод 2 работает так, как если бы вы набрали следующую команду:

$ mv dir1/file1.txt file2.txt dir2

/bin/mv: cannot stat 'file2.txt': No such file or directory
Linux++ | IT-Образование

Набираем обороты с pattern matching Чтобы повысить продуктивность, оболочка предусматривает ряд спецсимволов для оптимизации
Набираем обороты с pattern matching Чтобы повысить продуктивность, оболочка предусматривает ряд спецсимволов для оптимизации работы и выполнения ряда задач. Концепция работы с символами: *, ?, [], называется сопоставлением с шаблоном (pattern matching).
Если вы все еще путаетесь в том, что такое оболочка, предлагаю обратиться к моей последней сводке постов.
И так, команда может включать в себя символ групповых операций * для одновременной ссылки на несколько файлов:

$ ls *.py
data.py main.py user_interface.py
Этот символ обрабатывается командной оболочкой, а не программой ls. Оболочка заменяет выражение "*.py" списком имен подходящих файлов еще до запуска программы ls. Иными словами, ls не увидит символ групповой операции. С точки зрения утилиты, вы ввели команду следующего вида:

$ ls data.py main.py user_interface.py
Символ * соответствует любой последовательности из символов в путях к файлам или каталогам. Например, гораздо проще и быстрее, вместо прямого перечисления:

$ grep Linux chapter1 chapter2 chapter3 chapter4 chapter5...
Использовать базовый шаблон:

$ grep Linux chapter*
Остается за кадром, как командная оболочка (не программа grep) преобразует шаблон "chapter*" в список подходящих имен файлов. И только после этого оболочка запускает grep. Если шаблон не соответствует ни одному файлу, оболочка передает его в качестве аргумента команды:

$ grep Linux chapter* 
grep: chapter*: No such file or directory
Еще бывает полезно использовать знак ?, который соответствует любому единичному символу. Например, вы можете выполнить поиск слова Linux в главах с 1 по 9, используя ? для поиска совпадений:

$ grep Linux chapter?
Для поиска в главах с 10 по 99 придется использовать "??":

$ grep Linux chapter??
Менее известно использование скобок [] для запроса у оболочки соответствия одному из символов набора. Например, вы можете искать только в первых 3 главах:

$ grep Linux chapter[123]
$ grep Linux chapter[1-3]
Любые символы, не только цифры, могут быть помещены в квадратные скобки для сопоставления. Например, следующая команда заставит оболочку искать имена файлов, начинающиеся с заглавной буквы, содержащие символ _ и заканчивающиеся символом @:

$ ls [A-Z]*_*@
Linux++ | IT-Образование

Операторы перенаправления ввода-вывода [3] Я же уверен, что 80% из вас когда-то использовали оператор "|". Он невероятно поле
Операторы перенаправления ввода-вывода [3] Я же уверен, что 80% из вас когда-то использовали оператор "|". Он невероятно полезен - позволяет подключить стандартный вывод (STDOUT) одной команды к стандартному вводу (STDIN) другой, чтобы первая смогла передать свои выходные данные во вторую. Вертикальная черта "|" между командами — это символ канала (pipe), который реализовывает механизм конвейера (pipeline). Посмотрим на самый узнаваемый сценарий работы конвейера - отфильтровать вывод по шаблону:

$ ls /usr/lib | grep nma 
libnma.so.0 
libnma.so.0.0.0
В результате мы видим не все содержимое директории, а только то, с чем grep нашел пересечение по шаблону. Команды обычно и не знают, что являются частью конвейера: ls считает, что выводит данные на дисплей, хотя на самом деле ее вывод был перенаправлен на grep, который верит, что читает данные с клавиатуры, когда на самом деле получает вывод ls:
ls (STDOUT) -> grep (STDIN) -> grep (STDOUT)
Тут уже оболочка, на своем уровне, выполняет всю магию и использует системный вызов "pipe()" для перенаправления:

#include <unistd.h>

int pipe(int pipefd[2]);
Важно понимать, что любая утилита, которая читает стандартный ввод или записывает вывод, может участвовать в создании конвейера и быть полезна для решения комплексной задачи. Допустим, мы хотим узнать, сколько подкаталогов находится в "/usr/lib". Нет простой команды для получения ответа, поэтому создадим конвейер. Начнем с простого вывода содержимого директории. Обратите внимание, что команда "ls –l" помечает каталоги буквой "d" в начале строки:

$ ls -l /usr/lib
drwxrwxr-x ... 4kstogram
drwxr-xr-x ... NetworkManager
Используем cut, чтобы вывести первый столбец:

$ ls -l /usr/lib | cut -c1
d 
d
d
-
-
-
Затем используем grep, чтобы оставить только строки, содержащие букву "d":

$ ls -l /usr/lib | cut -c1 | grep d
d
d
d
Наконец, подсчитаем строки с помощью команды wc и получим ответ, созданный конвейером из четырех команд:

$ ls -l /usr/lib | cut -c1 | grep d | wc -l
145
Результат: директория "/usr/lib" содержит 145 подкаталогов. Что мы сделали? Превратили небольшую горстку команд в набор комбинируемых инструментов. Как говорится, целое всегда есть нечто большее, чем сумма его частей. Linux++ | IT-Образование

Операторы перенаправления ввода-вывода [2] В некоторых случаях полезно перенаправить стандартный ввод STDIN, чтобы он поступа
Операторы перенаправления ввода-вывода [2] В некоторых случаях полезно перенаправить стандартный ввод STDIN, чтобы он поступал из файла, а не с клавиатуры. Выполнить данную задачу мы можем, используя оператор '<'. Например, команда tr принимает данные от пользователя (через клавиатуру), чтобы выполнить преобразование всех символов нижнего регистра в верхний:

$ tr 'a-z' 'A-Z'
hello
HELLO
Команда не получает файл в качестве аргумента. Чтобы все-таки скормить ей статический поток данных, можно использовать то самое перенаправление STDIN:

$ cat animals.txt
1 retriever
2 badger

$ tr 'a-z' 'A-Z' < animals.txt
1 RETRIEVER
2 BADGER
Оператор '<<' heredoc Оператор '<<' дает возможность перенаправлять многострочный поток данных (массив строк) на вход программе:

$ cat << EOF
> Сurrent user: $(whoami)
> Status: sysadmin
> EOF
Сurrent user: parallels
Status: sysadmin
Может быть удобно, если нужно передать текстовые данные прямо в команду без использования файлов - допустим, в скриптах оболочки для задач автоматизации. Как работает "heredoc" После оператора указывается метка (произвольное слово), которая обозначает начало и конец текста. Все строки между метками считаются входными данными для команды. Когда оболочка повторно встречает слово, которое шло сразу за '<<', ввод завершается:

$ cat << EOF > config.conf 
[settings] 
host=localhost 
port=8080 
debug=true 
EOF
В примере выше мы отправили команде cat на вход (STDIN) массив строк, вывод (STDOUT) которых перенаправили в конфиг. Еще одним полезным примером может быть передача SQL-запросов в БД через ручной ввод:

$ mysql -u root -p << EOF
CREATE DATABASE my_database;
USE my_database;
...
EOF
Linux++ | IT-Образование

Операторы перенаправления ввода-вывода [1] Недавно мы вот узнали, через какие потоки процесс пишет и читает данные. Теперь по
Операторы перенаправления ввода-вывода [1] Недавно мы вот узнали, через какие потоки процесс пишет и читает данные. Теперь пора глянуть на операторы оболочки, которые позволяют перенаправлять потоки на разные источники данных. Например, вывод одной команды подать на вход другой либо записать stdout, stderr в файл. Сделаем серию из 3 постов. Сегодня рассмотрим операторы для перенаправления вывода STDOUT и STDERR: >, >>. В следующие разы поговорим про STDIN и завершим тему оператором '|'. Перенаправление потоков вывода: Предположим, вы хотите создать файл, в который будет записан вывод STDOUT какой-то программы (разберем на примере date). Для того, чтобы данные оказались в файле, нужно добавить символ '>' после команды и перед именем целевого файла:

$ date > date.txt
$ cat date.txt 
Sat Dec 14 20:50:46 MSK 2024
Перенаправление вывода с использованием '>' создаст новый файл или полностью перезапишет содержимое существующего. Использование '>>' позволяет также создать новый файл при отсутствии и добавить данные в его конец - без полной перезаписи:

$ date >> date.txt 
$ cat date.txt 
Sat Dec 14 20:50:46 MSK 2024 
Sat Dec 14 20:51:40 MSK 2024
Подчеркну, что перенаправляется только STDOUT. Если программа писала ошибки в поток STDERR, они так и выведутся в терминал:

$ find /etc -type f > ~/results.txt 
find: ‘/etc/ssl/private’: Permission denied
Давайте дополнительно обработаем и STDERR - для этого нам явно нужно указать номер целевого дескриптора "2>":

$ find /etc -type f > ~/results.txt 2> ~/errors.txt

$ cat ~/errors.txt 
find: ‘/etc/ssl/private’: Permission denied 
Также бывает полезно подчистить вывод какой-нибудь команды от всех ошибок. Для этого нам нужно перенаправить STDERR в "/dev/null":

$ find /etc -type f 2> /dev/null
Все, что попадает в "/dev/null" исчезает на веки вечные. Такой себе "black hole". И, наконец, если нужно, чтобы всё попало в один файл, можно перенаправить оба потока в одно и то же место:

$ ls > /tmp/lsdata 2>&1
$ ls &> /tmp/lsdata
Выражение "2>&1" означает «отправлять stderr туда, куда направляется stdout». Linux++ | IT-Образование

Для чего учишь Linux? Пост нацелен на то, чтобы понять мотивацию аудитории и проработать контент.
Anonymous voting

⚡️Истовый инженер теперь в Telegram! В канале публикуют полезные статьи для инженеров и программистов со всех направлений. Во
⚡️Истовый инженер теперь в Telegram! В канале публикуют полезные статьи для инженеров и программистов со всех направлений. Вот, что мы лично прочитали и где поставили лайк: — Что изучить про алгоритмы и структуры данных разработчикам на С++ ( читать ) — CodeChecker: анализируем большой проект на С++ быстро, эффективно и бесплатно ( читать ) — 5 способов писать эффективный код на Go: от нейминга переменных до архитектуры ( читать ) — Ужасно подробные ошибки в API: пишем инструмент для работы с ними на Go ( читать ) 📝 и ещё 100+ полезных статей, лекций и кейсов от практиков Подписываемся, чтобы не потерять полезный канал @ultimate_engineer

Зачем процессы туда данные отправляют? Этот пост является неким анонсом публикации про перенаправление ввода-вывода: >, >>, |
Зачем процессы туда данные отправляют? Этот пост является неким анонсом публикации про перенаправление ввода-вывода: >, >>, |. Чтобы понять, как оно работает, следует сначала разобрать потоки данных, в которые перенаправление и происходит. И так, у каждой программы существует 3 системных потока: stdout, stderr, stdin. Потоки представляют собой сущности для транспорта информации - для простоты пока можно считать их файлами под дескрипторами 0 (in), 1 (out), 2 (err):

$ cd /proc/551981/fd
$ ls
0  1  103  2  43
В примере выше мы зашли в виртуальную директорию процесса 551981 (PID), в которой хранятся дескрипторы открытых файлов. По ним, как раз, происходит чтение и запись информации. STDIN Данный поток используется процессом для получения информации извне. Когда мы запрашиваем какой-то ввод от пользователя, данные попадают в stdin, после чего считываются из него программой. Давайте запустим команду cat:

$ cat
Hello bro
Hello bro
В примере выше cat ожидает от пользователя ввод, который впоследствии считывается с потока stdin (по 0 дескриптору) и направляется в поток stdout для вывода в терминал. Как результат, мы видим дубликат нашего текста. Никто нам также не запрещает закрыть дескриптор, отвечающий за stdin - в таком случае программа просто не сможет получить данные. STDOUT Поток stdout (дескриптор 1) отвечает за вывод информации программой. По умолчанию все, что в него попадает, выводится в терминал:

$ echo $USER 
parallels
STDERR Еще один поток вывода информации (дескриптор 2), который отвечает за отображение ошибок. Если программа не смогла сделать все как надо — она пишет именно в него. Например, когда rm пытается удалить несуществующий файл:

$ rm example.txt
rm: example.txt: No such file or directory
Если вы вдруг не различили stdout от stderr либо хотите убедиться в том, что программа завершилась с ошибкой, можете воспользоваться следующей командой для получения кода возврата:

$ rm example.txt
rm: example.txt: No such file or directory

$ echo $?
1
Значение 1 говорит о том, что в программе были ошибки, 0 - все хорошо. Такой прием бывает полезен для написания скриптов автоматизации - нам иногда следует понимать, нормально ли прога отработала. Linux++ | IT-Образование

Функции для работы с набором сигналов Многие системные вызовы, связанные с сигналами, работают с типом данных sigset_t, который представляет их набор. Например, sigaction() и sigprocmask() позволяют программе указать группу сигналов, которые должны быть заблокированы процессом, sigpending() возвращает группу, находящуюся в режиме ожидания. В общем-то, работать с сетом напрямую - дикость, поэтому сейчас рассмотрим несколько фунций, которые для этого используются. Функция sigemptyset() инициализирует пустой набор сигналов, sigfillset(), напротив, записывает в него все сигналы:

#include <signal.h>

// Возвращают 0 при успешном завершении, –1 при ошибке
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t, *set);
Для инициализации набора сигналов должна быть использована одна из функций выше. Это необходимо потому, что C за вас ничего не определит. Если проигнорировать данный шаг, можно спокойно аварийно положить приложение, т.к. сет заполнится мусором. После инициализации отдельные сигналы могут быть добавлены в набор с помощью функции sigaddset() и удалены — с помощью sigdelset():

#include <signal.h>
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
Функция sigismember() проверяет, является ли сигнал членом набора:

#include <signal.h>
int sigismember(sigset_t *set, int sig);
В GNU-библиотеке C реализованы три нестандартные функции, которые дополняют те, что были описаны выше:

#define _GNU_SOURCE
#include <signal.h>

int sigandset(sigset_t *set, sigset_t *left, sigset_t *right);
int sigorset(sigset_t *set, sigset_t *left, sigset_t *right);
int sigisemptyset(const sigset_t *set);
1) sigandset() — выполняет логическое И (пересечение) над двумя наборами left и right, помещает результат в dest; 2) sigorset() — выполняет логическое ИЛИ (объединение) над двумя наборами left и right, помещает результат в dest; 3) sigisemptyset() — возвращает 1, если в set нет ни одного сигнала, 0 в противном случае; Linux++ | IT-Образование

Навыки DevOps - тренд ценного разработчика! Так уж сложилось, что часто приходится общаться с разработчиками высокого грейда из разных БигТех компаний. Причем специализация у каждого своя: фронт, бэк, мобилки, безопасность. По личной насмотренности, могу сказать, что каждый 2 имел дело с такими задачами, для которых требовалось понимание DevOps и знание технологий: CI/CD, Ansible, Docker, Kubernetes и т.д. Для тех, кто хочет повысить свои технические навыки и вырасти профессионально, предлагаю курс "DevOps для эксплуатации и разработки" от Яндекс Практикума! Курс предназначен для IT-специалистов с опытом работы, которые хотят делать релизы — быстрыми и безопасными, инфраструктуру — настраиваемой и стабильной, а взаимодействие команд — слаженным и эффективным. По ходу дела вы системно погрузитесь в методологию DevOps и научитесь применять ее для усовершенствования процессов конкретной компании. Если карьерный рост вам близок, не теряем ресурс и детально ознакамливаемся. Реклама, АНО ДПО “Образовательные технологии Яндекса”, ИНН 7704282033, 119021, г. Москва, ул. Тимура Фрунзе, д. 11, корпус 2, erid: 2SDnjdFvFFy

Перехватывай сигналы, как профи: sigaction() Сегодня поговорим про вызов, который активно рекомендуется к использованию в качестве альтернативы примитивному signal(). Несмотря на то, что sigaction() сложнее в использовании, взамен он предоставляет большую гибкость:

#include <signal.h>

// Возвращает 0 при успешном завершении, –1 при ошибке
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
Аргумент sig означает сигнал, который мы хотим перехватить. Значением может быть любой экземпляр, кроме SIGKILL или SIGSTOP. Аргумент act — указатель на структуру, определяющую условия перехвата. Если нам необходимо получить актуальное значения, без изменений, можно указать NULL. Аргумент oldact — это указатель на структуру такого же типа. Используется для возврата информации о предыдущих правилах перехвата. Если нам это неинтересно, можно также задать значение NULL. Структура, на которую указывают аргументы act и oldact, выглядит следующим образом:

struct sigaction 
{
/* Адрес обработчика */
void (*sa_handler)(int); 

/* Адрес расширенного обработчика */
void (*sa_sigaction)(int, siginfo_t *, void *);

/* Сигналы, блокируемые во время вызова обработчика */
sigset_t sa_mask;

/* Флаги, контролирующие активацию обработчика */
int sa_flags;

/*  Устарел и не должен использоваться */
void (*sa_restorer)(void); 
};
Поле sa_handler соотносится с аргументом handler, передаваемым функции signal(). В данном поле указывается либо адрес обработчика либо одна из констант — SIG_IGN (сигнал будет проигнорирован) или SIG_DFL (процесс ответит дефолтным поведением на сигнал). Поле sa_mask определяет список сигналов, блокируемых во время активации обработчика. После старта его выполнения, все сигналы из набора автоматически добавляются в сигнальную маску. После того, как произойдет возврат из обработчика, они автоматически удалятся. Сделано это для того, чтобы никакой из сигналов не прервал выполнение обработчика. Поле sa_flags — битовая маска, устанавливающая разные параметры, контролирующие обработку сигнала. Биты могут быть объединены в этом поле битовой операцией ИЛИ (|). Более детально ознакомиться с флагами можете в мануале. Простой пример использования вызова sigaction():

#include <signal.h>
#include <stdio.h>

void handle_signal(int sig) {
    printf("Caught signal %d\n", sig);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = handle_signal;
    sa.sa_flags = 0;            
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }

    while (1);
}
И да, как видите, я проинициализировал почти все поля структуры. Если вы работаете на C, не забывайте, что это не C++ и тут нет конструкторов, деструкторов, дефолтной инициализации. Если явно не указать значения переменным, они могут спокойно заполниться мусором, что приведет к UB (undefined behavior). Где sigaction() прям 100% полезен? Мы с вами еще не говорили подробно о сигналах реального времени, но при работе с ними вам прям станет ясна необходимость sigaction(). Дело в том, что через настройку перехватчика мы cможем принимать дополнительные аргументы - пользовательские данные. Использовать для этого нам нужно флаг SA_SIGINFO:

sa.sa_flags = SA_SIGINFO;
Если флаг указан, обработчик должен присваиваться полю sa_sigaction и принимать расширенный интерфейс для пользовательских данных:

void handler(int sig, siginfo_t *info, void *context);
sa.sa_sigaction = handler;
Linux++ | IT-Образование

Работа с ожидающими сигналами Друзья, еще недолго осталось ворочать сигналы, но нужно дожать) Сегодня обсудим то, как можно проработать сигналы, которые заблокированы маской, но направлены процессу. Если процесс получает сигнал, который в данный момент подлежит блокированию - входит в сигнальную маску, то он добавляется в набор ожидающих сигналов. Когда сигнал будет разблокирован, он 100% будет доставлен в процесс. Для определения того, какие сигналы процесса находятся в режиме ожидания и пытаются пробиться, мы можем использовать sigpending():

#include <signal.h>
int sigpending(sigset_t *set);
Функция возвращает в set набор сигналов, которые находятся в режиме ожидания процесса. После этого мы сможем проверить содержимое set с помощью функции sigismember(), которая даст нам понимание того, входит ли указанный сигнал в структурку:

#include <signal.h>
int sigismember(sigset_t *set, int sig);
Функция возвращает 1, если sig является членом set, 0, если не является, и –1 при ошибке (например, sig не является допустимым номером сигнала):

sigset_t pending;
sigpending(&pending);

if (sigismember(&pending, SIGUSR1)) {
    printf("SIGUSR1 заблокирован\n");
}
Сигналы не ставятся в очередь Набор ожидающих сигналов — это лишь маска, она показывает факт возникновения того или иного сигнала, но не их количество. Иначе говоря, если один и тот же сигнал был сгенерирован несколько раз (будучи заблокированным), то он записывается в набор, а затем доставляется, но лишь ОДНАЖДЫ. Linux++ | IT-Образование

Защита от незваных гостей: сигнальная маска Для каждого процесса ядро хранит специальную маску, которая определяет те сигналы, которые будут временно заблокированы и необработаны. Если в процесс отправляется заблокированный сигнал, то доставка этого сигнала откладывается до тех пор, пока он не будет разблокирован путем удаления из сигнальной маски. Как же нам определить эту чудо-маску и замутить пару сигналов?) Использовать системный вызов sigprocmask():

#include <signal.h>
int sigprogmask(int how, const sigset_t *set, sigset_t *oldset);
Аргумент how определяет то, каким образом набор сигналов, передаваемый в set, изменит текущую маску процесса: 1. SIG_BLOCK — сигналы, включенные set, добавляются в сигнальную маску. Другими словами, блокируемые сигналы остаются таковыми, а новые добавляются к ним; 2. SIG_UNBLOCK — сигналы, включенные set, исключаются из сигнальной маски; 3. SIG_SETMASK — сигналы, включенные set, переопределяют текущую маску. Сигналы, не включённые в новое множество, больше не блокируются; Если аргумент oldset не равен NULL, то он указывает на переменную типа sigset_t, в которую записывается текущее значение сигнальной маски (до изменения). Если мы хотим через oldset получить сигнальную маску без каких-то изменений, можно установить значение NULL для аргумента set, в этом случае аргумент how будет проигнорирован. Ну все, давайте, ближе к делу, ближе к коду. Сейчас используем вообще все наши знания по сигналам: напишем перехватчик, отправим сигнал самому себе, определим маску и посмотрим на результат:

#include <signal.h>
#include <unistd.h>

void handler (int num) {
 write(STDOUT_FILENO, "I won't die!\n", 13);
}

int main() {
    signal(SIGINT, handler);

    sigset_t blockSet, prevMask;

    sigemptyset(&blockSet); 
    sigaddset(&blockSet, SIGINT);
    sigprocmask(SIG_BLOCK, &blockSet, &prevMask);

    raise(SIGINT);

    return 0;
}
Теперь скомпилим и запустим прогу:

$ gcc -o prog main.c
$ ./prog
И что... Бам, никакого вывода мы не увидим - маска успешно отработала. А теперь го еще прикол. Помните, ранее речь заходила о том, что отправленные сигналы не отменяются, а откладываются - как только мы уберем его из маски, процесс его примет и обработает. Проверить мы это сможем, если добавим следующий код после вызова "raise(SIGINT);":

sleep(5);
sigprocmask(SIG_SETMASK, &prevMask, NULL);
Ждем 5 сек и наблюдаем лог:

$ ./prog
I won't die!
Теперь пару слов про сигналы: SIGKILL и SIGSTOP. Их мы игнорировать не можем, УВЫ и АХ. Если мы попытаемся заблокировать эти сигналы, функция sigprocmask() ошибку не выдаст, но и желаемого результата мы также не получим. Для того чтобы добавить все сигналы в маску, кроме SIGKILL и SIGSTOP, мы можем использовать следующий вызов:

sigfillset(&blockSet);
Linux++ | IT-Образование

Отправка сигнала самому себе: raise() Иногда полезной практикой является отправка процессом сигнала самому себе. Эту задачу выполняет функция стандартной библиотеки (libc) raise():

#include <signal.h>
int raise(int sig);
В программе с одним потоком, вызов raise() аналогичен:

kill(getpid(), sig);
В приложениях с несколькими потоками, вызов raise() аналогичен:

pthread_kill(pthread_self(), sig);
Такая реализация означает, что сигнал будет доставлен ТОЛЬКО в тот поток, из которого был выполнен raise(). Вызов kill(getpid(), sig) посылает сигнал в вызыва­ющий процесс => сигнал может быть доставлен в любой его поток. Приведу небольшой пример для демонстрации работы функции (вызов signal() и механизм перехвата объясняются вот тут):

#include <signal.h>
#include <stdio.h>

void handler(int sig) {
   printf("Signal received : %d\n", sig);
}

int main() {
   signal(SIGILL, handler);
   printf("Signal sent : %d\n", SIGILL);
   raise(SIGILL);
   return 0;
}
Выполним программу и получим:

$ ./prog
Signal sent : 4
Signal received : 4
Возвращаемое значение Обратите внимание, что функция raise() возвращает ненулевой результат (не обязательно –1) при возникновении ошибки. Единственная проблема, которая может тут возникнуть — EINVAL (неверное значение sig). Linux++ | IT-Образование