Givendd
被认为对于这种就地行删除来说太危险了,我们需要一些其他方法来对文件系统调用进行相当细粒度的控制。我最初的冲动是用 c 写一些东西,但尽管可能,我认为这有点矫枉过正。相反,值得寻找常见的脚本(不是 shell 脚本)语言,因为这些语言通常具有相当低级的文件 API,它们以相当直接的方式映射到文件系统调用。我猜这可以使用 python、perl、Tcl 或许多其他可用的脚本语言之一来完成。我最熟悉 Tcl,所以我们开始吧:
#!/bin/sh
# \
exec tclsh "$0" "$@"
package require Tclx
set removeline [lindex $argv 0]
set filename [lindex $argv 1]
set infile [open $filename RDONLY]
for {set lineNumber 1} {$lineNumber < $removeline} {incr lineNumber} {
if {[eof $infile]} {
close $infile
puts "EOF at line $lineNumber"
exit
}
gets $infile line
}
set bytecount [tell $infile]
gets $infile rmline
set outfile [open $filename RDWR]
seek $outfile $bytecount start
while {[gets $infile line] >= 0} {
puts $outfile $line
}
ftruncate -fileid $outfile [tell $outfile]
close $infile
close $outfile
请注意,在我的特定盒子上,我有 Tcl 8.4,所以我必须加载 Tclx 包才能使用 ftruncate 命令。在 Tcl 8.5 中,chan truncate
可以使用 which 来代替。
您可以将要删除的行号和文件名传递给此脚本。
简而言之,脚本执行以下操作:
- 打开文件进行阅读
- 阅读前 n-1 行
- 获取下一行(第 n 行)开头的偏移量
- 读取第 n 行
- 使用新的 FD 打开文件进行写入
- 将写入 FD 的文件位置移动到第 n 行开头的偏移量
- 继续从 read FD 中读取剩余的行并将它们写入 write FD,直到整个 read FD 被读取
- 截断写FD
该文件被准确地编辑到位。不使用临时文件。
我很确定这可以用 python 或 perl 重写,或者......如果需要的话。
更新
好的,因此可以使用与上面的 Tcl 脚本类似的技术,在几乎纯 bash 中完成就地行删除。但最大的警告是您需要有truncate
可用的命令。我在我的 Ubuntu 12.04 VM 上确实有它,但在我较旧的基于 Redhat 的机器上没有。这是脚本:
#!/bin/bash
n=$1
filename=$2
exec 3<> $filename
exec 4<> $filename
linecount=1
bytecount=0
while IFS="" read -r line <&3 ; do
if [[ $linecount == $n ]]; then
echo "omitting line $linecount: $line"
else
echo "$line" >&4
((bytecount += ${#line} + 1))
fi
((linecount++))
done
exec 3>&-
exec 4>&-
truncate -s $bytecount $filename
#### or if you can tolerate dd, just to do the truncate:
# dd of="$filename" bs=1 seek=$bytecount count=0
#### or if you have python
# python -c "open(\"$filename\", \"ab\").truncate($bytecount)"
我很想听到一种更通用的(仅限 bash?)方法来在最后进行部分截断并完成此答案。当然,截断也可以完成dd
,但我认为我之前的回答已经排除了这一点。
为了记录,这个站点列出了如何用许多不同的语言进行就地文件截断——以防万一这些语言中的任何一种都可以在您的环境中使用。