iptables做SNAT的配置文件

# Generated by iptables-save v1.2.11 on Thu Nov 20 16:30:43 2008
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [1:92]
:OUTPUT ACCEPT [1:92]
-A POSTROUTING -s 10.11.12.0/255.255.255.0 -o eth0 -j SNAT –to-source 122.227.129.72
COMMIT
# Completed on Thu Nov 20 16:30:43 2008
# Generated by iptables-save v1.2.11 on Thu Nov 20 16:30:43 2008
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT – [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -i eth1 -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp –icmp-type any -j ACCEPT
-A RH-Firewall-1-INPUT -p 50 -j ACCEPT
-A RH-Firewall-1-INPUT -p 51 -j ACCEPT
-A RH-Firewall-1-INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
-A RH-Firewall-1-INPUT -s 10.11.12.0/24 -j ACCEPT
-A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 22 -j ACCEPT
-A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEPT
-A RH-Firewall-1-INPUT -m state –state NEW -p tcp -m tcp –dport 53 -j ACCEPT
-A RH-Firewall-1-INPUT -m state –state NEW -p udp -m udp –dport 53 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT –reject-with icmp-host-prohibited
COMMIT
# Completed on Thu Nov 20 16:30:43 2008

Bash的字符串匹配共有6种形式

Bash的字符串匹配共有6种形式:

1. ${variable#pattern}
如果pattern匹配variable的开始部分,从variable的开始处删除字符直到第一个匹配的位置,包括匹配部分,返回剩余部分。

2. ${variable##pattern}
如果pattern匹配variable的开始部分,从variable的开始处删除字符直到最后一个匹配的位置,包括匹配部分,返回剩余部分。

3. ${variable%pattern}
如果pattern匹配variable的结尾部分,从variable的结尾处删除字符直到第一个匹配的位置,包括匹配部分,返回剩余部分。

4. ${variable%%pattern}
如果pattern匹配variable的结尾部分,从variable的结尾处删除字符直到最后一个匹配的位置,包括匹配部分,返回剩余部分。

5. ${variable/pattern/string}
6. ${variable//pattern/string}
最后这两种用法用于匹配替换。因为我没用到,先不说了。(busybox 1.0.1 不支持最后这两种语法。)

举例:
str=tftp://hostname.com/onepath/anotherpath

echo ${str#*/}
输出:/hostname.com/onepath/anotherpath

echo ${str##*/}
输出:anotherpath

echo ${str%/*}
输出:tftp://hostname.com/onepath

echo ${str%%/*}
输出:tftp:/

——————–
下面说一说具体怎么用。

假设在某个系统中没有sed和awk, 只有grep,tr和cut。
如果要取得网卡的mac地址,可以:

mac_addr=$(ifconfig eth0 | grep HWaddr | cut -d’ ‘ -f11 | tr -d : )
echo $mac_addr
输出:0150BF9886BF

后面再说为什么我要把冒号去掉。这种方式在cut时要经过实验才知道我们要的是第11个field。现在换另一种方式:

mac_addr=$(ifconfig eth0 | grep HWaddr | tr -d ‘ :’)
echo $mac_addr
输出:eth0LinkencapEthernetHWa

ddr0150BF9886BF
然后:
mac_addr=${mac_addr#*HWaddr}
echo $mac_addr
输出:0150BF9886BF

现在要求写一个程序,接受命令行给定一个网址去下载一个文件,要求根据网址的协议的不同,采用不同的程序下载。如果给定的网址以.xml结尾,则认为要下载的文件已经在给定的网址中指定,否则要下载的文件名为本机的mac地址加.xml扩展名,不包括mac中的冒号。

例如给定 tftp://host/file.xml,则要用tftp命令下载host上的file.xml文件。
如果给定 http://host/path,则要用wget命令下载host/path上的0150BF9886BF.xml文件。

先取网址的协议,采用从右向左最大匹配”://”:
url=$1
proto=${url%%://*}

再判断文件名是否已经给定, 采用从左向右最大匹配”.xml”:
[ -z "${url##*.xml}" ] || url=$url/$mac_addr.xml

如果 $proto = “http” 或者 “ftp”
则执行
wget $url -O local_file

如果 $proto = “tftp”,这个有点麻烦,因为tftp的用法是:

tftp -g -r remote_file -l local_file host

所以还要把remote_file和host从url中提取出来。

先把url中的tftp://去掉:
tmp=${url#*://}

再从右向左最大匹配”/”得到host:
host=${tmp%%/*}

再从左向右最小匹配”/”得到路径和文件名:
remote_file=${tmp#*/}

shell中的条件测试

对于测试的对象不同,条件测试分为这么三种,文件测试、字串测试、数值测试。

1.文件测试

文件测试根据文件的属性(是否目录,正规文本,符号链接),文件的权限(是否可读,可写,可执行,有suid位),文件的size( 是否为空) 来测试,大致分为这么三类.

参数          解释
-d       是否目录
-f               是否正规文本
-L               是否符号链接

-r    是否可读            
-w              是否可写
-x               是否可执行
-u               是否有suid 位

-s               是否为空

2.字串测试

字串测试用来做两个字串做对比( 等或者不等), 以及检查某一个字串是否为空

=             字串相等
!=           字串不相等
-z           字串为空
-n          字串不为空

开始感觉似乎上面每组命令有两条命令有点多余, 相等判断为否了就是不等啊,但是因为条件测试不仅仅只是单一的判断字串,可能有其他多个条件组合起来判断,而条件判断又没有 “否” 的命令,因此想着这每组两条一点也不多余。

3.数值测试

数值测试用来比较两个数值的大小。 记住几个字符的含义应该能好理解一些:e/eq (equal 等于), t (than 比较 ), g ( great 大于), l ( less 小于),n ( not )

-eq                   等于
-ne                  不等于
-gt                   大于
-ge                  大于等于
-lt                    小于
-le                   小于等于

规范网站目录的权限脚本

#!/bin/sh
#
#desc:change wwwroot permission
#auther:luohui@forchina.com
#date:2009.01.17

#网站目录
wwwroot=’/var/www/newihompy’

#排除的目录
exclude=("/var/www/newihompy/attachment" "/var/www/newihompy/uc/data" "/var/www/newihompy/theme" "/var/www/newihompy/pet/images" "/var/www/newihompy/data" "/var/www/newihompy/uc_client/data")

#文件所有者
proguser=’root.root’

foreachd(){
    for file in $1/*
    do
            if [ -d $file ]
            then
            isexclude=’0′
            for exfile in ${exclude[*]}
            do
                if [[ $exfile == $file ]]
                then
                    isexclude=’1′
                fi
            done

            if [[ $isexclude == ‘0’ ]]
            then
                echo "chown $proguser $file"
                chown $proguser $file
                echo "chmod 755 $file"
                chmod 755 $file
                foreachd $file
            fi
        else
            echo "chown $proguser $file"
            chown $proguser $file
            echo "chmod 644 $file"
            chmod 644 $file
            fi
    done
}

echo "chown $proguser $wwwroot"
chown $proguser $wwwroot
echo "chmod 755 $wwwroot"
chmod 755 $wwwroot

foreachd $wwwroot

执行完后所以目录为755,文件为644。

shell比较两个字符串是否相等

比较两个字符串是否相等的办法是:
if [ "$test"x = "test"x ]; then
这里的关键有几点:
1 使用单个等号
2 注意到等号两边各有一个空格:这是unix shell的要求
3 注意到"$test"x最后的x,这是特意安排的,因为当$test为空的时候,上面的表达式就变成了x = testx,显然是不相等的。而如果没有这个x,表达式就会报错:[: =: unary operator expected

二元比较操作符,比较变量或者比较数字.注意数字与字符串的区别.
整数比较
-eq    等于,如:if [ "$a" -eq "$b" ]
-ne    不等于,如:if [ "$a" -ne "$b" ]
-gt    大于,如:if [ "$a" -gt "$b" ]
-ge    大于等于,如:if [ "$a" -ge "$b" ]
-lt    小于,如:if [ "$a" -lt "$b" ]
-le    小于等于,如:if [ "$a" -le "$b" ]
   大于(需要双括号),如:(("$a" > "$b"))
>=    大于等于(需要双括号),如:(("$a" >= "$b"))
小数据比较可使用AWK
字符串比较
=    等于,如:if [ "$a" = "$b" ]
==    等于,如:if [ "$a" == "$b" ],与=等价
   注意:==的功能在[[]]和[]中的行为是不同的,如下:
   1 [[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么将为true
   2 [[ $a == "z*" ]] # 如果$a等于z*(字符匹配),那么结果为true
   3
   4 [ $a == z* ]    # File globbing 和word splitting将会发生
   5 [ "$a" == "z*" ] # 如果$a等于z*(字符匹配),那么结果为true
   一点解释,关于File globbing是一种关于文件的速记法,比如"*.c"就是,再如~也是.
   但是file globbing并不是严格的正则表达式,虽然绝大多数情况下结构比较像.
!=    不等于,如:if [ "$a" != "$b" ]
   这个操作符将在[[]]结构中使用模式匹配.
   大于,在ASCII字母顺序下.如:
   if [[ "$a" > "$b" ]]
   if [ "$a" > "$b" ]
   注意:在[]结构中">"需要被转义.
   具体参考Example 26-11来查看这个操作符应用的例子.
-z    字符串为"null".就是长度为0.
-n    字符串不为"null"
   注意:
   使用-n在[]结构中测试必须要用""把变量引起来.使用一个未被""的字符串来使用! -z
   或者就是未用""引用的字符串本身,放到[]结构中。虽然一般情况下可
   以工作,但这是不安全的.习惯于使用""来测试字符串是一种好习惯.

perl邮件群发脚本(V0.3版)

#!/usr/bin/perl -w
#
######################################################################################################
#requirement:
#rpm -ivh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
#yum -y install perl-Authen-SASL perl-MIME-Base64 perl-DBI
#
#脚本功能: 邮件群发脚本
#
#作    者: Luo Hui(farmer.luo at gmail.com)
#最后时间: 2008.12.05
#版    本:Ver 0.3
#
#版本历史:
#0.3(2008.12.05):1)更新发送机制,更新为每发次一封邮件重新连一次SMTP服务器
#
#0.2(2008.12.02):1)更新由脚本自动到数据库内取邮件地址,不再从文件文件中取.
#                2)邮件内容文件由脚本参数设定,方便使用
#
#0.1(2008.12.01):1)初始版本
#
#######################################################################################################

use strict;
use warnings;
use DBI();
use File::Basename;
use MIME::Base64;
use Net::SMTP;

#发送服务器
my $smtp_server = ‘mail.test.com’;
#认证用户名
my $username = ‘service@test.com’;
#认证密码
my $password = ‘test’;
#发件人
my $from_mail = ‘service@test.com’;
#查询邮件地址的sql语句
my $strsql = "SELECT email FROM `uc_members` ";
#邮件主题
my $subject = encode_base64( ‘每日内容精选’, "" );

my $argvnum = @ARGV;
die "Usage:n" , basename( $0 ) , " Mail Body File n" unless ( $argvnum >= 1 );

my $mailfile = $ARGV[0];

if ( ! -e $mailfile ) {
    print "mail body file no exists!n";
    exit(1);
}

#连接数据库的资料可在这改
my $dbh = DBI->connect( "DBI:mysql:database=ucenter;host=10.1.1.1",
                        "root", "test",
                        {‘RaiseError’ => 1} );
                       
my $sth = $dbh->prepare( $strsql );
$sth->execute();
my $email = $sth->fetchall_arrayref or die "$sth->errstrn";
$sth->finish();

my ( $i, @to_mails );

for $i ( 0 .. $#{$email} ) {
    #print $email->[$i][0];
    push( @to_mails , $email->[$i][0] );
}

open(FHMF, $mailfile) or die "$!";
local($/) = undef;
my $mail_body = <FHMF>;
close( FHMF );
$/ = "n";

$mail_body = encode_base64( $mail_body );

$i = 0;
foreach my $to_mail( @to_mails )
{
    my $smtp = Net::SMTP -> new( Host => $smtp_server,
    #            Debug => 1,
                Hello => $smtp_server,
                ) || die "Can’t connect $smtp_server $!n";
   
    $smtp -> auth( $username, $password ) || die "Can’t authenticate: $!n";

    chomp($to_mail);
    $smtp -> mail( $from_mail );
    $smtp -> to( $to_mail );
    $smtp -> data( );

    # send mail head
    $smtp -> datasend( "From: $from_mailn" );
    $smtp -> datasend( "To: $to_mailn" );
    $smtp -> datasend( "MIME-Version: 1.0 n" );
    $smtp -> datasend( "Content-Type: text/html; charset=gb2312 n" );
    $smtp -> datasend( "Content-Transfer-Encoding: base64n" );
    $smtp -> datasend( "Subject: =?gb2312?B?$subject?=nn");
   
    # send mail body
    $smtp -> datasend( $mail_body );
    $smtp -> dataend();

    $i++;
    print "send mail:$i mail to:$to_mailn";
   
    $smtp -> quit();
}

perl邮件群发脚本(V0.2从数据库内取邮件地址版)

#!/usr/bin/perl -w
#
######################################################################################################
#requirement:
#rpm -ivh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
#yum -y install perl-Authen-SASL perl-MIME-Base64 perl-DBI
#
#脚本功能: 邮件群发脚本
#
#作    者: Luo Hui(farmer.luo at gmail.com)
#最后时间: 2008.12.02
#版    本:Ver 0.2
#
#版本历史:
#0.2(2008.12.02):1)更新由脚本自动到数据库内取邮件地址,不再从文件文件中取.
#                2)邮件内容文件由脚本参数设定,方便使用
#
#0.1(2008.12.01):1)初始版本
#
#######################################################################################################

use strict;
use warnings;
use DBI();
use File::Basename;
use MIME::Base64;
use Net::SMTP;

#发送服务器
my $smtp_server = ‘mail.test.com’;
#认证用户名
my $username = ‘service@test.com’;
#认证密码
my $password = ‘test’;
#发件人
my $from_mail = ‘service@test.com’;
#查询邮件地址的sql语句
#my $strsql = "SELECT email FROM `uc_members` WHERE username = ‘farmerluo’ LIMIT 0 , 30 ";
my $strsql = "SELECT email FROM `uc_members`";
#邮件主题
my $subject = encode_base64( ‘每日内容精选’, ‘ ‘ );

my $argvnum = @ARGV;
die "Usage:n" , basename( $0 ) , " Mail Body File n" unless ( $argvnum >= 1 );

my $mailfile = $ARGV[0];

if ( ! -e $mailfile ) {
    print "mail body file no exists!n";
    exit(1);
}

#连接数据库的资料可在这改
my $dbh = DBI->connect( "DBI:mysql:database=ucenter;host=10.11.0.1",
                        "root", "test",
                        {‘RaiseError’ => 1} );
                       
my $sth = $dbh->prepare( $strsql );
$sth->execute();
my $email = $sth->fetchall_arrayref or die "$sth->errstrn";
$sth->finish();

my ( $i, @to_mails );

for $i ( 0 .. $#{$email} ) {
    #print $email->[$i][0];
    push( @to_mails , $email->[$i][0] );
}

open(FHMF, $mailfile) or die "$!";
local($/) = undef;
my $mail_body = <FHMF>;
close( FHMF );
$/ = "n";

$mail_body = encode_base64( $mail_body, ‘ ‘ );

my $smtp = Net::SMTP -> new( Host => $smtp_server,
#            Debug => 1,
            Hello => $smtp_server,
            ) || die "Can’t connect $smtp_server $!n";

$smtp -> auth( $username, $password ) || die "Can’t authenticate: $!n";

$i = 0;
foreach my $to_mail( @to_mails )
{
    chomp($to_mail);
    $smtp -> mail( $from_mail );
    $smtp -> to( $to_mail );
    $smtp -> data( );

    # send mail head
    $smtp -> datasend( "From: $from_mailn" );
    $smtp -> datasend( "To: $to_mailn" );
    $smtp -> datasend( "MIME-Version: 1.0 n" );
    $smtp -> datasend( "Content-Type: text/html; charset=gb2312 n" );
    $smtp -> datasend( "Content-Transfer-Encoding: base64n" );
    $smtp -> datasend( "Subject: =?gb2312?B?$subject?=nn");
   
    # send mail body
    $smtp -> datasend( $mail_body );
    $smtp -> dataend();

    $i++;
    print "send mail:$i mail to:$to_mailn";
}

$smtp -> quit();

perl邮件群发脚本

#!/usr/bin/perl -w
#requirement:
#rpm -ivh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
#yum -y install perl-Authen-SASL perl-MIME-Base64
#
#Author: Luo Hui 2008.12.01
#
use warnings;
use strict;
use MIME::Base64;
use Net::SMTP;

#邮件内容
my $mailfile = ‘/root/mail1128.htm’;
#邮件列表文件,一行一个邮件地址
my $maillist = ‘/root/maillist.txt’;
#发送服务器
my $smtp_server = ‘mail.test.com’;
#认证用户名
my $username = ‘service@test.com’;
#认证密码
my $password = ‘111111’;
#发件人
my $from_mail = ‘service@test.com’;
#邮件主题
my $subject = encode_base64( ‘test test’, ‘ ‘ );

open(FHML, $maillist) or die "$!";
my @to_mails = <FHML>;
close( FHML );

open(FHMF, $mailfile) or die "$!";
local($/) = undef;
my $mail_body = <FHMF>;
close( FHMF );
$/ = "n";

$mail_body = encode_base64( $mail_body, ‘ ‘ );

my $smtp = Net::SMTP -> new( Host => $smtp_server,
#            Debug => 1,
            Hello => $smtp_server,
            ) || die "Can’t connect $smtp_server $!n";

$smtp -> auth( $username, $password ) || die "Can’t authenticate: $!n";

my $i = 0;
foreach my $to_mail( @to_mails )
{
    chomp($to_mail);
    $smtp -> mail( $from_mail );
    $smtp -> to( $to_mail );
    $smtp -> data( );

    # send mail head
    $smtp -> datasend( "From: $from_mailn" );
    $smtp -> datasend( "To: $to_mailn" );
    $smtp -> datasend( "MIME-Version: 1.0 n" );
    $smtp -> datasend( "Content-Type: text/html; charset=gb2312 n" );
    $smtp -> datasend( "Content-Transfer-Encoding: base64n" );
    $smtp -> datasend( "Subject: =?gb2312?B?$subject?=nn");
   
    # send mail body
    $smtp -> datasend( $mail_body );
    $smtp -> dataend();

    $i++;
    print "send mail:$i mail to:$to_mailn";
}

$smtp -> quit();

运行需要下面perl-Authen-SASL perl-MIME-Base64两个库,用下面命令安装:
rpm -ivh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
yum -y install perl-Authen-SASL perl-MIME-Base64

Grep学习笔记

整理:Jims of 肥肥世家

<>

Copyright © 2004 本文遵从GPL协议,欢迎转载、修改、散布。

发布时间:2004年7月16日

更新时间:2005年8月24日


Table of Contents

1. grep简介
2. grep正则表达式元字符集(基本集)
3. 用于egrep和 grep -E的元字符扩展集
4. POSIX字符类
5. Grep命令选项
6. 实例
7. 技巧

1. grep简介

grep(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。Unix的grep家族包 括grep、egrep和fgrep。egrep和fgrep的命令只跟grep有很小不同。egrep是grep的扩展,支持更多的re元字 符,fgrep就是fixed grep或fast grep,它们把所有的字母都看作单词,也就是说,正则表达式中的元字符表示回其自身的字面意义,不再特殊。linux使用GNU版本的grep。它功能 更强,可以通过-G、-E、-F命令行选项来使用egrep和fgrep的功能。

grep的工作方式是这样的,它在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。搜索的结果被送到屏幕,不影响原文件内容。

grep可用于shell脚本,因为grep通过返回一个状态值来说明搜索的状态,如果模板搜索成功,则返回0,如果搜索不成功,则返回1,如果搜索的文件不存在,则返回2。我们利用这些返回值就可进行一些自动化的文本处理工作。

2. grep正则表达式元字符集(基本集)

^

锚定行的开始 如:’^grep’匹配所有以grep开头的行。

$

锚定行的结束 如:’grep$’匹配所有以grep结尾的行。

.

匹配一个非换行符的字符 如:’gr.p’匹配gr后接一个任意字符,然后是p。

*

匹配零个或多个先前字符 如:’*grep’匹配所有一个或多个空格后紧跟grep的行。 .*一起用代表任意字符。

[]

匹配一个指定范围内的字符,如'[Gg]rep’匹配Grep和grep。

[^]

匹配一个不在指定范围内的字符,如:'[^A-FH-Z]rep’匹配不包含A-F和H-Z字母开头,紧跟rep的行。

(..)

标记匹配字符,如'(love)’,love被标记为1。

<

锚定单词的开始,如:'<grep’匹配包含以grep开头的单词的行。

>

锚定单词的结束,如’grep>’匹配包含以grep结尾的单词的行。

x{m}

重复字符x,m次,如:’0{5}’匹配包含5个o的行。

x{m,}

重复字符x,至少m次,如:’o{5,}’匹配至少有5个o的行。

x{m,n}

重复字符x,至少m次,不多于n次,如:’o{5,10}’匹配5–10个o的行。

w

匹配文字和数字字符,也就是[A-Za-z0-9],如:’Gw*p’匹配以G后跟零个或多个文字或数字字符,然后是p。

W

w的反置形式,匹配一个或多个非单词字符,如点号句号等。

b

单词锁定符,如: ‘bgrepb’只匹配grep。

3. 用于egrep和 grep -E的元字符扩展集

+

匹配一个或多个先前的字符。如:'[a-z]+able’,匹配一个或多个小写字母后跟able的串,如loveable,enable,disable等。

?

匹配零个或多个先前的字符。如:’gr?p’匹配gr后跟一个或没有字符,然后是p的行。

a|b|c

匹配a或b或c。如:grep|sed匹配grep或sed这两个字符串。

()

分组符号,如:love(able|rs)(ov)+匹配loveable或lovers,匹配一个或多个ov。

x{m},x{m,},x{m,n}

作用同x{m},x{m,},x{m,n}

4. POSIX字符类

为 了在不同国家的字符编码中保持一至,POSIX(The Portable Operating System Interface)增加了特殊的字符类,如[:alnum:]是A-Za-z0-9的另一个写法。要把它们放到[]号内才能成为正则表达式,如[A- Za-z0-9]或[[:alnum:]]。在linux下的grep除fgrep外,都支持POSIX的字符类。

[:alnum:]

文字数字字符

[:alpha:]

文字字符

[:digit:]

数字字符

[:graph:]

非空字符(非空格、控制字符)

[:lower:]

小写字符

[:cntrl:]

控制字符

[:print:]

非空字符(包括空格)

[:punct:]

标点符号

[:space:]

所有空白字符(新行,空格,制表符)

[:upper:]

大写字符

[:xdigit:]

十六进制数字(0-9,a-f,A-F)

5. Grep命令选项

-?

同时显示匹配行上下的?行,如:grep -2 pattern filename同时显示匹配行的上下2行。

-b,–byte-offset

打印匹配行前面打印该行所在的块号码。

-c,–count

只打印匹配的行数,不显示匹配的内容。

-f File,–file=File

从文件中提取模板。空文件中包含0个模板,所以什么都不匹配。

-h,–no-filename

当搜索多个文件时,不显示匹配文件名前缀。

-i,–ignore-case

忽略大小写差别。

-q,–quiet

取消显示,只返回退出状态。0则表示找到了匹配的行。

-l,–files-with-matches

打印匹配模板的文件清单。

-L,–files-without-match

打印不匹配模板的文件清单。

-n,–line-number

在匹配的行前面打印行号。

-s,–silent

不显示关于不存在或者无法读取文件的错误信息。

-v,–revert-match

反检索,只显示不匹配的行。

-w,–word-regexp

如果被<和>引用,就把表达式做为一个单词搜索。

-V,–version

显示软件版本信息。

6. 实例

要用好grep这个工具,其实就是要写好正则表达式,所以这里不对grep的所有功能进行实例讲解,只列几个例子,讲解一个正则表达式的写法。

$ ls | grep ‘^a’

通过管道过滤ls输出的内容,只显示以a开头的行。

$ grep ‘test’ d*

显示所有以d开头的文件中包含test的行。

$ grep ‘test’ aa bb cc

显示在aa,bb,cc文件中匹配test的行。

$ grep ‘[a-z]{5}’ aa

显示所有包含每个字符串至少有5个连续小写字符的字符串的行。

$ grep ‘w(es)t.*1’ aa

如果west被匹配,则es就被存储到内存中,并标记为1,然后搜索任意个字符(.*),这些字符后面紧跟着另外一个es(1),找到就显示该行。如果用egrep或grep -E,就不用""号进行转义,直接写成’w(es)t.*1’就可以了。

7. 技巧

  • 在结果集中显示彩色的字符。

    export GREP_OPTIONS=’–color=always’
    export GREP_COLOR=’1;32′

Awk学习笔记(下)

14. awk编程14.1. 变量

  • 在awk中,变量不需要定义就可以直接使用,变量类型可以是数字或字符串。

  • 赋 值格式:Variable = expression,如$ awk ‘$1 ~/test/{count = $2 + $3; print count}’ test,上式的作用是,awk先扫描第一个域,一旦test匹配,就把第二个域的值加上第三个域的值,并把结果赋值给变量count,最后打印出来。

  • awk 可以在命令行中给变量赋值,然后将这个变量传输给awk脚本。如$ awk -F: -f awkscript month=4 year=2004 test,上式的month和year都是自定义变量,分别被赋值为4和2004。在awk脚本中,这些变量使用起来就象是在脚本中建立的一样。注意,如 果参数前面出现test,那么在BEGIN语句中的变量就不能被使用。

  • 域变量也可被赋值和修改,如$ awk ‘{$2 = 100 + $1; print }’ test,上式表示,如果第二个域不存在,awk将计算表达式100加$1的值,并将其赋值给$2,如果第二个域存在,则用表达式的值覆盖$2原来的值。 再例如:$ awk ‘$1 == "root"{$1 ="test";print}’ test,如果第一个域的值是“root”,则把它赋值为“test”,注意,字符串一定要用双引号。

  • 内建变量的使用。 变量列表在前面已列出,现在举个例子说明一下。$ awk -F: ‘{IGNORECASE=1; $1 == "MARY"{print NR,$1,$2,$NF}’test,把IGNORECASE设为1代表忽略大小写,打印第一个域是mary的记录数、第一个域、第二个域和最后一个 域。

14.2. BEGIN模块

BEGIN 模块后紧跟着动作块,这个动作块在awk处理任何输入文件之前执行。所以它可以在没有任何输入的情况下进行测试。它通常用来改变内建变量的值,如 OFS,RS和FS等,以及打印标题。如:$ awk ‘BEGIN{FS=":"; OFS="t"; ORS="nn"}{print $1,$2,$3} test。上式表示,在处理输入文件以前,域分隔符(FS)被设为冒号,输出文件分隔符(OFS)被设置为制表符,输出记录分隔符(ORS)被设置为两个 换行符。$ awk ‘BEGIN{print "TITLE TEST"}只打印标题。

14.3. END模块

END不匹配任何的输入文件,但是执行动作块中的所有动作,它在整个输入文件处理完成后被执行。如$ awk ‘END{print "The number of records is" NR}’ test,上式将打印所有被处理的记录数。

14.4. 重定向和管道

  • awk 可使用shell的重定向符进行重定向输出,如:$ awk ‘$1 = 100 {print $1 > "output_file" }’ test。上式表示如果第一个域的值等于100,则把它输出到output_file中。也可以用>>来重定向输出,但不清空文件,只做追加 操作。

  • 输出重定向需用到getline函数。getline从标准输入、管道或者当前正在处理的文件之外的其他输入文件 获得输入。它负责从输入获得下一行的内容,并给NF,NR和FNR等内建变量赋值。如果得到一条记录,getline函数返回1,如果到达文件的末尾就返 回0,如果出现错误,例如打开文件失败,就返回-1。如:

    $ awk ‘BEGIN{ "date" | getline d; print d}’ test。执行linux的date命令,并通过管道输出给getline,然后再把输出赋值给自定义变量d,并打印它。

    $ awk ‘BEGIN{"date" | getline d; split(d,mon); print mon[2]}’ test。执行shell的date命令,并通过管道输出给getline,然后getline从管道中读取并将输入赋值给d,split函数把变量d转 化成数组mon,然后打印数组mon的第二个元素。

    $ awk ‘BEGIN{while( "ls" | getline) print}’,命令ls的输出传递给geline作为输入,循环使getline从ls的输出中读取一行,并把它打印到屏幕。这里没有输入文件,因为 BEGIN块在打开输入文件前执行,所以可以忽略输入文件。

    $ awk ‘BEGIN{printf "What is your name?"; getline name < "/dev/tty" } $1 ~name {print "Found" name on line ", NR "."} END{print "See you," name "."} test。在屏幕上打印”What is your name?",并等待用户应答。当一行输入完毕后,getline函数从终端接收该行输入,并把它储存在自定义变量name中。如果第一个域匹配变量 name的值,print函数就被执行,END块打印See you和name的值。

    $ awk ‘BEGIN{while (getline < "/etc/passwd" > 0) lc++; print lc}’。awk将逐行读取文件/etc/passwd的内容,在到达文件末尾前,计数器lc一直增加,当到末尾时,打印lc的值。注意,如果文件不存 在,getline返回-1,如果到达文件的末尾就返回0,如果读到一行,就返回1,所以命令 while (getline < "/etc/passwd")在文件不存在的情况下将陷入无限循环,因为返回-1表示逻辑真。

  • 可以在awk中打开一个管 道,且同一时刻只能有一个管道存在。通过close()可关闭管道。如:$ awk ‘{print $1, $2 | "sort" }’ test END {close("sort")}。awd把print语句的输出通过管道作为linux命令sort的输入,END块执行关闭管道操作。

  • system函数可以在awk中执行linux的命令。如:$ awk ‘BEGIN{system("clear")’。

  • fflush函数用以刷新输出缓冲区,如果没有参数,就刷新标准输出的缓冲区,如果以空字符串为参数,如fflush(""),则刷新所有文件和管道的输出缓冲区。

14.5. 条件语句

awk中的条件语句是从C语言中借鉴过来的,可控制程序的流程。

14.5.1. if语句格式:
{if (expression){
statement; statement; …
}
}

$ awk ‘{if ($1 <$2) print $2 "too high"}’ test。如果第一个域小于第二个域则打印。

$ awk ‘{if ($1 < $2) {count++; print "ok"}}’ test.如果第一个域小于第二个域,则count加一,并打印ok。

14.5.2. if/else语句,用于双重判断。格式:
{if (expression){
statement; statement; …
}
else{
statement; statement; …
}
}

$ awk ‘{if ($1 > 100) print $1 "bad" ; else print "ok"}’ test。如果$1大于100则打印$1 bad,否则打印ok。

$ awk ‘{if ($1 > 100){ count++; print $1} else {count–; print $2}’ test。如果$1大于100,则count加一,并打印$1,否则count减一,并打印$1。

14.5.3. if/else else if语句,用于多重判断。格式:
{if (expression){
statement; statement; …
}
else if (expression){
statement; statement; …
}
else if (expression){
statement; statement; …
}
else {
statement; statement; …
}
}14.6. 循环

  • awk有三种循环:while循环;for循环;special for循环。

  • $ awk ‘{ i = 1; while ( i <= NF ) { print NF,$i; i++}}’ test。变量的初始值为1,若i小于可等于NF(记录中域的个数),则执行打印语句,且i增加1。直到i的值大于NF.

  • $ awk ‘{for (i = 1; i<NF; i++) print NF,$i}’ test。作用同上。

  • breadkcontinue语句。break用于在满足条件的情况下跳出循环;continue用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。如:

    {for ( x=3; x<=NF; x++)
    if ($x<0){print "Bottomed out!"; break}}
    {for ( x=3; x<=NF; x++)
    if ($x==0){print "Get next item"; continue}}

  • next语句从输入文件中读取一行,然后从头开始执行awk脚本。如:

    {if ($1 ~/test/){next}
    else {print}
    }

  • exit语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非零值表示出错。

14.7. 数组

awk中的数组的下标可以是数字和字母,称为关联数组。

14.7.1. 下标与关联数组

  • 用 变量作为数组下标。如:$ awk {name[x++]=$2};END{for(i=0;i<NR;i++) print i,name[i]}’ test。数组name中的下标是一个自定义变量x,awk初始化x的值为0,在每次使用后增加1。第二个域的值被赋给name数组的各个元素。在END 模块中,for循环被用于循环整个数组,从下标为0的元素开始,打印那些存储在数组中的值。因为下标是关健字,所以它不一定从0开始,可以从任何值开始。

  • special for循环用于读取关联数组中的元素。格式如下:

    {for (item in arrayname){
    print arrayname[item]
    }
    } $ awk ‘/^tom/{name[NR]=$1}; END{for(i in name){print name[i]}}’ test。打印有值的数组元素。打印的顺序是随机的。

  • 用字符串作为下标。如:count["test"]

  • 用 域值作为数组的下标。一种新的for循环方式,for (index_value in array) statement。如:$ awk ‘{count[$1]++} END{for(name in count) print name,count[name]}’ test。该语句将打印$1中字符串出现的次数。它首先以第一个域作数组count的下标,第一个域变化,索引就变化。

  • delete 函数用于删除数组元素。如:$ awk ‘{line[x++]=$1} END{for(x in line) delete(line[x])}’ test。分配给数组line的是第一个域的值,所有记录处理完成后,special for循环将删除每一个元素。

14.8. awk的内建函数14.8.1. 字符串函数

  • sub函数匹配记录中最大、最靠左边的子字符串的正则表达式,并用替换字符串替换这些字符串。如果没有指定目标字符串就默认使用整个记录。替换只发生在第一次匹配的时候。格式如下:

    sub (regular expression, substitution string):
    sub (regular expression, substitution string, target string)

    实例:

    $ awk ‘{ sub(/test/, "mytest"); print }’ testfile
    $ awk ‘{ sub(/test/, "mytest"); $1}; print }’ testfile

    第一个例子在整个记录中匹配,替换只发生在第一次匹配发生的时候。如要在整个文件中进行匹配需要用到gsub

    第二个例子在整个记录的第一个域中进行匹配,替换只发生在第一次匹配发生的时候。

  • gsub函数作用如sub,但它在整个文档中进行匹配。格式如下:

    gsub (regular expression, substitution string)
    gsub (regular expression, substitution string, target string)

    实例:

    $ awk ‘{ gsub(/test/, "mytest"); print }’ testfile
    $ awk ‘{ gsub(/test/, "mytest"), $1 }; print }’ testfile

    第一个例子在整个文档中匹配test,匹配的都被替换成mytest。

    第二个例子在整个文档的第一个域中匹配,所有匹配的都被替换成mytest。

  • index函数返回子字符串第一次被匹配的位置,偏移量从位置1开始。格式如下:

    index(string, substring)

    实例:

    $ awk ‘{ print index("test", "mytest") }’ testfile

    实例返回test在mytest的位置,结果应该是3。

  • length函数返回记录的字符数。格式如下:

    length( string )
    length

    实例:

    $ awk ‘{ print length( "test" ) }’
    $ awk ‘{ print length }’ testfile

    第一个实例返回test字符串的长度。

    第二个实例返回testfile文件中第条记录的字符数。

  • substr函数返回从位置1开始的子字符串,如果指定长度超过实际长度,就返回整个字符串。格式如下:

    substr( string, starting position )
    substr( string, starting position, length of string )

    实例:

    $ awk ‘{ print substr( "hello world", 7,11 ) }’

    上例截取了world子字符串。

  • match函数返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式则返回0。match函数会设置内建变量RSTART为字符串中子字符串的开始位置,RLENGTH为到子字符串末尾的字符个数。substr可利于这些变量来截取字符串。函数格式如下:

    match( string, regular expression )

    实例:

    $ awk ‘{start=match("this is a test",/[a-z]+$/); print start}’
    $ awk ‘{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }’

    第一个实例打印以连续小写字符结尾的开始位置,这里是11。

    第二个实例还打印RSTART和RLENGTH变量,这里是11(start),11(RSTART),4(RLENGTH)。

  • toupper和tolower函数可用于字符串大小间的转换,该功能只在gawk中有效。格式如下:

    toupper( string )
    tolower( string )

    实例:

    $ awk ‘{ print toupper("test"), tolower("TEST") }’

  • split函数可按给定的分隔符把字符串分割为一个数组。如果分隔符没提供,则按当前FS值进行分割。格式如下:

    split( string, array, field separator )
    split( string, array )

    实例:

    $ awk ‘{ split( "20:18:00", time, ":" ); print time[2] }’

    上例把时间按冒号分割到time数组内,并显示第二个数组元素18。

14.8.2. 时间函数

  • systime函数返回从1970年1月1日开始到当前时间(不计闰年)的整秒数。格式如下:

    systime()

    实例:

    $ awk ‘{ now = systime(); print now }’

  • strftime函数使用C库中的strftime函数格式化时间。格式如下:

    systime( [format specification][,timestamp] )

    Table 3. 日期和时间格式说明符

    格式 描述 %a 星期几的缩写(Sun) %A 星期几的完整写法(Sunday) %b 月名的缩写(Oct) %B 月名的完整写法(October) %c 本地日期和时间 %d 十进制日期 %D 日期 08/20/99 %e 日期,如果只有一位会补上一个空格 %H 用十进制表示24小时格式的小时 %I 用十进制表示12小时格式的小时 %j 从1月1日起一年中的第几天 %m 十进制表示的月份 %M 十进制表示的分钟 %p 12小时表示法(AM/PM) %S 十进制表示的秒 %U 十进制表示的一年中的第几个星期(星期天作为一个星期的开始) %w 十进制表示的星期几(星期天是0) %W 十进制表示的一年中的第几个星期(星期一作为一个星期的开始) %x 重新设置本地日期(08/20/99) %X 重新设置本地时间(12:00:00) %y 两位数字表示的年(99) %Y 当前月份 %Z 时区(PDT) %% 百分号(%)

    实例:

    $ awk ‘{ now=strftime( "%D", systime() ); print now }’
    $ awk ‘{ now=strftime("%m/%d/%y"); print now }’

14.8.3. 内建数学函数

Table 4.

函数名称 返回值 atan2(x,y) y,x范围内的余切 cos(x) 余弦函数 exp(x) 求幂 int(x) 取整 log(x) 自然对数 rand() 随机数 sin(x) 正弦 sqrt(x) 平方根 srand(x) x是rand()函数的种子 int(x) 取整,过程没有舍入 rand() 产生一个大于等于0而小于1的随机数 14.8.4. 自定义函数

在awk中还可自定义函数,格式如下:

function name ( parameter, parameter, parameter, … ) {
statements
return expression # the return statement and expression are optional
}15. How-to

  • 如何把一行竖排的数据转换成横排?

    awk ‘{printf("%s,",$1)}’ filename