如何将 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
解释:
sys
变量从第二个字段中提取数字 - sys1、sys3 等(这将分别包含 1、3)
- 以逗号作为分隔符将第一个字段拆分为数组
a
- 根据需要循环打印字符串
a
- 输入文件中的字段 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
输出现在显然是颠倒的,但这根本不重要,因为这部分只是一个步骤,输出现在将被处理为另一个脚本。
感谢您提供的帮助。
我有一个文本文件,它是通过来自各种服务器 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
解释:
sys
变量从第二个字段中提取数字 - sys1、sys3 等(这将分别包含 1、3)- 以逗号作为分隔符将第一个字段拆分为数组
a
- 根据需要循环打印字符串
a
- 输入文件中的字段 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
输出现在显然是颠倒的,但这根本不重要,因为这部分只是一个步骤,输出现在将被处理为另一个脚本。
感谢您提供的帮助。