首页 > 技术文章 > shell study

followyou 2019-01-17 11:32 原文

shell记录

执行脚本

  • 作为可执行程序
chmod +x ./test.sh  #使脚本具有执行权限
./test.sh  #执行脚本
  • 作为解释器执行
/bin/sh test.sh
/bin/php test.php

变量使用

name='bob'
echo $name
echo "my name id $name"
echo "Hello,"$name"!"
echo ${#name}               #输出 4 (长度)

string="你本来就很帅"
echo "长度"${#string}
echo ${string:1:4}          #从第二个字符开始截取4个字符

echo `expr index "$string" 本来`  #查找字符本或来的位置(哪个字母先出现就计算哪个)

content="---
apiVersion: v1
kind: ConfigMap
metadata:
  name: $2
  namespace: bigdata"
`echo -e "${content}" >> ../configmap.yaml   # -e 开启转义 \n会换行

##shell数组
array_name=(value0 value1 value2 value3)
echo ${array_name[@]}                       #@获取数组所有元素
length=${#array_name[@]}                    #获取数组长度
echo $length
lengthn=${#array_name[2]}                   #获取第二个元素的长度
echo $lengthn

注释

#----------------------------------
# 这是一个注释
# author:walkingsun
#----------------------------------

# 多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF


:<<!
注释内容...
注释内容...
注释内容...
!

shell传递参数

#!/bin/bash

echo "Shell 传递参数实例!";
echo "执行的文件名:$0";                       # $0 为执行的文件名
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

执行

./test.sh 1 2 3
参数处理	说明
$#	传递到脚本的参数个数
$*	以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$	脚本运行的当前进程ID号
$!	后台运行的最后一个进程的ID号
$@	与$*相同,但是使用时加引号,并在引号中返回每个参数。
如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$-	显示Shell使用的当前选项,与set命令功能相同。
$?	显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

$* 与 $@ 区别:

相同点:都是引用所有参数。
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 " * " 等价于 "1 2 3"(传递了一个参数),而 "@" 等价于 "1" "2" "3"(传递了三个参数)。

运算符

## 算数运算符
运算符	说明	举例
+	加法	`expr $a + $b` 结果为 30。
-	减法	`expr $a - $b` 结果为 -10。
*	乘法	`expr $a \* $b` 结果为  200。
/	除法	`expr $b / $a` 结果为 2。
%	取余	`expr $b % $a` 结果为 0。
=	赋值	a=$b 将把变量 b 的值赋给 a。
==	相等。用于比较两个数字,相同则返回 true。	[ $a == $b ] 返回 false。
!=	不相等。用于比较两个数字,不相同则返回 true。	[ $a != $b ] 返回 true。

## 关系运算符
-eq	检测两个数是否相等,相等返回 true。	[ $a -eq $b ] 返回 false。
-ne	检测两个数是否不相等,不相等返回 true。	[ $a -ne $b ] 返回 true。
-gt	检测左边的数是否大于右边的,如果是,则返回 true。	[ $a -gt $b ] 返回 false。
-lt	检测左边的数是否小于右边的,如果是,则返回 true。	[ $a -lt $b ] 返回 true。
-ge	检测左边的数是否大于等于右边的,如果是,则返回 true。	[ $a -ge $b ] 返回 false。
-le	检测左边的数是否小于等于右边的,如果是,则返回 true。	[ $a -le $b ] 返回 true。


## 布尔运算符
运算符	说明	举例
!	非运算,表达式为 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 ] 返回 false。

## 逻辑运算符
&&	逻辑的 AND	[[ $a -lt 100 && $b -gt 100 ]] 返回 false
||	逻辑的 OR	[[ $a -lt 100 || $b -gt 100 ]] 返回 true

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


## 文件测试运算符

操作符	说明	举例
-b file	检测文件是否是块设备文件,如果是,则返回 true。	[ -b $file ] 返回 false。
-c file	检测文件是否是字符设备文件,如果是,则返回 true。	[ -c $file ] 返回 false。
-d file	检测文件是否是目录,如果是,则返回 true。	[ -d $file ] 返回 false。
-f file	检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。	[ -f $file ] 返回 true。
-g file	检测文件是否设置了 SGID 位,如果是,则返回 true。	[ -g $file ] 返回 false。
-k file	检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。	[ -k $file ] 返回 false。
-p file	检测文件是否是有名管道,如果是,则返回 true。	[ -p $file ] 返回 false。
-u file	检测文件是否设置了 SUID 位,如果是,则返回 true。	[ -u $file ] 返回 false。
-r file	检测文件是否可读,如果是,则返回 true。	[ -r $file ] 返回 true。
-w file	检测文件是否可写,如果是,则返回 true。	[ -w $file ] 返回 true。
-x file	检测文件是否可执行,如果是,则返回 true。	[ -x $file ] 返回 true。
-s file	检测文件是否为空(文件大小是否大于0),不为空返回 true。	[ -s $file ] 返回 true。
-e file	检测文件(包括目录)是否存在,如果是,则返回 true。	[ -e $file ] 返回 true。

echo

echo -e "OK! \n" # -e 开启转义 \n显示换行

echo -e "OK! \c" # -e 开启转义 \c 不换行

echo "It is a test" > myfile                        #显示结果定向到文件

echo `date`                                         #显示命令执行结果

printf

printf  format-string  [arguments...]

模仿c程序库的printf(php也是如此)

printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4s\n" walkingsun boy 150
# %s %c %d %f都是格式替代符
# %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
# %-4.2f 指格式化为小数,其中.2指保留2位小数。

test

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

# 数值测试
参数	说明
-eq	等于则为真
-ne	不等于则为真
-gt	大于则为真
-ge	大于等于则为真
-lt	小于则为真
-le	小于等于则为真


# 字符串测试
=	等于则为真
!=	不相等则为真
-z 字符串	字符串的长度为零则为真
-n 字符串	字符串的长度不为零则为真

# 文件测试
-e 文件名	如果文件存在则为真
-r 文件名	如果文件存在且可读则为真
-w 文件名	如果文件存在且可写则为真
-x 文件名	如果文件存在且可执行则为真
-s 文件名	如果文件存在且至少有一个字符则为真
-d 文件名	如果文件存在且为目录则为真
-f 文件名	如果文件存在且为普通文件则为真
-c 文件名	如果文件存在且为字符型特殊文件则为真
-b 文件名	如果文件存在且为块特殊文件则为真

流程控制

if ... else ...

if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi    # 查询进程中命令行包含ssh的数量是否大于1,是返回true   -c 统计数量

if ... elseif ...else ... fi

if condition1
then
    command1
elif condition2
then
    command2
else
    commandN
fi

for

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

实例:

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done

## 顺序输出字符串中的字符
for str in 'This is a string'
do
    echo $str
done

数字循环

#!/bin/bash  
  
for((i=1;i<=10;i++));  
do   
echo $(expr $i \* 3 + 1);  
done  

while

while condition
do
    command
done

实例:

#!/bin/bash
int=1
while(( $int<=5 ))
do
    echo $int
    let "int++"
done

无限循环

while :
do
    command
done
或者

while true
do
    command
done
或者

for (( ; ; ))

until

until 循环
until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

until 语法格式:

until condition
do
    command
done

case

case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

实例

echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3)  echo '你选择了 3'
    ;;
    4)  echo '你选择了 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac

跳出循环

break和continue (同PHP)

esac
需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示break。

函数

[ function ] funname [()]

{

    action;

    [return int;]

}

实例:

funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum 和 $anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"


# 传参
funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
#注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

注意


参数处理	说明
$#	传递到脚本的参数个数
$*	以一个单字符串显示所有向脚本传递的参数
$$	脚本运行的当前进程ID号
$!	后台运行的最后一个进程的ID号
$@	与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-	显示Shell使用的当前选项,与set命令功能相同。
$?	显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

输入输出重定向


命令	说明
command > file	将输出重定向到 file。
command < file	将输入重定向到 file。
command >> file	将输出以追加的方式重定向到 file。
n > file	将文件描述符为 n 的文件重定向到 file。
n >> file	将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m	将输出文件 m 和 n 合并。
n <& m	将输入文件 m 和 n 合并。
<< tag	将开始标记 tag 和结束标记 tag 之间的内容作为输入。
cat file1 >> file 将file1内容输出已追加的形式重定向到 file

实例

 who > users   #执行 who 命令,它将命令的完整的输出重定向在用户文件中(users)

重定向深入

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息
command 2 > file    #stderr 重定向到 file

command 2 >> file   #stderr 追加到 file 文件末尾

command > file 2>&1   #stdout 和 stderr 合并后重定向到 file

command < file1 >file2  #stdin 和 stdout 都重定向,command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2

Here Document

command << delimiter
    document
delimiter

实例

在命令行中通过 wc -l 命令计算 Here Document 的行数:

$ wc -l << EOF
    欢迎来到
    菜鸟教程
    www.runoob.com
EOF

3          # 输出结果为 3 行
$

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。

如果希望屏蔽 stdout 和 stderr,可以这样写:

$ command > /dev/null 2>&1

shell文件包含

. filename   # 注意点号(.)和文件名中间有一空格

或

source filename

nohup 退出帐户/关闭终端之后继续运行相应的进程

nohup command > myout.file 2>&1 &

0 – stdin (standard input),1 – stdout (standard output),2 – stderr (standard error) ;
2>&1是将标准错误(2)重定向到标准输出(&1),标准输出(&1)再被重定向输入到myout.file文件中。

& : 指在后台运行

&是指在后台运行,但当用户推出(挂起)的时候,命令自动也跟着退出

sh test.sh &

使命令永久的在后台执行

nohup COMMAND &

head/tail文本选取

head/tail是linux中常见的文本选取命令,用以选取头部/尾部的若干行文本

head: illegal option -- -
usage: head [-n lines | -c bytes] [file ...]
# 输出头部两行
head -n 2 configmap.yaml
# 输出匹配data的第一个数据
grep data configmap.yaml | head -1
# 输出尾部2行
tail -n 2 configmap.yaml
# 输出不断增长的日志文件
tail -f nginx.log

shell三剑客 grep、awk、sed

grep

基于正则表达式查找满足条件的内容

grep
grep [OPTIONS] PATTERN [FILE...]
-o 仅显示匹配到的字符串
-E 使用ERE,相当于egrep
--color=auto 对匹配到的文本着色显示
-v 显示不被pattern匹配到的行
-i 忽略字符大小写
-n 显示匹配的行号
-c 统计匹配的行数
-q 静默模式,不输出任何信息
-A # after, 后#行
-B # before, 前#行
-C # context, 前后各#行
-e 实现多个选项间的逻辑or关系
grep –e ‘cat ’ -e ‘dog’ file
-w 匹配整个单词
-F 相当于fgrep,不支持正则表达式

实例:

cat configmap.yaml |grep addr -n

更多参考正则表达式详解

sed

sed是一个强大而简单的文本解析转换工具,可以读取文本,并根据指定的条件对文本内容进行编辑,最后输出所有行活仅输出处理的某些行,sed可以在无交互的情况下实现相当复杂的文本处理操作。被广泛的应用于shell脚本中,用于完成各种自动化处理任务。

sed的工作流程主要包括:

  1. 读取:sed从输入流中读取一行内容不能够存储到临时的缓冲区中;
  2. 执行:默认情况下所有的sed命令都在模式空间中按顺序地执行,除非指定了行的地址,否则sed命令将会再所有行上依次执行;
  3. 显示:发送修改后的内容到输出流,再发送数据后,模式空间将会被清空。
    注意:在所有的文件内容都被处理完成之前,上述过程将重复执行,直至所有内容都被处理完。
sed [option]... 'script' inputfile
1、选项
-n 不输出模式空间内容到屏幕,即不自动打印
-e 多点编辑
-f /PATH/SCRIPT_FILE: 从指定文件中读取编辑脚本
-r 支持使用扩展正则表达式
-i 直接编辑文件
-i.bak 备份文件并原处编辑
2、script 地址定界
不给地址:对全文进行处理
单地址:
#: 指定的行,$:最后一行
/pattern/:被此处模式所能够匹配到的每一行
地址范围:
#,#
#,+#
/pat1/,/pat2/
`#,/pat1/
~:步进
1~2 奇数行
2~2 偶数行
3、编辑命令:
d 删除模式空间匹配的行,并立即启用下一轮循环
p 打印当前模式空间内容,追加到默认输出之后
a []text1 在指定行后面追加文本,支持使用\n实现多行追加
i []text 在行前面插入文本
c []text 替换行为单行或多行文本
w /path/somefile 保存模式匹配的行至指定文件
r /path/somefile 读取指定文件的文本至模式空间中匹配到的行后
= 为模式空间中的行打印行号
! 模式空间中匹配行取反处理
s///:查找替换,支持使用其它分隔符,s@@@,s###

用法

  • 输出符合条件的文本
# 输出所有内容,等同于“cat test.txt”
sed -n 'p' test.txt 

# 输出第三行
sed -n '3p' test.txt 
# 输出3-5 行
sed -n '3,5p' test.txt
# 输出所有奇数行,n表示读入下一行数据
sed -n 'p;n' test.txt
# 输出所有偶数行,n表示读入下一行数据
sed -n 'n;p' test.txt 
# 输出包含“the”的行
sed -n '/the/p' test.txt
# 输出从第4行到都第一个包含“the”的行
sed -n '4,/the/p' test.txt
# 输出包含“the”的行所在的行号(等号(=)用来输出行号)
sed -n '/the/=' test.txt
# 输出以“PI”开头的行
sed -n '/^PI/p' test.txt
# 输出包含单词wood的行,\&lt;、\&gt;代表单词边界
sed -n '/\&lt;wood\&gt;/p' test.txt 
  • 删除符合条件的文本
    nl命令用于计算文件的行数
# 删除第3行
nl test.txt | sed '3d'
# 删除第3-5行
nl test.txt | sed '3,5d'
# 删除包含cross的行,原本的第8行被删除
nl test.txt | sed '/cross/d'
# 删除不包含cross的行
nl test.txt | sed '/cross/! d'
# 删除以“.”结束的行
sed '/\.$/d' test.txt 
# 删除所有空行
sed '/^$/d' test.txt 
# 删除空行,连续的空行留一个
sed -e '/^$/{n;/^$/d}' test.txt
  • 替换符合条件的文本
    使用sed命令进行替换操作时需要用到的选项:s(字符串替换)、c(整行整块替换)、y(字符转换)等命令选项。
[root@localhost ~]# sed 's/the/THE/' test.txt
//将每行中的第一个the替换为THE
[root@localhost ~]# sed 's/l/L/2' test.txt
//将每行中的第三个“l”替换为“L”
[root@localhost ~]# sed 's/the/THE/g' test.txt 
//将文件中所有的“the”替换为“THE”
[root@localhost ~]# sed 's/o//g' test.txt 
//将文件中所有的“o”删除
[root@localhost ~]# sed 's/^/#/' test.txt 
//在每行的行首插入“#”号
[root@localhost ~]# sed '/the/s/^/#/' test.txt 
//在包含“the”的每行行首插入“#”号
[root@localhost ~]# sed 's/$/EOF/' test.txt 
//在每行行尾插入字符串“EOF”
[root@localhost ~]# sed '3,5s/the/THE/g' test.txt 
//将第3~5行中的所有“the”替换为“THE”
[root@localhost ~]# sed '/the/s/o/O/g' test.txt 
//将包含“the”的所有行中的o替换为“O”

# “sed -i”的命令则是立即生效的!
[root@localhost ~]# sed -i '1c 1111' a.txt 
//替换文中第一行的内容为“1111”
[root@localhost ~]# sed -i '1a 1111' a.txt 
//在第一行后面插入一行内容,内容为“1111”
[root@localhost ~]# sed -i '1i 2222' a.txt
//在第一行前面插入一行内容,内容为“2222”
[root@localhost ~]# sed -i '1d' a.txt
//删除第一行内容
sed -n "1,10p"
// 删除1到10行内容  
[root@localhost ~]# sed -n '1p' a.txt
//打印出第一行的内容
[root@localhost ~]# sed -i '1s/2222/3333/g' a.txt 
//将文本第一行内容“2222”替换为“3333”
  • 迁移符合条件的文本
    使用sed命令进行迁移文本操作时需要用到的选项有:g、G将剪贴板中的数据覆盖/追加到指定行;w保存为文件;r读取指定文件;a追加指定内容。
[root@localhost ~]# sed '/the/{H;d};$G' test.txt 
//将包含“the”的行迁移到文件末尾,“;”用于多个操作
[root@localhost ~]# sed '1,5{H;d};17G' test.txt 
//将第1~5行的内容转移到第17行后
[root@localhost ~]# sed '/the/w out.file' test.txt 
//将包含“the”的行另存为文件out.file
[root@localhost ~]# sed '/the/r /etc/hostname' test.txt 
//将文件/etc/hostname的内容添加到包含“the”的每行以后
[root@localhost ~]# sed '3aNEW' test.txt 
//在第3行后面插入一个新行,内容为“NEW”
[root@localhost ~]# sed '/the/aNEW' test.txt 
//在包含“the”的每行后插入一个新行,内容为“NEW”
[root@localhost ~]# sed '3aNEW1\nNEW2' test.txt
//在第3行后面多行内容,中间的“\n”表示换行

awk

报告生成器,格式化文本输出

Linux/UNIX系统中,awk是一个功能强大的编辑工具,逐行读取输入文本,并根据指定的匹配模式进行查找,对符合条件的内容进行格式化输出或者过滤处理,可以在无交互的情况下实现相当复杂的文本操作,被广泛应用于Shell脚本,完成各种自动化配置任务。

awk [options] 'program' file…
  • program:pattern{action statements;..}
    • pattern部分决定动作语句何时触发及触发事件 BEGIN,END
    • action statements对数据进行处理,放在{}内指明 print, printf
    awk '{print "  "$0}' file # 文件内容每行加空格输出
    
awk  -f  脚本文件 文件1 文件2 …
//从脚本中调用编辑指令,过滤并输出内容

awk从输入文件或者标准输入中读入信息,与sed一样,信息的读入也是逐行读取的。不同的是,awk命令将文本文件中的一行视为一个记录,而将一行中的某一部分(列)作为记录的一个字段。为了操作这些不同的字段(列),awk借用shell中类似于位置变量的方法,用$1、$2…$9顺序的表示不同列,$0表示整行。不同字段与不同字段可以通过指定的方式进行分隔,awk默认的分隔符是空格。awk命令允许使用“-F分隔符”的形式来指定分隔符。

# 打印 以:分隔的 第一个、第三个、第四个元素
awk -F ':' '{print $1,$3,$4}' /etc/passwd

awk内置变量:
image

用法

  • 按行输出文本
[root@localhost ~]# awk '{print}' test.txt 
//输出所有内容,等同于“cat test.txt”
[root@localhost ~]# awk '{print $0}' test.txt
//输出所有内容,等同于“cat test.txt”
[root@localhost ~]# awk 'NR==1,NR==3{print}' test.txt 
//输出第1~3行的内容
[root@localhost ~]# awk '(NR&gt;=1) && (NR&lt;=3) {print}' test.txt 
//输出第1~3行的内容
[root@localhost ~]# awk 'NR==1 || NR==3{print}' test.txt 
//输出第1行、第3行的内容
[root@localhost ~]# awk '(NR%2)==1 {print}' test.txt 
//输出所有奇数行的内容
[root@localhost ~]# awk '(NR%2)==0 {print}' test.txt 
//输出所有偶数行的内容
[root@localhost ~]# awk '/^root/{print}' /etc/passwd
//输出以“root”开头的行
[root@localhost ~]# awk '/nologin$/{print}' /etc/passwd
//输出以“nologin”结尾的行
[root@localhost ~]# awk 'BEGIN {x=0} ;/\/bin\/bash$/{x++};END {print x}' /etc/passwd
//统计以/bin/bash结尾的行数
[root@localhost ~]# grep -c "/bin/bash$" /etc/passwd
//统计以/bin/bash结尾的行数
[root@localhost ~]# awk 'BEGIN{RS=""}; END{print NR}' /etc/squid/squid.conf
//统计以空格分隔的文件段落数

注意:命令较多时,使用“BEGIN……END”

  • 按字段输出文本
[root@localhost ~]# awk '{print $3}' test.txt 
//输出每行中(以空格分隔)的第3个字段
[root@localhost ~]# awk '{print $1,$3}' test.txt 
//输出每行中(以空格分隔)的第1个和第3个字段
[root@localhost ~]# awk -F ":" '$2==""{print}' /etc/shadow
//输出/etc/shadow文件中(以“:”分隔)的第二个字段(密码为空的用户)
[root@localhost ~]# awk 'BEGIN {FS=":"}; $2=""{print}' /etc/shadow
//输出/etc/shadow文件中(以“:”分隔)的第二个字段(密码为空的用户)
[root@localhost ~]# awk -F ":" '$7~"/bash"{print $1}' /etc/passwd
//输出以“:”分隔且第7个字段中包含“/bash”的行的第1个字段
[root@localhost ~]# awk '($1~"nfs") && (NF==8) {print $1,$2}' /etc/services
//输出包含8个字段且第1个字段中包含“nfs”的行的第1、2个字段
[root@localhost ~]# awk -F ":" '($7!="/bin/bash") && ($7!="/sbin/nologin") {print}' /etc/passwd
//输出第7个字段既不为“/bin/bash”也不为“/bin/nologin”的所有行
  • 通过管道,双引号调用Shell命令
[root@localhost ~]# awk -F: '/bash$/{print | "wc -l"}' /etc/passwd
//调用“wc -l”命令统计使用“bash”的用户个数
[root@localhost ~]# grep -c "bash$" /etc/passwd
//同上一条命令一样的作用
[root@localhost ~]# awk 'BEGIN {while ("w" | getline) n++ ; {print n-2}}'
//调用“w”命令,并用力啊统计在线用户数
[root@localhost ~]# awk 'BEGIN { "hostname" | getline ; print $0}'
//调用“hostname”命令,并输出当前用户名
  • 使用awk命令进行简单的数学运算
[root@localhost ~]# awk 'BEGIN{ a=6;b=3;print"(a + b)=",(a + b)}'
(a + b)= 9
[root@localhost ~]# awk 'BEGIN{ a=6;b=3;print"(a - b)=",(a - b)}'
(a - b)= 3
[root@localhost ~]# awk 'BEGIN{ a=6;b=3;print"(a / b)=",(a / b)}'
(a / b)= 2
[root@localhost ~]# awk 'BEGIN{ a=6;b=3;print"(a % b)=",(a % b)}'
(a % b)= 0

实践

这里会记录典型的shell应用场景

系统日志太多,占空间,想清理掉一个月之前的日志,只保留近期一个月的日志

#!/bin/sh
# 清理日志
# author:walkingsun
# test

path=/data/app/WindBlog/runtime/logs/                   #指定清理目录
timeout=`expr 30 \* 86400`                              #过期时间(当前设为30天)
systime=`date +%s`                                      #获取当前系统的时间 (秒为单位)

files=$(ls $path)
for filename in $files
do
  fileuptime=`stat -c %Y $path$filename`                #获取文件修改时间(秒)
  if [ $[ $systime - $fileuptime ] -gt $timeout ]
  then
     echo $path$filename
     echo `rm -rf $apth$filename`
  fi
done

给定一个文件 file.txt,转置它的内容。

你可以假设每行列数相同,并且每个字段由 ' ' 分隔.

示例:

    假设 file.txt 文件内容如下:
    
    name age
    alice 21
    ryan 30
    应当输出:
    
    name alice ryan
    age 21 30

解答

#!/bin/bash

awk '{for(i=1;i<=NF;i++){if(NR==1){data[i]=$i}else{data[i]=data[i]" "$i}}}END{for(i=1;i<=NF;i++) print data[i]}' file.txt

给定一个包含电话号码列表(一行一个电话号码)的文本文件 file.txt,写一个 bash 脚本输出所有有效的电话号码。

你可以假设一个有效的电话号码必须满足以下两种格式: (xxx) xxx-xxxx 或 xxx-xxx-xxxx。(x 表示一个数字)

你也可以假设每行前后没有多余的空格字符。

示例:

假设 file.txt 内容如下:

987-123-4567

123 456 7890

(123) 456-7890

你的脚本应当输出下列有效的电话号码:

987-123-4567

(123) 456-7890

#!/bin/bash

awk '/^(\([0-9]{3}\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$/{print $0}' file.txt

写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。

    为了简单起见,你可以假设:
    
    words.txt只包括小写字母和 ' ' 。
    每个单词只由小写字母组成。
    单词间由一个或多个空格字符分隔。
    示例:
    
    假设 words.txt 内容如下:
    
    the day is sunny the the
    the sunny is is
    你的脚本应当输出(以词频降序排列):
    
    the 4
    is 3
    sunny 2
    day 1
#!/bin/bash

awk '{for(i=1;i<=NF;i++){a[$i]=a[$i]+1}}END{for(k in a){print k" "a[k]}}' words.txt |sort -nr -k 2

分表1024张,生成sql脚本

文件 db.sh

#!/bin/bash
start=$1
end=$2
file=$3
echo '' > $file
#for y in {$start..$end};do
for((i=$start;i<$end;i++));do
str=" create table reader_free_click_$i(\n
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n
\`adid\` varchar(32) NOT NULL DEFAULT '' COMMENT '广告计划id',\n
\`cid\` varchar(100) NOT NULL DEFAULT '' COMMENT '广告创意id',\n
\`imei_md5\` char(32) NOT NULL DEFAULT '' COMMENT '安卓设备识别码的md5',\n
\`mac\` char(32) NOT NULL DEFAULT '' COMMENT 'MAC地址的md5sum',\n
\`androidid\` varchar(40) DEFAULT NULL,\n
\`client_ip\` varchar(40) DEFAULT NULL,\n
\`source\` varchar(32) NOT NULL DEFAULT '' COMMENT '媒体来源',\n
\`timestamp\` bigint(18) NOT NULL DEFAULT '0' COMMENT '点击时间',\n
\`callback\` varchar(2048) NOT NULL DEFAULT '' COMMENT '回调参数',\n
\`channel\` varchar(40) DEFAULT NULL COMMENT '渠道唯一标示',\n
\`change_flag\` tinyint(1) NOT NULL DEFAULT '0' COMMENT '渠道是否变更(0:没有变更; 1:变更过)',\n
\`old_channel\` varchar(50) NOT NULL DEFAULT '' COMMENT '原始日志的渠道名',\n
\`book_id\` int(11) DEFAULT '0',\n
\`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入日期',\n
PRIMARY KEY (\`id\`),\n
UNIQUE KEY \`uk_its\` (\`imei_md5\`,\`timestamp\`,\`source\`)\n
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='免费小说媒体点击数据';\n\n";
echo $str >> $file
done

执行:

sh  db.sh 0 1023 db.sql

推荐阅读