Когда у нас возникает потребность асинхронно запустить какую то прогу из нашей, то в 90% случаев нам нужно узнать что она выведет, или по крайней мере когда завершится.

Обычная асинхронность

Просто асинхронно запустить что-то в Vala очень просто:

Первый аргумент это путь в каком каталоге запустить команду, второй это собственно сама команда и ее флаги, третий - получение $PATH чтобы не нужно было указывать путь до bin в первом аргументе, далее флаги, например есть флаг который позволяет чтение или запись в поток(все виды флагов), предпоследний аргумент (который  null ) это user_data которую вы бы хотели принять на той стороне, и последний аргумент это пид то есть уникальный индификатор, который ОС дает каждому процессу(можно сказать именно этим потоки отличаются от процессов, но нет, у процесса могут быть потоки, а у потока не может быть процессов)

Далее тут через лямбюду добавляется ChildWatch.add – реакция на событие завершения запущенного процесса.

Подытожу с помощью этого всего мы можем среагировать на завершение запущенной асинхронной проги, но допустим если это WGET который скачивает файл, без технологии пайпов мы никак не сможем получить вывод. Например процентов закачки.

UPD: И только сегодня я заметил что все это было про команду spawn_async, то есть универсальный спавн чего то асинхронного, существует также spawn_comand_async пример использования которой выглядит гораздо короче, но вообще не дает нам никаких возможностей.

public static int main (string[] args) {
	try {
		Process.spawn_command_line_async ("mkdir MY-NEW-DIR");
	} catch (SpawnError e) {
		print ("Error: %s\n", e.message);
	}
	return 0;
}
//valac --pkg glib-2.0 GLib.Process.spawn_command_line_async.vala

Pipes

wiki

В программировании  именованный канал  или  именованный конвейер (англ. named pipe) — один из методов межпроцессного взаимодействия, расширение понятия конвейера в Unix и подобных ОС. Именованный канал позволяет различным процессам обмениваться данными, даже если программы, выполняющиеся в этих процессах, изначально не были написаны для взаимодействия с другими программами. Это понятие также существует и в Microsoft Windows, хотя там его семантика существенно отличается. Традиционный канал — «безымянен», потому что существует анонимно и только во время выполнения процесса. Именованный канал — существует в системе и после завершения процесса. Он должен быть «отсоединён» или удалён, когда уже не используется. Процессы обычно подсоединяются к каналу для осуществления взаимодействия между ними.

я

Короче это способ присосаца к запущенной проге и читать её выхлоп, даже если в ней не предусмотрена эта возможность(см сокеты

Гранит и асинхронность с пайпами.

В Vala также существует spawn_async_with_pipes

Я не буду объяснять пример её использования, он в любом случае не поместится на скрин. Если интересно вот тут есть просто тонны текста и сам пример.
Основной вывод — это неудобна, хачу короче.

Иии библиотека гранит позволяет организовать это в 5 строк кода.

На самом деле гранит это фреймворк над GTK, а SimpleComand просто является одной из ее фич.

Значт так, первый аргумент - путь, второй команда. Всё, осталось только сделать  run();

Содержание класса:

А всё самое необходимое. Сигнал done о том что процесс завершился, отлов ерроров и оутпут, причем не только в качестве полей, но и самое главное сигналов, значит можно будет читать все в реальном времени, а не после завершения.

Пример использования

Попробуем для начала просто ls. Конектим сигнал output_changed с нашей функцией, которая просто будет выводить то что получила.

Итак вот простейшая программа с гуи которая делает ls в корне и выводит в виджет с текстом

Эти 5 строк целиком отвечают за асинхронный запуск программы ls, отлов каждого изменения в ее выхлопе, и добавление этого в конец буфера виджета текста.

Думаю коментарии излишни, это невероятно просто по сравнению с тем что предлагает spawn_async_with_pipes из GLib, да и с другими языками программирования.  Собственно SimpleComand это wrapper(обертка), и в сурсах библиотеки как раз видно что там используется spawn_async_with_pipes, просто все сделано за нас.

Код для копирования

//компилировать так valac 56.vala --pkg granite --pkg gtk+-3.0
using Gtk;
using Granite;
public class MyApplication : Gtk.Application {
	protected override void activate () {
		new MyWindow (this).show_all ();
	}
}
class MyWindow: Gtk.ApplicationWindow {
	internal MyWindow (MyApplication app) {
		Object (application: app, title: "Async Example");
		this.set_default_size (600, 400);

		var buffer = new Gtk.TextBuffer (null);
		var textview = new Gtk.TextView.with_buffer (buffer); 
		textview.set_wrap_mode (Gtk.WrapMode.WORD);

		var scrolled_window = new Gtk.ScrolledWindow (null, null);
		scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC,
		                            Gtk.PolicyType.AUTOMATIC);
		scrolled_window.add (textview);
		scrolled_window.set_border_width (5);		

		this.add (scrolled_window);
		
		var proc = new Granite.Services.SimpleCommand("/","/bin/ls");
		proc.output_changed.connect((str) => {
			buffer.insert_at_cursor(str,str.length);
		});
		proc.run();
	}
}
public int main (string[] args) {
	return new MyApplication ().run (args);
}

Чей то коментарий с конфы по Vala

Supplementary to the above, if you aren’t sure where the binary resides you can initially search the PATH. To get this var you can use GLib.Environ object