Как стать автором
Обновить

Комментарии 36

Хммм, возможно с помощью этого можно будет упростить некоторые мои монструозные скрипты… Интересная идея. А то та монструозность что есть вообще чудо что работает.
Вы не пробовали https://github.com/shelljs/shelljs?
У меня уже Chromium с Atom.io сжирают 80% оперативы, если ещё shell будет на js, то в чём же мне killall chromium && killall atom выполнять?
Мне кажется что когда вы отправляете сообщение, то получаете какую-то абракадабру:
Отправляете:
$ print -p abrakadabra1

Получаете:
$ read -p var; echo $var
bbrbkbdbbrb1

У вас буква a сломалась
Очевидно, в фоне выполняется
tr -u a b

который используется для замены всех a на b. Так что всё работает как и ожидается :)
Простите, не заметил. Что-то часто я при чтении отвлекаться начал
какая версия tr?
для (GNU Coreutils) 8.21 опции -u нет, поэтому пример, где «процедура записи/чтения» не отработает, а результат будет виден только после закрытия дескрипторов.
какая версия tr?

https://www.freebsd.org/cgi/man.cgi?tr
поэтому пример, где «процедура записи/чтения» не отработает

Вам стоить прочитать всю статью, а не урывок в конце и решение найдётся.
Да ну, прям всю статью? И что конкретно я должен был прочитать? А man к функциям я читать умею и справку вызывать.
man tr
DESCRIPTION
Translate, squeeze, and/or delete characters from standard input, writing to standard output.

-c, -C, --complement
use the complement of SET1

-d, --delete
delete characters in SET1, do not translate

-s, --squeeze-repeats
replace each input sequence of a repeated character that is listed in SET1 with a single occurrence of that character

-t, --truncate-set1
first truncate SET1 to length of SET2

--help display this help and exit

--version
output version information and exit


К тому же, вы нигде не упоминали, что это под freebsd. Покажите мне, с какой версии под линухом эта опция? Хамить я тоже умею, заметили?
Хамить я тоже умею, заметили?

То что это Ваш стиль общения исключительно Ваше дело.

А статью Вам действительно стоит прочитать всю, тогда Вы найдёте ответ на:
вы нигде не упоминали, что это под freebsd.

Хоть это и не имеет прямого отношения к вопросам статьи, отмечу, что все нижеприведённые примеры сделаны на

$ uname -opr
FreeBSD 10.1-STABLE amd64


И на
И что конкретно я должен был прочитать?

Хотя бы
Разумеется, если данное поведение нас не устраивает, его можно изменить, например используя stdbuf

$ coproc stdbuf -oL -i0 tr a b

Зеркальный ответ на ваше изначальное хамство.

Тема зачем? не раскрыта.

Тема раскрыта) Автор честно сказал, что не знает ответа на этот вопрос.

PS:
Ищущий да найдёт. И отпишется, чтобы мы тоже знали)
Если так получилось, что:
* Никак нельзя сделать не на баше
* Подготовка данных разбита на шаги
* А выполнение при этом не бесплатное (т.е. занимает время)

То это неплохой способ уменьшить общее время выполнения скрипта.

Как простой пример — persistent connection к БД.
Как простой пример — persistent connection к БД.

Андрей, хоть это и единственное найденное применение описанное в статье, на самом деле я при первой возможности ушёл от этого решения.
Причины:
— умирание сопроцесса при «битых» запросах (как следствие необходимость проверки жизнеспособности сопроцесса);
— потеря неизвлечённых данных при этом;
— геморойная обработка данных при извлечении и сопоставление их с запросом.
Всё-таки работа с БД — это не стихия *sh.
Хм. Но зачем нужны сопроцессы, если этот же функционал реализовывается немного проще, во ВСЕХ шеллах при помощи pipe:
процесс 1
mkpipe testpipe
echo abrakahabra >> testpipe

процесс 2
cat testpipe


И этот вариант, я даже несколько раз использовал. Новый функционал исключительно для создания «анонимного пайпа»?
mkpipe

Извините, а что это за зверь такой?
Это стандарт для файловых систем Posix — создание файла-потока, в который один процесс может писать, а другой читать.

Ох сорри, mkfifo, опечатался.

Работает именно так, как описано в статье, только это есть даже не просто в каком-то конкретно взятом шелле, а вообще просто фича всех POSIX файловых систем, можно пользоваться откуда угодно.
На диске лежит только имя файла, на самом деле вся информация передается через буферы.
mkpipe testpipe

Вероятно речь об mkfifo?

echo abrakahabra >> testpipe
cat testpipe

Это только каналы ввода/вывода, а где фоновый процесс?

Вариант реализации посредством mkfifo в статье приведен
Заголовок спойлера
$ mkfifo in out
$ tr -u a b <in >out &
$ exec 3> in 4< out
$ echo abrakadabra1 >&3
$ echo abrakadabra2 >&3
$ echo abrakadabra3 >&3
$ read var <&4 ; echo $var
bbrbkbdbbrb1
$ read var <&4 ; echo $var
bbrbkbdbbrb2
$ read var <&4 ; echo $var
bbrbkbdbbrb3


во ВСЕХ шеллах при помощи pipe

Разумеется, приведенный вариант не универсален и не будет работать во всех шеллах. Хотя, скорее всего, во всех Борн-шеллах должен отработать.

У FIFO есть много недостатков:


  1. FIFO нужно где‐то создать. Предположительно, вам придётся заморочиться с mktemp -d.
  2. FIFO нужно потом ещё и удалить. Причём удалить независимо от того, успешно ли завершился скрипт. Хотя, в принципе, можно удалить сразу после exec.
  3. FIFO не должен быть перехвачен другой программой: к примеру, другим процессом того же скрипта. Он особенно не должен быть доступен для других пользователей. Ещё один повод использовать именно mktemp -d.

Последний (и, кажется, единственный) раз, когда я попытался использовать сопроцессы в zsh я пожалел, что сопроцесс может быть только один (и ещё вроде были какие‐то проблемы). FIFO я не использовал вообще никогда, если мне нужно что‐то подобное я лучше либо перепишу всё так, что мне это не нужно будет (обычно process substitution и стандартного перенаправления через | хватает за глаза), либо перепишу на Python.

Здравствуйте.
В bash Linux — если сопроцессор завершается до начала считывания, то теряем вывод полученный от него через "${COPROC[0]}".
$ ls -Al
total 20360
-rw-rw-r--. 1 ilia ilia 20834292 авг 15 15:46 1
drwxrwxr-x. 2 ilia ilia     4096 авг 15 15:10 proba
drwxrwxr-x. 2 ilia ilia     4096 авг 15 15:10 proba2
drwxrwxr-x. 2 ilia ilia     4096 авг 15 15:10 proba3

$ coproc ls -Al .
[1] 16739
[1]+  Done                    coproc COPROC ls --color=auto -Al .

$ echo "${COPROC[0]}"


${COPROC[0]} — больше нет, получить неоткуда. Неудобно. Легче что-то другое придумать, у тех же FIFO такого нет.
А если в канал не помещается весь вывод, то уже лучше, сопроцессор ждет освобождения места в буфере для продолжения и не завершается. Но хвост вывода можем потерять.
$ coproc ls -alR /
[1] 16797

$ echo "${COPROC[0]}"
63

Выглядит как ошибка, в man bash я предупреждений по этому поводу не нашёл. zsh и ksh таким не страдают, так что никаких оправданий вроде «а у этих такая же фигня» не может быть.


Вы пробовали с этим вопросом пойти в bug tracker bash’а?

Вы пробовали с этим вопросом пойти в bug tracker bash’а?

Нет, не пробовал, т.к. об этой штуке узнал пару часов назад. Для меня не нужная вещь. Хотя выглядит действительно как ошибка. На всякий случай оставлю, вдруг кому-нибудь пригодится:
$ cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core) 

$ bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)

$ uname -a
Linux  3.10.0-327.28.2.el7.x86_64 #1 SMP Wed Aug 3 11:11:39 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

У меня то же самое в bash-4.3_p42-r1, Gentoo amd64. Хотя это не последняя версия в portage: есть ещё «нестабильная» 4.3_p46 и «замаскированная» 4.4_rc1.

ps afx | grep [6]053

Этот прием надо запомнить!

Не нужно, вы не в BSD. ps -p 6053. Если в совете предполагается пропустить вывод ps через grep на linux, то, скорее всего, этот совет не оптимальный и grep на самом деле трогать не нужно.

Хотя, судя по man ps (BSD) там тоже должно работать.

Вилимо имелось ввиду преобразование символьного класса и исключение лишней строки из вывода grep

Я знаю, зачем в строке grep []. Я не знаю, зачем вообще нужен grep здесь, если есть ps -p, позволяющий обойтись без grep. И есть ps -C позволяющий то же самое для нахождения конкретных процессов по имени исполняемого файла. grep нужен в редких случаях, когда у вас 100500 процессов вида myapp --foo bar и вам хочется отыскать среди них PID того, что запущен с --foo baz. Или когда вы представляете, что вам нужно лишь приблизительно (к примеру, браузеры имеют нехорошую привычку запускаться одной командой (e.g. vivaldi), а в списке висеть в виде другой (vivaldi-bin в примере), но это относится не только к vivaldi и даже не только к chromium-based).


ps -p 6053 вам покажет данные только для процесса с PID 6053 и безо всяких паразитных строчек (не считая заголовка), которые нужно будет исключать всякими хаками. Или покажет только заголовок, если процесс уже завершился (заголовок можно убрать, если нужно: ps hp 6053 или ps --no-headers -p 6053).


Ещё: вот будет у вас в фоне запущен mpv с именем серии с хэшем, содержащим 6053, вы его увидите, несмотря на ваш трюк с grep. И процесс с PID 16053 увидите. И процесс, который работает 6053 часов. И процесс на псевдотерминале pts/6053. С PID 6053 такое маловероятно, но PID бывают и маленькими — они вообще назначаются циклически или случайно (в зависимости от настроек ядра ОС). При использовании ps -p 6053 ничего из вышеперечисленного вы не увидите.


Ну и, конечно, ps -p 6053 требует меньше нажатий клавиш, чем ps afx | grep [6]053. Не забудьте также, что в zsh с такими привычками вы имеете хорошие шансы увидеть zsh: no matches found: [6]053. Bash тоже можно так настроить. Трюк пропадёт в никуда, если в каталоге есть файл 6053. При других настройках оболочки ваша команда выдаст


Использование: grep [ПАРАМЕТР]… ШАБЛОН [ФАЙЛ]…
Запустите «grep --help» для получения более подробного описания.

([6]053 было раскрыто в «ничего», потому что такого файла нет). Из‐за возможности таких фокусов никогда не полагайтесь на то, что провальные glob expression’ы будут заменены на самих себя. Хотите передать grep [6]053 — пишете '[6]053'.

Павел, Ваша экспрессия «в никуда».
Я лишь усомнился, что Михаила заинтересовал "|" и ещё более сомнительно, что его заинтересовал man ps.
ps -p 6053 вам покажет данные только для процесса с PID 6053

Спасибо, конечно, но я тоже знаю о существании ключа "-p" у ps.
При этом много чаще пользуюсь именно символьным преобразованием, так как это универсальнее.
Да, извините, Николай!

В статье лучше написать ps -p. В любом случае, символьное преобразование взять в кавычки.

Согласен и с тем и с тем, но десятилетняя привычка и большая вольность с закавычиванием в c-шеллах делают свое дело.

У меня привычки куда как дальше от работающих «по‐умолчанию»: вашу команду я бы написал как ps afx G '[6]053' и меня бы никто не понял (включая любую ненастроенную оболочку): zsh имеет такую замечательную вещь, как «глобальные alias’ы», позволяющую заменять не только команды. Конкретно G раскрывается в | grep*, есть и другие вроде alias -g L='| less'. Разумеется, это дало два набора привычек: «для скриптов» (там alias’ы не работают, не говоря уж о том, что часть скриптов мне приходится писать для POSIX sh, а не zsh) и «для печати в командной строке». При общении предпочитается именно последнее.


А у скриптов есть и ещё одна особенность: во‐первых, лучше написать всё так, чтобы не вылавливать потом баги, чем написать какое‐нибудь while ps afx | grep "[6]053" и внезапно обнаружить «бесконечный цикл» на ровном месте, т.к. какой‐то процесс попал на pts/6053. Во‐вторых, PID в этом случае окажется в переменной и доставать по отдельности первый и остальные символы будет как‐то сложновато (я к тому, что решение, оперирующее с цельным значением предпочтительнее решения, предполагающего разбиение его на части: в POSIX нет ни индексации строк (${PID[1]} или ${PID:0:0}), ни даже ${PID//sub/rep}).


* С некоторых пор (после этой статьи) я немного подумал и теперь он раскрывается в | nocorrect noglob grep (то же, но zsh не будет пытаться раскрыть [6]053).

Убедили :) Сделал update статьи. Спасибо!
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории