tee命令

tee命令读取标准输入,把内容输出到标准输出和(多个)文件中   tee不带参数时只输出到标准输出 tee参数为一个或多个文件时,输出到标准输出的同时,保存到这一个或多个文件中。如果文件不存在,则创建;如果已经存在,则覆盖

# seq 3 > a.txt
# cat a.txt 
1
2
3
# cat a.txt | tee
1
2
3
# cat a.txt | tee b.txt
1
2
3
# cat b.txt
1
2
3

  -a参数可以追加标准输入到文件中,如果文件不存在,则创建;如果已经存在,就在末尾追加内容,而不是覆盖。

# cat a.txt >> b.txt
# cat a.txt | tee -a c.txt
1
2
3
# cat b.txt 
1
2
3
1
2
3
# cat c.txt 
1
2
3
# cat a.txt | tee -a c.txt
1
2
3
# cat c.txt 
1
2
3
1
2
3

  – 短横杠参数表示输出到标准输出两次,- 越多输出的次数越多,若参数包含文件,只输出到文件一次

# cat a.txt |tee
1
2
3
# cat a.txt |tee -
1
2
3
1
2
3
# cat a.txt |tee - - -
1
2
3
1
2
3
1
2
3
1
2
3
# echo 'abc'|tee -
abc
abc
# echo 'abc'|tee - - - - - -
abc
abc
abc
abc
abc
abc
abc
# echo 'abc'|tee 1.txt 2.txt - - - - - - -
abc
abc
abc
abc
abc
abc
abc
abc
# cat 1.txt 
abc
# cat 2.txt 
abc

 

监控nginx的访问日志是否有被刷的情况

有时候网站被刷了我却不知道,所以想了个简单的办法,如下: 写个shell,每小时记录一次独立IP的访问次数到文件里

#!/bin/bash

if [ $# -ne 2 ];then
echo "Missing Operand..."
exit 1
fi

awk '{print $1,$4}' /usr/local/nginx/logs/$1 |grep 2014:`date|awk '{print $4}'|awk -F ':' '{print $1}'`|awk '{print $1}'|sort |uniq -c |sort -rn|head -$2 > /usr/local/uniqIpAccessCnt/`date +%Y-%m-%d-%H`.txt

第一个参数表示日志名,第二个参数表示想记录的独立IP数量,每小时在/usr/local/uniqIpAccessCnt/目录下生成一个文件,类似“2014-12-02-09.txt”,表示14年12月2号9点的独立IP访问情况,文件内容如下

10304 111.206.12.85
 3351 112.9.28.220
 2191 124.152.184.80
 1706 221.214.13.179
 1407 120.192.232.82
 1208 119.190.40.169
 1137 58.222.187.194
  927 122.224.148.178
  915 218.3.162.66
  899 27.13.83.173
  806 113.4.197.208
  800 114.239.129.160
  763 175.1.35.110
  720 180.89.233.72
  714 118.197.131.232
  652 221.192.232.38
  596 175.171.126.215
  509 117.174.26.117
  470 118.144.66.196
  465 218.241.217.203

再写个python监控生成的文件,我设定阈值为10000,超过就发邮件通知。

58 * * * * /usr/local/bin/get_uniq_ip_access_cnt.sh www.abc.com.log 20 
0 * * * * /usr/local/bin/malicious_access_mon.py
#!/usr/bin/python
# -*- coding: cp936 -*-

import os
import time
import datetime
import string
import email
import smtplib
import mimetypes
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

def SendMail():

    server = 'Web-1.2.3.4'
    mail_to = ["san.zhang@abc.com", "si.li@abc.com"]
    mail_host = "mail.abc.com"
    mail_user = "webmaster@abc.com"
    mail_pass = "passwd"
    mail_from = "webmaster@abc.com"
    mail_postfix = "abc.com"
    mail_subject = "Malicious Access - " + server +" !!!"
    mail_body = mailbody

    msg = MIMEMultipart()
    body = MIMEText(mail_body,_subtype='plain',_charset='gb2312')
    body = MIMEText(mail_body,_subtype='plain',_charset='utf-8')
    msg.attach(body)
    
    msg['Subject'] = mail_subject
    msg['From'] = mail_from
    msg['To'] = ";".join(mail_to)
    
    try:
        s = smtplib.SMTP()
        s.connect(mail_host)
        s.login(mail_user,mail_pass)
        s.sendmail(mail_from, mail_to, msg.as_string())
        s.close()
        return True
    except Exception, e:
        print str(e)
        return False  

def handlefile():

    path=r"/usr/local/uniqIpAccessCnt/"
    files=[(os.path.getmtime(path+x),path+x) for x in os.listdir(path)]
    files.sort()
    fname = files[-1][1]
    print fname

    f = open(fname, 'r')
    line = f.readline()
    cnt = (line.split('.')[0]).split(' ')[-2]
    print cnt
    if int(cnt) > 10000:
        global mailbody
        mailbody = 'Access record as follows in the last hour:\n\n\n  Count      IP\n\n' 
        lines = f.readlines()
        f.close()
        mailbody = mailbody + line
        for x in lines:
            mailbody = mailbody + x
        print mailbody
        SendMail()

def main():
    handlefile()

main()

 

sed命令

  • sed命令行格式
sed [-nefri] command 输入文本
  •  常用选项
-n  使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的资料 一般都会被列出到萤幕上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e  直接在指令列模式上进行 sed 的动作编辑;
-f  filename  直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的 sed 动作;
-r  sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)
-i  直接修改读取的档案内容,而不是由屏幕输出。
  •  常用命令
a  新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的 下一行)~
c  取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d  删除, 因为是删除所以 d 后面通常不接任何字符串;
i  插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p  列印, 亦即将某个选择的资料印出。通常 p 会与参数 sed -n 一起运作~
s  替换, 可以直接进行替换操作,通常这个 s 的动作可以搭配 正规表达式,例如 1,20s/old/new/g
  •  举例
假设文件名为ab
删除某行
$ sed '1d' ab #删除第一行
$ sed '$d' ab #删除最后一行
$ sed '1,2d' ab #删除第一行到第二行
$ sed '2,$d' ab #删除第二行到最后一行

显示某行
$ sed -n '1p' ab #显示第一行
$ sed -n '$p' ab #显示最后一行
$ sed -n '1,2p' ab #显示第一行到第二行
$ sed -n '2,$p' ab #显示第二行到最后一行

使用模式进行查询
$ sed -n '/ruby/p' ab #查询包括关键字ruby所在所有行
$ sed -n '/\$/p' ab #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义

增加一行或多行字符串
$ cat ab
Hello!
ruby is me,welcome to my blog.
end
$ sed '1a drink tea' ab #第一行后增加字符串"drink tea"
Hello!
drink tea
ruby is me,welcome to my blog.
end
$ sed '1,3a drink tea' ab #第一行到第三行后增加字符串"drink tea"
Hello!
drink tea
ruby is me,welcome to my blog.
drink tea
end
drink tea
$ sed '1a drink tea\nor coffee' ab #第一行后增加多行,使用换行符\n
Hello!
drink tea
or coffee
ruby is me,welcome to my blog.
end

代替一行或多行
$ sed '1c Hi' ab #第一行代替为Hi
Hi
ruby is me,welcome to my blog.
end
$ sed '1,2c Hi' ab #第一行到第二行代替为Hi
Hi
end

替换一行中的某部分
格式: sed 's/要替换的字符串/新的字符串/g' (要替换的字符串可以用正则表达式)
$ sed -n '/ruby/p' ab | sed 's/ruby/bird/g' #替换ruby为bird
$ sed -n '/ruby/p' ab | sed 's/ruby//g' #删除ruby

插入
$ sed -i '$a bye' ab #在文件ab中最后一行直接输入"bye"
$ cat ab
Hello!
ruby is me,welcome to my blog.
end
bye

 


    Sed单行命令参考(节选自http://sed.sourceforge.net/sed1line_zh-CN.html)

# 在每一行后面增加一空行
 sed G

# 在每5行后增加一空白行 (在第5,10,15,20,等行后增加一空白行)
 gsed '0~5G'                      # 只对GNU sed有效
 sed 'n;n;n;n;G;'                 # 其他sed

# 将原来的所有空行删除并在每一行后面增加一空行。
# 这样在输出的文本中每一行后面将有且只有一空行。
 sed '/^$/d;G'

# 在每一行后面增加两行空行
 sed 'G;G'

# 将第一个脚本所产生的所有空行删除(即删除所有偶数行)
 sed 'n;d'

# 在匹配式样“regex”的行之前插入一空行
 sed '/regex/{x;p;x;}'

# 在匹配式样“regex”的行之后插入一空行
 sed '/regex/G'

# 在匹配式样“regex”的行之前和之后各插入一空行
 sed '/regex/{x;p;x;G;}'

# 计算行数 (模拟 "wc -l")
 sed -n '$='

# 将每一行前导的“空白字符”(空格,制表符)删除;使之左对齐
 sed 's/^[ \t]*//' 

# 将每一行拖尾的“空白字符”(空格,制表符)删除
 sed 's/[ \t]*$//'  

# 将每一行中的前导和拖尾的空白字符删除
 sed 's/^[ \t]*//;s/[ \t]*$//'

# 在每一行开头处插入5个空格(使全文向右移动5个字符的位置)
 sed 's/^/     /'

# 在每一行中查找字串“foo”,并将找到的“foo”替换为“bar”
 sed 's/foo/bar/'                 # 只替换每一行中的第一个“foo”字串
 sed 's/foo/bar/4'                # 只替换每一行中的第四个“foo”字串
 sed 's/foo/bar/g'                # 将每一行中的所有“foo”都换成“bar”
 sed 's/\(.*\)foo\(.*foo\)/\1bar\2/' # 替换倒数第二个“foo”
 sed 's/\(.*\)foo/\1bar/'            # 替换最后一个“foo”

# 只在行中出现字串“baz”的情况下将“foo”替换成“bar”
 sed '/baz/s/foo/bar/g'

# 将“foo”替换成“bar”,并且只在行中未出现字串“baz”的情况下替换
 sed '/baz/!s/foo/bar/g'

# 不管是“scarlet”“ruby”还是“puce”,一律换成“red”
 sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g'  #对多数的sed都有效
 gsed 's/scarlet\|ruby\|puce/red/g'               # 只对GNU sed有效

# 将每两行连接成一行(类似“paste”)
 sed '$!N;s/\n/ /'

# 如果当前行以反斜杠“\”结束,则将下一行并到当前行末尾,并去掉原来行尾的反斜杠
 sed -e :a -e '/\\$/N; s/\\\n//; ta'

选择性地显示特定行:
--------

 # 显示文件中的前10行 (模拟“head”的行为)
 sed 10q

 # 显示文件中的第一行 (模拟“head -1”命令)
 sed q

 # 显示文件中的最后10行 (模拟“tail”)
 sed -e :a -e '$q;N;11,$D;ba'

 # 显示文件中的最后2行(模拟“tail -2”命令)
 sed '$!N;$!D'

 # 显示文件中的最后一行(模拟“tail -1”)
 sed '$!d'                        # 方法1
 sed -n '$p'                      # 方法2

 # 显示文件中的倒数第二行
 sed -e '$!{h;d;}' -e x              # 当文件中只有一行时,输入空行
 sed -e '1{$q;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,显示该行
 sed -e '1{$d;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,不输出

 # 只显示匹配正则表达式的行(模拟“grep”)
 sed -n '/regexp/p'               # 方法1
 sed '/regexp/!d'                 # 方法2

 # 只显示“不”匹配正则表达式的行(模拟“grep -v”)
 sed -n '/regexp/!p'              # 方法1,与前面的命令相对应
 sed '/regexp/d'                  # 方法2,类似的语法

 # 查找“regexp”并将匹配行的上一行显示出来,但并不显示匹配行
 sed -n '/regexp/{g;1!p;};h'

 # 查找“regexp”并将匹配行的下一行显示出来,但并不显示匹配行
 sed -n '/regexp/{n;p;}'

 # 显示包含“regexp”的行及其前后行,并在第一行之前加上“regexp”所
 # 在行的行号 (类似“grep -A1 -B1”)
 sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h

 # 显示包含“AAA”、“BBB”或“CCC”的行(任意次序)
 sed '/AAA/!d; /BBB/!d; /CCC/!d'  # 字串的次序不影响结果

 # 显示包含“AAA”、“BBB”和“CCC”的行(固定次序)
 sed '/AAA.*BBB.*CCC/!d'

 # 显示包含“AAA”“BBB”或“CCC”的行 (模拟“egrep”)
 sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d    # 多数sed
 gsed '/AAA\|BBB\|CCC/!d'                        # 对GNU sed有效

 # 显示包含“AAA”的段落 (段落间以空行分隔)
 # HHsed v1.5 必须在“x;”后加入“G;”,接下来的3个脚本都是这样
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'

 # 显示包含“AAA”“BBB”和“CCC”三个字串的段落 (任意次序)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d'

 # 显示包含“AAA”、“BBB”、“CCC”三者中任一字串的段落 (任意次序)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
 gsed '/./{H;$!d;};x;/AAA\|BBB\|CCC/b;d'         # 只对GNU sed有效

 # 显示包含65个或以上字符的行
 sed -n '/^.\{65\}/p'

 # 显示包含65个以下字符的行
 sed -n '/^.\{65\}/!p'            # 方法1,与上面的脚本相对应
 sed '/^.\{65\}/d'                # 方法2,更简便一点的方法

 # 显示部分文本——从包含正则表达式的行开始到最后一行结束
 sed -n '/regexp/,$p'

 # 显示部分文本——指定行号范围(从第8至第12行,含8和12行)
 sed -n '8,12p'                   # 方法1
 sed '8,12!d'                     # 方法2

 # 显示第52行
 sed -n '52p'                     # 方法1
 sed '52!d'                       # 方法2
 sed '52q;d'                      # 方法3, 处理大文件时更有效率

 # 从第3行开始,每7行显示一次    
 gsed -n '3~7p'                   # 只对GNU sed有效
 sed -n '3,${p;n;n;n;n;n;n;}'     # 其他sed

 # 显示两个正则表达式之间的文本(包含)
 sed -n '/Iowa/,/Montana/p'       # 区分大小写方式

选择性地删除特定行:
--------

 # 显示通篇文档,除了两个正则表达式之间的内容
 sed '/Iowa/,/Montana/d'

 # 删除文件中相邻的重复行(模拟“uniq”)
 # 只保留重复行中的第一行,其他行删除
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # 删除文件中的重复行,不管有无相邻。注意hold space所能支持的缓存
 # 大小,或者使用GNU sed。
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

 # 删除除重复行外的所有行(模拟“uniq -d”)
 sed '$!N; s/^\(.*\)\n\1$/\1/; t; D'

 # 删除文件中开头的10行
 sed '1,10d'

 # 删除文件中的最后一行
 sed '$d'

 # 删除文件中的最后两行
 sed 'N;$!P;$!D;$d'

 # 删除文件中的最后10行
 sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # 方法1
 sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # 方法2

 # 删除8的倍数行
 gsed '0~8d'                           # 只对GNU sed有效
 sed 'n;n;n;n;n;n;n;d;'                # 其他sed

 # 删除匹配式样的行
 sed '/pattern/d'                      # 删除含pattern的行。当然pattern
                                       # 可以换成任何有效的正则表达式

 # 删除文件中的所有空行(与“grep '.' ”效果相同)
 sed '/^$/d'                           # 方法1
 sed '/./!d'                           # 方法2

 # 只保留多个相邻空行的第一行。并且删除文件顶部和尾部的空行。
 # (模拟“cat -s”)
 sed '/./,/^$/!d'        #方法1,删除文件顶部的空行,允许尾部保留一空行
 sed '/^$/N;/\n$/D'      #方法2,允许顶部保留一空行,尾部不留空行

 # 只保留多个相邻空行的前两行。
 sed '/^$/N;/\n$/N;//D'

 # 删除文件顶部的所有空行
 sed '/./,$!d'

 # 删除文件尾部的所有空行
 sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'  # 对所有sed有效
 sed -e :a -e '/^\n*$/N;/\n$/ba'        # 同上,但只对 gsed 3.02.*有效

 # 删除每个段落的最后一行
 sed -n '/^$/{p;h;};/./{x;/./p;}'

  参考:http://test001.readthedocs.org/zh_CN/latest/sed.html         http://sed.sourceforge.net/sed1line_zh-CN.html

tr命令

  • 语法
tr (选项) (参数)
  • 选项
-c或——complerment:取代所有不属于第一字符集的字符;
-d或——delete:删除所有属于第一字符集的字符;
-s或--squeeze-repeats:把连续重复的字符以单独一个字符表示;
-t或--truncate-set1:先删除第一字符集较第二字符集多出的字符。
  • 参数

字符集1:指定要转换或删除的原字符集。当执行转换操作时,必须使用参数“字符集2”指定转换的目标字符集。但执行删除操作时,不需要参数“字符集2”; 字符集2:指定要转换成的目标字符集。

  • 实例

将输入字符由大写转换为小写。’A-Z’ 和 ‘a-z’都是集合,集合是可以自己制定的,例如:’ABD-}’、’bB.,’、’a-de-h’、’a-c0-9’都属于集合,集合里可以使用’\n’、’\t’,可以可以使用其他ASCII字符。 

# echo "HELLO WORLD" | tr 'A-Z' 'a-z' 
hello world

使用tr删除字符:
# echo "hello 123 world 456" | tr -d '0-9'
hello world 

将制表符转换为空格:
# cat text | tr '\t' ' '

字符集补集,从输入文本中将不在补集中的所有字符删除。此例中,补集中包含了数字0~9、空格和换行符\n,所以没有被删除,其他字符全部被删除了。

# echo aa.,a 1 b#$bb 2 c*/cc 3 ddd 4 | tr -d -c '0-9 \n' 
1 2 3 4

用tr压缩字符,可以压缩输入中重复的字符:
# echo "thissss is a text linnnnnnne." | tr -s ' sn' 
this is a text line.

使用tr做数字相加操作:
# echo 1 2 3 4 5 6 7 8 9 | xargs -n1 | echo $[ $(tr '\n' '+') 0 ]
45

删除Windows文件“造成”的'^M'字符:
# cat file | tr -s "\r" "\n" > new_file
或
# cat file | tr -d "\r" > new_file
tr可以使用的字符类

[:alnum:]:字母和数字 [:alpha:]:字母 [:cntrl:]:控制(非打印)字符 [:digit:]:数字 [:graph:]:图形字符 [:lower:]:小写字母 [:print:]:可打印字符 [:punct:]:标点符号 [:space:]:空白字符 [:upper:]:大写字母 [:xdigit:]:十六进制字符

使用方式

# tr '[:lower:]' '[:upper:]'
  转自:http://man.linuxde.net/tr

uniq命令

uniq的作用用两个字概括便是排重,一般会和sort命令进行组合使用,因为uniq只会检查相邻的重复行。 测试文件依然为emp.data

# cat emp.data
Aaron	6.10	30
Theo	5.10	12
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12
Cesc	7.10	2

  -c选项用于统计相邻的重复行,-i参数用来忽略大小写

# uniq -c emp.data
      1 Aaron	6.10	30
      1 Theo	5.10	12
      1 Beth	4.00	0
      1 Jacky	3.75	11
      1 kathy	4.00	10
      1 Mark	5.00	20
      3 Mary	5.50	22
      1 Susie	4.25	18
      1 Theo	5.10	12
      1 Cesc	7.10	2
# sort emp.data|uniq -c
      1 Aaron	6.10	30
      1 Beth	4.00	0
      1 Cesc	7.10	2
      1 Jacky	3.75	11
      1 kathy	4.00	10
      1 Mark	5.00	20
      3 Mary	5.50	22
      1 Susie	4.25	18
      2 Theo	5.10	12

  -d选项显示重复行一次,-D有几次重复就显示几次,而-u只显示不重复的行

# sort emp.data|uniq -d
Mary	5.50	22
Theo	5.10	12
# sort emp.data|uniq -D
Mary	5.50	22
Mary	5.50	22
Mary	5.50	22
Theo	5.10	12
Theo	5.10	12
# sort emp.data|uniq -u
Aaron	6.10	30
Beth	4.00	0
Cesc	7.10	2
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Susie	4.25	18

  -f选项用来跳过指定数量的字段,特别注意,不是跳过第N个字段,而是跳过前N个字段,在查看日志时非常有用,可以跳过每一行记录前面的时间戳。

# sed -i '2a Bethaaa\t4.00\t10' emp.data
# cat emp.data
Aaron	6.10	30
Theo	5.10	12
Bethaaa	4.00	10
Beth	4.00	10
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12
Cesc	7.10	2
# sort emp.data|uniq -f 1 -c
      1 Aaron	6.10	30
      2 Beth	4.00	10
      1 Cesc	7.10	2
      1 Jacky	3.75	11
      1 kathy	4.00	10
      1 Mark	5.00	20
      3 Mary	5.50	22
      1 Susie	4.25	18
      2 Theo	5.10	12
# sort emp.data|uniq -f 1 -u
Aaron	6.10	30
Cesc	7.10	2
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Susie	4.25	18
# sort emp.data|uniq -f 1 -d
Beth	4.00	10
Mary	5.50	22
Theo	5.10	12
# sort emp.data|uniq -f 1 -D
Beth	4.00	10
Bethaaa	4.00	10
Mary	5.50	22
Mary	5.50	22
Mary	5.50	22
Theo	5.10	12
Theo	5.10	12

  -s选项表示前N个字符被忽略,-w表示仅比较前N个字符

# sed -i '$a Adsc\t7.10\t2' emp.data
# cat emp.data
Aaron	6.10	30
Theo	5.10	12
Bethaaa	4.00	10
Beth	4.00	10
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12
Cesc	7.10	2
Adsc	7.10	2
# uniq -s 2 -c emp.data
      1 Aaron	6.10	30
      1 Theo	5.10	12
      1 Bethaaa	4.00	10
      1 Beth	4.00	10
      1 Jacky	3.75	11
      1 kathy	4.00	10
      1 Mark	5.00	20
      3 Mary	5.50	22
      1 Susie	4.25	18
      1 Theo	5.10	12
      2 Cesc	7.10	2
# uniq -w 4 -c emp.data
      1 Aaron	6.10	30
      1 Theo	5.10	12
      2 Bethaaa	4.00	10
      1 Jacky	3.75	11
      1 kathy	4.00	10
      1 Mark	5.00	20
      3 Mary	5.50	22
      1 Susie	4.25	18
      1 Theo	5.10	12
      1 Cesc	7.10	2
      1 Adsc	7.10	2
# uniq -w 2 -c emp.data
      1 Aaron	6.10	30
      1 Theo	5.10	12
      2 Bethaaa	4.00	10
      1 Jacky	3.75	11
      1 kathy	4.00	10
      4 Mark	5.00	20
      1 Susie	4.25	18
      1 Theo	5.10	12
      1 Cesc	7.10	2
      1 Adsc	7.10	2

sort命令

Sort是用于对单个或多个文本文件内容进行排序的Linux程序。Sort命令以空格作为字段分隔符,将一行分割为多个关键字对文件进行排序。比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。需要注意 的是除非你将输出重定向到文件中,否则Sort命令并不对文件内容进行实际的排序(即文件内容没有修改),只是将文件内容按有序输出。 用于测试的文件为

# cat emp.data
Theo	5.10	12
Beth	4.00	0
Jacky	3.75	11
Aaron	6.10	30
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Susie	4.25	18

不加参数会默认按照第一列排序

# cat emp.data
Theo	5.10	12
Beth	4.00	0
Jacky	3.75	11
Aaron	6.10	30
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Susie	4.25	18
# sort emp.data
Aaron	6.10	30
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12

-u参数用来去掉重复的行

# sed '1a Mary 5.50 22' emp.data
Theo	5.10	12
Mary	5.50	22
Beth	4.00	0
Jacky	3.75	11
Aaron	6.10	30
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Susie	4.25	18
# sed -i '1a Mary 5.50 22' emp.data
# sort emp.data
Aaron	6.10	30
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12
# sort -u emp.data
Aaron	6.10	30
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12

-r选项用来降序排列,默认为升序

# sort emp.data
Aaron	6.10	30
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12
# sort -r emp.data
Theo	5.10	12
Susie	4.25	18
Mary	5.50	22
Mary	5.50	22
Mark	5.00	20
kathy	4.00	10
Jacky	3.75	11
Beth	4.00	0
Aaron	6.10	30

-o选项用于将排序后的结果直接输出到文件,这个文件可以是原文件,也可以是新的文件;输出重定向‘>’只能把结果输出到新文件,而不能输出到原文件,这便是-o参数的意义。也就是说要直接修改原文件只能用-o参数,要把结果输出到新文件,用重定向或者-o参数都可以。

# cat emp.data
Theo	5.10	12
Mary	5.50	22
Beth	4.00	0
Jacky	3.75	11
Aaron	6.10	30
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Susie	4.25	18
# sort emp.data -o emp.data
# cat emp.data
Aaron	6.10	30
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12

-t和-k选项,-t用来指定分隔符,默认为空格或制表符;-k用来指定字段

# sort -k 1 emp.data
Aaron	6.10	30
Beth	4.00	0
Jacky	3.75	11
kathy	4.00	10
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Susie	4.25	18
Theo	5.10	12
# sed -i 's/\t/:/g' emp.data
# cat emp.data
Aaron:6.10:30
Beth:4.00:0
Jacky:3.75:11
kathy:4.00:10
Mark:5.00:20
Mary:5.50:22
Mary:5.50:22
Susie:4.25:18
Theo:5.10:12
# sort -t: -k2 emp.data
Jacky:3.75:11
Beth:4.00:0
kathy:4.00:10
Susie:4.25:18
Mark:5.00:20
Theo:5.10:12
Mary:5.50:22
Mary:5.50:22
Aaron:6.10:30

-n选项用来指定按数值方式排序

# sed -i '$a Cesc 7.10 2' emp.data
# sort -k3 emp.data
Beth	4.00	0
kathy	4.00	10
Jacky	3.75	11
Theo	5.10	12
Susie	4.25	18
Cesc	7.10	2
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Aaron	6.10	30
# sort -n -k3 emp.data
Beth	4.00	0
Cesc	7.10	2
kathy	4.00	10
Jacky	3.75	11
Theo	5.10	12
Susie	4.25	18
Mark	5.00	20
Mary	5.50	22
Mary	5.50	22
Aaron	6.10	30

其他的sort常用选项 -f会将小写字母都转换为大写字母来进行比较,亦即忽略大小写 -c会检查文件是否已排好序,如果乱序,则输出第一个乱序的行的相关信息,最后返回1 -C会检查文件是否已排好序,如果乱序,不输出内容,仅返回1 -M会以月份来排序,比如JAN小于FEB等等 -b会忽略每一行前面的所有空白部分,从第一个可见字符开始比较。   参考:http://roclinux.cn/?p=1350https://linux.cn/article-5372-1.html

批量修改linux服务器用户密码的shell脚本

首先使用ssh-keygen与其他服务器建立信任关系,以便免密码登陆其他服务器,这里省略。

再写一个iplist.txt文件如下

$ more iplist.txt 
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5
192.168.1.6

需要修改san.zhang,si.li,wu.wang三个用户的密码,脚本如下

#!/bin/bash

namelist=(san.zhang si.li wu.wang)

for name in ${namelist[@]}

  do

    if cat /etc/passwd | awk -F : '{print $1}' | grep $name >/dev/null 2>&1;then

      echo "'$name' already exists, Changing password..."

      if [ $name = san.zhang ];then
        (/bin/echo "zhangsan123" ;sleep 1;/bin/echo "zhangsan123") | passwd san.zhang
      elif [ $name = si.li ];then
        (/bin/echo "lisi123" ;sleep 1;/bin/echo "lisi123") | passwd si.li
      elif [ $name = wu.wang ];then
        (/bin/echo "wangwu123" ;sleep 1;/bin/echo "wangwu123") | passwd wu.wang
      fi

    else

      /usr/sbin/useradd $name -d /home/$name/ -m -g test -s /bin/bash
      echo "'$name' does not exist, Have been added, Changing password..."

      if [ $name = san.zhang ];then
        (/bin/echo "zhangsan123" ;sleep 1;/bin/echo "zhangsan123") | passwd san.zhang
      elif [ $name = si.li ];then
        (/bin/echo "lisi123" ;sleep 1;/bin/echo "lisi123") | passwd si.li
      elif [ $name = wu.wang ];then
        (/bin/echo "wangwu123" ;sleep 1;/bin/echo "wangwu123") | passwd wu.wang
      fi

    fi
done

 

如果用户不存在,则会自动创建用户,并修改密码,执行情况如下

$ for ip in `cat iplist.txt`;do echo $ip&&ssh -p22 root@$ip -l root  "`cat cmd.txt`";done;
192.168.1.1
'san.zhang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'si.li' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'wu.wang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
192.168.1.2
'san.zhang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'si.li' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'wu.wang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
192.168.1.3
'san.zhang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'si.li' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'wu.wang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
192.168.1.4
'san.zhang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'si.li' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'wu.wang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
192.168.1.5
'san.zhang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'si.li' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'wu.wang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
192.168.1.6
'san.zhang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'si.li' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
'wu.wang' already exists, Changing password...
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully

其实就是在远程服务器上执行本地的一个shell脚本,然后加了个判断而已。机器比较少的时候这样可行,如果机器多了,还是得使用saltstack,puppet之类的配置管理工具。

同步线上Mysql数据库备份文件到本地并还原的shell脚本

需要事先配置rsync,使用ssh方式传输数据文件,脚本如下

#!/bin/bash

STAMP=`date "+%Y-%m-%d"`
PORT=22
REMOTE_IP=1.1.1.1
REMOTE_DIR="/usr/local/mysql/backup"
LOCAL_DIR="/usr/local/mysql_data_backup"
PASSWD='123456'


echo `date +%F-%T` '################## Began to date sync...'
/usr/bin/rsync -avzp -e 'ssh -p '$PORT'' root@$REMOTE_IP:$REMOTE_DIR/bak_*_full_$STAMP*.gz $LOCAL_DIR/

if [ $? -eq 0 ];then

  echo `date +%F-%T` 'Data Had Been Synced Successfully!'

  for file in `find  $LOCAL_DIR/bak_*_full_$STAMP*.gz  -exec  ls {} \;`
  
    do
    
      cd $LOCAL_DIR&&echo `date +%F-%T` '------------------- unzip '$file'...'&&gzip -d $file
      DBNAME=`echo $file|awk -F  "bak_" '{print $2}'|awk -F "_full" '{print $1}'`
    
      if [ `/usr/local/mysql/bin/mysql -uroot -p$PASSWD -e'show databases;'|grep ^$DBNAME$|wc -l` -eq 1 ];then
        echo `date +%F-%T` ''$DBNAME' began to restore...'
        /usr/local/mysql/bin/mysql -u root -p$PASSWD -e "source ${file%.*}"  $DBNAME

        if [ $? -eq 0 ];then
          echo `date +%F-%T` ''$DBNAME' restore completed...'
        else
          echo `date +%F-%T` ''$DBNAME' restore Failure!'
        fi

      else
        echo `date +%F-%T` ' create database '$DBNAME'...'
        /usr/local/mysql/bin/mysql -uroot -p$PASSWD -e'create database '$DBNAME''
        echo `date +%F-%T` ''$DBNAME' began to restore...'
        /usr/local/mysql/bin/mysql -u root -p$PASSWD -e "source ${file%.*}"  $DBNAME

        if [ $? -eq 0 ];then
          echo `date +%F-%T` ''$DBNAME' restore completed...'
        else
          echo `date +%F-%T` ''$DBNAME' restore Failure!'
        fi

      fi
  
    done

else

  echo `date +%F-%T` 'Data Sync Failure! Break...'

fi

 

在crontab中添加任务计划

45 4 * * * /usr/local/bin/mysql_backup_sync.sh >> /var/log/mysql_backup_sync.log 2>&1

 

每天4:45开始同步线上的数据库备份文件到本地服务器,并在本地服务器上还原


 

实际应用中,由于网络不太稳定,在rsync时候经常中断,所以修改了脚本,加上了重试的功能,并修改在获取数据库名时候 的一个bug,如下

#!/bin/bash

STAMP=`date "+%Y-%m-%d"`
PORT=22
REMOTE_IP=1.1.1.1
REMOTE_DIR="/usr/local/mysql/backup"
LOCAL_DIR="/usr/local/mysql_data_backup"
PASSWD='123456'

while [ 0 -eq 0 ]  
do  
  echo `date +%F-%T` '################## Began to date sync...'
  /usr/bin/rsync -avzP --exclude='bak_anydb_full_*-*-*-*.gz' -e 'ssh -p '$PORT'' root@$REMOTE_IP:$REMOTE_DIR/bak_*_full_$STAMP*.gz $LOCAL_DIR/

  if [ $? -eq 0 ]; then  
    echo `date +%F-%T` 'Data Had Been Synced Successfully!'
    break;
  else
    echo `date +%F-%T` 'Data Sync Failure! retry in 2 seconds ..........'
    sleep 2
  fi
done

for file in `find  $LOCAL_DIR/bak_*_full_$STAMP*.gz  -exec  ls {} \;`
  do
    cd $LOCAL_DIR&&echo `date +%F-%T` '------------------- unzip '$file'...'&&gzip -d $file
    #DBNAME=`echo $file|awk -F "/" '{print $5}'|awk -F  "^bak_" '{print $2}'|awk -F "_full_" '{print $1}'`
    FILENAME=`basename $file`
    DBNAME=`echo $FILENAME|awk -F "^bak_" '{print $2}'|awk -F "_full_" '{print $1}'`

    if [ `/usr/local/mysql/bin/mysql -uroot -p$PASSWD -e'show databases;'|grep ^$DBNAME$|wc -l` -eq 1 ];then
      echo `date +%F-%T` ''$DBNAME' began to restore...'
      /usr/local/mysql/bin/mysql -u root -p$PASSWD -e "source ${file%.*}"  $DBNAME

      if [ $? -eq 0 ];then
        echo `date +%F-%T` ''$DBNAME' restore completed...'
        /bin/rm -f ${file%.*}
      else
        echo `date +%F-%T` ''$DBNAME' restore Failure!'
      fi

    else
      echo `date +%F-%T` ' create database '$DBNAME'...'
      /usr/local/mysql/bin/mysql -uroot -p$PASSWD -e "create database '$DBNAME'"
      echo `date +%F-%T` ''$DBNAME' began to restore...'
      /usr/local/mysql/bin/mysql -u root -p$PASSWD -e "source ${file%.*}"  $DBNAME

      if [ $? -eq 0 ];then
        echo `date +%F-%T` ''$DBNAME' restore completed...'
        /bin/rm -f ${file%.*}
      else
        echo `date +%F-%T` ''$DBNAME' restore Failure!'
      fi

    fi
  
  done

 


			

将java的堆栈信息输出到文件的python脚本

把java的堆栈信息保存起来,可以方便排查问题。

这个脚本是把之前的优化了一下,方便管理。

给脚本传一个参数进去,参数是可以唯一标识java进程的一个关键字,脚本里也包括了清理堆栈文件的内容

以glassfish为例,写个任务计划

*/1 * * * * /usr/local/bin/thread_dump.py domain1

每分钟输出一次domain1的堆栈信息到文件。

脚本如下

#!/usr/bin/python

import commands
import os
import sys
import string
import datetime

def main():

    if len(sys.argv) == 1:
        print "Missing Operand..."
        return

    dumpdir = "/usr/local/thread_dump/"
    if os.path.exists(dumpdir) == False:
        os.makedirs(dumpdir)
    if os.path.exists(dumpdir + '__bak') == False:
        os.makedirs(dumpdir + '__bak')

    getpid = "ps -ef|grep " + sys.argv[1] + "|grep -v -E 'grep|more|tail|thread_dump'|awk '{print $2}'"
    pid = os.popen(getpid).read().rstrip()
    if pid != "":
        dumpThread(pid)
    else:
       print "Can't find " + sys.argv[1] + " Process"

    cleardump = "/usr/bin/find " + dumpdir + " -maxdepth 1 -mmin +30 -name '*dump*.txt' -exec mv {} " + dumpdir + "/__bak \;&&/usr/bin/find " + dumpdir + "__bak -maxdepth 1 -mtime +3 -name '*dump*.txt' -exec rm {} \;"
    os.system(cleardump)

def dumpThread(pid):

    try:
        filename = dumpdir + sys.argv[1] + "_dump_"+ datetime.datetime.strftime(datetime.datetime.today(),'%y%m%d_%H%M')+".txt"
        dump = "/bin/su - anyuser -c \'/usr/local/jdk/bin/jstack " + pid + " > " + filename + "\'"
        os.system(dump)
        print filename+" has been created!"
    except Exception, ex:
        print str(ex)
        pass

main()

 


			

Linux的Split命令

用法:split [选项]... [输入 [前缀]]
将输入内容拆分为固定大小的分片并输出到"前缀aa"、"前缀ab",...;
默认以 1000 行为拆分单位,默认前缀为"x"。如果不指定文件,或
者文件为"-",则从标准输入读取数据。

长选项必须用的参数在使用短选项时也是必需的。
  -a, --suffix-length=N	指定后缀长度为N (默认为2)
  -b, --bytes=大小		指定每个输出文件的字节大小
  -C, --line-bytes=大小	指定每个输出文件里最大行字节大小
  -d, --numeric-suffixes 	使用数字后缀代替字母后缀
  -l, --lines=数值		指定每个输出文件有多少行
      --verbose		在每个输出文件打开前输出文件特征
      --help     显示此帮助信息并退出
      --version  输出版本信息并退出

SIZE 可以是一个可选的整数,后面跟着以下单位中的一个:
KB 1000,K 1024,MB 1000*1000,M 1024*1024,还有 G、T、P、E、Z、Y。

 

常用操作

1.将文件每200行分割

split -200 access.log 或split -l 200 access.log

操作之后产生xaa,xab,xac三个文件

2. 指定文件名,并以数字作为后缀

split -l200 -a3 -d access.log access.split

将产生三个文件 access.split000,access.split001,access.split002

3.指定文件大小以20M进行分割

split -b20m access.log

4.指定文件大小以20M,还保证维持每行的完整性进行分割

split -C20m access.log

5.文件的合并,将以上分割的文件合并

cat access.split*>access.log.old

不管用什么方式切割,合并方法不变

6. 指定文件名,指定文件大小,并以数字作为后缀

split -b20m -a3 -d access.log access.split

 

 

转自:http://www.lampblog.net/2011/06/split命令-对文件进行分割/