标准输入、标准输出、标准错误概念以及在go中的使用

322 阅读6分钟

一、概念

1.1 标准输入输出

在计算机编程和操作系统中,标准输入(stdin)、标准输出(stdout) 和 标准错误(stderr) 是三个非常重要的概念。它们是程序与外部环境(如用户或其他程序)进行交互的主要方式。

  1. 标准输入(Standard Input, stdin)

    • 定义:标准输入是程序从外部接收数据的默认通道。
    • 常见来源:

      • 用户通过键盘输入。
      • 来自其他程序的管道输入。
    • 文件描述符:0

  2. 标准输出(Standard Output, stdout)

    • 定义:标准输出是程序向外部发送正常输出数据的默认通道。

    • 常见目标:

      • 显示在终端或命令行窗口中。
      • 输出到文件或通过管道传递给另一个程序。
    • 文件描述符:1

  3. 标准错误(Standard Error, stderr)

    • 定义:标准错误是程序向外部发送错误信息的默认通道。

    • 目的:将错误信息与正常输出分开,以便更容易调试和处理错误。

    • 常见用途:

      • 打印错误消息。
      • 记录异常情况。
    • 文件描述符:2

1.2 文件描述符

  • 文件描述符 是操作系统用于标识打开文件或其他I/O资源的整数。对于标准输入、标准输出和标准错误,它们分别对应以下文件描述符:

    • 0:标准输入(stdin)
    • 1:标准输出(stdout)
    • 2:标准错误(stderr)

1.3实际应用

在批处理脚本或命令行工具中,您可以重定向这些流以实现更灵活的数据处理

二、go中的标准输入输出

在Go语言中,标准输入(stdin)、标准输出(stdout)和标准错误(stderr)通常通过 os 包中的预定义变量来访问。这些变量默认指向操作系统的标准文件描述符,其实理解成标准输入输出文件。

  1. 标准输出和标准错误的默认行为

    • 标准输出(stdout) 和 标准错误(stderr) 默认情况下会输出到终端或命令行窗口。
    • 如果程序被重定向(例如通过命令行),则它们会被重定向到指定的目标文件或其他流。
  2. Go 中的标准文件描述符

    • Go 的 os 包提供了以下预定义变量:

      • os.Stdin:表示标准输入。
      • os.Stdout:表示标准输出。
      • os.Stderr:表示标准错误。
    • 这些变量底层使用了操作系统提供的文件描述符:

      • 0:标准输入(stdin)
      • 1:标准输出(stdout)
      • 2:标准错误(stderr)
  3. 如何确定输出目标

    • 命令行重定向(常见):使用 > 或 2> 等符号可以将标准输出或标准错误重定向到文件。

    • 环境配置:配置标准输入输出的默认行为

    • 程序内部重定向

           f, err := os.OpenFile("output.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
           if err != nil {
               log.Fatal(err)
           }
           defer f.Close()
      ​
           // 将标准输出重定向到文件
           log.SetOutput(f)
      ​
           // 或者使用 os.Stdout 和 os.Stderr
           os.Stdout = f
           os.Stderr = f
           
      

三、输入输出默认行为的控制

3.1 默认控制

默认情况下,标准输出(stdout)和标准错误(stderr)会输出到终端或命令行窗口,这是由操作系统和运行时环境共同决定的。具体来说:

  1. 操作系统层面:

    • 操作系统为每个进程分配了三个标准文件描述符:0(stdin)、1(stdout)和2(stderr)。
    • 这些文件描述符最初指向终端设备(例如TTY),即命令行窗口。
  2. 运行时环境:

    • 当程序启动时,操作系统会将这些文件描述符传递给程序的运行时环境。
    • 对于Go程序,Go的运行时库会初始化 os.Stdin、os.Stdout 和 os.Stderr,并将它们绑定到这些文件描述符。
  3. 应用程序层面:

    • 应用程序通过标准库(如Go的 fmt.Println 或 log.Printf)使用这些文件描述符进行输入输出操作。
    • 如果没有显式的重定向或修改,这些操作会直接写入初始绑定的目标(通常是终端)。

3.2 命令行重定向

当您在命令行中使用重定向符号(如 > 或 2>)时,操作系统会在启动程序之前修改这些文件描述符的绑定目标。

myprogram > output.txt 2> error.txt
​

在这个例子中,操作系统会将 stdout 绑定到 output.txt 文件,将 stderr 绑定到 error.txt 文件,然后再启动 myprogram。

3.3 代码显示修改流传向

这其实是修改了go中预定义变量的指向,指向其他文件描述符,操作系统层面标准文件描述符未变。

package main
​
import (
    "fmt"
    "io"
    "log"
    "os"
)
​
func main() {
    // 打开一个日志文件
    f, err := os.OpenFile("output.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
​
    // 将标准输出重定向到文件
    oldStdout := os.Stdout
    os.Stdout = f
​
    // 输出内容将写入文件
    fmt.Println("This goes to the log file.")
​
    // 恢复标准输出
    os.Stdout = oldStdout
    fmt.Println("This goes to the terminal.")
}
​

四、是不是重定向命令行程序没法接收,程序只能处理命令行参数?

重定向与命令行参数的区别

重定向和命令行参数是两种不同的机制,它们分别用于处理输入输出流和传递参数给程序。理解这两者的区别有助于更好地设计和使用命令行程序。

4.1 命令行参数

  • 定义:命令行参数是在启动程序时通过命令行传递给程序的参数。

  • 用途:用于配置程序的行为、指定文件路径、设置选项等。

  • 示例:

      myprogram --input=input.txt --output=output.txt
    

    在Go中,可以通过 os.Args 或使用第三方库(如 flag 包)来解析命令行参数

    package main
    ​
    import (
        "flag"
        "fmt"
    )
    ​
    func main() {
        input := flag.String("input", "", "input file path")
        output := flag.String("output", "", "output file path")
        flag.Parse()
    ​
        fmt.Printf("Input file: %s\n", *input)
        fmt.Printf("Output file: %s\n", *output)
    }
    ​
    

4.2 重定向

  • 定义:重定向是指将标准输入、标准输出或标准错误流重新指向其他文件或设备。

  • 用途:用于捕获程序的输出、提供批量输入数据、分离正常输出和错误信息等。

  • 示例:

    • 将标准输出重定向到文件:

          myprogram > output.txt
      
    • 将标准错误重定向到文件:

          myprogram 2> error.txt
      
    • 将文件内容作为标准输入:

          myprogram < input.txt
      

    在Go中,程序不需要显式处理重定向,因为操作系统会自动将重定向后的文件描述符传递给程序,重定向命令行是操作系统层面的,012已经重定向指向对应的文件。程序只需像往常一样读取 os.Stdin 和写入 os.Stdout、os.Stderr 即可。

4.3 结合使用

您可以同时使用命令行参数和重定向。例如:

myprogram --output=output.txt < input.txt > processed.txt 2> error.txt

在这个例子中:

  • --output=output.txt 是命令行参数,指定输出文件路径。
  • < input.txt 将 input.txt 的内容作为标准输入。
  • 1> processed.txt(> processed.txt) 将标准输出重定向到 processed.txt。
  • 2> error.txt 将标准错误重定向到 error.txt。

程序如何处理

命令行参数:程序需要显式解析这些参数,通常在 main 函数中进行。

重定向:程序无需特别处理,只要正常使用 os.Stdin、os.Stdout 和 os.Stderr 即可。

package main
​
import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)
​
func main() {
    // 解析命令行参数
    output := flag.String("output", "", "output file path")
    flag.Parse()
​
    // 读取标准输入
    scanner := bufio.NewScanner(os.Stdin)
    var lines []string
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        log.Fatalf("Error reading input: %v", err)
    }
​
    // 写入标准输出或文件
    var writer io.Writer
    if *output == "" {
        writer = os.Stdout
    } else {
        f, err := os.OpenFile(*output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
        if err != nil {
            log.Fatalf("Error opening output file: %v", err)
        }
        defer f.Close()
        writer = f
    }
​
    for _, line := range lines {
        fmt.Fprintln(writer, line)
    }
}
​

4.4 总结

命令行参数 用于配置程序行为,需要显式解析。

重定向 用于处理输入输出流,由操作系统自动处理,程序只需正常使用标准文件描述符。

您可以在同一个程序中同时使用命令行参数和重定向,以实现更灵活的功能。