为什么侠盗猎手3四?改了分辨率就没有了中文和英文的呢!就只有一些奇怪的符号。(((((((((凉,大

当前访客身份:游客 [
Web 安全从业者
:我觉得15-29题可以使用这个 '^[\d|(]?\d?\d?\d?...
:按照楼主说的,编出来了!!!
:引用来自“coolcao”的评论我进行编译的时候出现...
:我进行编译的时候出现如下错误,不知道怎么回事,...
:不错,基本涵盖了常用的入侵手段
今日访问:3
昨日访问:44
本周访问:3
本月访问:771
所有访问:61488
列表模式: |
原文地址(the5fire的博客):
先做性能分析 - 两个工具
django.db.connection
from django.db import connection
print connection.queries
''' result is:
'time': '0.002',
'sql': u'SELECT `django_session`.`session_key`, `django_session`.`session_data`, `django_session`.`expire_date` FROM `django_session` WHERE (`django_session`.`session_key` = ddc2d5e084
AND `django_session`.`expire_date` &
10:39:36 )'}]
django_debug_toolbar&
标准的数据库优化技巧
Indexes, 分析应该添加什么样的索引,使用&django.db.models.Field.db_index
使用对应的字段类型
title = models.CharField(max_length=100, blank=True, db_index=True, verbose_name=u'标题')
理解QuerySets
理解QuerySet的求值过程
QuerySets是惰性的
news_list = News.object.all()
# 此时并未执行数据库查询
print news_list
# 用时方执行查询操作
何时它们被执行.
# 用时方执行查询操作
print news_list
数据如何被缓存
这样的QuerySet没有被缓存
print([e.headline for e in Entry.objects.all()])
print([e.pub_date for e in Entry.objects.all()])
entries = Entry.objects.all()
print([e.headline for e in entries])
理解被缓存的属性
QuerySet 会被缓存
不可被调用的属性会被缓存
&&& news = News.objects.get(id=1)
&&& news.channel
# 此时的channel对象会从数据库取出
&&& news.channel
# 这时的channel是缓存的版本,不会造成数据库访问
方法的调用每次都会触发数据库查询
&&& news = News.objects.get(id=1)
&&& news.authors.all()
# 执行查询
&&& news.authors.all()
# 再次执行查询
模板系统不允许使用括号,但它会自动调用可被调用的属性
自定义的属性需要由你来实现缓存。
使用with模板标签
在模板中使用QuerySet缓存,需要使用with标签
使用iterator()
获取大量数据时
news_list = News.objects.filter(title__contains=u'违法')
for news in news_list.iterator():
print news
让数据库做它自己的工作
使用&filter and exclude&在数据库层面执行过滤操作
news_list = News.objects.filter(title__contains=u'和谐').exclude(status=1)
使用&F() object query expressions&在同一模型中使用不同字段进行对比过滤
# 查询所有title和sub_title相同的数据
queryset = News.objects.filter(title=F('sub_title'))
# 给每个对象添加一个news_count的属性
cl = Channel.objects.filter(parent__id=1).annotate(news_count=Count('news'))
print cl[0].news_count
如果这些还不足以生成你需要的SQL的话,继续往下看:
使用&QuerySet.extra()
显式的执行SQL语句
cl = Channel.objects.filter(parent__id=1).extra(
'another_news_count': 'SELECT COUNT(*) FROM web_news WHERE web_news.channel_id = web_channel.id'
print cl[0].another_news_count
使用原生的SQL
cl = Channel.objects.raw('SELECT * FROM web_channel WHERE parent_id = 1')
# &RawQuerySet: 'SELECT * FROM web_channel WHERE parent_id = 1'&
for c in cl:
预加载数据
尽量一次加载你需要的数据
QuerySet.select_related()&, 针对foreign key 和 one-to-one
news = News.objects.select_related().get(id=)
print news.channel # 不会访问数据库
QuerySet.prefetch_related()&, 1.4中存在, 和select_related()类似,针对many-to-many
不要获取你不需要的数据
使用&QuerySet.values()&和&values_list()
当只需要一个字段的值,返回list或者dict时,使用
news_list = News.objects.values('title').filter(channel__id=1)
print news_list
# [{'title': ''}, ...]
values_list
news_list = News.objects.values_list('title').filter(channel__id=1)
print news_list
# [('新闻标题',),('新闻标题', ) ...]
使用&QuerySet.defer()&和&only()
QuerySet.defer() 来延迟加载某字段,加载时会产生额外查询
news_list = News.object.defer('title').all()
n = news_list[0]
print n.title
# 会产生额外的查询语句
QuerySet.only() 只加载某字段,之后读取任何属性都会产生查询
使用 QuerySet.count()
如果你只是想要获取有多少数据,不要使用&len(queryset)&。
nl = News.objects.filter(channel__id=2)
nl.count()
# SELECT COUNT(*) FROM `web_news` WHERE `web_news`.`channel_id` = 2 ; 'time': '0.014'
# 'time': '0.422'
使用 QuerySet.exists()
如果你只是想要知道是否至少存在一个结果,不要使用&if querysets&。
不要过度使用&count()&和&exists()
比如,假设有一个Email的model,有一个&body&的属性和一个多对多关系的User 属性,下面的模板代码是最优的:
{% if display_inbox %}
{% with emails=user.emails.all %}
{% if emails %}
&p&You have {{ emails|length }} email(s)&/p&
{% for email in emails %}
&p&{{ email.body }}&/p&
{% endfor %}
{% else %}
&p&No messages today.&/p&
{% endif %}
{% endwith %}
{% endif %}
它是最优的是因为:
因为QuerySet是惰性的,如果 'display_inbox' 是False的话,这不会产生数据库 查询。
使用&with&意味着我们会存储&user.emails.all&在一个变量中供后面使用,这允许被缓存以便重用。
{% if emails %}&其实是调用&QuerySet.__nonzero__()&,在数据库层面执行user.emails.all()&,然后返回结果,放入缓存。
{{ emails|length }}&的使用将调用&QuerySet.__len__(),数据已在缓存
for&循环的email数据已经在缓存中了。
with的使用是关键
每次的QuerySet.count()调用都会产生查询
使用&QuerySet.update()&和&delete()
批量更新使用&QuerySet.update()
批量删除使用&QuerySet.delete()
批量操作不会调用类中定义的&save()&或&delete()&方法
直接使用外键的值
获取频道ID:
news.channel_id
news.channel.id
用&django.db.models.query.QuerySet.bulk_create()&批量创建对象,减少SQL查询的 数量。比如
Entry.objects.bulk_create([
Entry(headline=&Python 3.0 Released&),
Entry(headline=&Python 3.1 Planned&)
Entry.objects.create(headline=&Python 3.0 Released&)
Entry.objects.create(headline=&Python 3.1 Planned&)
这同样适用于&ManyToManyFields, 因此,这么做
team.members.add(me, my_friend)
...而不是这么做
team.members.add(me)
team.members.add(my_friend)
...这里&team&和&members&是多对多的关系。
参考资源:
发布于 2年前,
阅读(139) | 评论(0) |
投票(0) | 收藏(18)
每个人都会遇到情绪波动,有时抑郁有时愤怒。本文作者以自己两个时间段的情绪经历来分析情绪的分析和控制。
今天和大家聊聊情绪,很多人都会遇到的情绪。
我之前是个很容易急躁的人,最近四五年,看佛经、老子、圣经、经典哲学书籍,心性有了一些变化,虽然偶尔还是容易激动,但不会太影响情绪,当场就能处理好,遇到大一点的事情顶多在心里一两天就能放下,也不会影响心情。也许是我没有遇到让我处理不了的事情,现在能说大话。即使这样,周围有些朋友也觉得我状态不错,向我请教是如何控制情绪的,是如何走出情绪的。这是个很好的话题。
刚好昨天下午我就有点烦躁,我今天就拿昨天的例子和大家分享。情绪产生的过程很有点长,大家慢慢看。
昨天下午HR同事电话告诉我,她老板说我之前推荐的那个小伙子还需要再安排一次面试,让我推荐更高Level的技术leader和HR资深总监一起面试。我听了以后顿时觉得很不爽,因为之前已经见过两轮,加上我有三轮,专门讨论过两次,我还单独写了一封邮件说明,一个级别也不算高的员工,只是因为其学历背景有些特殊,来来回回搞了两个多星期。我当时有点恼火的回复说,要安排你们自己安排,我没人推荐也没时间安排,然后挂了电话。我想:既然你们不信任我,做事拖拖拉拉,你们就自己玩好了,我懒得参与!
接下来,我把昨天晚上写好的文章《老子智慧-强大处下》推送出去,在闲谈部分和之前的作者翔云哥搞了个小活动,链接到淘宝的某个商品上。发现微信审核没通过,微博的推送也有问题。顿时觉得这些平台真是太过分了,连加个淘宝链接都不行,这个活动还是我免费帮翔云哥推送的,他之前给我投稿过,而且他这个活动给鬼友有非常大的折扣,我一分钱都没收,他搞活动也不赚钱,就是为了回馈鬼友。我想:我每天辛苦写文章,就算放个淘宝链接做广告也应该可以啊,nnd,凭什么就不通过?!
接下来,我把文章中的活动信息全部删除,链接删除,重新发送,这次微博微信发送成功了。微博上私信有几个人又在说:你tmd烦不烦啊每天推送广告;微信中有个留言说是心灵鸡汤,让我当时忍不住回复了他,大家看看下图(从下往上看):
昨天的文章我真的觉得写得很好。我想:你丫看都没看怎么知道?居然把老子智慧说成是心灵鸡汤,滚!最后顺手拉黑了。
发生这么多事情后,我察觉到内心的浮躁,于是我在微信朋友圈发了四个字:浮躁,浮、躁!
接下来我开始观察自己的情绪,我是为什么心烦气躁呢?因为同事不信任,因为微信微博平台太苛刻,因为读者不认可。三件事情,任何单独一件,我估计一会儿就好了。但三件事情连在一起,就让我情绪波动很大。
真的是如此么?仔细想想,其实不是。微博微信平台要求苛刻,我早就知道,我以前也遇到过类似情况,半点波澜都没起,这次为什么为心生怨气?读者不认可我的文章,我早就知道,比那个留言说的更恶心的也有,我大部分时候一笑而过。但这次我为什么会生气还去主动回复?最根本的原因是第一件事情的情绪还没有处理完,情绪带入了第二件事情,第二件事情的情绪没有处理好,接着带入到第三件事请。
想起奥修写过一个故事:
一个科学家早上起床没找到拖鞋,有点恼火。当他没注意到自己在生气,他去卫生间洗簌,这时候生气一直在自己生长,他刮胡子,不小心剃须刀掉地上了,捡起来不小心又掉了,他心情更糟。但他不能对剃须刀发脾气啊,他走出来卫生间,了解到小孩昨天的作业没有做完,于是他大发雷霆,打了小孩一巴掌。他老婆莫名其妙,于是他们吵了起来。科学家摔门而出,开车上班去办公室。但最后他没到达办公室,因为路上出车祸了。
整个事情的起因居然是因为早上没有找到拖鞋!实际上不是这样的,没找到拖鞋,如果当时观察到自己的情绪,就不会生气了,因为我们犯不上为一双拖鞋而生气;剃须刀掉了,如果能当时观察自己的情况,也不会生气了,因为我们犯不上为一把剃须刀生气;当然也犯不上因为小孩子没写完作业就打一巴掌。
明白情绪是什么了吗?如果还不明白,再举几个例子。
我们看电灯在发光,学过物理的人都知道,其实是电流在交叉流动;我们看蜡烛在发光,仔细看,是一层层新的火焰产生;河流,是一滴一滴的水连在一起向前走。
情绪,也是一条河流,一个小念头接着一个小念头,在持续往前走。
我昨天下午的例子,还只有三个大的阶段,如果把每个阶段再细分一下,估计会更有意思。如果我每经过一件事,能观察到自己情绪的变化,及时处理,后面的事情都不会发生。而且发现的越早,自己越有能力处理。越晚发现,情绪会积累的越厉害。当小溪汇聚成大河,力量就势不可挡。
情绪不是靠控制解决的,而是靠观察。只要自己察觉到情绪,观察情绪,情绪会自然消失。回到之前我的例子:
1 同事是在不信任我么?他们只是在走流程而已。我为流程而生气不太值得吧。
2 微信微博平台苛刻?他们也不是针对我一个人,我对客观环境生气没有必要啊?
3 读者不认可?那么多读者都在表示感谢呢,难道要祈求100%的人都说好话?
当我当时没有意识到自己的情绪变化,于是情绪积累越来越多。最后平时不会在意的事情,也产生了情绪。
早上去大连,早上发现钱包找不到了,身份证,银行卡,信用卡,现金都在里面,关键那个钱包还很有纪念意义,我心情顿时烦躁起来。不过我觉察到了自己的情绪,我想:钱包丢了,心情再不好就损失更大了啊,只是补办麻烦一点而已,赶紧想办法解决吧。
我在机场去办理临时身份证,办证的人有点拖拉,好不容易办完了,又说没有零钱找我,我说零钱就算了。等我办好了去柜台办理登机牌,告诉我已经来不及了,早5分钟还可以,现在已经关舱门了。我顿时无语啊!身边有个哥们也是如此,他在大声抱怨!我淡定的笑了笑,因为我已经观察了自己的情绪了。
去南航售票柜台办理改签,小姑娘告诉我说只能改签到晚上10点多。当时是上午10:40,还要我等12个小时。身边那个一样误点的哥们快崩溃了,咆哮说为什么这么晚,为什么下午没有飞机,为什么晚了5分钟就要等12个小时!我淡定的笑了笑,因为我已经观察了自己的情绪。
我让同事帮我把票退了,重新订了厦航的航班。打了几个电话处理了一下工作。接到老婆的微信说,姐早上带老妈去医院,医生说老妈的糖尿病比较严重,小医院治不了一定要去大医院治疗。我回电话问了一下情况,然后说你们下午去医院吧,实在不行就住院几天应该问题不大,我出差会尽快回来的,放心。
处理完这些事情,我安静的坐在机场把这个过程记录下来,观察整个情绪的过程。
假如我一开始没有观察到我的情绪,我今天上午是个多么糟糕的上午啊! &钱包丢失、身份证、信用卡、银行卡都丢了,办证拖延让我耽误航班,老妈身体还不好……我会心浮气躁,会跟身边任何人发脾气,搞不好还会跟航空公司的人吵起来。但这样做除了伤害了自己,伤害了别人,对解决问题有任何作用么?
庆幸的是我一开始就观察到了自己的情绪,每次情绪起来的时候,当下就能平复,有什么好烦躁的呢?说不定我的钱包能找回来,说不定那个小伙子的招聘会很快有结果,说不定老妈的病情很快能好转,说不定一会儿在飞机上身边还坐着一位美女…….
我现在能平静的坐在机场,看看窗外,又有一只不知名的鸟飞过。喔,为什么我说“又”?
情绪就是一条河,如果没有及时观察,这条河流会汇聚越来越多的小溪流,越来越大。河流平时很平静,但一旦爆发,力量无法控制。
合抱之木,生于毫末;九层之台,起于垒土。我搬不动九层之台,但我可以很容易处理掉一小块垒土;我折不断合抱之木,但我可以很容易折断一棵小树苗。处理情绪也是如此。
修行之人说:活在当下。我好像有点懂了,分享给你,希望对你也有帮助。
——原文: 淘宝鬼脚七
发布于 2年前,
阅读(21) | 评论(0) |
投票(0) | 收藏(0)
【7哥导读】老子不倡导美, 也不倡导善,其实所有倡导老子都不乐意。如果世界上只剩下一个人,他是美还是丑,是善还是恶?老子把很多习以为常的结论都打破了,用优美的语言告诉我们,生活就是生活,不需要那么多判断(关注微信&鬼脚七& 回复m 查看更多精彩文章)。
天下皆知美之为美,斯恶已;皆知善之为善,斯不善已。
故有无相生,难易相成,长短相形,高下相倾,音声相和,前后相随。
是以圣人处无为之事,行不言之教。
万物作焉而弗始,生而弗有,为而弗恃,功成而弗居。
夫唯弗居,是以不去。
—-老子《第二章》
[参考译文]
天下都知道美之所以为美,丑的观念也就产生了; 都知道善之所以为善,不善的观念也就产生了。 有和无互相生成,难和易相互促就,场合断互为显示,高和下互为呈现,音和声彼此应和,前和后链接相随。 所以有道的人以无为的态度来处理世事,实行不言的教导;万物兴起而不加干涉;生养万物而不据为己有;作育万物而不自持己能;功业成就而不自我夸耀。 因他不自我夸耀,所以他的功绩不会泯没。 —-陈鼓应《老子今注今译》
这一章是所有章节中最容易混淆,也较难懂的一章,但这一章基本上是老子观点的总纲:处无为之事,行不言之教。
好与坏、美与丑、善与恶、长与短这些概念在我们脑海中已经根深蒂固了。我记得小时候看电影,总是要问,谁是好人谁是坏人。现在我们看电视剧,也会区分谁是好人谁是坏人,每个角色都很明显。但真正的生活中,有谁会觉得自己是坏人么?
老子的观点是,不要去区分好还是坏,不要去区分美还是丑,也不要去区分善还是不善。当我们都知道什么是美的时候,让丑的观念就产生,本身就是一件不美的事情。当天下知道什么是善的时候,让不善的观念产生,这件事情就很不善。如果社会上大家都不知道什么是美,世界才真是美好的;上大家都不知道什么是善,世界才真是善良的。
一个有意思的现象,当社会宣传什么,一定是社会缺乏什么。宣传要建和谐社会,那么社会一定不怎么和谐;宣传公务员一定要廉洁,那么一定是有太多的腐败。同样的道理,一个人强调的一定是他缺乏的,追求的一定是他没有的。炫富的一定不是真正的富人,真正的富人从来不会觉得富有多了不起;哭穷的人也不会是真正的穷人,真正的穷人最害怕别人知道他穷;经常满口仁义道德的人,很有可能就是伪君子;整天说自己不想赚钱的人,一定是内心很想赚钱。有时候一再否定,也是反应内心的肯定。
老子刚说完不要用二分法,马上又用二分法的方式来解释有和无、高和低。在我们世俗的眼里,几乎每个事情都有两面性,快和慢、高和低、长和短、美和丑、善和恶…….老子说看上去对立的东西都不是真正对立的,他们是互补的,是相互依存密不可分的。没有丑,怎么会有美?没有慢也不会有快。如果社会上都是美女,那么也就没有美女了;社会上都是大善人,也就不存在大善人。
美和丑不是一件事情的两个对立面,而是一体的,就像生命和死亡是一体的一样。大家想像一下,如果没有死亡,会不会有生命?生命也是时刻在死亡。如果没有地狱,会不会有天堂?
回到本源,本来没有美和丑,只有我们人为定义。也没有善和恶,只有社会形成的道德。现在苗条为美,唐朝体胖为美;现在多一个老婆是小三,以前娶两个老婆是本事,三妻四妾才正常。如果还不理解,我们可以思考一个极端情况,当地球上只剩下你一个人的时候,你说你是美还是丑?是善还是恶?是高还是矮?是胖还是瘦?所有的一切判断,都是比较出来的。
有人犯法了,就会被送往监狱。但社会何尝不是一个监狱?社会有法律,社会有道德,社会有家庭,有婚姻,有户口,有交通规则…….这些条条框框和监狱约束犯人本质是一样的,只不过活动范围大了一些而已。特别是教育和道德的约束,跟让我们的思想形成了看不见的牢笼。很多人都看过一部电影《肖申克的救赎》,有个犯人从监狱里呆了几十年,出狱以后他上厕所都要报告一下。如果我们的教育是所有人上厕所都要报告,是不是效果和监狱一样?有人觉得这有些荒唐,其实一点也不荒唐,在80年代跳交谊舞就等同耍流氓,听说现在朝鲜,私自带手机也是违法。
社会就是一个大监狱,只是我们每个人都没意识到而已。
好了,接着回来说老子。虽然老子不倡导区分,但是社会还是有区分,怎么办?我们还是有美和丑,还是有善和恶。道家的另外一位领军人物庄子说了一个观点:
为善无近名,为恶无近刑。
庄子的观点更加怪异:行善可以,但不要太过,不要追求名利而为善;为恶也不是不可以,不要太强求,不违背大原则就好。现代人的观点,这完全是离经背道了,与我们倡导的学习雷锋完全不一样,但庄子说这才是养生和保全自己的方法。
每个人看完这一章,都有不同的理解和领悟。我这里说说我的观点。
我们不要去判断一个人。世界上没有绝对的善和恶,好和坏,美和丑。我们认为善的,对别人可能就是恶。以鬼脚七做自媒体为例,鬼脚七每天写文章,对大家有帮助,是在行善,但是对家人来说,少了很多时间陪家人陪父母,这就是恶。一名父亲为了养活儿女偷了别人的东西,对社会是恶,对他儿女就是行善。我们看见那么多情侣,有时候心里会为某一位打抱不平,你这么好的条件,怎么会看上TA?其实不然,因为情人眼里出西施。虽然我们一直在判断,但我们为什么要去判断?这个问题值得思考。
我们要去接受一个完整的人。每个人都有别人眼里的优点和缺点,每个人都有自己的长处和不足。我们喜欢上一个人,经常是因为他的优点,讨厌一个人经常是因为他的缺点。但优点和缺点都是这个人,我们接受优点的时候也需要接受其缺点。喜欢玫瑰花的美,就应该接受它的刺;欣赏雪花的漂亮,就要接受它的寒冷。
婚姻也是如此,长相厮守,会有很多好处,也会带来很多烦恼。接受爱情的同时,我们也要接受生活的琐碎。接受了对方的美好,长时间也会了解到对方的缺点。之所以那么多夫妇离婚,也就是因为无法接受一个完整的对方。
我们经常有这种感觉,容易跟父母说话很不耐烦,或者容易跟很亲近的人发火。正是因为越亲近的人,越容易发现其缺点。亲情又有些不一样,我们接受了亲情,但整个过程中,父母逐渐老去,越老他们的缺点就会越明显:反应迟钝、啰嗦、忘事、固执。我们容易接受他们的年轻,但不大愿意接受他们的老去。造成很多年轻人对父母越来越不耐烦。
我们不要过于在乎别人的判断。有人赞美你,就一定会有人批评你;有人帮助你,就一定会有人打击你。所有人的评价,只代表他自己的价值观,就算全社会的人对你是这个评价,也只是代表这个社会是这个价值观。价值观,本身就没有对和错,只有合适还是不合适,只有你认同还是不认同。但丁说,走自己的路,让别人去说吧。做到这一点,就真的可以洒脱了。
我们经常在走自己的路,但非常在意别人的评价。包括我自己也是,很多时候还是放不下。当有人表扬自己的时候,特开心;当有人批评的自己的时候,特别是一些自己在乎的人批评自己的时候,就非常沮丧。这时候我会提醒自己,这就是我:无论他们认不认同,这就是我;无论选择是对是错,这就是我。每当我多提醒自己几次以后,就慢慢放下了。
说到放下,在生活中很多地方可以修炼自己放下的能力,不只是有感情。有一次我问一个禅师,你会发脾气么?他说,我偶尔会发脾气,发不发脾气没关系,关键在于你发完脾气能否放下。就像小孩子一样,他哭完了能马上就笑。这是一种放下的能力,小孩子从来不会失眠。我们成人就很难做到,一旦伤心,会伤心很久,几天,几十天,或者几年。
理解了老子对立统一的思想,也就可以慢慢理解老子的核心观念:反者道之动。理解之后应该如何?老子说:处无为之事,行不言之教,功成而弗居。
——原文: 淘宝鬼脚七
发布于 2年前,
阅读(120) | 评论(3) |
投票(0) | 收藏(0)
以下所列出的链接均为在线文档,有志于信息安全的爱好者可由此作为入门指南。
应用软件安全
多学科课程
涉及源码审计、Web安全、逆向工程、漏洞挖掘、Post-Exploitation、应用软件安全
涉及逆向工程、漏洞挖掘、取证技术、恶意软件分析
涉及源码审计、应用软件安全、漏洞挖掘、网络安全、Web安全、Post-Exploitation
涉及漏洞挖掘、网络安全、Web安全
涉及漏洞挖掘、网络安全、移动安全、应用软件安全、Web安全、恶意软件分析
涉及网络安全、应用软件安全、漏洞挖掘、Post-Exploitation
多学科资源
应用软件安全
嵌入式设备安全
应用程序安全
作者:Mark Dowd、John McDonald、Justin Schuh
作者:Tobias Klein
作者:Michael Sutton、Adam Greene、Pedram Amini
作者:Jon Erickson
作者:Chris Anley、John Heasman、Felix Lindner、Gerardo Richarte
作者:Stuart McClure、Joel Scambray、George Kurtz
作者:Chris Eagle
作者:Eldad Eilam
作者:Dafydd Stuttard、Marcus Pinto
作者:Michal Zalewski
(全文完)
版权声明:
自由转载-非商用-非衍生-保持署名&|&
原文链接:
发布于 2年前,
阅读(141) | 评论(3) |
投票(0) | 收藏(3)
java 定时任务的话 可以用 JDK 自带的TimerTask &来实现(具体查阅api 文档), 而这个定时任务 为了跟随 web 项目 启动而启动的话 有两种 实现方式。
1:web.xml里配置一个Servlet
设置其随web server的启动而启动。然后在该Servlet的init()方法里启动定时器,在destory()方法里销毁定时器。
2:web.xml里配置一个Listener
在该Listener的初始化方法里启动定时器,在其销毁的方法销毁定时器。
下面是 demo:
继承TimerTask&
public class CrmDataTimerTasker
extends TimerTask {
private static Logger log=Logger.getLogger(CrmDataTimerTasker.class);
public void run() {
//需要执行的任务
System.out.println(&这里是你要调用的任务&);
public class TimerManager {
private static final long PERIOD_DAY = 24*60*60*1000;
private static Timer timer = new Timer(false);
public TimerManager(){
System.out.println(&-------------------------------------------定时任务初始化&);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 15);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
Date date=calendar.getTime();
//如果第一次执行定时任务的时间 小于 当前的时间
//此时要在 第一次执行定时任务的时间 加一天,以便此任务在下个时间点执行。如果不加一天,任务会立即执行。
if(date.before(new Date())){
date=this.addDay(date, 1);
CrmDataTimerTasker task = new CrmDataTimerTasker();//这个是你的任务
timer.schedule(task, date,PERIOD_DAY);
public static void cancel(){
timer.cancel();
public Date addDay(Date date,int num){
Calendar calendar=Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, num);
return calendar.getTime();
我选择在 web.xml 里面 配置一个监听器
&listener&
&display-name&StartListener&/display-name&
&listener-class&com.aisino.crm.remote.service.crm.until.crmTaskerListener&/listener-class&
&/listener&
crmTaskerListener.java
public class crmTaskerListener implements ServletContextListener {
private static Logger log=Logger.getLogger(crmTaskerListener.class);
public void contextInitialized(ServletContextEvent sce) {
(&++++++++++++++++++++我开始监听了++++++++++++++++++++++++&);
(&++++++++++++++++++++我开始监听了++++++++++++++++++++++++&);
new TimerManager();
public void contextDestroyed(ServletContextEvent sce) {
(&==================销毁了=====================&);
TimerManager.cancel();
一些相关资料可以参阅:
web.xml 详细介绍&
定时任务相关:&
发布于 2年前,
阅读(118) | 评论(0) |
投票(0) | 收藏(2)
什么是CSRF
CSRF(Cross-site request forgery跨站请求伪造,也被称成为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
比如可以这样进行攻击:
一个网站用户Bob可能正在浏览聊天论坛,而同时另一个用户Alice也在此论坛中,并且后者刚刚发布了一个具有Bob银行链接的图片消息。设想一下,Alice编写了一个在Bob的银行站点上进行取款的form提交的链接,并将此链接作为图片tag。如果Bob的银行在cookie中保存他的授权信息,并且此cookie没有过期,那么当Bob的浏览器尝试装载图片时将提交这个取款form和他的cookie,这样在没经Bob同意的情况下便授权了这次事务。
对于web站点,将持久化的授权方法(例如cookie或者HTTP授权)切换为瞬时的授权方法(在每个form中提供隐藏field),这将帮助网站防止这些攻击。一种类似的方式是在form中包含秘密信息、用户指定的代号作为cookie之外的验证。
另一个可选的方法是“双提交”cookie。此方法只工作于Ajax请求,但它能够作为无需改变大量form的全局修正方法。如果某个授权的cookie在form post之前正被JavaScript代码读取,那么限制跨域规则将被应用。如果服务器需要在Post请求体或者URL中包含授权cookie的请求,那么这个请求必须来自于受信任的域,因为其它域是不能从信任域读取cookie的。
与通常的信任想法相反,使用Post代替Get方法并不能提供卓有成效的保护。因为JavaScript能使用伪造的POST请求。尽管如此,那些导致对安全产生“副作用”的请求应该总使用Post方式发送。Post方式不会在web服务器和代理服务器日志中留下数据尾巴,然而Get方式却会留下数据尾巴。
尽管CSRF是web应用的基本问题,而不是用户的问题,但用户能够在缺乏安全设计的网站上保护他们的帐户:通过在浏览其它站点前登出站点或者在浏览器会话结束后清理浏览器的cookie。
如何用 Django 进行防范&
django的csrf机制,它实际就是后台生成一个随机字符串,在你显示表单的时候在表单中加入一个hidden input,比如:
&input type='hidden' name='csrfmiddlewaretoken' value='QtF7jsxQddj5Dj9uKuIs3J7jgm0Bf6sg' /&
这个hidden input就是{% csrftoken %}在模板中根据后台传过来的随机字符串生成的,所以你在开启csrf后,在每个视图函数中需要做的就是生成这个随机字符串,并将这个字符串传递给你用来render template的那个字典(当然你的模板的表单内也要加入{% csrftoken %}),所以要csrf正常工作,需要一下几步:
1. 确保'django.middleware.csrf.CsrfViewMiddleware'在MIDDLEWARE_CLASSES中
2. 在模板的form中添加{% csrf_token %}
3. 在视图函数中用来render 模板的字典中带上{'csrfmiddlewaretoken': 'random string'}, 其中random string是后台生成的,主要有以下两种常见的方法生成
- 添加context_instance=RequestContext(request) 到render_to_response中,注意现在django.core.context_processors.csrf已经默认在TEMPLATE_CONTEXT_PROCESSORS,至少1.5是如此
- 调用csrf(request)生成{'csrfmiddlewaretoken': 'random string'}字典,并显式的将这个字典加入你用来render template的字典中就好
要注意ajax POST调用时必须带上{'csrfmiddlewaretoken': 'random string'}这个字典,否则,你会得到403错误
资料参考自:&和百度百科.
发布于 2年前,
阅读(824) | 评论(0) |
投票(0) | 收藏(5)
最近在用 django 写 应用,发现不好进行单步调试. &不过总算 找到个可以进行单步调试的 插件Django-Pdb
好了,现在说说这个插件是如何使用的.
首先,下载 下来解压出来
python setup.py install
安装好后, 在你应用里面 设置
INSTALLED_APPS = (
'django_pdb',
MIDDLEWARE_CLASSES = (
'django_pdb.middleware.PdbMiddleware',
然后在你需要调试的视图 函数前面加入:
pdb.set_trace()
. . 然后启动你的服务器吧
这时候,你会发现你的程序已经停了下来
help 一下,
n 是 下一步,s 步入
好了,剩下的 自己折腾吧..
当然,你也可以不加 在函数头上,直接 print() 输出相对应的值,这个就会在控制print 的值就会在控制面板输出了啦
发布于 2年前,
阅读(631) | 评论(0) |
投票(0) | 收藏(6)
我在Twitter上看到一幅有趣的图片。我不是来责备发帖的这个人,也不是责备转发的人或图的原作者。但如果不说出我对这个图的背后的一些思考,我会憋得难受。
上面的图中,程序员被描绘成一个挖坑人。这Twitter帐号明显属于一个程序员,这幅图的标题被写成“悲惨,但却是事实…”
我毫不同情这挖坑的程序员。因为我不相信会有这样的事情。
很容易我们会想到那个铁铲子是键盘的比喻,但我从来没遇到过自己作为一个程序员努力的干活儿的同时,一帮“经理们”在那指手画脚的说挖什么,在哪挖,如何挖的场景。我也不认为其他程序员会有这样的经历。
相比起图片中围绕着我们那个努力干活的兄弟的10个人,实际工作中我会遇到比这还要多的做管理的人。企业里都有很多管理人员,但管理人员也是要干活的。如果 你盲目的在那里挖坑,而他们只是站在那里观看,那你从我这里得不到半点赞誉。从坑里爬出来,用铁锹打他们的头。(免责声明:我并不支持用真的铁铲去打这些 低能的技术寄生虫,但是抗争还是龟缩,那要看你自己了。)
我知道一个程序员拿这种事情开管理人员的玩笑有点反应过度,但我知道很多人真的相信图中的情形。如果作为一个程序员,你对那个挖坑的家伙深有同感,我要劝你赶紧辞职。如果做为一个程序员,你感觉被迫去挖坑,赶紧走人。外面有很多你可以逃难的地方。
编 程是科学和艺术结合的美丽产物。你可以自由的去推理,自由的表现。好的管理会提供环境给你,会对你说,“嗨,我想我们解决问题的方法是在这里挖个坑,但我 想我们需要一个比铲子更强的工具来完成这个。”我不同情那些处在压迫蹂躏中的程序员,我跟那些把自己比作用铲子挖坑的人的程序员划清界线。
但我敬佩干这种工作的人,有了他们,我们才不用去干这些事情。
[英文原文:
源自:外刊IT评论
发布于 2年前,
阅读(42) | 评论(0) |
投票(0) | 收藏(0)
自近代已降,“东亚病夫”在中国就是一个很著名的词汇。很长一段时期内,它都被主流意识形态解读为一个由外国人强加给中国人的蔑称,专门用来讽刺中国人生理上的虚弱不堪。因之,当新中国的竞技体育运动获得了天翻地覆的进步,当我们中国的运动员在奥运会等重大国际比赛中站到了冠军的领奖台上之后,很多媒体流行的宣传口径就是“终于洗刷了百年来强压在中国人身上的‘东亚病夫’的耻辱”云云。
而其实这是对“东亚病夫”的严重误读。
首先,最早提出“东亚病夫”一词的并不是外国人,而恰恰是如假包换的中国人!此人就是晚清时期大名鼎鼎的改良派思想家、曾经担任过京师大学堂校长一职的严复。
1895年,中国刚刚遭遇甲午惨败,全国上下反思中国文化的声音开始萌发。此时,严复在天津的《直报》上发表了题为《原强》的文章,这篇文章中写道:“盖一国之事,同于人身。今夫人身,逸则弱,劳则强者,固常理也。然使病夫焉,日从事于超距赢越之间,以是求强,则有速其死而已矣。今之中国,非犹是病夫耶?”严复以后,一大批觉醒的知识分子纷纷沿用了他的这个提法。1905年,小说家曾朴在写作《孽海花》一书时,用的笔名就是“东亚病夫”。
而反观外国人用“东亚病夫”一词,据说是从上海的英文报纸——《字林西报》开始。日,有英国人在《字林西报》上撰写题为《中国实情》的文章。文中说,“夫中国——东方病夫也,其麻木不仁久矣”。根据今人的考证,这篇文章始出于当时的《伦敦学校岁报》,上海的《字林西报》只是转载而已。之后,梁启超的《时务报》引用了这个称谓,遂扩大了“东亚病夫”的知名度。
不过,相对说来,英国人的这个“病夫”提法根本是在严复之后,加之严复当时在英国的影响力,因此不能排除英国人是受到了这位中国维新思想家的影响而采用这个提法的。如果做这样的理解,则“东亚病夫”很可能是“出口转内销”的货色。把这个提法的发明权让渡给外国人,如果不是别有用心的歪曲,就是对历史的无知。
更为重要的是,考察“东亚病夫”一词产生的背景,可以知道这个提法的初始含义并不是用来讽刺中国人生理上的不健康,而主要是说中国人在精神思想层面上的麻木、萎缩、愚昧,以及面对极权统治的无动于衷、逆来顺受、苟且偷生和没有信仰的国民特性。相对于精神思想层面上的“东亚病夫”,生理层面上的“东亚病夫”实在是不值得一提的“小者焉”。
事实上,我们完全可以通过现代著名作家鲁迅先生的心路历程,找寻到理解什么才是真正的“东亚病夫”的钥匙。
正如大家所知道的,年轻的鲁迅当年东渡日本,本来是想学西医以“救治像我父亲似的被误的病人的疾苦”。这可以被解读为先生是想从疗救生理层面上的“东亚病夫”入手,达到救国的目的。
然而,在日本的一次经历,让鲁迅醒悟到了生理层面的“东亚病夫”并非是治疗中国病症的急务。在《呐喊自序》里,他这样说道:“其时正当日俄战争的时候,关于战事的画片自然也就比较的多了,我在这一个讲堂中,便须常常随喜我那同学们的拍手和喝彩。有一回,我竟在画片上忽然会见我久违的许多中国人了,一个绑在中间,许多站在左右,一样是强壮的体格,而显出麻木的神情。据解说,则绑着的是替俄国做了军事上的侦探,正要被日军砍下头颅来示众,而围着的便是来赏鉴这示众的盛举的人们。”
从鲁迅先生的叙述里,我们可以看到先生眼中的中国人其实并非是弱不禁风的,而恰恰是“强壮”的。但就是这样“强壮”的中国人,却在做着愚昧的“看客”,犹然是典型的“东亚病夫”!
于是,先生的思想乃有很大的转变,终于决定“弃医从文”了。“从那一回以后,我便觉得医学并非一件紧要事,凡是愚弱的国民,即使体格如何健全,如何茁壮,也只能做毫无意义的示众的材料和看客,病死多少是不必以为不幸的。所以我们的第一要著,是在改变他们的精神,而善于改变精神的是,我那时以为当然要推文艺,于是想提倡文艺运动了。”
提倡文艺运动,从“精神”上改变中国人,正是鲁迅先生那一代优秀的知识分子在疗救“东亚病夫”问题上的先知先觉的飞跃。
梳理鲁迅先生“弃医从文”的心路历程,对于我们今天准确地理解“东亚病夫”这个称谓,具有重要的启发意义。倘作如是观,则我们可以说今天在奥运会等国际重大的体育赛事上争金夺银,固然是洗刷“东亚病夫”耻辱的一个方面,但并不是说我们有了数百个世界冠军,有了刘翔和姚明,就等于是彻底扔掉了“东亚病夫”的帽子。恰恰相反,观之今天的某些现实,任何一个理性的中国人恐怕都不能不面对这样的一个历史的吊诡,这就是:如果把“东亚病夫”的内涵界定在思想精神层面,那么,对于“我们现在还是不是‘东亚病夫’”这样一个沉重的命题,我们实在没有十足的底气做出理直气壮的否定性的回答!也可以说,要完全摘掉我们头上的这顶“东亚病夫”的帽子,可能还有很长一段路要走。
而这大约正是我们需要长期逾越的一道“雄关”。
自古至今,中国人评价官员一向爱用简单的“两分法”:“贪官”和“清官”。与之相对应的则是对于“贪官”的无比憎恨,可以说是“必欲食其肉,寝其皮”方才满意;而对于“清官”,则一律加以敬仰膜拜、感激涕零。其最近的例子就是为刚刚去世的江苏省某县的纪委书记送“万民伞”。“万民伞”在中国本不是什么新鲜玩意儿,但是,当历史的车轮已经隆隆地驶进了21世纪,这把“万民伞”竟然还有着如此顽强的生命力,则实实在在是个耐人寻味的现象。
由于对清官有一种“盼之如云霓”的迫切心理,在中国的民间甚至形成了一股浓郁的“清官情结”,其最突出的表现就是在中国的京剧、各地的地方戏、通俗小说以及最为民间化的评书等文艺形式中,“清官戏”和“清官故事”已经成为一个畅行不衰和最为人津津乐道的热门题材。
然而,如果对这些所谓的“清官戏”或者“清官故事”认真深挖下去的话,就会发现这些文艺作品所歌颂的所谓“清官”实在是屈指可数,最著名的不过就是翻来覆去的包拯、海瑞等几个人而已。与这几个人相比,自古至今中国能够称得上“官”的人真可谓是恒河沙数了。数量如此庞大的官员竟然只有这么几个耳熟能详的清官,以至于简直可以对之忽略不计,不能不说是一种莫大的悲哀。
尽管恐怕很少有人从如上的角度对清官问题进行思考,但是从中国民间对于清官题材的文艺作品的那股子热乎劲儿上,则完全可以反证出清官实际上的“稀缺”来。正因为在历史上真正的清官少之又少,所以才越加稀罕;而“物以稀为贵”的结果就是让处于社会底层的民众更加盼望清官,蕴含在民间的“清官情结”才越发强烈。倘作如是观,则在清官题材的文艺作品走红的背后,所隐藏的不过就是一种极为无奈的情绪的无意识发泄而已。
其实,像中国民间这样简单地用“清”或者“贪”的“二元标准”来评价官员,未免失之于偏颇和单一。对于官员来说,“清”与“贪”,固然是一个很重要的评价标准,但是更为重要的评价标准还有很多,譬如“能”与“庸”、“明”与“浊”、“勤”与“惰”,等等。而如果能够采取更加实用一些的标准的话,甚至完全可以这样看问题——即如果一个官员是“能吏”,是真有治世之才和治世之能的话,那么即令是他“贪”那么一点点,相对于那些既无才能又十分贪婪的官员而言,这个官员也还是属于最不坏的一类。简单地以官员之“清”和“贪”来论处官员的人往往忽略了一点,这就是一个官员如果仅仅只是个清官,但却能力平平,整天浑浑噩噩,甚至藉靠在道德上的白璧无瑕而肆意妄为的话,那对升斗小民的影响可能会更加糟糕,其危害程度是一点也不次于那些有点治世能力的贪官的。这样说也许令人在感情上难以接受,但却是可以被历史和现实的诸多案例加以复验的一个结论。
撇开这些不说。即以中国传统文艺作品所精心打造的那几个清官形象而论,倘若把这些作品中的清官形象与历史镜像中真实的清官一一加以对照,就会发现这些文艺作品中的清官形象,其实都是一些被深度加工之后,寄托了中国民间审美情趣和诉求的虚构式人物,与真实的历史有天壤之别。
具体而言,其加工的原则一是无限拔高、夸大或者平添这些清官“清”的一面,甚至把很多人的优点移花接木全部挪移到一个人的身上;二是闭口不谈这些人物的弱点和不足。在这样子人为的加工润色之下,文艺作品里出现的清官形象颇有些类似于文革时期的那些文艺作品中的正面人物,一律“高、大、全”,尽善尽美且无所不能。
最典型的例子是中国传统清官戏中的一大主角包拯。实际上,历史上真实的包拯一辈子大部分时间担任的都是“御史”之类的闲职,平生最大的官职不过是“枢密副使”,大约相当于副宰相的位置。但是,在包括京剧在内的各个地方戏的“包公戏”中,他硬是凭空被赋予了三口阴森森的铡刀,成了“上能铡皇族,下能铡黎民百姓”的厉害角色。对此,稍有历史常识的人都会知道,这完全是不了解庙堂实情的乡间底层百姓们的一厢情愿的臆想而已。
在所有的“包公戏”中,最为荒唐走板的是一部叫做《打龙袍》的传统京剧。包公出行时遇到了被前朝奸臣们迫害的流落民间双目失明的当朝天子的生母,于是便在庙堂上为皇太后打抱不平,经过一番小小的波折,终于使得皇太后顺利返回宫中。在母子团聚的庙堂之上,皇太后为惩罚皇帝在开始处理这一事件时对包拯的不公,竟然赐给包拯一根“紫金棍”,要求包公当庭责打“无道的昏君”。包公领命之后,玩了点“幼稚园大班水平”的小谋略,请皇帝脱下了龙袍,以“打龙袍”替代了“打皇帝”。皇太后大悦,立即赏赐包公“尚方宝剑”,赋予了他先斩后奏的巨大权力。一场大戏最后在包公怀捧宝剑“雄赳赳气昂昂”中徐徐落幕……
应当说,这出十足出自社会底层,包含了民间诸多“庙堂想象”成分的“包公戏”,是中国民间“清官情结”的一个最典型的样本。尽管它在舞台上经久不衰,广受戏迷的欢迎,但若衡之于正史,则毋宁说是一个漏洞百出、十分拙劣的玩笑。
就像“包公戏”中的包公和历史上真实的包拯完全不是一回事一样,历史上的清官不仅不像这些文艺作品中所描画的那么神通广大、道德高尚、完美无瑕,更进一步说,他们甚至往往都同时具有一些不能忽略的人格缺点和毛病。
如明朝的海瑞,其实是个性格古怪的悲剧性人物。四川作家聂作平先生曾经在一篇题为《可怕的海瑞》的文章中,转述了海瑞的一则轶事。海瑞有一个五岁的小女儿,某一天因为接受了男家僮给予的一个甜饼,而被海瑞严厉叱责,甚至暗示小姑娘立即以绝食的方式求死以保持贞节。结果,可怜的小女孩竟然真的遵循父命在七天之后绝食死去了。
对于这样一位被中国人公认为“清官”的家伙,著名历史学家黄仁宇先生在《万历十五年》里评价是“古怪”和“惹是生非”;聂作平先生则将其比喻为大明朝的“愤青”。而根据现有的资料判断,我认定这位海瑞“海青天”很有可能患有严重的精神疾病和人格障碍,否则就无以解释他在历史上做出的种种“堂o吉诃德”式的反常举动。
事实上,历史上一些真实的清官不仅像海瑞一样在人格方面存在问题,还往往是人见人怕的“酷吏”。综观一部中国历史,“酷吏”历朝历代都有,但主要还是集中在汉武帝和后来的武则天时期。并且很怪异的一点是,历史上的“酷吏”绝大部分都是真正意义上的“清官”。譬如,司马迁在《史记》的《酷吏列传》中提到被汉武帝重用过的“酷吏”赵禹时,说他“府中皆称其廉平”、“禹为人廉倨”。而同时代的另一位著名的“酷吏”张汤,在死后“家产直不过五百金,皆所得奉赐,无他业”,以至于下葬时“有棺无椁”,令皇帝也为之动容。究竟在“酷吏”和“清官”这两者之间有什么必然的紧密联系?看来,还真是一个不能忽视的历史命题。
对于这个命题,晚清时期的小说家刘鹗在其《老残游记》里有一段深刻的分析,权可作为一个全新的解读思路,他说:“赃官可恨,人人知之。清官尤可恨,人多不知。盖赃官自知有病,不敢公然为非;清官则自以为不要钱,何所不可?刚愎自用,小则杀人,大则误国,吾人亲目所见,不知凡几矣。”刘鹗根据真实的生活原型当时的山东省曹州知府毓贤,刻画了一个叫做“玉贤”的清官加酷吏的艺术形象,对其害民误国的行为大加鞭笞,并且声言:“历来小说皆揭赃官之恶,有揭清官之恶者,自《老残游记》始。”
来源: 博客
作者: 周英杰
发布于 2年前,
阅读(105) | 评论(1) |
投票(1) | 收藏(0)
本文是一篇译文,有不足之处请指出。
原文链接:。
我一般使用2个空格来缩进(尽管大多人使用4个空格),原因是:
输入简单快速;
没有输入一个Tab键,避免不同环境下显示的差异问题;
缩进的效果已经足够,并且没有浪费太多的空间;
译者注:本人也是使用4个空格,如果你也与本文作者的风格不一样,下面说到2个空格的地方请自觉替换成你实际使用的空格数。个人认为,缩进只是一个个人的风格,只要不影响可读性即可。
顺便说一句,尽量不要使用Tab键,它们容易带来麻烦,我只能想到一种情况下它是有用的:here document中的缩进。
如果需要分隔过长的代码,你可以使用下面的任意一种方法:
1) 使用与命令宽度相同的缩进
activate some_very_long_option \
some_other_option
2) 使用2个空格缩进
activate some_very_long_option \
some_other_option
从个人的角度来说,除非有特别的需要,我更倾向于第一种形式,因为它突出“上下两行的内容是一起的”这一联系。
分离复合命令
译者注:其实这里的复合命令就是指块语句,例如for/while循环, if分支结构等等。
HEAD_KEYWORD parameters; BODY_BEGIN
BODY_COMMANDS
我习惯于:
将HEAD_KEYWORD和初始化命令或者参数放在第一行;
将BODY_BEGIN同样放在第一行;
复合命令中的BODY部分以2个空格缩进;
BODY_END部分独立一行放在最后;
1)if/then/elif/else分支语句
if ...; then
elif ...; then
2)for循环
for f in /etc/*; do
3) while/until循环
while [[ $answer != [YyNn] ]]; do ... done
4) case分支语句
case $input in
echo &You said hello&
echo &You said bye&
echo &You said something weird...&
几点注意的地方:
如果不是100%需要,匹配部分左右的括号不需要写(译者注:例如写成hello)而不是(hello));
匹配模式与分支的终止符号;;位于同一缩进级别
分支内部的命令多缩进一层;
尽管是可选的,这里还是把最后一个分支的终止符号也写上了;
语法和编码指引
晦涩的语法结构
我们都喜欢一些晦涩的语法结构,因为它们很简洁。但是如果不是100%需要用到,尽量不要使用它们,否则大多数人无法理解你的代码。
所以有有时候,我们需要在代码的智能,效率与可读性之间找到一个平衡点。
如果你一定要使用这种语法结构,记得在用的地方写上一小段注释。
译者注:Shell提供的一些语法糖很难理解,但是有非常简洁实用,本人也很喜欢用,这样可以省下一大堆精力,而且用熟了也没有什么难以理解的,但是作者说的也有道理,这一点就仁者见仁,智者见智了
因为所有保留的变量名都是大写的,最安全的方法是仅使用小写字母作为变量名,例如读入用户的输入、循环变量等等……:
变量名尽量选择小写字母;
如果你使用大写的变量名,不要使用保留的变量名(一份不完全的列表参见);
如果你使用大写的变量名,最后在变量名前面加一个独特的前缀(例如下面例子中的MY_);
下面是一个例子:
#!/bin/bash
# the prefix 'MY_'
MY_LOG_DIRECTORY=/var/adm/
for file in &$MY_LOG_DIRECTORY&/*; do
echo &Found Logfile: $file&
变量初始化
正如C语言一样,最好的处理是在变量声明的时候初始化。
用户可以将一个变量以环境变量的形式传递到脚本中。如果你盲目地假定你使用的所有变量都是未初始化的,其它人可以以环境变量的形式劫持一个变量。
译者注:一个例子说明这一点:
$ cat b.sh
if [ -z &$var& ]; then
echo &$var is not set&
echo &Now, var is equals to $var&
var=2 sh b.sh
Now, var is equals to 2
解决这个问题的方法很简单,将变量初始化:
my_input=&&
my_array=()
my_number=0
除非你知道自己做的事情,请在参数展开的地方使用双引号
当然,也有一些地方并不需要使用双引号,例如:
[[ ]]测试表达式内部是不会展开的;
在case $WORD in语法中WORD也不会展开的;
在变量赋值var=$WORD的地方也是不会展开的
但是在这些地方使用引号并不会出错,如果你习惯于在每个可能展开参数的地方使用引号,你写得代码会很安全。
如果你要传递一个参数作为一个单词列表,你可以不使用引号,例如:
list=&one two three&
# you MUST NOT quote $list here
for word in $ do
函数名称应该采用小写的形式,并且有一个很好的意义。函数名称应该容易让人理解,比如f1这个名称虽然容易输入但是对调试和其它人阅读代码造成了很大的困难,它说明不了任何东西。好的函数名称可以帮助说明代码,而不需要额外的注释。
一个或多或少有趣的是:如果你无意这样做,不要把函数名称命名为常见的命令名,新手往往比较容易将脚本或者函数名命名成test,这样就和UNIX的test命令冲突了。
除非绝对必要,仅使用字母、数字和下划线作为函数名称。/bin/ls也是一个合法的Bash函数名称。
译者注:/bin/ls不是一个合法的函数名称。
正如文章中提及的,你应该使用$( .. )形式。
不过,如果可移植性是一个问题,你可能必须使用反引号的形式`...`。
在任何情况,如果其它展开或者单词分隔并不是你期望的,你应该将命令替换用双引号引起来。
正如Greg据说的:“If eval is the answer, surely you are asking the wrong question.”。
避免它,除非绝对必要:
eval can be your neckshot(可能是你的麻烦?)
很有可能有其它的方法来实现你需要的;
如果可能,重新思考下脚本的工作过程,当eval的使用不可避免的时候;
如果你实在需要使用,小心慎用;
脚本的基本结构
一个脚本的基本结构是这样的:
#!SHEBANG CONFIGURATION_VARIABLES
FUNCTION_DEFINITIONS
如果可能,请不要忘记shebang。
请小心使用/bin/sh作为shebang,在Linux系统中,/bin/sh就是Bash这是一个错误的观点。
于我而言,shebang有两个目的:
说明直接执行时以哪个解释器来执行;
明确该脚本应该以哪个解释器来执行;
在这里,我将这一类变量——可以被用户更改的——叫做配置变量。
让这类变量容易找到,一般放在脚本的头部,给它们有意义的名称并且加上注释说明。正如上面说的,仅当你知道你为什么这么做的时候,才用大写的变量名形式,否则小写形式更加安全。
所有函数定义应该在脚本主要代码执行之前,这样可以给人全局的印象,并且确保所有函数在使用之前它是已知的。
你应该使用可移植性高的函数定义形式,即不带function关键字的形式。
脚本行为和健壮性
当脚本检测到问题时尽早退出,以免执行潜在的问题;
如果你需要用到的命令可能并没有安装在系统上,在脚本执行的时候最好检查命令是否存在并且提醒用户缺少什么;
采用有意义的脚本返回值,例如0代码成功,1代码错误或者失败;
if the script is interactive, if it works for you and if you think this is a nice feature, you can try to&&after execution;(译者注:不理解这一点是什么意思)
在屏幕中输出简单易理解的消息;
使用颜色或者特别的前缀区分错误和警告信息;
输出正常的内容到STDOUT,而输出错误、警告或者诊断的信息到STDERR;
在日志文件中输出所有详细的信息;
不要盲目地假设任何事情,如果你希望用户输入一个数字,请在脚本中主动检查它是否真得是一个数字,检查头部是否包含0,等等。我们都应该知道这一点,用户仅仅是用户而不是程序员,他们会做他们想要的,而不是程序想要的。
——————————————————华丽的分割线 begin—————————————
1. for i in $(ls *.mp3)
Bash写循环代码的时候,确实比较容易犯下面的错误:
for i in $(ls *.mp3); do
some command $i
for i in $(ls)
for i in `ls`
for i in $(find . -type f)
for i in `find . -type f`
files=($(find . -type f))
for i in ${files[@]}
使用命令展开时不带引号,其执行结果会使用IFS作为分隔符,拆分成参数传递给for循环处理;
我们不能避免某些文件名中包含空格,Shell会对$(ls *.mp3)展开的结果会被做单词拆分()的处理。假设有一个文件,名字为01 - Don't Eat the Yellow Snow.mp3,for循环处理的时候,会今次遍历文件名中的每个单词:01, -, Don't, Eat等等:
$ for i in $(ls *.mp3); do echo $i; done 01 - Don't
比这更差的情况是,上面命令展开的结果可能被Shell进一步处理,比如。比如,ls执行的结果中包含*号,按照通配符的规则, *号会被展开成当前目录下的所有文件:
$ touch &1*.mp3& &1.mp3& &11.mp3& &12.mp3&
$ for i in $(ls *.mp3); do echo $i; done
1*.mp3 1.mp3 11.mp3 12.mp3
不过,在这种场景下,你即使加上引号,也是无济于事的:
$ for i in &$(ls *.mp3)&; do echo --$i--; done
--1*.mp3 1.mp3 11.mp3 12.mp3--
加上引号后,ls执行的结果会被当成一个整体,所以for循环只会执行一次,达不到预期的效果。
事实上,这种情况下,根本不需要使用ls命令。ls命令的结果本身就设计成给人读的,而不是给脚本解析的。正确的处理方法是,直接使用文件名展开(通配符)的功能:
$ for i in *.mp3; do
文件名展开是位于各种展开(花括号展开、变量替换、命令展开等)功能中的最后一个环节,所以不会有之前不带引号的命令展开的副作用。如果你需要递归地处理文件,可以考虑。
到这一步,之间的问题看样子已经修复了。但是,如果你进一步思考,假设当前目录上没有文件时会怎么样?没有文件的时候,*.mp3不会被展开直接传递给for循环处理,所以这个时候循环还是会执行一次。这种情况不是我们预期的行为。保险起见,可以在循环处理的时候,检查下文件是否存在:
for i in *.mp3; do
[ -e &$i& ] || continue
some command &$i&
如果你有和避免的习惯,你完全可以避免很多错误。
注意下循环体内部的&$i&,这里会导致下面我们要说的另外一个比较容易犯的错误。
2. cp $file $target
上面的命令有什么问题呢?如果你提前知道,$file和$target文件名中不会包含空格或者*号。否则,这行命令执行前在经过单词拆分和文件名展开的时候会出现问题。所以,两次强调,在使用展开的地方切勿忘记使用引号:
$ cp -- &$file& &$target&
如果不带引号,当你执行如下命令时就会出错:
$ file=&01 - Don't Eat the Yellow Snow.mp3&
$ target=&/tmp&
$ cp $file $target
cp: cannot stat ‘01’: No such file or directory
如果带上引号,就不会有上面的问题,除非文件名以'-'开头,在这种情况下,cp会认为你提供的是一个命令行选项,这个错误下面会介绍。
3. 文件名中包含短横'-'
文件名以'-'开头会导致许多问题,*.mp3这种通配符会根据当前的展开成一个列表,但在绝大多数环境下,'-'排序的时候会排在大多数字母前。这个展开的列表传递给有些命令的时候,会错误的将-filename解析成命令行选项。这里有两种方法来解决这个问题。
第一种方法是在命令和参数之间加上--,这种语法告诉命令不要继续对--之后的内容进行命令行参数/选项解析:
$ cp -- &$file& &$target&
这种方法可以解这个问题,但是你需要在每个命令后面都要加上--,而且依赖具体的命令解析的方式,如果一些命令不兼容这种约定俗成的规范,这种做法是无效的。
另外一种方法是,确保文件名都使用相对或者绝对的路径,以目录开头:
for i in ./*.mp3; do
cp &$i& /target
这种情况下,即使某个文件以-开头,展开后文件名依然是./-foo.mp3这种形式,完全不会有问题。
4. [ $foo = &bar& ]
这是一个与第2个问题类似的问题,虽然用到了引号,但是放错了位置,对于字符串字面值,除非有特殊符号,否则不大需要用引号括起来。但是,你应该把变量的值用括号括起来,从而避免它们包含空格或能通配符,这一点我们在前面的问题中都解释过。
这个例子在以下情况下会出错:
如果[中的变量不存在,或者为空,这个时候上面的例子最终解析结果是:
[ = &bar& ] # 错误!
并且执行会出错:unary operator expected,因为=是二元操作符,它需要左右各一个操作数。
如果变量值包含空格,它首先在执行之前进行单词拆分,因此[命令看到的样子可能是这样的: [ multiple words here = &bar& ];
正确的做法应该是:
[ &$foo& = bar ]
这种写法,在POSIX兼容的实现中都不会有问题,即使$foo以短横&-&开头,因为POSIX实现的test命令通过传递的参数来确定执行的行为。
只有一些非常古老的shell可能会遇到问题,这个时候你可以使用下面的写法来解决(相信你肯定看到过这种写法):
# POSIX / Bourne
[ x&$foo& = xbar ]
在Bash中,还有另外一种选择是使用:
# Bash / Ksh
[[ $foo == bar ]]
这里你不需要使用引号,因为在[[里面参数不会进行展开,当然带上引号也不会有错。
不过有一点要注意的是,[[里的==不仅仅是文本比较,它会检查左边的值是否匹配右侧的表达式,==右侧的值加上引号,会让它成为一个普通的字面量,*?等通配符会失去特殊含义。
5. cd $(dirname &$f&)
这又是一个引号的问题,命令展开的结果会进一步地进行单词拆分或者文件名展开。因此下面的写法才是正确的:
cd &$(dirname &$f&)&
但是,上面引号的写法可能比较怪异,你可能会认为第一、二个引号,第三、四个引号是一组的。
但是事实上,Bash将命令替换里面的引号当成一组,外面的当成另外一组。如果你是用反引号的写法,引号的行为就不是这样的了,所以。
6. [ &$foo& = bar && &$bar& = foo ]
不要在内部使用&&,Bash解析器会把你的命令分隔成两个命令,在&&之前和之后。你应该使用下面的写法:
[ bar = &$foo& ] && [ foo = &$bar& ] # POSIX
[[ $foo = bar && $bar = foo ]]
# Bash / Ksh
尽量避免使用下面的写法,虽然它是正确的,但是这种写法可移植性不好,并且已经在POSIX-2008中被废弃:
[ bar = &$foo& -a foo = &$bar& ]
7. [[ $foo & 7 ]]
原文作者认为算术比较不应该用[[,而是用((,我没弄明白是为什么。
如果有理解的同学,欢迎以评论回复,谢谢。
8. grep foo bar | while read -r; do ((count++)); done
这种写法初看没有问题,但是你会发现当执行完后,count变量并没有变化。原因是管道后面的命令是在一个中执行的。
POSIX规范并没有说明管道的最后一个命令是不是在子Shell中执行的。一些shell,例如ksh93或者Bash&=4.2可以通过shopt -s lastpipe命令,指明管道中的最后一个命令在当前shell中执行。由于篇幅限制,在此就不展开,有兴趣的可以看。
9. if [grep foo myfile]
初学者会错误地认为,[是if语法的一部分,正如C语言中的if ()。但是事实并非如此,if后面跟着的是一个命令,[是一个命令,它是内置命令test的简写形式,只不过它要求最后一个参数必须是]。下面两种写法是一样的:
if [ false ]; then echo &HELP&; fi
then echo &HELP&; fi
两个都是检查参数&false&是不是非空的,所以上面两个语句都会输出HELP。
if语句的语法是:
if COMMANDS
then &COMMANDS&
elif &COMMANDS& # optional
then &COMMANDS&
else &COMMANDS& # optional
fi # required
再次强调,[是一个命令,它同其它常规的命令一样接受参数。if是一个复合命令,它包含其它命令,[并不是if语法中的一部分。
如果你想根据grep命令的结果来做事情,你不需要把grep放到[里面,只需要在if后面紧跟grep即可:
if grep - then
如果grep在myfile中找到匹配的行,它的执行结果为0(true),then后面的部分就会执行。
10. if [bar=&$foo&]; then ...
正如上一个问题中提到的,[是一个命令,它的参数之间必须用空格分隔。
11. if [ [ a = b ] && [ c = d ] ]; then ...
不要用把[命令看成C语言中if语句的条件一样,它是一个命令。
如果你想表达一个复合的条件表达式,可以这样写:
if [ a = b ] && [ c = d ]; then ...
注意,if后面有两个命令,它们用&&分开。等价于下面的写法:
if test a = b && test c = d; then ...
如果第一个test(或者[)命令返回false,then后面的语句不会执行;如果第一个返回true,第二个test命令会执行;只有第二个命令同样返回true的情况下,then后面的语句才会执行。
除此之外,还可以使用[[关键字,因为它支持&&的用法:
if [[ a = b && c = d ]]; then ...
12. read $foo
read命令中你不需要在变量名之前使用$。如果你想把读入的数据存放到名为foo的变量中,下面的写法就够了:
或者,更加安全地方法:
IFS= read -r foo
read $foo会把一行的内容读入到变量中,该变量的名称存储在$foo中。所以两者的含义是完全不一样的。
13. cat file | sed s/foo/bar/ & file
你不应该在一个管道中,从一个文件读的同时,再往相同的文件里面写,这样的后果是未知的。
你可以为此创建一个临时文件,这种做法比较安全可靠:
# sed 's/foo/bar/g' file & tmpfile && mv tmpfile file
或者,如果你用得是 GNU Sed 4.x 以上的版本,可以使用-i 选项即时修改文件的内容:
# sed -i 's/foo/bar/g' file
14. echo $foo
这种看似无害的命令往往会给初学者千万极大的困扰,他们会怀疑是不是因为 $foo 变量的值是错误的。事实却是因为,$foo 变量在这里没有使用双引号,所以在解析的时候会进行和,最终导致执行结果与预期大相径庭:
msg=&Please enter a file name of the form *.zip&
这里整句话会被拆分成单词,然后其中的通配符会被展开,例如*.zip。当你的用户看到如下的结果时,他们会怎样想:
Please enter a file name of the form freenfss.zip lw35nfss.zip
再举一个例子(假设当前目录下有以 .zip 结尾的文件):
var=&*.zip&
# var 包括一个星号,一个点号和 zip
echo &$var&
# 输出 *.zip
# 输出所有以 .zip 结尾的文件
实际上,这里使用 echo 命令并不是绝对的安全。例如,当变量的值包含-n时,echo 会认为它是一个合法的选项而不是要输出的内容(当然如果你能够保证不会有-n 这种值,可以放心地使用 echo 命令)。
完全可靠的打印变量值的方法是使用 printf:
printf &%s\n& &$foo&
15. $foo=bar
16. foo = bar
当赋值时,等号两边是不允许出现空格的,这同 C 语言不一样。当你写下 foo = bar 时,shell 会将该命令解析成三个单词,然后第一个单词 foo 会被认为是一个命令,后面的内容会被当作命令参数。
同样地,下面的写法也是错误的:
$foo = # COMPLETELY WRONG!
正确的写法应该是这样的:
&pre class=&prettyprint lang-sh&&
# More Right.
17. echo &&EOF
当脚本需要嵌入大段的文本内容时,往往是一个非常有用的工具,它将其中的文本作为命令的标准输入。不过,echo 命令并不支持从标准输入读取内容,所以下面的写法是错误的:
# This is wrong:
echo &&EOF
Hello world
How's it going?
正确的方法是,使用 cat 命令来完成:
# This is what you were trying to do:
Hello world
How's it going?
或者可以使用双引号,它也可以跨越多行,而且因为 echo 命令是内置命令,相同情况下它会更加高效:
echo &Hello world
How's it going?&
18. su -c 'some command'
这种写法“几乎”是正确的。问题是,在许多平台上,su 支持 -c 参数,但是它不一定是你认为的。比如,在 OpenBSD 平台上你这样执行会出错:
$ su -c 'echo hello'
su: only the superuser may specify a login class
在这里,。如果你想要传递 -c 'some command' 给 shell,最好在之前显示地指定 username:
$ su root -c 'some command' # Now it's right.
19. cd / bar
如果你不检查 cd 命令执行是否成功,你可以会在错误的目录下执行 bar 命令,这有可能会带来灾难,比如 bar 命令是 rm -rf *。
你必须经常检查 cd 命令执行是否有错误,简单的做法是:
cd /foo && bar
如果在 cd 命令后有多个命令,你可以选择这样写:
cd /foo || exit 1
bat ... # Lots of commands.
出错时,cd 命令会报告无法改变当前目录,同时将错误消息输出到标准错误,例如&bash: cd: /foo: No such file or directory&。如果你想要在标准输出同时输出自定义的错误提示,可以使用复合命令():
cd /net || { echo &Can't read /net. Make sure you've logged in to the Samba network, and try again.&; exit 1; }
more_stuff
注意,在{号和 echo 之间需要有一个空格,同时}之前要加上分号。
顺便提一下,如果你要在脚本里频繁改变当前目录,可以看看 pushd/popd/dirs 等命令,可能你在代码里面写的 cd/pwd 命令都是没有必要的。
说到这,比较下下面两种写法:
find ... -type d -print0 | while IFS= read -r -d '' do
cd &$subdir& && whatever && ...
cd &$here&
find ... -type d -print0 | while IFS= read -r -d '' do
(cd &$subdir& || ...)
下面的写法,在循环中 fork 了一个子 shell 进程,子 shell 进程中的 cd 命令仅会影响当前 shell的环境变量,所以父进程中的环境命令不会被改变;当执行到下一次循环时,无论之前的 cd 命令有没有执行成功,我们会回到相同的当前目录。这种写法相较前面的用法,代码更加干净。
20. [ bar == &$foo& ]
正确的用法:
[ bar = &$foo& ] && echo yes
[[ bar == $foo ]] && echo yes
21. for i in {1..10}; do ./something &; done
你不应该在&后面添加分号,删除它:
for i in {1..10}; do ./something & done
或者改成多行的形式:
for i in {1..10}; do ./something & done
&和分号一样也可以用作命令终止符,所以你不要将两个混用到一起。一般情况下,分号可以被换行符替换,但是不是所有的换行符都可以用分号替换。
22. cmd1 && cmd2 || cmd3
有些人喜欢把&&和||作为if...then...else...fi 的简写语法,在多数情况下,这种写法没有问题。例如:
[[ -s $errorlog ]] && echo &Uh oh, there were some errors.& || echo &Successful.&
但是,这种结构并不是在所有情况下都完全等价于 if...fi 语法。这是因为在&&后面的命令执行结束时也会生成一个返回码,如果该返回码不是真值(0代表 true),||后面的命令也会执行,例如:
true && ((i++)) || ((i--))
echo $i # 输出 0
看起来上面的结果应该是返回1,但是结果却是输出0,为什么呢?原因是这里 i++ 和 i-- 都执行了一遍。
其中,((i++))命令执行算术运算,表达式计算的结果为0。这里和 C 语言一样,表达式的结果为0被认为是 false。所以当 i=0 的时候,((i++))命令执行的返回码为1(false),从而会执行接下来的((i--))命令。
如果我们在这里使用前缀自增运算符的话,返回的结果恰恰为1,因为((++i))执行的返回码是0(true):
true && (( ++i )) || (( --i ))
echo $i # Prints 1
不过在你无法保证 y 的执行结果是,绝对不要依靠 x && y || z这种写法。上面这种巧合,在 i 初始化为-1时也会有问题。
如果你喜欢代码更加安全健壮,建议使用 if...fi 语法:
echo $i # 输出 1
23. echo &Hello World!&
在交互式的 Shell 环境下,你执行以上命令会遇到下面的错误:
bash: !&: event not found
这是因为,在默认的交互式 Shell 环境下,Bash 发现感叹号时会执行历史命令展开。在 Shell 脚本中,这种行为是被禁止的,所以不会发生错误。
不幸地是,你认为明显正确地修复方法,也不能工作,你会发现:
# echo &hi\!&
最简单地方法是禁用 histexpand 选项,你可以通过 set +H 或者 set +o histexpand 命令来完成。
下面四种写法都可以解决:
# 1. 使用单引号
echo 'Hello World!'
# 2. 禁用 histexpand 选项
echo &Hello World!&
# 3. 重置 histchars
histchars=
# 4. 控制 shell 展开的顺序,命令行历史展开是在单词拆分之前执行的
# 参见: exmark='!'
echo &Hello, world$exmark&
24. for arg in $*
和大多数 Shell 一样,Bash 支持依次读取单个命令行参数的语法。不过这并是$*或者$@,这两种写法都不正确,它们只能得到完整的参数列表,并非单独的一个个参数。
正确的语法是(没错要加上引号):
for arg in &$@&
# 或者更简单的写法
在脚本中遍历所有参数是一个再普遍不过的需求,所以 for arg 默认等价于 for arg in &$@&。$@使用双引号后就有特殊的魔力,每个参数展开后成为一个独立的单词。(&$@&等价于&$1& &$2& &$3& ...)
下面是一个错误的例子:
for x in $*; do echo &parameter: '$x'& done
执行的结果为:
$ ./myscript 'arg 1' arg2 arg3
parameter: 'arg'
parameter: '1'
parameter: 'arg2'
parameter: 'arg3'
正确的写法:
for x in &$@&; do echo &parameter: '$x'& done
执行的结果为:
$ ./myscript 'arg 1' arg2 arg3
parameter: 'arg 1'
parameter: 'arg2'
parameter: 'arg3'
上面正确的例子中,第一个参数'arg 1'在展开后依然是一个独立的单词,而不会被拆分成两个。
25. function foo()
这种写法不一定能够兼容所有 shell,兼容的写法是:
foo() { ... }
26. echo &~&
仅当~没有引号的时候发生,在上面的例子中,只会向标准输出打印~符号,而不是当前用户的家目录路径。
当用引号将路径参数引起来时, 如果要用引号将相对于家目录的路径引起来时,推荐使用 $HOME 而不是 ~, 假如 $HOME 目录是&/home/my photos&,路径中包含空格。
下面是几组例子:
&~/dir with spaces& # expands to &~/dir with spaces&
~&/dir with spaces& # expands to &~/dir with spaces&
~/&dir with spaces& # expands to &/home/my photos/dir with spaces&
&$HOME/dir with spaces& # expands to &/home/my photos/dir with spaces&
27. local varname=$(command)
当在函数中声明局部变量时,作为一个独立的命令,这种奇特的行为有时候可能会导致困扰。比如,当你想要捕获的返回码时,你就不能这样做。local命令的返回码会覆盖它。
这种情况下,你只能分成两行写:
local varname
varname=$(command) rc=$?
28. export foo=~/bar
export 与 local 命令一样,并不是赋值语句的一部分。因此,在有些 Shell 下(比如Bash),export foo=~/bar会展开,但是有些(比如 Dash)却不行。
下面是两种比较健壮的写法:
foo=~/ export foo
export foo=&$HOME/bar&
29. sed 's/$foo/good bye/'
单引号内部不会展开 $foo变量,在这里可以换成双引号:
foo=&hello&; sed &s/$foo/good bye/&
但是要注意,如果你使用了双引号,就需要考虑更多转义的事情,具体可以看这一页。.
30. tr [A-Z] [a-z]
这里至少有三个问题。第一个问题是, [A-Z] 和 [a-z] 会被 shell 认为是通配符。如果在当前目录下没用文件名为单个字母的文件,这个命令似乎能正确执行,否则会错误地执行,也许你会在周末耗费许多小时来修复这个问题。
第二个问题是,这不是 tr 命令正确的写法,实际上,上面的命令会把[转换成[,将任意大写字符转换成对应的小写字符,将]转换成],所以你根本不需要加上括号,这样第一个问题就可以解决了。
第三个问题是,上面的命令执行结果依赖于当前的&,A-Z 或者 a-z 不一定会代表26个 ASCII 字母。实际上,在一些语言环境下,z 位于字母表的中间位置。这个问题的解法,取决于你希望发生的行为是哪一种。
如果你仅希望改变26个英文字母的大小写(强制 locale为 C):
LC_COLLATE=C tr A-Z a-z
如果你希望根据实际的语言环境来转换:
tr '[:upper:]' '[:lower:]'
31. ps ax | grep gedit
这里的根本问题是正在运行的进程名称,本质上是不可靠的。可能会有多个合法的gedit进程,也有可能是别的东西伪装成gedit进程(改变执行命令名称是一件简单的事情 ),更多细节可以看这一篇文章。
执行以上命令,往往会在结果中包含 grep 进程:
# ps ax | grep gedit
6:23 gedit
32118 pts/0
0:00 grep gedit
这个时候,需要过滤多余的结果:
# ps ax | grep -v grep | grep gedit
上面的写法比较丑陋,另外一种方法是:
# ps ax | grep [g]edit
32. printf &$foo&
如果$foo 变量的值中包括\或者%符号,上面命令的执行结果可能会出乎你的意料之外。
下面是正确的写法:
printf %s &$foo&
printf '%s\n' &$foo&
33. for i in {1..$n}
Bash的会优先,所以这时大括号{}表达式里面看到的是文字上的$n(没有展开)。$n 不是一个数值,所以这里的大括号{}并不会展开成数字列表。可见,这导致很难使用大括号来展开大小只能在运行时才知道的列表。
可以用下面的方法:
for ((i=1; i&=n; i++)); do ... done
注:之前我也有写过一篇文章来介绍这个问题:。
34. if [[ $foo = $bar ]]
在[[内部,当=号右边的值没有用引号引起来,bash 会将它当作模式来匹配,而不是一个简单的字符串。所以,在上面的例子中 ,如果 bar 的值是一个*号,执行的结果永远是 true。
所以,如果你想检查两侧的字符串是否相同,等号右侧的值一定要用引号引起来。
if [[ $foo = &$bar& ]]
如果你确实要执行模式匹配,聪明的做法是取一个更加有意义的变量名(例如$patt),或者加上注释说明。
35. if [[ $foo =~ 'some RE' ]]
同上,如果=~号右侧的值加上引号,它会散失特殊的正则表达式含义,而变成一个普通的字符串。
如果你想使用一个长的或者复杂的正则表达式,避免大量的反斜杠转义,建议把它放在一个变量中:
re='some RE' if [[ $foo =~ $re ]]
36. [ -n $foo ] or [ -z $foo ]
这个例子中,$foo 没有用引号引起来,当$foo包含空格或者$foo为空时都会出问题:
$ foo=&some word& && [ -n $foo ] && echo yes -bash: [: some: binary operator expected
$ foo=&& && [ -n $foo ] && echo yes
正确的写法是:
[ -n &$foo& ]
[ -z &$foo& ]
[ -n &$(some command with a &$file& in it)& ]
[[ -n $foo ]]
[[ -z $foo ]]
37. [[ -e &$broken_symlink& ]] returns 1 even though $broken_symlink exists
这里-e 选项是看文件是否存在,当紧跟的文件是一个软链接时,它不看软链接是否存在,而是看实际指向的文件是否存在。所以当软链接损坏时,即实际指向的文件被删除后,-e 的结果返回1。
所以如果你确实要判断后面的文件是否存在,正确的写法是:
[[ -e &$broken_symlink& || -L &$broken_symlink& ]]
38. ed file &&&&g/d\{0,3\}/s//e/g& fails
ed 命令使用的正则语法,不支持0次出现次数,下面的就可以正常工作:
ed file &&&&g/d\{1,3\}/s//e/g&
略过,现在很少会有人用 ed 命令吧。
39. expr sub-string fails for &match&
下面的例子多数情况下运行不会有问题:
word=abcde
expr &$word& : &.\(.*\)& bcde
但是当 $work 不巧刚好是 match 时,就有可能出错了(MAC OSX 下的 expr 命令不支持 match,所以依然能正常工作):
word=match
expr &$word& : &.\(.*\)&
原因是 match 是 expr 命令里面的一个特殊关键字,针对 GNU系统,解决方法是在前面加一个'+':
word=match
expr + &$word& : &.\(.*\)& atch
'+'号可以让 expr 命令忽略后续 token 的特殊含义。
另外一个建议是,不要再使用 expr 命令了,expr 能做的事情都可以用 Bash 原生支持的参数展开()或者字符串展开(Substring Expansion)来完成。并且相同情况下,内置的功能肯定比外部命令的效率要高。
上面的例子,目的是为了删除单词中的首字符,可以这样做:
$ word=match
$ echo &${word#?}&
$ echo &${word:1}&
40. On UTF-8 and Byte-Order Marks (BOM)
多数情况下,UNIX 下 UTF-8 类型的文本不需要使用 BOM,文本的编码是根据当前语言环境,MIME类型或者其它文件元数据信息确定的。人为阅读时,不会因为在文件开始处加 BOM 标记而腚影响,但是当文件要被脚本解释执行时,BOM 标记会像 MS-DOS 下的换行符(^M)一样奇怪。
41. content=$(&file)
这里没有什么错误,不过你要知道命令替换会删除结尾多余的换行符。
略过,原文给的优化方法需要 Bash 4.2+ 以上的版本,手头没有这样的环境。
42. somecmd 2&&1 &&logfile
这是一个很常见的错误,显然你本来是想将标准输出与标准错误输出都重定向到文件logfile 中,但是你会惊讶地发现,标准错误依然输出到屏幕中。
这种行为的原因是,在命令执行之前解析,并且是从左往右解析。上面的命令可以翻译成,将标准错误输出重定向到标准输出(此刻是终端),然后将标准输出重定向到文件 logfile 中。所以,到最后,标准错误并没有重定向到文件中,而是依然输出到终端:
somecmd &&logfile 2&&1
更加详细的说明见。
43. (( ! $? )) || die
只有需要捕获上一个命令的执行结果进,才需要记录$?的值,否则如果你只需要检查上一个命令是否执行成功,直接检测命令:
if cmd; then ... fi
或者使用 case 语句来检测多个或能的返回码:
case $status in
echo success &&2
echo 'Must supply a parameter, exiting.' &&2
echo 'Unknown error, exiting.' &&2
exit $status
发布于 2年前,
阅读(55) | 评论(0) |
投票(0) | 收藏(0)}

我要回帖

更多关于 侠盗猎手5 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信