Салют! В этой небольшой статье я попытался собрать необычные и малоизвестные трюки с перенаправлениями в 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)


  1. johnfound
    13.01.2024 22:42
    +8

    Хорошо, но мало.


  1. debagger
    13.01.2024 22:42
    +3

    Посоветуйте, что можно почитать/посмотреть для прокачки скилов по bash?


    1. ShefEr
      13.01.2024 22:42
      +3

      man bash :)


      1. debagger
        13.01.2024 22:42

        Вы мне напомнили одного персонажа, который для изучения JavaScript рекомендует изучать исключительно спецификацию ECMAScript


    1. AVX
      13.01.2024 22:42
      +2

      Не посчитайте рекламой, но мне нравится канал в телеге basdays. Неформально, с огоньком и матерком, и много интересного всякого.

      Ну и по классике Advanced Bash Scripting Guide



    1. Kirikekeks
      13.01.2024 22:42

      salt bash from google.


  1. 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


    или чтение посточное чтение из файла в while

      while 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
    }


    1. DayDve
      13.01.2024 22:42

      <( cat "somefile.txt" )

      зачем?
      масло масляное
      достаточно < somefile.txt


  1. saboteur_kiev
    13.01.2024 22:42
    +5

    tar 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"


  1. 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}. Или я что-то не понимаю...


    1. funtastick Автор
      13.01.2024 22:42

      Спасибо, исправил


  1. Tolik-5
    13.01.2024 22:42

    объясните подробнее, пожалуйста, что значит

    exec 5<>/dev/tcp/host/port


    1. saboteur_kiev
      13.01.2024 22:42

      Открыть на input/output еще один дескриптор, связав его с указанным хостом/портом.


  1. RranAmaru
    13.01.2024 22:42

    Второй пример можно покороче:

    { echo -e "GET / HTTP/1.1\n\n" >&5; cat <&5; } 5<>/dev/tcp/google.de/80