记使用sed的一次坑

sed做为linux下的三剑客,自然功能强大,但是如果使用不当,反而适得其反,今天就因为这个命令采了很深坑,分析一下原因,以谏后来者。

情景回顾:

项目中使用的一个python爬虫采用的是多线程并发爬取,输入为一个存放url的文件,因为程序随时可能停止,所以每次重启程序的时候需要将以爬取过的url去除,实现思路如下:

但是这样实现自动化的时候多有不便,一个程序就涉及五个文件,在加上其他的逻辑,文件众多,用shell脚本实现自动化写出来的代码可读性差,不易理解,所以想重新更改一下策略。有一种思路就是在程序启动的时候将文本读入一个list,然后删除掉处理完成的,程序关闭时将list写入文件,这样看似轻松,但是文本内容过大,而且如果程序异常退出,就会丢失掉内存中list内容,干了半天白干了。

旁边的一个小哥给了个建议,说是处理完就直接从Input中删掉,这样就不用再写shell自动化了,程序重启直接读取input文件就Ok了。

但是如何用python实现删除文件中指定的一行呐?总不能把文件读成列表,然后把那一行删除,然后再写回文件。因为是个大文件,这样效率很低。尤其是在多线程并发的情况下这样会导致大量IO,旁边的小哥又献策说可以用os.system(‘sed -i 1001d %s‘ % filename) 系统调用sed删除该行,在无可奈何的情况下,一想这样编码也不是很简单吗? 于是就欣然接收了这个提议,很快就实现了。

程序运行起来后才发现不是自己想的那样,自己还是没有搞明白sed,

原理剖析:

sed 是一种在线流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

使用-i选项可以直接在原文件进行修改,他会将sed原本输出到屏幕的内容输出到一个以sed开头的临时文件,处理完毕后他会将这个临时文件替换原本的文件,如果运行的时候出现错误,这个临时文件就不会被删除,一直留在那里。

而且在多个线程同时调用sed的时候,就会出现以下情况

线程B删除了第二行但第一行并未删除,当他处理完的时候替换原文本又会将第一行带回去,这样本来已经在线程A中删除的第一行,现在又出现了,于是被下一个线程读取处理,又被删除,又被替换回来,这样就造成一个循环,运行程序但并不能有效删除处理完毕的行。

         sed固然强大,但是应该在直到其实现原理的情况下合理调用,避免在多线程情况下调用。

时间: 2024-08-27 22:58:18

记使用sed的一次坑的相关文章

记C语言浮点数运算处理 "坑" 一则

看一小段C语言程序: int main() { float x = 1.3; x = x - (int)x; int i = (int)(x*10); return 0; } 在你心目中, 变量 I 是怎样的结果? 如果你理所当然地认为是3的话, 那么你就错了~~~ 实际结果应该是2.   为什么? 简而言之, x在内存的值并不是精确的1.3, 实际上可能是1.29999999...... 因为在计算机组成原因中有说过, 浮点数无法被准确地表示出来, 只能是一个非常精确的值.. 就算现在你已经知

记C函数指针的“小坑”

今天遇到一个C函数指针的小坑,索性记下来. 我在a.c 文件里面,引用b.c 文件的函数声明作为指针引用 比如在a.c生命一个函数指针 typedef void (*free)(void *val) fun b.c中的有一个函数 void char_free(void* val){ if (val) LIST_FREE(val); } 但是当我编译的时候 编译器报错:‘char_free’ undeclared 我就纳闷了,一般情况下,在同一可执行程序的源文件中,只要函数不声明为static,其

记一个JAVA关于日期的坑

JAVA解析日期格式代码,之前一直写成:“yyyy-MM-dd hh:mm”,比如"2016-01-18 11:00"."2016-01-18 15:00"都可以正常解析.但是其实用这个格式解析有个“坑”,就是当小时为12时,会解析成0点,因为hh是12制的日期格式.应该写成:“yyyy-MM-dd HH:mm”.踩坑好痛,引以为戒!

菜鸟成长记(八)----- 一个萝卜一个坑

“生活不止眼前的苟且,还有诗和远方……”,忍受着现在的苟且,只是因为心中还存有自己未到过的远方?远方,好模糊的一个词,自己都不清楚它到底通向何方,落脚点在何处!或许,也正因为一切未知,通向它的路途才更有趣,也正因为如此,它才更值得自己去努力,去探索吧.. 上班的时间总感觉要比读书的时候过得快很多,很多时候自己都还没反应过来,时间的流逝就已经超出自己的预期,感觉还有很多事没做,怎么一天的时间就没了,16年就已经在毫无防备的情况下过了快三分之一了,现在自己的情况感觉依旧没什么改善,只是在心态上基本完

Apache POI - 记导出Excel所遇到的坑

Apache POI是个用Java编写的免费开源的,用来操作Microsoft Office格式的文档.实际工作中,经常需要把数据导入到Excel表中.这个目前来说,应该是比较好用的.以前用过NPOI,是其的.Net版本实现,原理差不太多.这篇文章主要说我遇到的问题,并记录一下,好了,上代码: public class HelloWorld{ private String name; private int age; public String getName() { return name;

记一次使用openrowset 的坑

前几天被老大训斥连openrowset 都不会用,然后我就去看了文档,想测试一下栗子~ openrowset 的具体语法我就不贴了,戳这里:https://msdn.microsoft.com/zh-cn/library/ms190312(v=sql.120).aspx 按照文档里面的样例来测试,首先创建一个文档 values.txt 1 Data 1 For 2 Data 2 For 3 Data 3 Fro 然后创建一个格式化文件 v.fmt ,具体非xml格式化文件的写法戳这里 :http

记一次被yield return坑的历程。

事情的经过是这样的: 我用C#写了一个很简单的一个通过迭代生成序列的函数. public static IEnumerable<T> Iterate<T>(this Func<T, T> f, T initVal, int length) { Checker.NullCheck(nameof(f), f); Checker.RangeCheck(nameof(length), length, 0, int.MaxValue); var current = initVal

记 suds 模块循环依赖的坑

下面是soa接口调用的核心代码 #! /usr/bin/python # coding:utf-8 from suds.client import Clientdef SoaRequest(wsdl,fnname,data): soaService = Client(wsdl).service soaRep = getattr(soaService,fnname)(data) return soaRep 问题就这样出现了: 我调用一个接口,总是报错,见下图: 之后Debug断点定位到suds模块

centos7中keeplived代码随记(虚拟机做的真的坑,多练吧)

调度服务器地址192.168.100.100节点1 192.168.100.110节点2 192.168.100.120 -------------------------DR调度服务器------------------------------yum install keepalived ipvsadm -y 关闭防火墙安全组件vi /etc/sysctl.confnet.ipv4.ip_forward=1net.ipv4.conf.all.send_redirects = 0net.ipv4