子プロセス (subprocess)

サブプロセスの生成・終了待ちと、標準入出力による通信。

C言語

簡易サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスの終了を待って次に進む。

#include <stdio.h>    // printf
#include <stdlib.h>   // system
#include <sys/wait.h> // WEXITSTATUS

int main()
{
	int status;

	// 起動し、終了を待つ。
	status = system("ls -F");

	// 終了コードを得る。簡易には status = status >> 8; も可。
	// 正しくはシグナルによる終了なども判定する。
	// 詳しくは次のサンプルと man 2 wait
	status = WEXITSTATUS(status);

	printf("%d\n", status);

	return 0;
}

メニューに戻る

サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	pid_t pid;
	int status;

	pid = fork();
	if (pid == 0) {
		// ここに来るのはサブプロセス
		execlp("ls", "ls", "-F", (char *)NULL);
		// ここには到達しない。
	}

	// ここに来るのはメインプロセス
	// 以降、サブプロセスと並行動作

	if (pid == -1) {
		return 1;
	}

	// サブプロセス終了待ち
	for (;;) {
		// サブプロセス終了コード取得
		wait(&status);
		if (WIFEXITED(status)) {
			printf("Exit status:%d\n", WEXITSTATUS(status));
			break;
		} else if (WIFSIGNALED(status)) {
			printf("Signal:%d\n", WTERMSIG(status));
			break;
		}
	}

	return 0;
}

メニューに戻る

サブプロセスを起動して片方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

サブプロセス → メインプロセス方向、またはメインプロセス → サブプロセス方向の通信を行う。

別々に起動した複数プロセス間での通信は、いわゆるプロセス間通信を行う。

#include <stdio.h>
#include <sys/wait.h> // WEXITSTATUS

#define BUF_SIZE (100)

int main()
{
	FILE *fp;
	size_t size;
	char buf[BUF_SIZE];
	int status;

	//////// サブプロセス → メインプロセス
	//////// ls の結果を取得

	// シェルでの ls -C | cat に相当する処理。
	// ls をサブプロセス、cat をメインプロセスで行う。

	// サブプロセス → メインプロセス方向のパイプを作って
	// ls を起動
	fp = popen("ls -C", "r");

	// ls の出力をそのまま stdout へ
	while ((size = fread(buf, sizeof (char), BUF_SIZE, fp)) > 0)
		fwrite(buf, sizeof (char), size, stdout);

	// パイプを閉じて、サブプロセスの終了を待つ (wait4)
	status = pclose(fp);

	// サブプロセスの終了コード
	status = WEXITSTATUS(status);
	printf("%d\n", status);


	//////// メインプロセス → サブプロセス
	//////// tr へ出力

	// シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' に相当する処理。
	// echo をメインプロセス、tr をサブプロセスで行う。

	// メインプロセス → サブプロセス方向のパイプを作って
	// tr を起動
	fp = popen("tr '[A-Z]' '[a-z]'", "w");

	// tr へ送る
	fwrite("AbCdE\n", sizeof (char), 6, fp);

	// パイプを閉じて、サブプロセスの終了を待つ (wait4)
	status = pclose(fp);

	// サブプロセスの終了コード
	status = WEXITSTATUS(status);
	printf("%d\n", status);

	return 0;
}

メニューに戻る

サブプロセスを起動して双方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

標準入出力を介して双方向通信を行う。

以下の例ではパイプを二つ使用しているが、上の例はこのどちらかを簡易に使用している。

別々に起動した複数プロセス間での通信は、いわゆるプロセス間通信を行う。

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

#define BUF_SIZE (100)

// シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' | cat に相当する処理。
// echo と cat をメインプロセス、tr をサブプロセスで行う。

int main()
{
	int m2s[2], s2m[2];
	pid_t pid;
	char buf[BUF_SIZE];
	size_t size;
	int status;

	if (pipe(m2s) == -1) {
		return 1;
	}
	if (pipe(s2m) == -1) {
		close(m2s[0]);
		close(m2s[1]);
		return 1;
	}

	pid = fork();
	if (pid == 0) {
		// ここに来るのはサブプロセス

		// 不要ファイルを閉じておく
		close(m2s[1]);
		close(s2m[0]);

		// パイプを標準入出力と差し替え
		dup2(m2s[0], STDIN_FILENO);
		dup2(s2m[1], STDOUT_FILENO);

		// tr 起動
		execlp("tr", "tr", "[A-Z]", "[a-z]", (char *)NULL);
		// ここには到達しない。
	}

	// ここに来るのはメインプロセス

	// 不要ファイルを閉じておく
	close(m2s[0]);
	close(s2m[1]);

	if (pid == -1) {
		close(m2s[1]);
		close(s2m[0]);
		return 1;
	}

	// tr へ送る
	write(m2s[1], "AbCdE\n", 6);
	close(m2s[1]);  // tr への入力が終了し、tr が終了する

	// tr の出力を取得
	size = read(s2m[0], buf, BUF_SIZE);
	close(s2m[0]);

	printf("%.*s", size, buf);

	// サブプロセス終了待ち
	for (;;) {
		wait(&status);
		if (WIFEXITED(status)) {
			printf("Exit status:%d\n", WEXITSTATUS(status));
			break;
		} else if (WIFSIGNALED(status)) {
			printf("Signal:%d\n", WTERMSIG(status));
			break;
		}
	}

	return 0;
}

メニューに戻る

Java

他の言語と違い、サブプロセスの標準入出力はメインプロセスの標準入出力とつながっていないので、起動端末でサブプロセスの標準出力を簡易に得ることはできず、メインプロセスに中継処理が必要。

以降の例の

Runtime.getRuntime().exec(...)
new ProcessBuilder(...).start()
としても良い。

サブプロセスを起動して片方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

サブプロセス → メインプロセス方向の通信を行う。

//////// サブプロセス → メインプロセス
//////// ls の結果を取得

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

...

// シェルでの ls -C | cat に相当する処理。
// ls をサブプロセス、cat をメインプロセスで行う。

BufferedReader br = null;
try {
	// サブプロセス → メインプロセス方向のパイプを作って
	// ls を起動
	Process proc = Runtime.getRuntime().exec(
		new String[]{"ls", "-C"}  // "ls -C" でもいけるみたい
		);

	// ls の出力をそのまま stdout へ
	br = new BufferedReader(
		new InputStreamReader(proc.getInputStream()));

	String line;
	while ((line = br.readLine()) != null) {
		System.out.println(line);
	}

	// サブプロセスの終了を待つ
	proc.waitFor();

	// サブプロセスの終了コード
	System.out.println(proc.exitValue());
} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		if (br != null) {
			br.close();
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

メニューに戻る

サブプロセスを起動して双方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

標準入出力を介して双方向通信を行う。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;

...

// シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' | cat に相当する処理。
// echo と cat をメインプロセス、tr をサブプロセスで行う。

BufferedWriter bw = null;
BufferedReader br = null;
try {
	// 双方向のパイプを作って tr を起動
	Process proc = Runtime.getRuntime().exec(
		new String[]{"tr", "[A-Z]", "[a-z]"}
		// "tr [A-Z] [a-z]" でもいいみたい
		);

	// tr へ送る
	bw = new BufferedWriter(
		new OutputStreamWriter(proc.getOutputStream()));
	bw.write("AbCdE");

	// tr への入力が終了し、tr が終了する
	try {
		if (bw != null) {
			bw.close();
		}
	} catch (IOException e) {
		e.printStackTrace();
	}

	// tr の出力を取得
	br = new BufferedReader(
		new InputStreamReader(proc.getInputStream()));

	String line;
	while ((line = br.readLine()) != null) {
		System.out.println(line);
	}

	// サブプロセスの終了を待つ
	proc.waitFor();

	// サブプロセスの終了コード
	System.out.println(proc.exitValue());
} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		if (br != null) {
			br.close();
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

メニューに戻る

Python

簡易サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスの終了を待って次に進む。

import subprocess

try:
	# 起動し、終了を待つ
	status = subprocess.call(["ls", "-F"])

	# 終了コードを得る
	if status < 0:
		print "Signal:", -status
	else:
		print "Exit status:", status
except:
	print "Fail"

メニューに戻る

サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

import subprocess

try:
	# サブプロセス起動
	subProc = subprocess.Popen(["ls", "-F"])

	# 以降、サブプロセスと並行動作

	# サブプロセス終了待ち
	subProc.wait()

	# サブプロセス終了コード取得
	if subProc.returncode < 0:
		print "Signal:", -subProc.returncode
	else:
		print "Exit status:", subProc.returncode
except:
	print "Fail"

メニューに戻る

サブプロセスを起動して片方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

サブプロセス → メインプロセス方向、またはメインプロセス → サブプロセス方向の通信を行う。

import subprocess
import sys

######## サブプロセス → メインプロセス
######## ls の結果を取得

# シェルでの ls -C | cat に相当する処理。
# ls をサブプロセス、cat をメインプロセスで行う。
try:
	# サブプロセス → メインプロセス方向のパイプを作って
	# ls を起動
	subProc = subprocess.Popen(["ls", "-C"], stdout=subprocess.PIPE)

	# ls の出力をそのまま stdout へ
	for line in subProc.stdout:
		sys.stdout.write(line)

	# サブプロセスの終了を待つ
	subProc.wait()

	# サブプロセスの終了コード
	if subProc.returncode < 0:
		print "Signal:", -subProc.returncode
	else:
		print "Exit status:", subProc.returncode
except:
	print "Fail"


######## メインプロセス → サブプロセス
######## tr へ出力

# シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' に相当する処理。
# echo をメインプロセス、tr をサブプロセスで行う。
try:
	# メインプロセス → サブプロセス方向のパイプを作って
	# tr を起動
	subProc = subprocess.Popen(["tr", "[A-Z]", "[a-z]"], stdin=subprocess.PIPE)

	# tr へ送る
	subProc.stdin.write("AbCdE\n")
	subProc.stdin.close() # tr への入力が終了し、tr が終了する

	# サブプロセスの終了を待つ
	subProc.wait()

	# サブプロセスの終了コード
	if subProc.returncode < 0:
		print "Signal:", -subProc.returncode
	else:
		print "Exit status:", subProc.returncode
except:
	print "Fail"

メニューに戻る

サブプロセスを起動して双方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

標準入出力を介して双方向通信を行う。

import subprocess
import sys

# シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' | cat に相当する処理。
# echo と cat をメインプロセス、tr をサブプロセスで行う。
try:
	# メインプロセス ←→ サブプロセス双方向のパイプを作って
	# tr を起動
	subProc = subprocess.Popen(["tr", "[A-Z]", "[a-z]"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

	# tr へ送る
	subProc.stdin.write("AbCdE\n")
	subProc.stdin.close() # tr への入力が終了し、tr が終了する

	# tr の出力を取得
	for line in subProc.stdout:
		sys.stdout.write(line)

	# サブプロセスの終了を待つ
	subProc.wait()

	# サブプロセスの終了コード
	if subProc.returncode < 0:
		print "Signal:", -subProc.returncode
	else:
		print "Exit status:", subProc.returncode
except:
	print "Fail"

この程度の簡易通信ならば、次の例の方が簡単。

メニューに戻る

簡易なサブプロセスとのやりとり

標準入出力と標準エラー出力を通して、サブプロセスと小さいデータのやりとりを行うには、communicate( ) が便利。

以下は、上の例と同じ機能。

import subprocess
import sys

# シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' | cat に相当する処理。
# echo と cat をメインプロセス、tr をサブプロセスで行う。
try:
	# tr を起動
	subProc = subprocess.Popen(["tr", "[A-Z]", "[a-z]"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

	# tr へ送り、終了を待って出力を得る
	(stdout, stderr) = subProc.communicate("AbCdE\n")

	sys.stdout.write(stdout)

	# サブプロセスの終了コード
	if subProc.returncode < 0:
		print "Signal:", -subProc.returncode
	else:
		print "Exit status:", subProc.returncode
except:
	print "Fail"

メニューに戻る

排他

mkdir の排他的性質を利用。

import time
import os

LOCKFILE = '/tmp/lock'
INTERVAL = 1

def enter():
	flag = True
	while flag:
		try:
			os.mkdir(LOCKFILE)
			flag = False
		except:
			time.sleep(INTERVAL)

def leave():
		try:
			os.rmdir(LOCKFILE)
		except:
			pass

enter()
# 排他的処理
leave()

メニューに戻る

Ruby

簡易サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスの終了を待って次に進む。

# 起動し、終了を待つ
system("ls -F")

# 終了コード
puts $?

メニューに戻る

サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

fork {
    # ここに来るのはサブプロセス  
    exec("ls -F")
    # ここには到達しない。  
}  

# ここに来るのはメインプロセス  
# 以降、サブプロセスと並行動作  


# サブプロセス終了待ち  
Process.wait

# サブプロセス終了コード取得  
puts "Exit status:#{$?}"

メニューに戻る

サブプロセスを起動して片方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

サブプロセス → メインプロセス方向、またはメインプロセス → サブプロセス方向の通信を行う。

######## サブプロセス → メインプロセス  
######## ls の結果を取得  

# シェルでの ls -C | cat に相当する処理。  
# ls をサブプロセス、cat をメインプロセスで行う。  

# サブプロセス → メインプロセス方向のパイプを作って  
# ls を起動  
io = IO.popen("ls -C", "r");  

# ls の出力をそのまま stdout へ  
io.each { |line|
	puts line
}

# パイプを閉じて、サブプロセスの終了を待つ
io.close

# サブプロセスの終了コード  
puts $?


######## メインプロセス → サブプロセス  
######## tr へ出力  

# シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' に相当する処理。  
# echo をメインプロセス、tr をサブプロセスで行う。  

# メインプロセス → サブプロセス方向のパイプを作って  
# tr を起動  
io = IO.popen("tr '[A-Z]' '[a-z]'", "w");  

# tr へ送る  
io.puts "AbCdE\n"

# パイプを閉じて、サブプロセスの終了を待つ
io.close

# サブプロセスの終了コード  
puts $?

サブプロセスの小さい標準出力を得るだけであれば、次の例の方が簡単。

puts `ls -F`
puts $?

メニューに戻る

サブプロセスを起動して双方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

標準入出力を介して双方向通信を行う。

# シェルでの echo AbCdE | tr '[A-Z]' '[a-z]' | cat に相当する処理。  
# echo と cat をメインプロセス、tr をサブプロセスで行う。  

# メインプロセス ←→ サブプロセス双方向のパイプを作って  
# tr を起動  
io = IO.popen("tr '[A-Z]' '[a-z]'", "r+")

# tr へ送る  
io.puts "AbCdE\n"
io.close_write # tr への入力が終了し、tr が終了する

# tr の出力を取得  
io.each { |line|
	puts line
}

# パイプを閉じる
io.close

# サブプロセスの終了コード  
puts "Exit status:#{$?}"

メニューに戻る

Shell

簡易サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスの終了を待って次に進む。

# 起動し、終了を待つ
ls -F

# 終了コード
echo $?

メニューに戻る

サブプロセス起動

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

# サブプロセス起動  
ls -F &
  
# 以降、サブプロセスと並行動作  

pid=$!
  
# サブプロセス終了待ち  
wait $pid
  
# サブプロセス終了コード取得  
echo $?

メニューに戻る

サブプロセスを起動して片方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

サブプロセス → メインプロセス方向、またはメインプロセス → サブプロセス方向の通信を行う。

######## サブプロセス → メインプロセス  
######## ls の結果を取得  

# サブプロセス → メインプロセス方向のパイプを作って  
#  ls を起動  
# サブプロセスの終了を待つ
ls -C |

#  ls の出力をそのまま stdout へ  
while read line; do
	echo "$line"
done


######## メインプロセス → サブプロセス  
######## tr へ出力  

# メインプロセス → サブプロセス方向のパイプを作って  
#  tr を起動し、tr へ AbCdE を送る  
# サブプロセスの終了を待つ
echo "AbCdE" | tr '[A-Z]' '[a-z]'

# サブプロセスの終了コード  
echo $?

パイプの直前のコマンドの出力がパイプの直後のコマンドに渡されるので、上記の記法ではパイプの前後に他の処理を入れることができない。

この問題を解消するには、以下のように関数やブロックを使う。

function post() {
	n=1
	while read line; do
		echo "$n: $line"
		n=`expr $n + 1`
	done
}

ls -C | post



function pre() {
	echo "AbCdE"
	echo "aBcDe"
}

pre | tr '[A-Z]' '[a-z]'



ls -C | {
	n=1
	while read line; do
		echo "$n: $line"
		n=`expr $n + 1`
	done
}



{
	echo "AbCdE"
	echo "aBcDe"
} | tr '[A-Z]' '[a-z]'

メニューに戻る

サブプロセスを起動して双方向通信

サブプロセスを起動する。メインプロセスはサブプロセスと並行動作する。

標準入出力を介して双方向通信を行う。

前項の例と類似で、パイプを多段につなげただけ。

# メインプロセス ←→ サブプロセス双方向のパイプを作って  
# tr を起動  
echo 'AbCdE' | tr '[A-Z]' '[a-z]' | cat
{
	echo "AbCdE"
	echo "aBcDe"
} |

tr '[A-Z]' '[a-z]' |

{
	n=1
	while read line; do
		echo "$n: $line"
		n=`expr $n + 1`
	done
}

メニューに戻る