我想搜索文件中的每条记录(记录由空行定义)以查找模式NAME#AAAA
。如果匹配,则#
在记录AGE
行前面插入 ,并将该行移动到段落顶部。然后AGE NIL
在末尾插入该行:
输入文件:
NAME#AAAA
STD 1
SEC A
AGE 5
NAME#BBBB
STD 2
SEC B
AGE 6
NAME#CCCC
STD 3
SEC C
AGE 7
NAME#AAAA
STD 4
AGE 9
NAME#AAAA
STD 7
SEC A
AGE 12
预期产出
#AGE 5
NAME#AAAA
STD 1
SEC A
AGE NIL
NAME#BBBB
STD 2
SEC B
AGE 6
NAME#CCCC
STD 3
SEC C
AGE 7
#AGE 9
NAME#AAAA
STD 4
AGE NIL
#AGE 12
NAME#AAAA
STD 7
SEC A
AGE NIL
另外,我需要它的逆。只是为了恢复所做的更改。请注意,我是在 AIX 机器上执行所有这些操作的。
答案1
ex
这是POSIX 指定的文件编辑工具的完美用例。
vi
(顺便说一句,如果您曾经使用过,您可能会很熟悉,ex
因为您输入的vi
以冒号开头的所有内容:
都是ex
命令。 ex
是 的前身vi
。)
printf %s\\n 'g/NAME#AAAA/ /AGE/t- | s/^/#/ | /AGE/s/.*/AGE NIL/' x | ex input.txt
如果你想在实际保存文件之前进行测试,请将x
管道符号之前的最后一个更改为%p
,修改后的文件将不会被保存,但修改后的版本将打印到stdout
.所以这是测试命令:
printf %s\\n 'g/NAME#AAAA/ /AGE/t- | s/^/#/ | /AGE/s/.*/AGE NIL/' %p | ex input.txt
解释:
printf %s\\n
提供了一种简单的方法来输入多个命令,并ex
在每个命令后添加一个换行符。
g/regex/
是全局命令;它在与给定正则表达式匹配的每一行上运行后面的命令(直到下一个换行符)。
/AGE/t-
将与模式匹配的下一行复制/AGE/
到当前行(即该行)之前的位置NAME#AAAA
。它还将光标移动到该行的新副本(以便现在成为“当前行”)。
|
是 中的命令分隔符ex
。
s/^/#/
在复制的AGE
行前添加主题标签。 (或者井号,具体取决于您的方言。);)
下一个命令实际上有两个部分:/AGE/
是地址,它使该命令在包含该模式的下一行上运行,并s/.*/AGE NIL/
用 替换该行的任何内容AGE NIL
。
x
保存对文件的更改并退出。
撤销更改
要撤销更改,我将执行以下操作:
printf %s\\n 'g/NAME#AAAA/ ?^#AGE? m /^AGE/ | s/^#// | -d' %p | ex input.txt
然后,当更改得到验证时,实际保存更改:
printf %s\\n 'g/NAME#AAAA/ ?^#AGE? m /^AGE/ | s/^#// | -d' x | ex input.txt
解释:
与以前一样的全局命令。
取出 NAME 行之前的以 开头的行#AGE
,将其移到以 开头的下一行之后AGE
。
删除前导#
.
删除紧邻的前一行-d
(即 NIL 年龄行)。
打印或保存更改。
答案2
perl -pe 'BEGIN{$/=""}
s/^(NAME#AAAA.*\n)(AGE.*?)(\n+)$/#$2\n$1AGE NIL$3/s' ex1
非常简短的解释:
For all the registers in input | perl -p
separator= one or more empty lines | BEGIN{$/=""}
do:
| substitute | s/
| ^(NAME AAAA.*\n)(AGE.*?)(\n+)$ | regex /
| 1 2 3 |
| by | /subst. string including
| # $2 \n $1 AGE NIL $3 | capture groups/
| |
| and print | ...from option -p
更新:
是否可以用变量代替 NAME#AAAA ?
perl -pe '
BEGIN{
$/="";
$f=shift; }
s/^(NAME#$f.*\n)(AGE.*?)(\n+)$/#$2\n$1AGE NIL$3/s' AAAA ex1
在此版本中我们必须提供一个图案参数(例如:“AAAA”):
- 第 4 行:从命令行获取第一个参数(“AAAA”)并将其存储在 $f 中
- 第 5 行:在替换模式中展开 $f。