我想要grep
通过某些行keyword
并将输出重定向到现有文件的特定行号。
命令
grep "key" temp_file >> desired.txt
我需要的是我可以将行附加到文件的grepped
特定行号x
desired.txt
答案1
如果你正确地安排了所采取的步骤,这会很容易。最重要的是获得源文件的缓冲区,如果过度工作,该缓冲区不会崩溃。唯一真正的方法是使用另一个文件 - shell 使这变得非常容易。
{ head -n "$((num_lines_before_insert))"
grep key temp_file; sed \$d
} <<SOURCE_FILE >desired.txt
$( cat <source_file;echo .)
SOURCE_FILE
所以,对于大多数 shell,(包括bash
and zsh
,但不包括dash
or yash
)当您获得<<
here_document时,shell会在中创建一个唯一命名的临时文件${TMPDIR:-/tmp}
,exec
将其放在您指定的输入文件描述符上 -(或者,默认情况下,仅为 0)- 并立即将其删除。当它作为命令的输入时,它是一个未命名文件 - 它没有到任何文件系统的剩余链接,只是等待内核在它完全消失之前清理它。这是一个正确的文件 - 它的数据存在于磁盘上的某个位置(或者,至少在可能的情况下在 VFS 内tmpfs
)并且内核将确保至少在您释放文件描述符之前继续这样做。
这样,只要您的 shell 获得了heredoc的实际支持文件,它们就代表了处理临时文件需求的非常安全且简单的方法,因为它们已完全写入,并且在您读取它们之前,所有文件系统名称都已从中删除。因此,当您工作时,他们的数据无法被篡改。
上面的块首先使用 - 写入临时文件,cat
并保留命令替换中的任何/所有尾随空白行echo
- 这会在文件的末尾添加一行。从{
复合命令}
语句中,它的三个三个命令的输出被写入desired.txt
- 其中两个命令依次从此处读取head
源文件的尾部 - 以及grep
插入key
匹配项的命令。
我不确定您是否需要这个 - 但我认为这与表明您可以使用这样的序列简单而安全地完全覆盖源文件有关。
如果你的外壳不获取heredocs的实际文件,您可以模拟它的功能...
{ set "$$" "${TMPDIR:-/tmp}" "$@"
exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head "-n$((before))"
grep ... keyfile; cat
} <source_file 1<>source_file
...这将确保在采取任何不可逆转的操作之前所有文件都是可写的并安全地分配给文件描述符,而且还会清理所有文件系统前做同样的事。
这是我为了证明这一点而运行的测试:
cd /tmp
set "$$" "${TMPDIR:-/tmp}" "$@"
seq 5000000 >test
printf line\ %s\\n 1 2 3 4 5 >test2
{ exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head -n2500000
grep 3 test2;cat
} <test 1<>test
首先创建了两个文件 - 一个名为/tmp/test
500 万行编号的文件seq
,第二个名为/tmp/test2
5 行,例如...
line 1
line 2
line 3
line 4
line 5
接下来我运行了上面的块,然后我做了......
sed -n '1p;$p;2499999,2500002l' <test
wc -l test
...有趣的是,执行该操作实际上花费了与插入操作相同的时间,并打印了:
1
2499999$
2500000$
line 3$
2500001$
5000000
5000001 test
这是它的工作原理:
- 重定向
1<>
很重要 - 它在标准输出上设置 O_RDWR 标志,并确保每个进程写入文件时都会覆盖文件以前的内容。换句话说,这意味着源/目标文件在任何时候都不会被截断,而是从头到尾重写。 - 命令替换 可以
exec
尽快完成活泼的部分(或者一旦我知道可以)。在命令子内诺克洛伯是set
这样,如果"${TMPDIR:-/tmp}/$$"
已经存在扩展结果exec <"${TMPDIR:-/tmp}/"
,在交互式 shell 中将立即停止整个过程,或者在脚本中,将导致脚本退出并出现有意义的错误,因为 shell 无法将exec
目录作为 stdin。 - 在命令内,子副本
cat
复制source_file
到尚不存在的临时文件,并将echo
名称写入标准输出。 - 一旦所有文件句柄都被
exec
编辑rm
unlink()
为新的临时文件,那么它现在唯一存在的短暂声明就是<
刚刚分配的重定向。 head
查找 250 万行并写入 的source_file
前 250 万行。重点是在两个文件中寻找相等的偏移量。- 请记住,如果新创建的 tmp 文件位于 tmpfs 上并且源文件位于磁盘上(如果此处反转 I/O 并
head
从磁盘文件读取并写入),则这部分的 I/O 效率可能会更高RAM 中的文件。 - 如果您想这样做,那么您需要使
exec <>"$(... head ... <&1 >&0
tmp 文件可读/可写,并且可能在尾部使用head
/指定的行数。tail
在这种情况下,数字甚至不需要准确 - 您可以环形以类似的方式过度输入 - 一次仅推进一点偏移量。 shell 的内置函数read
可用于测试 EOF - 或者wc
可用于循环打开。 - 这是因为
cat
它可能会挂在<>
标准输入上,因为它永远不会看到 EOF。
- 请记住,如果新创建的 tmp 文件位于 tmpfs 上并且源文件位于磁盘上(如果此处反转 I/O 并
grep
从其他文件读取一些数据并将其写入source_file
覆盖仅与从其他地方读取的字节数相同的字节数。cat
纠正grep
由于将其 stdin 的剩余部分写入其 stdout 可能引起的任何差异1<>source_file
。
答案2
不太适合大文件,但ed
可以r
读取命令输出并将其插入到寻址行之后,例如:
ed -s desired.txt <<IN
4r !grep "key" temp_file
w
q
IN
或者,一行:
printf '%s\n' '4r !grep "key" temp_file' w q | ed -s desired.txt
您可以在不同的行号插入不同命令的输出,请记住你必须逆向工作ed
通过行号地址进行迭代时:
ed -s desired.txt <<IN
48r !grep "another_key" another_temp_file
4r !grep "key" temp_file
w
q
IN
答案3
如果desired.txt
是一个非常大的文件,那么您可能需要对其进行优化。它可能可以在外壳级别上做得更优雅,但由于我不熟悉,tcsh
我建议一些应该在那里工作的东西。
sed -n '1,4p' desired.txt >file.tmp
grep "key" temp_file >>file.tmp
sed -n '5,$p' desired.txt >>file.tmp
mv file.tmp desired.txt
答案4
我不明白为什么每个人都试图从简单的任务中制造麻烦?
sed -i "4a$(grep "key" temp_file)" desired.txt
(更改4
为您需要的任何行号)
或者(对于多行grep
输出)
grep "key" temp_file > grepped.tmp
sed -i '4r grepped.tmp' desired.txt