Лицензия Creative Commons
Содержимое блога доступно по лицензии Creative Commons Атрибуция — С сохранением условий
(Attribution-ShareAlike) 3.0 Unported
, если не указано иное.

понедельник, 2 января 2012 г.

Конвейерная обработка данных в Unix

Доброго времени суток, случайные и не случайные читатели этого блога.

Сегодня я хотел бы рассмотреть пример программы, которая может работать в режиме "конвейера" (pipeline), что является обыденным делом для утилит Unix-подобных систем.

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

Наша программа будет принимать на вход вывод некой другой программы, обрабатывать эти данные неким образом и передавать на стандартный вывод.

Просто? Просто.

Исходный текст программы тоже очень простой:

#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
  char ch;

  while (fread(&ch, sizeof(char), 1, stdin))
    putchar(toupper(ch));
  
  exit(EXIT_SUCCESS);
}

Теперь немного технических деталей. Когда вы пишете что-то вроде

$ cat my-file.txt | sort

командная оболочка (shell) связывает стандартный поток вывода stdout команды cat(1) со стандартным потоком ввода stdin команды sort(1). Таким образом, эти команды связываются в конвейер, проходя через который данные преобразуются определённым образом. В данном примере, мы выводим (считываем) содержимое файла my-file.txt на stdout, команда sort(1) принимает эти данные на stdin, сортирует их и выводит на stdout. На этом конвейер заканчивается и обработанные данные выводятся на терминал.

Поскольку для организации конвейера используются стандартные потоки ввода-вывода stdin и stdout, и связывание программ в конвейер осуществляет командная оболочка, от программы в общем случае требуется только получить данные с stdin, обработать их и вывести в stdout для (возможно) последующей обработки. Ещё более замечательным является тот факт, что одной программе не нужно знать внутреннее устройство другой программы, чтобы работать с ней в одном конвейере.

Важно заметить, что программы, составляющие конвейер, выполняются параллельно. Если рассматривать приведённый ранее пример, то команда cat(1) и sort(1) работают одновременно. Как только cat(1) выводит данные на stdout, они сразу же могут быть считаны командой sort(1) из stdin.

Сравните с последовательностью команд, разделённых точкой с запятой, которые выполняются одна за другой:

$ cat my-file.txt; sort

Сначала будет выполнена команда cat(1), которая выведет содержимое файла my-file.txt на экран, а потом будет выполнена команда sort(1), которая будет ждать ввода данных с stdin.

Скомпилируем наконец нашу программу и посмотрим, что полезного она может сделать.

$ gcc tu.c -o tu

Протестируем работу программы. Свяжем в конвейер команду awk(1) и нашу програму:

$ awk --copyleft | ./tu

Команда awk(1) с параметром --copyleft выводит краткий вариант лицензии GPL на stdout. Далее наша программа считывает в цикле по одному символу с stdin, преобразует считанный символ в прописной и выводит этот символ на stdout.

Вывод нашей программы можно так же отправить дальше по конвейеру.

$ awk --copyleft | ./tu |\
   sed s/'1989, 1991-2010 FREE SOFTWARE FOUNDATION'/'2012 Artyom Poptsov'/

Неплохой обзор конвейерной обработки данных в Unix дан в следующих статьях:

воскресенье, 1 января 2012 г.

Новогодняя ёлка на Emacs Lisp

Доброго времени суток, случайные и не случайные читатели.

Пользуясь случаем, поздравляю вас с уже наступившим Новым Годом. Дабы немного поднять вам (и себе) новогоднее настроение, публикую исходный код программы на Emacs Lisp, которая рисует замечательную новогоднюю ACSII-ёлку.

;;;
;;; Copyright (C) 2011 Artyom Poptsov
;;;
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; For more information, see http://www.gnu.org/licenses/
;;;

;; Result of running this program sends to other buffer
(with-output-to-temp-buffer "*tree*"

  ;; Get height of the tree from stdin
  (setf HIGHT (read))

  ;; There are symbols that I will use for drawing the tree
  (setq C_BG    ":")
  (setq C_TREE  "^")
  (setq C_TRUNK "#")

  ;; A few variables
  (setq PADDING 2) ; Padding from edges of the "canvas".
  (setf TRUNK_H (/ HIGHT 4))                              

  ;; I'm using this funtions for printing empty strings filled with BG
  (defun print-bg-for-picture ()
    "This function prints out a few strings filled with background"
    (loop for i from 1 to PADDING do
   (loop for j from 0 to (+ (* HIGHT 2) (* PADDING 3)) do
  (princ C_BG))
   (princ "\n")))

  (print-bg-for-picture)

  ;; Main loop
  (loop for i from 0 to (+ HIGHT TRUNK_H (* PADDING 2)) do
 (loop for j from 0 to PADDING do
       (princ C_BG))
 (if (< i HIGHT)
     ;; Top of the tree
     (loop for j from 0 to (* HIGHT 2) do
    (if (or (< j (- HIGHT i))
     (> j (+ HIGHT i)))
        (princ C_BG)
      (princ C_TREE)))
   ;; Trunk of the tree
   (loop for j from 0 to (* HIGHT 2) do
  (if (or (< j (- HIGHT (/ HIGHT 10)))
   (> j (+ HIGHT (/ HIGHT 10))))
      (princ C_BG)
    (princ C_TRUNK))))
 (loop for j from 0 to PADDING do
       (princ C_BG))
 
 (princ "\n"))
  (print-bg-for-picture))

Вы можете вычислить (выполнить) эту программу следующим образом: запустите текстовый редактор Emacs (предположим, что он у вас уже установлен), скопируйте исходный код в буфер *scratch*, поместите точку (курсор) после последней скобки в конце программы и нажмите C-x C-e (Ctrl+x Ctrl+e). Далее вы можете ввести размер ёлки.

В результате, если вы всё сделали правильно, у вас откроется новое окно, в котором отобразится результат работы программы:

::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::^::::::::::::::::::::::: ::::::::::::::::::::::^^^:::::::::::::::::::::: :::::::::::::::::::::^^^^^::::::::::::::::::::: ::::::::::::::::::::^^^^^^^:::::::::::::::::::: :::::::::::::::::::^^^^^^^^^::::::::::::::::::: ::::::::::::::::::^^^^^^^^^^^:::::::::::::::::: :::::::::::::::::^^^^^^^^^^^^^::::::::::::::::: ::::::::::::::::^^^^^^^^^^^^^^^:::::::::::::::: :::::::::::::::^^^^^^^^^^^^^^^^^::::::::::::::: ::::::::::::::^^^^^^^^^^^^^^^^^^^:::::::::::::: :::::::::::::^^^^^^^^^^^^^^^^^^^^^::::::::::::: ::::::::::::^^^^^^^^^^^^^^^^^^^^^^^:::::::::::: :::::::::::^^^^^^^^^^^^^^^^^^^^^^^^^::::::::::: ::::::::::^^^^^^^^^^^^^^^^^^^^^^^^^^^:::::::::: :::::::::^^^^^^^^^^^^^^^^^^^^^^^^^^^^^::::::::: ::::::::^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^:::::::: :::::::^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^::::::: ::::::^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^:::::: :::::^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^::::: ::::^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^:::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: :::::::::::::::::::::#####::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::

Happy New Year and happy coding!