You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Go实现bash管道中文本流的读取与格式化?

问题描述

我现在用这条命令格式化npm脚本的输出:

npm run startWin | while IFS= read -r line; do printf '%b\n' "$line"; done | less

这个命令在Linux上正常工作,但同事不用Linux,我想用Go实现while IFS= read -r line; do printf '%b\n' "$line"; done的功能,生成二进制文件用于管道,也就是:

npm run startWin | magical-go-formater

我尝试了下面的代码,但程序没有输出任何文本流内容:

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

func main() {
  fi, _ := os.Stdin.Stat() // get the FileInfo struct

  if (fi.Mode() & os.ModeCharDevice) == 0 {

    bytes, _ := ioutil.ReadAll(os.Stdin)
    str := string(bytes)
    arr := strings.Fields(str)

    for _, v := range arr {
      fmt.Println(v)
    }
}

问题分析与修复

你的代码有几个关键问题导致无输出或功能不符合预期:

  1. 语法错误:main函数里的if块未闭合,编译时直接报错,程序无法正常运行。
  2. 逻辑偏差:strings.Fields会把所有空白字符(换行、空格、制表符等)作为分隔符,将整段输入拆成单个单词输出,完全偏离了逐行处理的需求。
  3. 非流式处理:ioutil.ReadAll会等待整个输入流结束才开始处理,对于持续输出的npm脚本(比如开发服务器日志),会导致迟迟看不到输出。
  4. 未实现核心功能:bash的printf '%b'会解析字符串中的反斜杠转义序列(比如\n转换行、\t转制表符),你的代码直接输出原始字符串,没做转义解析。
  5. 忽略错误处理:os.Stdin.Stat()和ioutil.ReadAll的错误被直接忽略,潜在问题无法排查。

下面是修复后的代码,完全实现目标功能:

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// 实现bash printf '%b'的转义解析逻辑
func parseEscapes(s string) string {
	var result strings.Builder
	n := len(s)
	for i := 0; i < n; i++ {
		if s[i] != '\\' {
			result.WriteByte(s[i])
			continue
		}
		i++
		if i >= n {
			result.WriteByte('\\')
			break
		}
		switch s[i] {
		case 'n':
			result.WriteByte('\n')
		case 't':
			result.WriteByte('\t')
		case 'r':
			result.WriteByte('\r')
		case 'b':
			result.WriteByte('\b')
		case 'f':
			result.WriteByte('\f')
		case '\\':
			result.WriteByte('\\')
		case '\'':
			result.WriteByte('\'')
		case '"':
			result.WriteByte('"')
		case '0', '1', '2', '3', '4', '5', '6', '7':
			// 解析八进制转义,最多3位
			j := i
			for j < n && j < i+3 && s[j] >= '0' && s[j] <= '7' {
				j++
			}
			octal := s[i:j]
			val, err := strconv.ParseInt(octal, 8, 8)
			if err == nil {
				result.WriteByte(byte(val))
			} else {
				result.WriteString("\\" + octal)
			}
			i = j - 1
		case 'x':
			// 解析十六进制转义,2位
			if i+2 > n {
				result.WriteString("\\x")
				break
			}
			hex := s[i+1 : i+2]
			val, err := strconv.ParseInt(hex, 16, 8)
			if err == nil {
				result.WriteByte(byte(val))
				i += 1
			} else {
				result.WriteString("\\x" + hex)
				i += 1
			}
		default:
			// 未知转义,保留原样
			result.WriteByte('\\')
			result.WriteByte(s[i])
		}
	}
	return result.String()
}

func main() {
	// 检查是否存在管道输入
	fi, err := os.Stdin.Stat()
	if err != nil {
		fmt.Fprintf(os.Stderr, "读取标准输入信息失败: %v\n", err)
		os.Exit(1)
	}

	if (fi.Mode() & os.ModeCharDevice) == 0 {
		// 逐行读取输入,流式处理实时输出
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			line := scanner.Text()
			// 解析转义序列,对应bash的printf '%b'
			parsedLine := parseEscapes(line)
			// 输出解析后的内容并添加换行,对应printf '%b\n'
			fmt.Println(parsedLine)
		}

		if err := scanner.Err(); err != nil {
			fmt.Fprintf(os.Stderr, "读取标准输入失败: %v\n", err)
			os.Exit(1)
		}
	} else {
		// 直接运行程序时提示用法
		fmt.Println("用法: 程序需要通过管道接收输入,例如:")
		fmt.Println("npm run startWin | magical-go-formater")
	}
}

代码说明

  1. 逐行流式处理:用bufio.Scanner逐行读取标准输入,收到一行就处理一行,适合实时日志输出场景。
  2. 转义解析:parseEscapes函数实现了bashprintf '%b'的核心转义解析逻辑,支持常见的转义序列。
  3. 错误处理:添加完整的错误捕获和提示,方便排查问题。
  4. 交互提示:直接运行程序时会显示用法说明,避免用户误用。

编译与使用

在Go环境下编译:

go build -o magical-go-formater main.go

编译出的二进制文件可在Windows、Linux、macOS上直接使用,用法和预期一致:

npm run startWin | magical-go-formater

内容的提问来源于stack exchange,提问作者Pedro Gomes Branquinho Facti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 21:45:38