yae*_*ael 4 sed awk perl text-processing
我们有以下示例文件:
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
systat 11/tcp users
systat 11/udp users
daytime 13/tcp
daytime 13/udp
qotd 17/tcp quote
qotd 17/udp quote
msp 18/tcp # Message send protocol (historic)
msp 18/udp # Message send protocol (historic)
chargen 19/tcp ttytst source
chargen 19/udp ttytst source
Run Code Online (Sandbox Code Playgroud)
我们如何将以下几行附加到文件的开头?
# The latest IANA port assignments can be gotten from
# http://www.iana.org/assignments/port-numbers
# The Well Known Ports are those from 0 through 1023.
# The Registered Ports are those from 1024 through 49151
# The Dynamic and/or Private Ports are those from 49152 through 65535
#
# Each line describes one service, and is of the form:
#
# service-name port/protocol [aliases ...] [# comment]
Run Code Online (Sandbox Code Playgroud)
这样文件看起来像:
# The latest IANA port assignments can be gotten from
# http://www.iana.org/assignments/port-numbers
# The Well Known Ports are those from 0 through 1023.
# The Registered Ports are those from 1024 through 49151
# The Dynamic and/or Private Ports are those from 49152 through 65535
#
# Each line describes one service, and is of the form:
#
# service-name port/protocol [aliases ...] [# comment]
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
systat 11/tcp users
systat 11/udp users
daytime 13/tcp
daytime 13/udp
qotd 17/tcp quote
qotd 17/udp quote
msp 18/tcp # Message send protocol (historic)
msp 18/udp # Message send protocol (historic)
chargen 19/tcp ttytst source
chargen 19/udp ttytst source
Run Code Online (Sandbox Code Playgroud)
简单的解决方案是将原始文件复制到file.bck
,将新行追加file.bck
到文件中,然后追加到文件中。
但这不是一个优雅的解决方案。
相对使用优雅溶液POSIX指定的文件编辑器ex
-至少优雅的意义上,这将处理任何任意的内容,而不是依赖于特定的格式(后反斜杠)或特定的不存在的格式。
printf '0r headerfile\nx\n' | ex file-with-contents
Run Code Online (Sandbox Code Playgroud)
这将file-with-contents
在 中打开ex
,读入headerfile
最顶部的全部内容,然后将修改后的缓冲区保存回file-with-contents
.
如果性能是一种严重的关注和文件是巨大的,这可能不是适合你的方式,但(一)有没有高性能的通用方式在前面加上数据文件和(b)我不指望你会编辑您的/etc/services
文件是经常。
稍微简洁的语法(我实际编码的方式):
printf '%s\n' '0r headerfile' x | ex file-with-contents
Run Code Online (Sandbox Code Playgroud)
下面是一段更复杂但更收敛的代码,它将检查 的开头是否services
与 的全部匹配,逐header
字节匹配,然后 IF NOT 将添加header
to的全部内容services
并保存更改,如下所示。
这是完全符合 POSIX 的。
dd if=services bs=1 count="$(wc -c < header)" 2>/dev/null |
cmp -s - header ||
printf '%s\n' '0r header' x |
ex services
Run Code Online (Sandbox Code Playgroud)
一个更简单的版本,使用 GNUcmp
的“-n”选项:
cmp -sn "$(wc -c <header)" header services ||
printf '%s\n' '0r header' x | ex services
Run Code Online (Sandbox Code Playgroud)
当然,这两种方法都不够智能,无法检查 PARTIAL 匹配项,但这远远超出了简单单行的能力,因为本质上会涉及猜测。
通常,你就是这样做的。在文件中添加行很困难,因为文件只是字节序列,因此您需要将现有数据向前移动以为新数据腾出空间,并且没有直接的方法(至少没有标准方法)。理论上,人们可能会想象一个基于可变长度记录的文件系统,您可以在开始处或现有记录之间添加新记录,但这并不是它在实践中的工作方式。
某些文件系统可以移动数据块,但它们是固定大小的块,因此对于行长度可变的文本文件没有多大用处。
即使您执行类似sed -i
或 之类的操作perl -i
,他们也会出于这个原因在幕后创建一个临时文件。
所以,无论优雅与否,我都会选择:
cat prefix data > data.new && mv data.new data
Run Code Online (Sandbox Code Playgroud)
对于几行,您可以使用(在 GNU sed 中):
sed -i.bak -e '1i first prefix line' -e '1i second prefix line' data
Run Code Online (Sandbox Code Playgroud)
但是生成插入命令或为要添加的每一行添加反斜杠也不是很优雅。