如何将 bash 中的输入文件格式化为特殊格式?

How to format an input file in bash to a special format?

我有一个文本文件,它是通过来自各种服务器 crontab 的 bash 脚本生成的,格式如下:

0 sys1 server1
5 sys2 server2
2,3 sys3
0 sys3 server3
7 sys4 server4
....

我希望它格式化为:

0 sys1 server1
5 sys2 server2
2 sys3 server3
3 sys3 server3
0 sys3 server3
7 sys4 server4
....

我遇到的问题是,如果每个 sys/server 有两个数字或更多,并且每个服务器也可能有两个以上的 crontab 条目,我总是可以更改它。

我的尝试是这样的:

    rday_old=""
    rsys_old=""
    rser_old=""
    [[ -e output ]] && rm output
    while read -u5 -r -a line; do
            rday=${line[0]}
            rsys=${line[1]}
            rser=${line[2]} 
            if [[ "$rsys_old" == "$rsys" ]]; then
                    echo "$rday_old $rsys_old $rser" >> output
            else
                    echo "$rday $rsys $rser" >> output    
            fi   
            rday_old=$rday
            rsys_old=$rsys
            rser_old=$rser  
    done 5< input_file

问题很明显,它不会像预期的那样工作。我不知道解决这个问题的最佳方法是什么。我的尝试没有考虑到如果一天的格式为 2,3 order 甚至 2,4,5 最多 7 天(crontab 格式(这将是一个 * 符号)),则需要拆分。此外,我可能需要存储多个 rday_old、rsys_old 和 rser_old.

我希望我以一种可以理解的方式陈述我的问题。 提前感谢您的帮助。

编辑:

在@anishsane 的回答后,我将他的回答修改为

cat input | awk '{sys=; ser=; split(,a,","); for(i in a){print a[i]" "  sys " " ser}}' > output

但它现在输出结果:

0 sys1 server1
5 sys2 server2
2 sys3 
3 sys3 server3
0 sys3 server3
7 sys4 server4
....

所以我快到了。

这是一个 Python 解决方案:

#!/usr/bin/env python

import fileinput
import re

for line in fileinput.input():
    matches = re.match('(\d),(\d) sys(\d+)', line)
    if matches:
        first, last, sys = matches.groups()
        for ii in range(int(first), int(last) + 1):
            print ii, 'sys' + sys, 'server' + sys
    else:
        print line,

它使用一个简单的正则表达式来匹配第一个字段中带有逗号的行。如果匹配,则从数字范围的开头到结尾打印行。否则打印原始行。

gawk方法:

$ cat srvlist
0 sys1 server1
5 sys2 server2
2,3 sys3
0 sys3 server3
7 sys4 server4

$ awk '{sys=gensub("sys","","",); split(,a,","); for(i in a){print a[i] " sys" sys " server" sys}}' srvlist
0 sys1 server1
5 sys2 server2
2 sys3 server3
3 sys3 server3
0 sys3 server3
7 sys4 server4

解释:

  1. sys 变量从第二个字段中提取数字 - sys1、sys3 等(这将分别包含 1、3)
  2. 以逗号作为分隔符将第一个字段拆分为数组a
  3. 根据需要循环打印字符串 a
  4. 输入文件中的字段 3 被忽略。至少对于提供的输入,字段 2 和 3 始终具有相同的数字。

现在解决了:

[[ -e output ]] && rm output
tac input > rev_input
rser_old=""
while read -u5 -r -a line; do
        rday=${line[0]}
        rsys=${line[1]}
        rser=${line[2]}
        OIFS=$IFS
        IFS=','
        for x in $rday
        do
                [[ "$rser" == "" ]] && echo "$x $rsys $rser_old" >> output
                [[ "$rser" != "" ]] && echo "$x $rsys $rser" >> output
        done
        rser_old=$rser
        IFS=$OIFS
done 5< rev_input

输出现在显然是颠倒的,但这根本不重要,因为这部分只是一个步骤,输出现在将被处理为另一个脚本。

感谢您提供的帮助。