分类: LINUX
2007-03-02 15:39:42
11111
dsfdsf
sdfdsf
sdfd
22222
kalsdfja;sdf
jask;dflasdf
sed -n '#然而, 输出是空!#,# #p' filename
sed -n '!呢? !不是注释符, 可恶的是它竟然也没有报错, 就是闷声不响地不干活.!,! !p' filename
/h
s#.*#DEBUG: tablespace: p
g#在{}括起来的子命令之前.
:next N
=
p
b pair
}#但断然不可能放在s命令的flags部分, 也叫s的参数, s的参数只能一个紧挨一个, 不能象{}分组的子命令那样用;隔开
s#$#___#p
b end#最规矩的位置, 在一个命令之前
:pair /\n<\/table/{
s#\n#|#gp
b end
}
b next#为了彰显它是一个空语句, 我故意在其前后放上了分号
;:end;
另外, 标号最长7个字符
goto inside_a_block;
....
if( condition )
{
...
inside_a_block:
...
}
s#part1#part2#命令中, \n出现在part1部分可以匹配除最后一个换行符之外的其它换行符, 换句话说, 它匹配那些embedded的换行符. 比如你用
echo $'abc\n123\nxyz' | sed '/abc/{N;s#\n##g;}'可以把abc那行跟123那行给连接起来, 原因是abc行尾的\n被替换为了"空", 尽管s命令有g这个参数, sed也不允许\n匹配到123这一行的行尾换行符, s命令发生时在待匹配的内部缓冲区中存放的内容是
abc而替换之后的结果是
123
abc123
出现在part2部分的\n总是会被替换成一个新行, 一个换行, 或者说\n, ASCII(10), 是的, 我知道有些人做过:
sed 's#^M##g' a_ms_dos_crlf_file不奇怪, 我也这么干过, 在bash命令行上, 那个^M是通过先按下CTRL-V, 然后敲回车键得到的, 在从键盘读入时, 它是三个键, 两个击键动作, 你的终端驱动程序把这个过程的结果转换成了一个字符, 它不是\n, 而是\r, ASCII为13. 回车和换行是不同的.
从sed的命令构成来看, s命令的p参数是个多余之物, 因为
s#src#dest#gp
完全可以通过
s#src#dest#g
p
两个命令来实现, 在命令行上, 也可以通过
s#src#dest#g;p
来让它更紧凑一点
我猜sed的设计者注意到人们往往希望在修改了一样东西之后看看它的结果. 所以p
作为命令的分隔符, 不论是全局的sed命令还是框在{}中的子命令, 换行符和;总是可以互相代替, 了解了这一点, 再看看对{}的说明
sed -n ' { p ;p } '这里你可以在{}的之前和之后都加上空格. 最后一个p还可以没有;号
printf是占据着最调用市场的大量份额, 如果一段sed脚本或一个sed命令行不工作, 怎样知道它有没有匹配到你指定的某个模式, 下面是如何在sed中实现printf:
echo $'abc\nxyz' | sed '/abc/{h;s/abc/DEBUG: [&] matched/p;g}'这个技巧是通过 s命令的p参数来输出调试信息, 因为s命令总是操纵缓冲区, 所以如果你想让你的调试信息独立于pattern 空间中要输出的东西, 可以象这样, 先用h把它暂存, 注意暂存不是move, 而是copy, 所以h命令之后pattern 空间中的内容还在, 你也可以对它执行操作, 修改它的内容是你能输出调试信息的关键, 最后的g把保存起来的pattern空间的内容恢复
能把调试信息输出到stderr而不是stdout吗, 至少我不能!
=输出行号, 它很简单, 但有用, =号用来跟踪你当前处理到了哪些行, 它跟p命令并不重复, 有时候你用了太多的N命令, 或者原始的输入中有太多重复的行, 或者仅仅只是内容太多时, p命令的输出会让你迷失方向
象开头的文本那样, 那是我写这篇东西的原始动因
11111原始的需求只是输出
dsfdsf
sdfdsf
sdfd
22222
kalsdfja;sdf
jask;dflasdf
sed -n '/这绝不是一笔挥就的结果. 之前我几乎没用过label, 而且认为它基本上没什么用. 让我找个放大镜, 把上面的代码放大:
/下面是它的伪代码#我自鸣得意的printf
h
s#.*#DEBUG: tablespace: p
g
#只要没遇到
:next N
#看看我处理到了第几行
=
#看看当前的pattern空间中都读进来了什么
p
#每读一行都要检查是不是遇到了结束的
b pair
}
#如果当前行不是处于和 之间
#就会跑到这里, 这一行实际上是个短路操作, 直接处理下一行了
b end
#检查当前pattern 空间中有没有读入一个开头的行
:pair /\n<\/table/{
#如果有, 整个地址空间的内容都已经被完整读到了pattern space中
#去除行间的换行符
s#\n#|#gp
#至此, 这个"开始"..."结束"的块已经处理完毕, 继续下一行
b end
}
#如果自从最近一次已经匹配到以来还没有碰到
#的话, 会到这里, 直接跳去读下一行
b next
#为了彰显它是一个空语句, 我故意在其前后放上了分号
:end
while( 从输入中读取一行 )其中 label_检查是否已经读取到了结束的块: 和相关的goto语句是不必要的, 但在 sed中必需要通过跳转来实现这种分支结构.
{
if( 匹配到开始 )
{
label_读取下一行:
读取下一行
goto label_检查是否已经读取到了结束的块
}
else
{
break;
}
label_检查是否已经读取到了结束的块:
if( 匹配到结束 )
{
将所有读入的行连接成一行, 输出
break;
}
else
{
goto label_读取下一行
}
}
label_检查是否已经读取到了结束的块:
tee 命令来自T型管的思想, 一处输入, 两处输出. 对于输出过长的命令尤其有用, 既可以通过屏幕输出让你了解当前程序的执行情况, 知道程序并没死掉. 又可以把结果存成文件留待分析. 最有用的用法其实是
command 2>&1 | tee result.txt
unix中当前tee也是很容易得到的, 但一旦没有呢? sed的w命令也可堪此任:
command 2>&1 | sed 'w result.txt'
就这么简单, sed默认对于每个pattern空间中的内容是会输出的, 所以它会向屏幕上输出, 同时w result.txt命令又会输出到result.txt中.
再进一步, 如果屏幕上输出内容太多, 而且你是通过telnet在操作一个速度很慢的远程机器, 如之奈何? 最好的办法是把整个结果都能存成文件, 同时又在屏幕上间隔性的输出, 比如, 每隔100行输出一次.
command 2>&1 | sed -n '1~100p; w result.txt'-n 把默认的每行输出关闭, 而通过1~100p中的命令p显式决定何时输出. 想试一下功效如何不必手工生成100行的文件:
seq 1 100 | sed -n '1~10p; w result.txt'
当然, 用sed实现tee的缺点是文件不能被追加, 只能被改写, 不过这似乎不是一个什么困扰.
比如你想知道某个汉字的UTF-8内码表示和GB2312内码表示, 假设你的终端当前配置成utf-8的
echo -n 中国 | sed l那是小写的L, 不是数字1, 它的作用是输出非可见字符的内码, 你得到的是八进制, 不过变成16进制也决非难事:
for i in $(echo 中国 | sed l | sed -n '1{s#.$##;s#\\# 0#gp}');do printf "%0#x\n" $i; done当然, 如果系统里有xxd 的话, 这些显得太舍近求远了:
echo 中国 | xxd -g1xxd是vim附带的一个小工具. 在Redhat Linux上, 所谓的vi正是vim. 所以xxd 还算是可以依靠的工具.
head -100000 large_file | tail -1用这个查看一个很大的文件的第10万行当然很好, 因为head会在读到这一行时停止. 但还有更好的:
sed -n '100000{p;q}' large_file它更好是因为通过 q避免了对整个文件后续行的不必要的处理, sed进程马上退出, 同时又避免了管道和另一个进程的开销.