我想操作一个文本文件。它包含许多以下形式的块
CLASS
...some stuff ...
END
我想复制每个块并添加和删除其内容的一行。我可以编写脚本吗?
答案1
好吧,你能编写任何内容。
但是,如果我这样做,我会使用vim
宏。 (如果你还不熟悉vim
,你应该学习它,否则忽略它)
一些基本的指导:
q -- 开始录制宏
qx -- 停止录制宏并将其命名为“x”
@x -- 运行名为“x”的宏
宏将立即重新运行您在录制会话期间键入的任何命令。
因此,如果您想要一个宏来复制类似该类型的块:
q start macro
/CLASS jump to next "CLASS"
V start selecting full lines
/END jump to next "END
Y yank selected lines (yank = copy)
j scroll down one line
p put (paste)
qd stop recording and name the macro d
有很多关于 vim 用法和 vim 宏的好资源和示例。
就其价值而言,sed 是 vim 的一种命令行版本,因此您也许能够将我所说的内容改编为 sed,但我对它不太熟悉,所以我不能保证这一点。
~~编辑~~
使其更有用的另一件事是vim
能够打开多个文件。打开的多个文件称为缓冲区。您可以通过在后面添加多个文件名来激活此功能vim
(IEvim *.txt
打开所有 txt 文件)
要在多个缓冲区上运行宏,请首先同时打开所有文件。然后,使用在每个缓冲区上:bufdo normal @x
运行宏。x
请注意,您必须将其:w
作为宏的最后一步,以避免在当前缓冲区未保存的情况下尝试切换到下一个缓冲区时陷入困境。
:bn
将手动转到下一个缓冲区。
另请参阅https://stackoverflow.com/questions/1291962/replay-a-vim-macro-until-end-of-buffer了解如何为每一行运行宏。连连看 :)
答案2
用于此目的的自然工具是 awk 和 Perl(假设您想要编写脚本:一次性的,自然工具是交互式编辑器)。这是一个 awk 脚本,它复制所有CLASS
…END
块(不支持平衡:每个块CLASS
与下一个块匹配END
),但foo
第二个副本中省略了行。
awk '
/^CLASS$/ { store = 1; } # start storing
store && ! /^foo$/ { hold = hold ORS $0; } # if storing, maybe save line
/^END$/ {
$0 = $0 hold; # append hold copy to current line
store = 0; hold = ""; # end of block
}
1 { print; } # print original line, with hold prepended if at end of block
'
这是一个 sed 解决方案;别太认真了。如果CLASS
/END
行不是严格交替的,则不要指望它会起作用。
sed -e '/^CLASS$/,/^END$/!b' \
-e '/^CLASS$/{' -e 'h' -e 'b' -e '}' \
-e '/^foo$/!H' \
-e '/^END$/G'
答案3
确切使用的工具取决于工作的具体情况。如果该作业只会发生一次,您可以考虑在vim
或中使用宏emacs
。如果你真的想编写脚本,我会研究sed
:
Sed 是一个流编辑器。流编辑器用于对输入流(文件或来自管道的输入)执行基本文本转换。
同样,执行此操作的确切方法取决于您的情况,但要在以 PATTERN 开头的行之后添加一行,您可以执行以下操作:
sed -e 's/\(^PATTERN.*\)/\1\nTextToAdd/' file.old > file.new
要在以 PATTERN 开头的行之前添加一行,您可以执行以下操作:
sed -e 's/\(^PATTERN.*\)/TextToAdd\n\1/' file.old > file.new
删除以 PATTERN 开头的行
sed -e 's/\(^PATTERN.*\)//' file.old > file.new
诀窍是找到正确的模式,只捕获您想要的线条。
您可以通过执行以下操作来组合这些:
sed -e 's/\(^PATTERN1.*\)/\1\nTextToAdd/' -e 's/\(^PATTERN2.*\)/TextToAdd\n\1/' file.old > file.new
答案4
我的Python解决方案:
file = open('a.txt', 'r')
arr = []
tmp = []
remember = False
for line in file:
if "CLASS" in line:
remember = True # start storing lines
tmp.append(line) # we're storing lines in list
continue # go to next line
if "END" in line:
tmp.append(line) # store line
remember = False # stop storing lines
arr.append(tmp) # add line to list
tmp = [] # clear temp list
continue # go to next line
if remember:
tmp.append(line) # add to list line between CLASS and END
file.close()
file = open('a.txt', 'a')
for tmp in arr:
if ' ...some stuff ...1\n' in tmp: # we can skip
continue # whole block if we find this
for i in tmp:
if "333" in i: # we can skip only one line
continue
file.write(i)
file.close()
我认为它比 awk 版本最容易理解和修改(如果你不知道 awk)...顺便说一句,awk 是我想学习的语言列表中的下一种语言...;)