// 第1出力(stdout)
msg.payload = "コマンドの標準出力"
// 第2出力(stderr)
msg.payload = "エラーメッセージ"
// 第3出力(終了コード)
msg.payload = {
code: 0, // 終了コード(0=成功)
signal: null // シグナル(あれば)
}
📋 サンプルフロー(クリックで展開)
参照元:Node-REDエディター内サンプルフロー
[
{
"id": "fcde1833fb43f65c",
"type": "tab",
"label": "exec",
"disabled": false,
"info": "",
"env": []
},
{
"id": "6a5f26a9.0cc2d8",
"type": "inject",
"z": "fcde1833fb43f65c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "Hello World!",
"payloadType": "str",
"x": 290,
"y": 200,
"wires": [
[
"fc2b343c.bbe2f8"
]
]
},
{
"id": "fc2b343c.bbe2f8",
"type": "exec",
"z": "fcde1833fb43f65c",
"command": "echo",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "",
"x": 430,
"y": 200,
"wires": [
[
"2f3bcd73.6fedf2"
],
[],
[
"3280586e.4e3d28"
]
]
},
{
"id": "2f3bcd73.6fedf2",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 610,
"y": 200,
"wires": []
},
{
"id": "2b7bc9f1.ab36a6",
"type": "comment",
"z": "fcde1833fb43f65c",
"name": "Example: Execute external command appending additional args",
"info": "",
"x": 380,
"y": 100,
"wires": []
},
{
"id": "3280586e.4e3d28",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 610,
"y": 260,
"wires": []
},
{
"id": "feba83da.8f227",
"type": "comment",
"z": "fcde1833fb43f65c",
"name": "↓ execute echo command",
"info": "",
"x": 490,
"y": 155,
"wires": []
},
{
"id": "f507b27c.fff1",
"type": "inject",
"z": "fcde1833fb43f65c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 460,
"wires": [
[
"28b4f75a.eae548"
]
]
},
{
"id": "28b4f75a.eae548",
"type": "exec",
"z": "fcde1833fb43f65c",
"command": "/non/existing/command",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "",
"x": 470,
"y": 460,
"wires": [
[],
[
"27992c1f.a1c964"
],
[
"6e7ff001.2412d"
]
]
},
{
"id": "6e7ff001.2412d",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 690,
"y": 520,
"wires": []
},
{
"id": "27992c1f.a1c964",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 690,
"y": 460,
"wires": []
},
{
"id": "77e85c7c.a560d4",
"type": "comment",
"z": "fcde1833fb43f65c",
"name": "Example: Execute external command and get error output",
"info": "",
"x": 360,
"y": 360,
"wires": []
},
{
"id": "c188c28c.f7d34",
"type": "comment",
"z": "fcde1833fb43f65c",
"name": "↓ try to execute non-existing command",
"info": "",
"x": 510,
"y": 414,
"wires": []
},
{
"id": "2e7be550.92ddfa",
"type": "inject",
"z": "fcde1833fb43f65c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 280,
"y": 740,
"wires": [
[
"53a714a8.1214dc"
]
]
},
{
"id": "53a714a8.1214dc",
"type": "exec",
"z": "fcde1833fb43f65c",
"command": "/bin/sh -c \"while true; do echo Hello; sleep 2; done\"",
"addpay": false,
"append": "",
"useSpawn": "true",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "Repeat message output",
"x": 510,
"y": 740,
"wires": [
[
"d1815f06.463f3"
],
[
"322d9b72.fc9194"
],
[
"2dbf8e03.2d6a52"
]
]
},
{
"id": "7f351806.547908",
"type": "comment",
"z": "fcde1833fb43f65c",
"name": "Example: Execute external command in spawn mode",
"info": "",
"x": 350,
"y": 640,
"wires": []
},
{
"id": "2dbf8e03.2d6a52",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 780,
"wires": []
},
{
"id": "d1815f06.463f3",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 700,
"wires": []
},
{
"id": "28467ba9.c96284",
"type": "comment",
"z": "fcde1833fb43f65c",
"name": "↓ spawn mode: repeat message output",
"info": "",
"x": 550,
"y": 694,
"wires": []
},
{
"id": "322d9b72.fc9194",
"type": "debug",
"z": "fcde1833fb43f65c",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 740,
"wires": []
},
{
"id": "d5a990ce.c660b",
"type": "inject",
"z": "fcde1833fb43f65c",
"name": "Kill process",
"props": [
{
"p": "kill",
"v": "",
"vt": "date"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 290,
"y": 800,
"wires": [
[
"53a714a8.1214dc"
]
]
}
]
コマンド: echo
msg.payloadを追加: チェックON
出力形式: exec(待機)
入力: msg.payload = "Hello World!"
実行: echo Hello World!
出力(stdout): "Hello World!\n"
出力(rc): { code: 0 }
コマンド: /non/existing/command
msg.payloadを追加: チェックOFF
実行: /non/existing/command
出力(stderr): "spawn /non/existing/command ENOENT"
出力(rc): { code: null, signal: null } または エラーコード
コマンド: /bin/sh -c "while true; do echo Hello; sleep 2; done"
出力形式: spawn(リアルタイム)
動作:
- 2秒ごとに "Hello\n" を出力
- msg.kill を受信するとプロセス終了
- 終了時に第3出力から終了コードを出力
コマンド: hostname(または date, df -h, uname -a など)
msg.payloadを追加: チェックOFF
出力例(hostname): "raspberrypi\n"
※ 末尾の改行を除去するにはChangeノードやFunctionノードでtrim()を使用
コマンド: python3 /home/pi/scripts/process.py
msg.payloadを追加: チェックON(スクリプトへの引数として)
// Pythonスクリプト側でsys.argvで引数を受け取り
// 結果をprint()でJSON形式で出力
// Node-RED側でJSON.parse()して利用
演習3: エラーハンドリング中級
📝 課題:
コマンド実行結果の成功/失敗を判定し、適切なメッセージを出力してください。
🎯 要求仕様:
- Inject、Exec、Switch、Debugノードを使用
- 終了コードが 0 なら「成功」、それ以外なら「失敗」と判定
- 成功時と失敗時で異なるDebugノードに出力
📊 期待される動作:
- 正常なコマンド実行 → 「成功」側のDebugに出力
- エラーになるコマンド実行 → 「失敗」側のDebugに出力
✅ 成功の条件:
- 「成功テスト」Injectを押すと、「成功」Debugノードにのみメッセージが届き、「失敗」Debugには届かない
- 「失敗テスト」Injectを押すと、「失敗」Debugノードにのみメッセージが届き、「成功」Debugには届かない
- 成功時の終了コードが
{"code": 0}、失敗時が {"code": 127} など0以外になっている
- Switchノードが
msg.payload.code を参照してルーティングしている
💡 ヒント
Execノードの第3出力(終了コード)を使用
Switchノードの設定:
- プロパティ:
msg.payload.code
- 条件1:
== 0 → 成功
- 条件2:
その他 → 失敗
テスト用コマンド:
- 成功:
echo test
- 失敗:
/non/existing/command
✅ 解答例フロー
[
{
"id": "ex3_inject_ok",
"type": "inject",
"name": "成功テスト",
"props": [{"p": "payload"}],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "test",
"payloadType": "str",
"x": 120,
"y": 80,
"wires": [["ex3_exec"]]
},
{
"id": "ex3_inject_ng",
"type": "inject",
"name": "失敗テスト",
"props": [{"p": "payload"}],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 120,
"y": 140,
"wires": [["ex3_exec_ng"]]
},
{
"id": "ex3_exec",
"type": "exec",
"name": "echoコマンド",
"command": "echo",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"x": 290,
"y": 80,
"wires": [[], [], ["ex3_switch"]]
},
{
"id": "ex3_exec_ng",
"type": "exec",
"name": "存在しないコマンド",
"command": "/non/existing/command",
"addpay": false,
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"x": 320,
"y": 140,
"wires": [[], [], ["ex3_switch"]]
},
{
"id": "ex3_switch",
"type": "switch",
"name": "成功/失敗判定",
"property": "payload.code",
"propertyType": "msg",
"rules": [
{"t": "eq", "v": "0", "vt": "num"},
{"t": "else"}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 520,
"y": 100,
"wires": [["ex3_debug_ok"], ["ex3_debug_ng"]]
},
{
"id": "ex3_debug_ok",
"type": "debug",
"name": "成功",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 690,
"y": 80,
"wires": []
},
{
"id": "ex3_debug_ng",
"type": "debug",
"name": "失敗",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 690,
"y": 140,
"wires": []
}
]
Injectノード(定期実行: 1分ごと)
↓
Execノード: python3 /home/pi/read_sensor.py
↓
Functionノード: JSON.parse(msg.payload)
↓
MQTTノード or ダッシュボード表示
用途: DHT22温湿度センサー、BME280気圧センサーなどからデータ取得
Injectノード(定期実行: 5分ごと)
↓
Execノード: df -h / free -m / uptime
↓
Functionノード: 出力をパース
↓
閾値チェック → アラート通知
用途: ディスク使用率、メモリ使用量、システム稼働時間の監視
Injectノード(定期実行: 毎日深夜2時)
↓
Execノード: /home/pi/backup.sh
↓
終了コード確認(Switchノード)
↓
成功: ログ記録
失敗: Slack/メール通知
用途: データベースバックアップ、ログローテーション
HTTPリクエスト(/led/on)
↓
Execノード: gpio write 0 1
↓
レスポンス返却
用途: LED、リレー、モーターなどの制御
※ wiringPiやpigpioコマンドを使用
// 危険な例(ユーザー入力を直接使用)
コマンド: rm -rf
msg.payloadを追加: ON ← ユーザーが "/ --no-preserve-root" を入力したら...
// 安全な例(ホワイトリスト方式)
Functionノードで入力値を検証してからExecノードに渡す
許可された値のみを使用する