Shell笔记

Shell笔记bash基础字符串数组普通数组关联数组运算符算术运算法关系运算符布尔运算符字符串运算符文件测试运算符语句条件语句if then ficase when循环语句whilefor函数分离函数体执行函数应用文件重命名百分比显示多进程awk基础变量模式和正则awk运算符awk语句循环和退出awk脚本awk脚本传参awk脚本awk数组应用awk区间统计awk多维数组统计awk计算文件重合度awk计算时间差awk输入输出重定向sed基础正则模式空间向sed传递变量sed脚本应用包含指定内容的行替换为新行grep基础正则find基础应用文件转换转置tab和空格Linux和DOS格式参考

bash

基础

目录和文件判断

 
AخA
1
-e #filename 如果 filename存在,则为真
2
-d #filename 如果 filename为目录,则为真 
3
-f #filename 如果 filename为常规文件,则为真
4
-L #filename 如果 filename为符号链接,则为真
5
-r #filename 如果 filename可读,则为真 
6
-w #filename 如果 filename可写,则为真 
7
-x #filename 如果 filename可执行,则为真
8
-s #filename 如果文件长度不为0,则为真
9
-h #filename 如果文件是软链接,则为真
10
filename1 -nt filename2 #如果 filename1比 filename2新,则为真。
11
filename1 -ot filename2 #如果 filename1比 filename2旧,则为真。
12
-eq #等于
13
-ne #不等于
14
-gt #大于
15
-ge #大于等于
16
-lt #小于
17
-le #小于等于
18
#至于!号那就是取非
字符串

按分割符截取(4种)

 
x
1
var=http://www.aaa.com/123.htm
2
1. # 号截取,删除左边字符,保留右边字符。
3
echo ${var#*//}
4
# 其中 var 是变量名,# 号是运算符,*// 表示从左边开始删除第一个 // 号及左边的所有字符, 即删除 http://
5
# 结果是 :www.aaa.com/123.htm
6
7
2. ## 号截取,删除左边字符,保留右边字符。
8
echo ${var##*/}
9
##*/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符
10
# 即删除 http://www.aaa.com/
11
# 结果是 123.htm
12
13
3. %号截取,删除右边字符,保留左边字符
14
echo ${var%/*}
15
# %/* 表示从右边开始,删除第一个 / 号及右边的字符
16
# 结果是:http://www.aaa.com
17
18
4. %% 号截取,删除右边字符,保留左边字符
19
echo ${var%%/*}
20
# %%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符
21
# 结果是:http:

按开始位置和数量截取(4种)

 
xxxxxxxxxx
11
1
# 从左边第几个字符开始,及字符的个数
2
echo ${var:0:5}   #其中的 0 表示左边第一个字符开始,5 表示字符的总个数。
3
4
# 从左边第几个字符开始,一直到结束
5
echo ${var:7}
6
7
# 从右边第几个字符开始,及字符的个数
8
echo ${var:0-7:3}  #其中的 0-7 表示右边算起第七个字符开始,3 表示字符的个数。
9
10
# 从右边第几个字符开始,一直到结束
11
echo ${var:0-7}

参考

shell脚本8种字符串截取方法

字符串替换

b=${a/123/321};将${a}里的第一个123替换为321
b=${a//123/321};将${a}里的所有123替换为321

字符串匹配

 
xxxxxxxxxx
19
1
# 方式0(利用第三方匹配)
2
export LANG=zh_CN.UTF-8
3
A="米饭 + 爆炒牛肉 + 豉汁肉片 + 蒜蓉芥蓝"
4
B="牛肉"
5
echo $A | grep "$B" &>/dev/null  # 该等同于echo $A | grep -q "$B" 抑制grep的输出
6
test $? -eq 0 && echo "[Match]:$A include $B" ||echo "[NotMatch]:$A  not include $B"
7
if [[ $? == 0 ]]; then
8
   echo "[Match]:$A include $B"
9
else
10
   echo "[NotMatch]:$A  not include $B"
11
fi
12
13
# 方式1(利用bash的匹配)
14
A="米饭 + 爆炒牛肉 + 豉汁肉片 + 蒜蓉芥蓝"
15
F="牛肉"
16
if [[ $A =~ $F ]] ; then echo "$A include $F" ;else echo "$A not include $F";fi
17
18
# 方式2(注意不要加[],此处不利用返回值)
19
if  echo "aha"|grep "x" &>/dev/null ;then echo "find";else echo "no find";fi

参考:

linux shell中做匹配

awk中字符串匹配

数组
普通数组

读取

 
xxxxxxxxxx
15
1
a=(1 2 3 4 5)
2
echo ${a[0]}
3
echo ${a[@]} # 或者echo ${a[*]}
4
# 取长度
5
lentmp=${#a[@]}
6
len=$((lentmp-1))
7
# 遍历
8
for i in `seq 0 $((${#a[@]}-1))`;do
9
    if [ $i -eq '0'];then
10
        echo -n ${line:0:4}"/"${line:4:2}"/"${line:6:2}
11
    else
12
        echo -en "\t"$i
13
    fi
14
    echo 
15
done

删除

 
xxxxxxxxxx
6
1
unset a
2
echo ${a[*]}  # 结果为空
3
4
unset a[1]
5
echo ${a[*]}  # 结果为1 3 4 5
6
echo ${#a[*]} # 结果为4

赋值

 
xxxxxxxxxx
2
1
a[1]=100
2
echo ${a[*]} # 结果为1 100 2 3 4 5

分片

 
xxxxxxxxxx
2
1
echo ${a[@]:0:3}  # 结果为 1 2 3
2
# 直接通过 ${数组名[@或*]:起始位置:长度} 切片原先数组,返回是字符串,中间用“空格”分开,因此如果加上”()”,将得到切片数组,上面例子:c 就是一个新数据

替换

 
xxxxxxxxxx
1
1
# ${数组名[@或*]/查找字符/替换字符} 该操作不会改变原先数组内容,如果需要修改,可以看上面例子,重新定义数据。

参考:

Linux Shell数组建立及使用技巧

关联数组

命令

 
xxxxxxxxxx
6
1
echo ${!garray[*]}      #取关联数组所有键  
2
echo ${!garray[@]}      #取关联数组所有键  
3
echo ${garray[*]}       #取关联数组所有值  
4
echo ${garray[@]}       #取关联数组所有值  
5
echo ${#garray[*]}      #取关联数组长度  
6
echo ${#garray[@]}      #取关联数组长度  

创建

 
xxxxxxxxxx
6
1
# 方法1:(先声明)
2
declare -A garray
3
garray["jim"]=158  
4
garray["amy"]=168
5
# 方法2:(直接使用内嵌“索引-值”列表法:)
6
array=(["jim"]=158 ["amy"]=168)  

遍历

 
xxxxxxxxxx
10
1
# 以key的方式遍历
2
for key in ${!garray[*]};do  
3
    echo "key:"$key
4
    echo "value:"${garray[$key]}
5
done
6
7
# 以value的方式遍历
8
for value in ${garray[*]};do  
9
    echo "value:"${value}
10
done

删除

 
xxxxxxxxxx
7
1
# 清空数组元素
2
# 但是这样清空后,garray中仍有“fe”这个key,只是其对应的值被清空了 
3
unset garray["fe"]
4
5
# 清空数组
6
# 但是这样清空后,array的key是没有了,但是整个garray也不能再用了,不再是关联数组,需要重新声明使用:
7
unset garray

累加

 
xxxxxxxxxx
34
1
# 关联数组累加
2
function arr_dict_sum(){
3
    declare -A anum
4
    for item in a a b c d e f g k a c b d;do
5
        anum[$item]=$((${anum[$item]}+1));
6
    done
7
8
    for k in ${!anum[*]};do
9
        echo "$k:${anum[$k]}"
10
    done
11
}
12
13
#例子:单词计数
14
function words_conut()
15
{
16
    declare -A cn
17
    while read line;do
18
        for w in $line;do
19
            cn[$w]=$((${cn[$w]}+1))
20
        done
21
    done < words.txt
22
    
23
    res=""
24
    for wc in ${!cn[*]};do
25
        if [ -z $res ];then
26
            ct="$wc\t${cn[$wc]}"
27
        else
28
            ct="\n$wc\t${cn[$wc]}"
29
        fi
30
        res="$res$ct"
31
    done
32
33
    echo -e "$res"|sort -k2 -rn
34
}

判断key是否在关联数组中

 
xxxxxxxxxx
8
1
function isinkey(){  
2
    for key in ${!count_result[*]};do  
3
      if [ "$1" = "$key" ];then  
4
        return 1  
5
      fi  
6
    done  
7
    return 0  
8
} 

数组参数

 
xxxxxxxxxx
14
1
function f1(){  
2
  declare -a array  
3
  array[0]="h1"  
4
  array[1]="h2"  
5
  arr=`echo "${array[*]}"`  
6
  local val="h1"  
7
  f2 $val $arr  
8
}  
9
function f2(){  
10
  local arr2=`echo "$2"`  
11
  for value in ${arr2[*]};do  
12
     echo "value: $value"  
13
  done  
14
} 

尚存在问题没有解决,只能传递数组的第一个值

运算符

算术运算法
关系运算符
布尔运算符
运算符说明举例
!非运算,表达式为 true 则返回 false,否则返回 true[ ! false ] 返回 true。
-o或运算,有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true
-a与运算,两个表达式都为 true 才返回 true[ $a -lt 20 -a $b -gt 100 ] 返回
字符串运算符

运算符 说明 举例 = 检测两个字符串是否相等,相等返回 true。 [ b ] 返回 false。 != 检测两个字符串是否相等,不相等返回 true。 [ b ] 返回 true。 -z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。 -n 检测字符串长度是否为0,不为0返回 true。 [ -z $a ] 返回 true。 str 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

注意字符串运算符的等判断和数字的等判断的区别

文件测试运算符
 
xxxxxxxxxx
1
1

语句

条件语句
if then fi
 
xxxxxxxxxx
17
1
# 两重
2
if [ $a == $b ];then
3
   echo "a is equal to b"
4
else
5
   echo "a is not equal to b"
6
fi
7
8
# 多重嵌套
9
if [ expression 1 ];then
10
   Statement(s) to be executed if expression 1 is true
11
elif [ expression 2 ];then
12
   Statement(s) to be executed if expression 2 is true
13
elif [ expression 3 ];then
14
   Statement(s) to be executed if expression 3 is true
15
else
16
   Statement(s) to be executed if no expression is true
17
fi
case when
 
xxxxxxxxxx
11
1
case 变量名 in  
2
    值1)  
3
      指令1  
4
    ;;  
5
    值2)  
6
      指令2  
7
    ;;  
8
    值3)  
9
      指令3  
10
    ;;  
11
esac  
循环语句
while
 
xxxxxxxxxx
1
1
for
 
xxxxxxxxxx
1
1

函数

​ 函数的返回值通过$?获得只能是整数,不接受其它类型的返回值,可以通过修改变量的方式实现返回其它类型的值。另外函数可以不显示的使用return,此时函数的返回值是函数的退出状态,0代表成功退出,非0代表函数执行过程中有异常。

函数的三种定义方式:

 
xxxxxxxxxx
14
1
function 函数名 () {  
2
        指令...  
3
        return -n  
4
}  
5
  
6
function 函数名 {  
7
        指令...  
8
        return -n  
9
}  
10
  
11
函数名 () {  
12
    指令...  
13
    return -n  
14
}  
分离函数体执行函数

分离函数体

应用

文件重命名
 
xxxxxxxxxx
10
1
# 第一种实现 find+awk+sh
2
find . -maxdepth 1 -type f | awk '!/png$/{print "mv" $1,$1".png" }' |sh
3
4
# 第二种实现 for+sed实现
5
for sql in `find /root -name “*.sql”`;do  mv $sql `echo $sql|sed  ‘s/sql/txt/'` ;done
6
7
# 第三种实现 rename
8
rename  .sql  .txt *.sql  //好像不能递归目录,其中最后一个是要修改文件类型的列表
9
10
# find+xargs+sed
百分比显示
 
xxxxxxxxxx
6
1
 # 方法1:使用bc
2
 r_o_ratio="`echo "scale=2;${remain_i}*100/${odl_i}"|bc`%"
3
 # 方法2:使用awk(注意此语句中的BEGIN不能省略)
4
 r_o_ratio=$(awk -v a=12 -v b=260 'BEGIN{printf("%4.2f%%",a*100/b);}')
5
 # 方法2.1(注意通过管道传递过来的数据不能在begin中使用,可以在前面加上END)
6
 echo "12 260" | awk '{printf("%4.2f%%",$1*100/$2);}'

参考:

Shell脚本批量重命名文件后缀的3种实现

Shell重命名(智慧大碰撞)

使用awk进行数字计算

多进程
 
xxxxxxxxxx
21
1
tables=(xmpcloud2 xmpconv xmpconv2 xmptipsex2)
2
dbs=(pgv3_split_t1 pgv3_split_t2 pgv3_split_c1 pgv3_split_c2)
3
4
declare -A Apdb
5
for table in ${tables[@]}; do
6
    pids=""
7
    for db in ${dbs[@]}; do
8
        cmd="do something"
9
        $cmd &
10
        pids="$pids $!"
11
        Apdb[$!]=$db
12
    done
13
14
    #  wait $pids
15
    for p in $pids;do
16
        wait $p
17
        if [[ "$?" -ne "0" ]];then
18
            # do something
19
        fi
20
    done
21
done

此处的多进程是处理每个表的多个数据来源的时候采用并发的多进程来处理,没有锁的高级使用

shell的多进程之间没有锁,只有靠wait变相实现

awk

awk由模式和操作组成,主要用于列式文本处理,进行列的分割,判断处理。

 
xxxxxxxxxx
1
1
awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file

其中模式pattern可以是以下任意一个:

基础

变量

内置变量

NR:表示awk开始执行程序后所读取的数据行数。
FNR:awk当前读取的记录数,其变量值小于等于NR(比如当读取第二个文件时,FNR是从0开始重新计数,而NR不会)。

变量传递

 
xxxxxxxxxx
9
1
# 向awk命令行程序传递变量
2
1.  awk '{print a, b}' a=111 b=222 yourfile
3
注意, 变量位置要在 file 名之前, 否则就不能调用,还有, 于 BEGIN{}中是不能调用这些的variable. 要用之后所讲的第二种方法才可解决.
4
5
2.  awk –v a=111 –v b=222 '{print a,b}' yourfile
6
注意, 对每一个变量加一个 –v 作传递.
7
8
3.  awk '{print "'"$LOGNAME"'"}' yourfile
9
如果想调用environment variable, 要用以上的方式调用, 方法是:"'"$LOGNAME"'"

注释

 
xxxxxxxxxx
24
1
awks='BEGIN{
2
            #print "begin:"frow;
3
      }
4
      {
5
              # 列名获取
6
              if(NR==1){
7
                for(i=1;i<=NF;++i)
8
                    headers[i]=$i;
9
                next;
10
              }
11
              
12
              # 行头部
13
              rowf=$1;
14
              for(j=2;j<=frow;j++)
15
                 rowf=rowf"\t"$j;
16
              #print "rowf:"rowf;
17
              
18
              for(i=frow+1;i<=NF;++i){
19
                printf("%s\t%s\t%s\n",rowf,headers[i],$i);
20
              }
21
      }
22
      END{
23
            #print "done"
24
      }'

awk中的注释用#

模式和正则

awk本身支持扩展正则,不需要加额外的参数

 
xxxxxxxxxx
11
1
# 模式是用来对行进行筛选的,常见的模式筛选规则举例:
2
3
# 正则表达式-----输出以字符T开头的行  
4
result=`awk '/^T/ { print }' scores.txt` 
5
6
# 混合模式(输出以K开头的行,同时第2列分数大于80分的行)
7
result=`awk '/^K/ && $2 > 80 { print }' scores.txt`
8
result=`awk '$2~/^K/ && $2 > 80 { print }' scores.txt`
9
10
# 区间模式(以Nancy开头的行为起始,第2列等于92分的行为终止,输出之间的连续的行。注意:当满足patter1或者pattern2的行不只一行的时候,会自动选择第一个符合要求的行。)
11
result=`awk '/^Nancy/, $2==92 { print }' scores.txt` 
awk运算符

逻辑运算符||&&

 
xxxxxxxxxx
1
1
awk 'BEGIN{a=1;b=2;print (a>5 && b<=2),(a>5 || b<=2);}'

赋值运算符= += -= *= /= %= ^= **=

算术运算符

正则运算符~ ~! 匹配正则表达式和不匹配正则表达式

awk语句
循环和退出
 
xxxxxxxxxx
15
1
# while,for循环语句
2
awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test
3
awk '{for (i = 1; i<NF; i++) print NF,$i}' test
4
5
# break语句
6
用于在满足条件的情况下跳出循环;
7
8
# continue语句
9
用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。
10
11
# next语句
12
next语句从输入文件中读取一行,然后从头开始执行awk脚本
13
14
# exit语句
15
exit语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非零值表示出错。
getline 语句

实现两个文件的同步读取,当然另一种方法是利用字典实现
awk脚本
awk脚本传参
 
xxxxxxxxxx
17
1
#!/bin/bash
2
3
awk -f stat.awk "para1=value1" "para2=value2" inputfile
4
#或者
5
./stat.awk "para1=value1" "para2=value2" inputfile
6
7
#其中stat.awk的脚本中引用变量可以采用如下的方式:
8
#!/usr/bin/awk -f 
9
BEGIN{
10
    a=0;
11
}
12
{
13
    a++;   
14
}
15
END{
16
    print para1"\t"a;
17
}

参考:

shell中调用awk脚本传递参数问题

awk脚本
 
xxxxxxxxxx
66
1
#!/usr/bin/awk -f
2
# 弹幕日志统计的awk实现
3
# 日期:2017年1月23日
4
# 作者:tuling56
5
6
#日志格式(标准的日志格式):
7
#27.27.28.235 - - [21/Dec/2016:14:13:01 +0800] "GET /getID?type=local&key=A451D48686CBE4BB9A9575F51D2591E1E724C060&duration=148654&subkey=312235673&md5=84b14712129a5ba0eedf3e6b92263e3b HTTP/1.1" 200 81 "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)"
8
9
BEGIN{FS="\""}
10
11
{  
12
    print "-----------------------------------------------"
13
    if($3~/200/)
14
    {
15
        num200++;
16
        # part1:请求时间(计算峰值访问量)
17
        split($1,iptime,"[");
18
        split(iptime[2],dtime," ");
19
        reqtime=substr(dtime[1],1,17)
20
        reqfreq[reqtime]++;
21
        print reqtime
22
23
        # part2:请求体解析
24
        split($2,url," ");
25
        split(url[2],urlparas,"?");
26
27
        ## url请求类型统计(计算每个类别的访问量)
28
        urltype=urlparas[1];
29
        urltypenum[urltype]++;
30
31
        ## GET参数解析
32
        paras_str=urlparas[2];
33
        paras_len=split(paras_str,paras,"&");
34
        print urltype,paras_str;
35
        for(i=1;i<paras_len;i++)
36
            print paras[i];
37
    }
38
    else if($2~/favicon\.ico/ && $3~/404/)
39
    {
40
        error404++;
41
    }
42
    else
43
    {
44
        unerror++;
45
    }
46
47
}
48
49
END{  # 这个括号不能移到下一行
50
    print "===============信息汇总======================";
51
    # 总请求量
52
    tnum=num200+error404+unerror;
53
    print tnum,error404,unerror;
54
55
    #峰值请求量
56
    for(tmf in reqfreq)
57
    {
58
        print tmf,reqfreq[tmf]|"sort -r -n -k2";
59
    }
60
61
    #类别访问量
62
    for(cl in urltypenum)
63
    {
64
        print cl,urltypenum[cl]|"sort -r -n -k2";
65
    }
66
}
awk数组
 
xxxxxxxxxx
1
1
awk 'BEGIN{a[0,0]=12;a[1,1]=13;}END{for(k in a) {print k,a[k];split(k,idx,SUBSEP);print idx[1],idx[2],a[idx[1],idx[2]]}}' </dev/null

参考:

awk数组的使用

应用

awk区间统计

问题描述,给出一堆数据,然后将该堆数据进行分组(分组区间自己指定),然后统计每个分组内的个数

固定区间间隔

 
xxxxxxxxxx
5
1
# 统计落在每个区间段内的数量
2
awk '{intfloat=$1/500;split(intfloat,intn,".");v=intn[1];dict[v]++;}END{for(i in dict) print i*500"\t"i*500"~"(i+1)*500"\t"dict[i]|"sort -n -k1"}'  view_num
3
4
#改进版(利用awk提供的int函数,获取除法的整数部分)
5
awk '{v=int($1/500);dur[v]++;}END{for(i in dur) print i*500"\t"i*500"~"(i+1)*500"\t"dur[i]|"sort -n -k1"}'  view_num

指定区间间隔

 
xxxxxxxxxx
1
1
awk '{if(1<=$1&& $1<2) dur[1,2]++;else if (2<=$1 && $1<5) dur[2,5]++;else if( 5<=$1 && $1<10) dur[5,10]++; else if(10<=$1 && $1<50) dur[10,50]++;else if(50<=$1) dur[50,"+"]++; else print "nodur"}END{split(i,idx,SUBSEP); print "["idx[1]"~"idx[2]")\t"dur[i]"\t"dur[i]*100/NR"%"|"sort -t'~' -n -k1.2";}'  view_num

awk实现行转列

 
xxxxxxxxxx
1
1
# 利用awk的数组来实现
awk多维数组统计
 
xxxxxxxxxx
17
1
# 数据格式(按第一列汇总,求第二列和第三列的和)
2
anime   6645    3776
3
movie   40184   20706
4
teleplay    30341   15223
5
tv  6232    3746
6
anime   206 106
7
documentary 418 172
8
femalestars 342 146
9
joke    618 232
10
lovers  11  9
11
malestars   22  10
12
vmovie  745 299
13
mvzhibo 3879    2263
14
zhibo   2332    1447
15
16
# 统计程序
17
awk '{if (a in arr) {split(arr[a],puv,"\t");pv=puv[1]+$2;uv=puv[2]+$3;} else arr[$1]=$2"\t"$3;}END{ for(a in arr) print a,arr[a]|"sort -rn -k2"}' 
awk计算文件重合度
 
xxxxxxxxxx
2
1
# 同时会给出两个文件各自的行数
2
awk '{if(NR==FNR){a[$1]=$1;overlap_num=0;f1num=f1num+1;}else{if($1 in a) overlap_num++;}}END{print ARGV[1]"\t"f1num"\n"ARGV[2]"\t"FNR"\noverlap\t"overlap_num}' file1 file2  
awk计算时间差
 
xxxxxxxxxx
1
1
awk -v s="20110510" -v t="20110605" 'BEGIN{"date +%s -d "s|getline a;"date +%s -d "t|getline b;print (b/3600-a/3600)/24}'

参考:

awk计算时间差

awk输入输出重定向

输出重定向:

 
xxxxxxxxxx
1
1
awk '$1=100{print $1 > "output_file"}' file

输入重定向:

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

可以在awk中直接执行shell命令

 
xxxxxxxxxx
2
1
awk 'BEGIN{"date"|getline d;split(d,a);print a[2]}'
2
# 执行linux的date命令,并通过管道输出给getline,然后再把输出赋值给自定义变量d,并以默认空格为分割符把它拆分开以数字1开始递增方式为下标存入数组a中,并打印数组a下标为2的值。下面我们再看看一些复杂的运用。
文件a:文件b:
220 34 50 7010
553 556 32 218
1 1 14 98 332
2 2 3 3 4 5 5 6 347

要求文件a的每行数据与文件b的相对应的行的值相减,得到其绝对值

 
xxxxxxxxxx
2
1
awk '{getline j<"b";for(i=1;i<=NF;i++){$i>j?$i=$i-j:$i=j-$i}}1' a|column -t
2
# columnt -t的作用是让结果列对齐,该程序还有些问题

sed

sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有 改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

sed的处理流程如下:

sed维护着两个数据缓冲区:一个活动的模版空间(pattern space),另一个辅助的保留空间(hold space),初始都是空的,没有数据。
1、sed从输入中读取一行文本,去掉行尾可能的换行符(\n)后放到模版空间里;
2、用指定的执行脚本中的命令依次来处理模版空间里数据,直到脚本结束;
3、向模版空间中的数据尾添加上换行符(没有进行去换行符操作就不添加),显示输出(选项-n将阻止输出) 模版空间中的数据后清空模版空间;
4、sed再读取下一行文本重复上面处理过程。
5、上面的4步处理过程称为一个sed处理循环。而sed就是重复这循环直到遇到退出命令或文件处理完毕。
注意:保留空间中的数据是保持不变的,除非有命令改变它。

基础

sed多条指令执行

 
xxxxxxxxxx
3
1
sed '/test/d;/boy/d' test.txt > test_new.txt      # 删除含字符串"test"或“boy"的行
2
# 等效于
3
sed -e '/test/d' -e'/boy/d' test.txt > test_new.txt 

显示从第n行到结尾

 
xxxxxxxxxx
1
1
sed -n '2,$p' xxxx

打印匹配行和行号

 
xxxxxxxxxx
15
1
#方法1
2
sed -n '/6645/{=;p; }' awk.data
3
4
# 方法2
5
sed -n -e '/6645/=' -e '/6645/p' awk.data
6
7
# 方法3
8
sed -n '/6645/=;/6645/p' awk.data
9
10
#结果如下(注意结果是换行了,如何在一行上显示呢,需要使用到模式空间的互换了)
11
1
12
anime   6645    3776
13
14
#### 显示匹配行和行号
15
sed -n '/6645/{=;p}' awk.data |sed 'N;s/\n/\t/' 

删除匹配行的下一行、

 
xxxxxxxxxx
1
1
sed -n '/root/{n;d}'  /etc/passwd       #将匹配root行的下一行删除 

删除匹配行和下一行

 
xxxxxxxxxx
1
1
sed -n '/root/{N;d}' /etc/passwd        # 将匹配root行和下一行都删除  

合并相邻行

 
xxxxxxxxxx
3
1
sed 'N;s/\n/:/' /etc/passwd         # 合并相邻行,并用:分割(注意不要添加-n选项,不然无输出)
2
# awk实现是
3
awk '{if(NR%2==0){printf $0 "\n"}else{printf "%s\t",$0}}' /etc/passwd

退出命令

 
xxxxxxxxxx
1
1
sed '/hrwang/{s/hrwang/HRWANG/;q;}' datafile  #匹配到hrwang的行处理后就退出sed程序
正则

sed添加-r才支持扩展正则

模式空间

流文本编辑器,处理行的时候十分方便。

暂存空间(hold sapce )-->模式空间(patter space)

命令意义
ghold sapce--> patter space[delete]
Ghold sapce--> patter space[append]
hpattern sapce--> hold space[delete]
Hpattern sapce--> hold space[append]
xhold sapce <--> patter space

模式空间和存储空间的参考例子:

img

向sed传递变量
 
xxxxxxxxxx
3
1
ststr=`date +%d\\\/%b\\\/%Y:%H` # 这个日期的转换,在脚本内要使用三个\才能代表一个\,脚本外可使用两个
2
sed -n "/${ststr}/p" ${log_path}/${log} > ${log_path_bak}/${log}_${datehour}
3
# 注意使用双引号,而不是单引号,这样变量才能传递过去
sed脚本

从文件读入命令

 
xxxxxxxxxx
1
1
sed -f sedscript.sh

sedscript.sh的内容如下:

 
xxxxxxxxxx
3
1
!/bin/sed -f
2
s/root/yerik/p  
3
s/bash/csh/p

Sed对于脚本中输入的命令非常挑剔,在命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔。以#开头的行为注释行,且不能跨行

直接运行脚本

 
xxxxxxxxxx
2
1
chmod u+x
2
./sedscript.sh xxxxfile

sed命令如何接收外部参数

应用

包含指定内容的行替换为新行
 
xxxxxxxxxx
1
1
 sed -i '/g_tool_hive=/c g_tool_hive="/usr/local/complat/cdh5.10.0/hive/bin/hive"' `sl 'g_tool_hive'`

grep

基础

grep排除指定的文件夹

 
xxxxxxxxxx
3
1
grep -i --exclude-dir=\.svn --exclude-dir=".git" -Rl --color
2
#或者
3
grep -i --exclude-dir={.svn,.git} -Rl --color

grep查找指定类型文件的内容

 
xxxxxxxxxx
2
1
find . -name *.py |xargs grep xxxx
2
find . -name *.py -exec grep xhh {} \;  # 问题修正,注意{}和 \;之间的空格,不然给提示exec缺少参数

grep和sed结合使用

 
xxxxxxxxxx
2
1
# 批量替换多个文件中的字符串
2
sed -i 's/oldstr/newstr/g' `grep oldstr -rl odlstr $datadir`
正则

grep加-E支持扩展正则?+|(),相当于egrep

find

基础

 
xxxxxxxxxx
3
1
#  搜索但跳出指定目录
2
#(注意-prune后面的-o不能缺少),另外需要说明的是跳出的是目录内部的内容,但目录本身还是会被包含进去的
3
find . -path "./sk" -prune -o -name "*.txt" -print
 
xxxxxxxxxx
1
1
find . -type f -exec echo "{}" \;  # 为输出的文件名加上双引号

文件名中存在空格的时候无法处理,解决方式如下:

 
xxxxxxxxxx
20
1
# 方法1:无法处理名字中包含空格的问题
2
function method1()
3
{
4
        find . -type f -mtime -7 -name *.py
5
        sed -i 's/\t/    /g'  $(find . -type f -name "*.py")
6
}
7
8
9
# 方法2:解决了文件名中包含空格的问题(使用了read的方法)
10
function method2()
11
{
12
        find . -type f -mtime -7 -name "*.py"|while read f;do
13
                sed -i 's/\t/    /g'  "$f"    # 注意$f两边的引号不可缺少
14
                echo "$f"
15
        done
16
}
17
18
method2
19
20
exit 0

应用

文件转换

转置

行转列,实现方法如下:

标准shell

 
xxxxxxxxxx
11
1
headrow=($(head -1 $inputf))
2
colnum=${#headrow[*]}
3
for c in $(seq 1 $colnum);do
4
    if [ "$inseq" = "\t" ];then
5
        #cut -f$c $inputf |paste -d"$outseq" -s
6
        cut -f$c $inputf |tr '\n' ' '
7
    else
8
        #cut -d"$inseq" -f$c $inputf |paste -d"$outseq" -s
9
        cut -d"$inseq" -f$c $inputf |tr '\n' ' '
10
    fi
11
done

存在的问题是内存超出限制(处理超长列的时候)

awk实现

 
xxxxxxxxxx
12
1
awk '{i=1;
2
      while(i<=NF){
3
          col="col"i;
4
          a[col]=a[col]" "$i;
5
          i=i+1;
6
        }
7
     }
8
     END{
9
        for(v in a){
10
            print substr(a[v],2);
11
        }
12
    }' $inputf
tab和空格

tab转space

 
xxxxxxxxxx
1
1
expand -t4 xxx.txt > yyy.txt

space转tab

 
xxxxxxxxxx
1
1
unexpand -t4 xxx.txt >yyy.txt
Linux和DOS格式
 
xxxxxxxxxx
2
1
dos2unix xx.txt
2
unix2dos xx.txt

参考