Салют! В этой небольшой статье я попытался собрать необычные и малоизвестные трюки с перенаправлениями в bash, которые могут значительно упростить работу.
/dev/tcp
Многие пользователи Linux не подозревают, что bash особым образом обрабатывает перенаправление в файл /dev/tcp/host/port, и если host - это допустимое имя хоста или ip адрес, а port - целое число порта или имя службы, пытается открыть соответствующий TCP-сокет.
Например, перенаправим вывод TCP сервера времени на cat и выведем на экран:
$cat < /dev/tcp/time.nist.gov/13
60311 24-01-02 10:43:53 00 0 0 798.9 UTC(NIST) *
Более сложный пример - HTTP запрос. Давайте создадим новый файловый дескриптор, перенаправим на него строку HTTP-запроса, затем прочитаем ответ.
$exec 5<>/dev/tcp/google.de/80
$echo -e "GET / HTTP/1.1\nhost: google.de\n\n" >&5
$cat <&5 | head
HTTP/1.1 301 Moved Permanently
Location: http://www.google.de/
Content-Type: text/html; charset=UTF-8
Date: Thu, 18 Nov 2021 08:27:18 GMT
Expires: Sat, 18 Dec 2021 08:27:18 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 218
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Placeholder -
Некоторые команды, например tar, воспринимают символ - как перевод на стандартный вывод, что позволит воспользоваться результатом работы команды без создания промежуточных файлов, например создадим бэкап директории /www/data и скопируем по ssh:
tar zcvf - /www/data | ssh user@remoter "cat > /backup/wwwdata.tar.gz"
Группировка команд
Передача стандартного вывода одной команды в стандартный ввод другой команды - мощный инструмент. Но что, если вам нужно передать стандартный вывод нескольких команд? Для того чтобы объединить вывод нескольких команд можно использовать группировку команд, для этого используется синтаксис:{ command1; command2; }, например объединим вывод команд ls, чтобы подсчитать количество файлов в двух директориях:
$mkdir dir{1,2}
$touch dir1/test1.txt
$touch dir2/test2.txt
${ ls dir1; ls dir2; } | wc -l
2
Подмена процессов
Подстановка(подмена) процессов - это мощная и гибкая функция Bash, которая позволяет обрабатывать выходные данные команды как файл, упрощая объединение и манипулирование данными из нескольких команд. Для реализации использует следующий синтаксис: <(команда) — этот синтаксис создает именованный канал (также известный как FIFO) и соединяет с ним выходные данные команды внутри скобок. Именованный канал ведет себя как файл, что позволяет использовать его в качестве входных данных для другой команды. >(команда) – этот синтаксис также создает именованный канал, но подключает его к вводу команды внутри скобок. Это позволяет перенаправить вывод одной команды на ввод другой команды.
Таким образом, можно сравнить вывод двух команд с помощью diff, как если бы мы сохранили его в файлы:
$diff <(ls dir1) <(ls dir2)
< test1.txt
---
> test2.txt
Или перенаправим вывод ls на вход grep и wc:
ls | tee >(grep 'txt$') >(wc -l) > /dev/null
Заключение
Надеюсь, приведенные примеры показались вам интересными и полезными, благодарю за ваше время и внимание, эффективных скриптов, элегантных решений вам!
Комментарии (15)
debagger
13.01.2024 22:42+3Посоветуйте, что можно почитать/посмотреть для прокачки скилов по bash?
AVX
13.01.2024 22:42+2Не посчитайте рекламой, но мне нравится канал в телеге basdays. Неформально, с огоньком и матерком, и много интересного всякого.
Ну и по классике Advanced Bash Scripting Guide
DayDve
13.01.2024 22:42https://www.gnu.org/software/bash/manual/html_node/index.html
лучшего точно нет
Dmitri-D
13.01.2024 22:42+4хорошо работают функции и перенаправление в них
LOG_FILE="my.log" log() { local lin while read lin; do echo $(date)" $lin" >>LOG_FILE done } ... ls | log
или чтение посточное чтение из файла в whilewhile read lin; do echo $(date)" $lin" >>LOG_FILE done <( cat "somefile.txt" ) 
хорошо работает
set
, напримерset -exo pipefail
чтобы остановиться на первой ошибке, чтобы получить ошибку из последней команде в pipe, чтобы вывести лог исполненияtrap 'at_exit' EXIT INT TERM at_exit() { trap - EXIT INT TERM # cleanup at exit }
saboteur_kiev
13.01.2024 22:42+5tar zcvf - /www/data | ssh user@remoter "cat > /backup/wwwdata.tar.gz"
Тут можно просто опустить опцию -f, и оно пойдет в stdout. А на удаленной стороне можно сразу распаковать:
tar cvz . /www/data| ssh student1@localhost "tar xvz --directory=/www/data"
johnfound
13.01.2024 22:42+5$mkdir folder{1,2} $touch dir1/test1.txt $touch dir2/test2.txt ${ ls dir1; ls dir2; } | wc -l 2
Кстати здесь, мне кажется, ошибка. Должно быть
mkdir dir{1,2}
. Или я что-то не понимаю...
Tolik-5
13.01.2024 22:42объясните подробнее, пожалуйста, что значит
exec 5<>/dev/tcp/host/port
saboteur_kiev
13.01.2024 22:42Открыть на input/output еще один дескриптор, связав его с указанным хостом/портом.
RranAmaru
13.01.2024 22:42Второй пример можно покороче:
{ echo -e "GET / HTTP/1.1\n\n" >&5; cat <&5; } 5<>/dev/tcp/google.de/80
johnfound
Хорошо, но мало.