用另一个脚本处理stderr

时间:2018-04-10 14:51:40

标签: bash stderr

我有一个脚本(例如,compute.sh)在我的Ubuntu 16.04上做了一些事情并且可能以错误结束,让我们说:

[compute.sh]

#!/bin/bash

...

MISCELLANEOUS OPERATIONS

...

[if error occours]
echo "Error 55a" >&2;
exit 1;

...

echo "Execution ok";
exit 0;

和另一个处理错误的脚本(例如error_handler.sh)

[error_handler.sh]

error=$(cat)

for i in "$@"
do
case $i in
    -u=*|--user=*)
    username="${i#*=}"
    shift
    ;;
    -t=*|--task=*)
    task="${i#*=}"
    shift
    ;;
esac
done

...

SENDS ERROR REPORTING THROUGH AN API LISTENING REMOTELY

...

我想执行compute.sh,将其stderr重定向到error_handler.sh,以便通知我的API系统发生错误

如果我尝试这个测试:

user@ubuntu: echo "test message" | ./error_handler.sh --user=dude --task="simple task"

我的error_handler.sh脚本接受字符串"测试消息"并正确处理它

如何只将stderr输出重定向到我的error_handler.sh脚本?

编辑:因为有时compute.sh可能会在没有错误消息的情况下失败(只需执行退出1;)我不确定错误是否= $(cat)它是捕获错误的正确方法error_handler.sh中的消息。还有其他选择吗?

EDIT2:任务可以在crontab中执行,所以我需要在同一个命令中执行所有操作

1 个答案:

答案 0 :(得分:3)

最简单的方法是创建一个命名管道作为两者之间的缓冲区。

mkfifo errors           # "errors" is an arbitrary file name
compute.sh 2> errors &  # Run in the background
error_handler.sh < errors

作为一行:

mkfifo errors; compute.sh 2> errors & error_handler.sh 

现在两个进程同时运行,error_handler.sh可以从errors读取compute.sh写入它。缓冲区的大小有限,因此如果compute.sh已满,error_handler.sh将自动阻止。 compute.sh消费一些输入后,error_handler.sh会自动恢复。只要错误不会产生得太快(即比compute.sh处理它们的速度快),error_handler.sh就会像缓冲区一样无限运行。

如果缓冲区被清空,compute.sh将阻塞,直到有更多输入可用,或者直到foo | bar通过退出关闭其管道末尾。

常规管道语法mkfifo tmp foo > tmp & bar < tmp 创建一个匿名管道(或未命名管道),它只是

的快捷方式
using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Linq;
using HoloToolkit.Unity;
using System.Collections.Generic;
using UnityEngine.UI;
using Newtonsoft.Json;
#if !UNITY_EDITOR
using Windows.Networking.Sockets;
using Windows.Networking.Connectivity;
using Windows.Networking;

#endif
#if !UNITY_EDITOR
public class RootObject
{
    public int Index { get; set; }
    public bool Moto { get; set; }
    public bool Start { get; set; }
    public bool StartWINCC { get; set; }
    public bool Stop { get; set; }
    public bool StopWINCC { get; set; }
    public bool Tag1 { get; set; }
    public bool Tag2 { get; set; }
}
#endif
public class UDPCommunication : Singleton<UDPCommunication>
{
    // Connection variables
    public string port = "8000";
    public string externalIP = "172.16.24.136";
    public string externalPort = "8000";
    // UI/Text elements
    public Text testert;
    public Image moto;
    public Image start;
    public Image startwincc;
    public Image stop;
    public Image stopwincc;
    public Image tag1;
    public Image tag2;
    public String uitext;
    // Sets up a Queue
    public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();


#if !UNITY_EDITOR
    // Socket initialization
    DatagramSocket socket;
#endif
#if !UNITY_EDITOR
    // use this for initialization
    async void Start()
    {
        Debug.Log("Waiting for a connection...");

        socket = new DatagramSocket();
        socket.MessageReceived += Socket_MessageReceived;

        HostName IP = null;
        try
        {
            var icp = NetworkInformation.GetInternetConnectionProfile();

            IP = Windows.Networking.Connectivity.NetworkInformation.GetHostNames()
            .SingleOrDefault(
                hn =>
                    hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
                    == icp.NetworkAdapter.NetworkAdapterId);

            await socket.BindEndpointAsync(IP, port);
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            Debug.Log(SocketError.GetStatus(e.HResult).ToString());
            return;
        }

        var message = "hello from " + IP;
        await SendMessage(message);
        await SendMessage("hello");

        Debug.Log("exit start");
    }

    private async System.Threading.Tasks.Task SendMessage(string message)
    {
        using (var stream = await socket.GetOutputStreamAsync(new Windows.Networking.HostName(externalIP), externalPort))
        {
            using (var writer = new Windows.Storage.Streams.DataWriter(stream))
            {
                var data = Encoding.UTF8.GetBytes(message);

                writer.WriteBytes(data);
                await writer.StoreAsync();
                Debug.Log("Sent: " + message);
            }
        }
    }
#else
    // Use this for initialization.
    void Start()
    {

    }
#endif
    // Update is called once per frame.
    void Update()
    {
        // Dequeues items until there are no more items on the queue.
        while (ExecuteOnMainThread.Count > 0)
        {
            ExecuteOnMainThread.Dequeue().Invoke();
        }
    }
#if !UNITY_EDITOR
    // this method is purely for setting the UI elements based on the received JSON string.
    private void setStuff(string input){
        // Turns the json string into an object
        var results = JsonConvert.DeserializeObject<RootObject>(input);
        // Sets the UI element(and converts it to string, because it is an int)
        testert.text = results.Index.ToString();
        // sets the image green if the variable is true, and red if it's not
        if (results.Moto == true)
        {
            moto.GetComponent<Image>().color = Color.green;
        }
        else
        {
            moto.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Start == true)
        {
            start.GetComponent<Image>().color = Color.green;
        }
        else
        {
            start.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.StartWINCC == true)
        {
            startwincc.GetComponent<Image>().color = Color.green;
        }
        else
        {
            startwincc.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Stop == true)
        {
            stop.GetComponent<Image>().color = Color.green;
        }
        else
        {
            stop.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.StopWINCC == true)
        {
            stopwincc.GetComponent<Image>().color = Color.green;
        }
        else
        {
            stopwincc.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Tag1 == true)
        {
            tag1.GetComponent<Image>().color = Color.green;
        }
        else
        {
            tag1.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Tag2 == true)
        {
            tag2.GetComponent<Image>().color = Color.green;
        }
        else
        {
            tag2.GetComponent<Image>().color = Color.red;
        }
    }
    //this method gets called when a message is received
    private async void Socket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args)
    {
        // Read the received message.
        Stream streamIn = args.GetDataStream().AsStreamForRead();
        StreamReader reader = new StreamReader(streamIn);
        string message = await reader.ReadLineAsync();
        Debug.Log("MESSAGE: " + message);
        // if the count is zero, the message will be relayed to the setStuff method, which processes the string continuously.
        // The message contains a JSON string which is received from the server.
        if (ExecuteOnMainThread.Count == 0)
        {
            ExecuteOnMainThread.Enqueue(() =>
            {
                setStuff(message);
            });
        }
    }
#endif
}

但限制您将一个命令的标准输出连接到另一个命令的标准输入。使用其他文件描述符需要扭曲重定向。使用命名管道要稍微长一些,但可以 更清楚地阅读。