艾莉亚的猫 Time is limited, To be a better man

Awk、Sed、Find工具的使用

最近在写shell脚本,主要工作就是解析配置文件,那么学习sed与awk就在所难免了,另外还系统的看了find命令的使用,现在就将这些成果汇总起来,以便今后参考。

Awk命令

awk 是一种编程语言,对文本和数据进行处理,支持正则表达式,突出特点是对文本列的操作。

1 常见操作

awk ‘{print $1,$3}’ tmp.txt

awk –F ‘:’ ‘{print $1”|”$3}’ tmp.txt  //以冒号作为域分割符并且输出的分割符为|

2 打印记录

awk ‘BEGIN {print “Begin   \n-----------------”}

{print $0}’ tmp.txt //打印头

awk ‘BEGIN {print “Begin   \n-----------------”}

{print $0}

END {print” -----------------\nEnd”}’ tmp.txt    //打印头尾

3 条件运算符

awk ‘{if ($1~/tomy/) print $0}’ tmp.txt //匹配文本域1为tomy的记录

awk ‘$2==”30” {print $1,”age is 30.”}’ tmp.txt   //精确匹配

4 利用正则表达式

awk ‘/[Gg]reen/’ tmp.txt    //匹配Green或者green

awk ‘$4~/[Gg]reen/’ tmp.txt

awk ‘$4~/([Gg]reen|[Rr]ed)/’ tmp.txt    //或关系匹配

5 使用内置变量

awk ‘{print NF,NR,$0}

END {print FILENAME}’ tmp.txt //NF为列数,NR为行号

6 使用内置字符串函数

awk ‘gsub (/30/,thirty) {print $0}’ tmp.txt    //把有30的行中的30改为thirty

awk ‘BEGIN {print split(“123#456#789”,m_array,”#”)}’  //split返回数组m_array的下标数3,m_array取值:m_array[1]为123,m_array[2]为456,m_array[3]为789

7 输出函数printf

awk ‘{printf “%-10s---%s\n”,$1,$3}’ tmp.txt      //类似C语言printf函数

Sed命令

sed是非交互文本流编辑器,使用sed可以从文件和字符串中抽取所需信息。

1 打印范围

sed –n ‘1,3p’ sed.txt > sed.tmp

sed –n ‘1,$p’ sed.txt > sed.tmp

2 模式查询

sed –n ‘/should/p’ sed.txt //查找should的行

sed –n ‘/should/=’ sed.txt //输出匹配的行号

3 特殊字符查询

sed –n ‘/\./’p sed.txt        //查找有.的行

4 删除文本

sed ‘1,3d’ sed.txt

sed ‘/should/d’ sed.txt

5 替换文本

sed ‘s/PS/ps/’ sed.txt       //替换每行第一个匹配

sed ‘s/PS/ps/g’ sed.txt           //全文替换

sed ‘1,3 s/should/SHOULD/g’ sed.txt         //指定范围替换

sed ‘1 s/should/SHOULD/g’ sed.txt      //指定行替换

6 sed脚本文件

6.1 含sed命令的脚本
  $ cat append.sed

  #!/bin/sed –f

  /should/ a\

	Just a test.

	$ chmod +x append.sed

	$ ./append.sed sed.txt
6.2 不含sed命令脚本
	$ cat append.sed

	/should/ a\

	Just a test.

	$ chmod +x append.sed

	$ sed -f append.sed sed.txt

Find命令

1 name选项

find ~ -name “*.txt” –print

find . –name “[A-Z]*” –print

2 perm选项

find . –perm 755 -print

3 按更改时间查找

find / -mtime -5 –print     //更改时间在五日以内

find / -mtime +3 –print    //更改时间在三日之前

4 type选项

find /etc –type d –print    //查找/etc目录下所有的目录

find /etc –type l –print     //查找/etc目录下所有的符号链接文件

5 使用exec或ok

find . –type f –exec ls –l {} \; //注意{}和\;之间有空格

find . -name “*.log” –mtime +5 -exec rm {} \;

运算符重载

本文主要讨论的是运算符重载中有关++/–(增量和减量)的重载并介绍了前缀后缀应用机制的发展演变。

##运算符重载的基本概念

在c++语言中,可以用关键字operator加上运算符来表示函数,叫做运算符重载。运算符重载函数是一种特殊形式的函数,即运算符本身就是函数名,并且不改变它们作为内置运算符时的使用方法。例如两个复数相加函数:

Complex Add(const Complex& a, const Complex& b);

可以用运算符重载来定义:

Complex operator+(const Complex& a, const Complex& b);

运算符与普通函数在调用时的不同之处在于:对于普通函数,实参出现在圆括号内;而对于运算符,实参出现在其两侧(或一侧)。运算符重载是支持数据抽象和泛型编程的利器。比如一般来说,凡是用作容器元素类型的class(包括struct)都需要重载“=”、“==”和“<”等运算符,因为容器可能会使用它们来排序或者拷贝元素,某些泛型算法都是以假设元素类型已经重载了“=”和“<”为前提的。

##重载++和–

重载++和–这两个运算符之所以令许多人困惑,是因为它们都存在前置版本和后置版本。许多教科书中关于后置版本的语义解释并不完全正确,而在c++程序中,你可能要为某些类重载“++”和“–”运算符,正确理解它们的语义是非常重要的。c++标准规定:当为一个类型重载“++”/“–”的前置版本时,不需要参数;当为一个类型重载“++”/“–”的后置版本时,需要一个int类型的参数作为标志(哑元,非具名参数)。想知道这个哑元的由来么?为什么会是这样,而不是通过一个特殊的关键字来标识这种位置关系,《the design and evolution of c++》中是这样说的:

增量运算符++和减量运算符–都是用户可以定义的运算符,但是release1.0并没有提供区分前缀或后缀应用的机制,于是乎:

class Ptr{
  // ...
  void operator++();
};

在这两种情况中使用的都是同一个Ptr:operator++():

void f(Ptr& p){
  p++;    // p.operator++()
  ++p;    // p.operator++()
}

Bjarne收到很多的建议,特别是Brian Kernighan指出,从c语言的观点来看,这种限制是不自然的,它也阻止了人们定义那种能够用来取代常规指针的类。他考虑到了最明显的解决办法,在c++里增加关键字prefix和postfix:

class Ptr_to_X{
  // ...
  X& operator prefix++();    //prefix ++
  X operator postfix++();    //postfix ++
};

或者:

class Ptr_to_X{
  // ...
  X& prefix operator++();    //prefix ++
  X postfix operator++();    //postfix ++
};

这样一来,又有一些人开始叫喊了:“随随便便”增加关键字什么的最讨厌了!人们建议了几个并不涉及新关键字的方案。例如:

class Ptr_to_X{
  // ...
  X& ++operator();    //prefix ++
  X operator++();      //postfix ++
};

或者:

class Ptr_to_X{
  // ...
  X& operator++(); //prefix because it returns a reference
  X operator++();   //postfix because it doesn't return a reference
};

c++之父觉得前一个太做作,而后一个又太微妙(我既没有看出哪个做作也看不出什么微妙,惭愧),最后停止在了这个可能是既做作又微妙的方案上(不一般的思维):

class Ptr_to_X{
  // ...
  X& operator++();    //prefix: no argument
  X operator++(int);  //postfix: because of the argument
};

c++设计与演化关于此的原话摘录如下:

This may be both too cute and too subtle, but it works, requires no new syntax, and has a logic to the madness. Other unary operators are prefix and take no arguments when defined as member functions. The “odd” and unused dummy int argument is used to indicate the odd postfix operators. In other words, in the prefix case, ++ comes between the first(real) operand and the second(dummy) argument and is thus postfix.

These explanations are needed because the mechanism is unique and therefore a bit of a wart. Given a choice, I would probably have introduced the prefix and postfix keywords, but that didn’t appear feasible at the time. However, the only really important point is that the mechanism works and can be understood and used by the few programmers who really need it.

最后举一个例子来说明如何重载这两个运算符

class Integer{
	public:
		Integer(long data):m_data(data){}
		Integer& operator++(){        //前置版本,返回引用
			cout<<"Integer::operator++() called!"<<endl;
			m_data++;
			return *this;
		}

		Integer operator++(int){       //后置版本,返回对象的值
			cout<<"Integer::operator++(int) called!"<<endl;
			Integer temp = *this;
			m_data++;                       
			return temp;                     //返回this对象的旧值
		}

		...
	private:
			long m_data;
};

int main(){
	Integer x = 1;    //call Integer(long), ?Integer(1)
	++x;              //call operator++()
	x++;              //call operator++(int)

	return 0;
}

当“++”/“–”应用于基本数据类型时,前置版本和后置版本在效率上没有多大差别。然而,当应用于用户定义类型,尤其是大对象的时候,前置版本就会比后置版本的效率高许多。后置版本总是要创建一个临时对象,在退出函数时还要销毁它,而且返回临时对象的值时还会调用其拷贝构造函数。所以,如果可以选择,请尽量使用前置版本。

2013年阅读书单

Head First Java

Python基础教程

算法竞赛入门经典

算法竞赛入门经典

C程序设计语言

Effective C++

C++ Primer(第4版)(评注版)

深度探索C++对象模型

STL源码剖析

林语堂散文经典全集

2012年阅读书单

C++程序设计语言

深入理解LINUX内核(第3版)

LINUX设备驱动程序

Shell脚本学习指南

高质量程序设计指南

UNIX环境高级编程

UNIX网络编程 卷1

Linux操作系统