写这篇文章的目的很简单,因为爱并恨过;
前段时间要改个安卓产品的脚本,惊奇发现理论是用shell,虽然实现的功能不复杂,但如果对没了解过shell或懂皮毛的同学,改起来是相当痛苦(如jb),调试半天各种找文档,性价比太低了,恨不得用python重构。。

虽然还是能完成想要的结果,但花费的时间远超一开始想的,目前shell给jb的感觉就如去年正则带来的噩梦一般, 知道这玩意,会一丢丢,但是真正撸码改内容的时候,查半天文档,调试半天不如愿;
因此就有了该篇,对新同学而言,目的是让你快速了解shell,对于老同学而言,目的是提供常用的shell命令快速唤醒记忆,并且贴出例子来快速实验,大神请右上,谢谢~
此处手动@敏爷,因为敏爷支持也咨询了个shell的问题,建议都看看,巩固下;
shell是什么shell是一个用C编写的程序,是用户使用linux的桥梁;
shell和shell脚本的概念而平常说的shell就是指shell脚本,是一种为shell编写的脚本程序;
而shell还有另一种概念,是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务;
Ken Thompson的sh是第一种Unix Shell,windows Explorer是一个典型的图形界面Shell;
用途 自动化常用的命令 执行系统管理和故障排除 执行简单的应用程序 处理文本或文件 示例本文中说的shell,大部分指shell脚本,那就来看示例吧,想知道shell脚本是什么:
#!/bin/sh cd ~ mkdir jb_shell cd jb_shell for ((i=0; i<5; i++)); do touch jb_$i.txt done 复制代码示例解释:
第1行:指定脚本解释器,这里是用/bin/sh做解释器 第2行:切换到当前用户的home目录 第3行:创建一个目录jb_shell 第4行:切换到jb_shell目录 第6行:循环条件,一共循环5次,每次循环i+1 第7行:创建一个jb_0-4.txt文件 第8行:循环体结束mkdir , touch 都是系统自带的程序,一般都在/bin或/usr/bin目录下;
for , do , done 是 sh 脚本语言的关键字;
环境shell编程跟java、Python编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了;
OS当前主流的操作系统都支持shell编程,本文档所述的shell编程是指Linux下的shell,讲的基本都是POSIX标准下的功能,所以,也适用于Unix及BSD(如Mac OS);
LinuxLinux默认安装就带了shell解释器;
Mac OSMac OS不仅带了sh、bash这两个最基础的解释器,还内置了ksh、csh、zsh等不常用的解释器;
Windows上的模拟器windows出厂时没有内置shell解释器,需要自行安装,为了同时能用grep, awk, curl等工具,最好装一个cygwin或者mingw来模拟linux环境。
cygwin mingw 脚本解释器 sh即Bourne shell,POSIX(Portable Operating System Interface)标准的shell解释器,它的二进制文件路径通常是/bin/sh,由Bell Labs开发;
本文讲的是sh,如果你使用其它语言用作shell编程,请自行参考相应语言的文档。
bashBash是Bourne shell的替代品,属GNU Project,二进制文件路径通常是/bin/bash;
业界通常混用bash、sh、和shell,比如你会经常在招聘运维工程师的文案中见到: 熟悉Linux Bash编程,精通Shell编程 。
在CentOS里,/bin/sh是一个指向/bin/bash的符号链接:
[root@centosraw ~]# ls -l /bin/*sh -rwxr-xr-x. 1 root root 903272 Feb 22 05:09 /bin/bash -rwxr-xr-x. 1 root root 106216 Oct 17 2012 /bin/dash lrwxrwxrwx. 1 root root 4 Mar 22 10:22 /bin/sh -> bash 复制代码但在Mac OS上不是, /bin/sh和/bin/bash是两个不同的文件 ,尽管它们的大小只相差100字节左右:
iMac:~ wuxiao$ ls -l /bin/*sh -r-xr-xr-x 1 root wheel 1371648 6 Nov 16:52 /bin/bash -rwxr-xr-x 2 root wheel 772992 6 Nov 16:52 /bin/csh -r-xr-xr-x 1 root wheel 2180736 6 Nov 16:52 /bin/ksh -r-xr-xr-x 1 root wheel 1371712 6 Nov 16:52 /bin/sh -rwxr-xr-x 2 root wheel 772992 6 Nov 16:52 /bin/tcsh -rwxr-xr-x 1 root wheel 1103984 6 Nov 16:52 /bin/zsh 复制代码 高级编程语言理论上讲,只要一门语言提供了解释器(而不仅是编译器),这门语言就可以胜任脚本编程,常见的解释型语言都是可以用作脚本编程的,如:Perl、Tcl、Python、php、Ruby;
Perl是最老牌的脚本编程语言了,Python这些年也成了一些linux发行版的预置解释器;
编译型语言,只要有解释器,也可以用作脚本编程,如C shell是内置的(/bin/csh),Java有第三方解释器Jshell,Ada有收费的解释器AdaScript;
如下是一个PHP Shell Script示例(假设文件名叫test.php):
#!/usr/bin/php <?php for ($i=0; $i < 10; $i++) echo $i . "\n"; 复制代码执行:
/usr/bin/php test.php 复制代码或者:
chmod +x test.php ./test.php 复制代码 第一个shell脚本 编写打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行;

输入一些代码,第一行一般是这样:
#!/bin/bash 复制代码#! 是一个 约定的标记 ,它 告诉系统这个脚本需要什么解释器来执行 ;
运行运行Shell脚本有两种方法:
作为可执行程序 chmod +x test.sh ./test.sh 复制代码注意, 一定要写成./test.sh,而不是test.sh ;
运行其它二进制的程序也一样,直接写test.sh,linux系统会去PATH里寻找有没有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里;
所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找;

通过这种方式运行bash脚本,第一行一定要写对,好让系统查找到正确的解释器;
这里会有疑问, #!/bin/bash 可以不写吗?
答案是可以的,如果这个系统是使用 /bin/bash 作为解释器的话,就可以省去,或者使用解释器参数运行就不需要;
作为解释器参数这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:
/bin/sh test.sh bash test.sh 复制代码
这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。 变量 定义变量
定义变量时, 变量名不需要加美元符号($) ,如:
your_name="jb" # 这种情况,冒号可加可不加 your_name="jb test" #如果有空格要用双引号 复制代码注意, 变量名和等号之间不能有空格 ,这里踩坑多次,切忌切忌,用其他语言的时候习惯空格了;
使用变量使用一个定义过的变量,只要在变量名前面加美元符号即可,如:
your_name="jb" echo $your_name 复制代码echo ${your_name} 变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
for girl in 苍老师 前田 波多 大榄; do echo "I like ${girl}Style" done 复制代码如果不给 girl 变量加花括号,写成echo "I like ${girl}Style ",解释器就会把$girlStyle当成一个变量(其值为空),代码执行结果就不是想要的效果了;
重定义变量已定义的变量,可以被重新定义,如:
your_name="jb" echo $your_name your_name="jbtest" echo $your_name 复制代码这样写是合法的,但注意,第二次赋值的时候不能写 $your_name="jbtest" , 使用变量的时候才加美元符 ;
注释以 # 开头的行就是注释,会被解释器忽略;
多行注释sh里没有多行注释,只能每一行加一个#号。就像这样:
#-------------------------------------------- #jb test 复制代码但是,如果有几十行的代码要临时注释,每一个都价格 # 号太麻烦了,那有没有骚操作了?
有的,就是利用 : 跟 << 连个,那先来介绍这两个玩意:
: 冒号在 是一个空命令 ,可以认为与shell的内建命令true相同,它的返回值是0.;
在while循环中 while : 与 while true 的作用是等效的; 在 if/then 中可作为占位符;
if conditions then: #什么都不做 else take action fi 复制代码<< tag 是将开始标记 tag 和结束标记 tag 之间的内容作为输入, tag 不是关键字,可以随意替换,只是要前后统一即可;
那另外一种多行注释的方式如下:
:<<jbtest echo "I am jb" echo "I am jb" echo "I am jb" jbtest 复制代码: 冒号空命令,啥都不做, << 是将内容重定向输入,两个 jbtest (可任意替换,不是关键字)之间的内容通过 << 追加给冒号 : ,但是 : 冒号对它们啥都不做,就相当于没做任何处理和输出,就相当于注释了;
这操作,的确牛,但是会有兼容性问题,如注释的内容里面带有冒号的话,会报错;

字符串
字符串是shell编程中最常用最有用的数据类型,字符串 可以用单引号,也可以用双引号,也可以不用引号 ;
单引号 str='this is jb' 复制代码单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的 单引号字串中不能出现单引号(对单引号使用转义符后也不行) 双引号 your_name='jb' str="Hello, I know your are \"$your_name\"! \n" 复制代码 双引号里可以有变量 双引号里可以出现转义字符 字符串操作 拼接字符串 your_name="jb" greeting="hello, "$your_name" !" greeting_1="hello, ${your_name} !" echo $greeting $greeting_1 输出:hello, jb ! hello, jb ! 复制代码 获取字符串长度:取字符串长度;
string="jb" echo ${#string} expr length $string 两个都是输出4,但一般用第一种较多; 复制代码 提取字符串取特定长度的字符串内容;
string="jb is a jb" echo ${string:0:4} # 输出jb i,从第0个位置开始提取4个字符串; echo ${string:4} # 输出s a jb,从第4个位置开始提取字符串 echo ${string:(-6):3} # 输出s a,负数表示从右开始计数,从右侧数起第6个位置提取3个字符串; 复制代码 查找字符串位置找出具体字符串位置;
str="abc" expr index $str "ab" # 输出:1 expr index $str "b" # 输出:2 expr index $str "x" # 输出:0,不存在则0 复制代码 截取子串 str="abbc,def,ghi,abcjkl" 命令 意义&输出 echo${str#a*c} 输出 ,def,ghi,abcjkl 一个井号(#) 表示从左边截取掉最短的匹配 (这里把abbc字串去掉) echo ${str##a*c} 输出 jkl 两个井号(##) 表示从左边截取掉最长的匹配 (这里把abbc,def,ghi,abc字串去掉) echo ${str#"a*c"} 输出 abbc,def,ghi,abcjkl 因为str中没有"a*c"子串 echo ${str##"a*c"} 输出 abbc,def,ghi,abcjkl 同理 echo ${str#d*f} 输出 abbc,def,ghi,abcjkl echo ${str#*d*f} 输出 ,ghi,abcjkl echo ${str%a*l} 输出 abbc,def,ghi, 一个百分号(%)表示从右边截取最短的匹配 echo ${str%%b*l} 输出 a 两个百分号表示(%%)表示从右边截取最长的匹配 echo ${str%a*c} 输出 abbc,def,ghi,abcjkl1个井号 # 带一个数字,所以截取最短,2个井号则截取最长; 1个百分号 % 只是从右侧开始算,同上;
字符串替换 str="apple, tree, apple tree" echo ${str/apple/APPLE} #替换第一次出现的apple,输出:APPLE, tree, apple tree echo ${str//apple/APPLE} # 替换所有apple,输出:APPLE, tree, APPLE tree echo ${str/#apple/APPLE} # 如果字符串str以apple开头,则用APPLE替换它,输出:APPLE, tree, apple tree echo ${str/%apple/APPLE} # 如果字符串str以apple结尾,则用APPLE替换它,输出:apple, tree, apple tree 复制代码这里面也有明显的问题,就是如果想替换第二、第三个怎么办? 抱歉,是不行的,只能用 sed ,下面会提及到;
数组数组中可以存放多个值。 Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小; 与大部分编程语言类似,数组元素的下标由0开始;
Shell 数组用括号来表示,元素用 空格符号分割开 ,语法格式如下:
array_name=(value1 ... valuen) arr_num=(1 2 3 4 5 ) arr_string=("abc" "edf" "sss") 复制代码也可以使用下标来定义数组:
array_name[0]=value0 array_name[1]=value1 array_name[2]=value2 复制代码 获取数组长度下面两种方式均可
${#arr_number[*]} ${#arr_number[@]} 复制代码例子:
arr_number=(1 2 3 4 5) echo "数组元素个数为: ${#arr_number[*]}" echo "数组元素个数为: ${#arr_number[@]}" 输出:5 复制代码 读取某个下标的值 arr_number=(1 2 3 4 5) echo "${#arr_number[2]}" ${数组名[下标]} 输出:1 复制代码 对某个下标进行赋值这里分两种情况: 指定的下标是存在的 直接替换新的指定值;
arr_number[2]=100,数组被修改为(1 2 100 4 5) 复制代码指定的下标是不存在的那新的赋值会添加后数组的尾部;
arr_number[100]=100,数组被修改为(1 2 100 4 5 100) 复制代码 删除清除某个元素:
unset arr_number[1] # 这里清除下标为1的数组; 复制代码清空整个数组:
unset arr_number 复制代码 分片访问 分片访问形式为: ${数组名[@或*]:开始下标:结束下标} , 注意,不包括结束下标元素的值; ${arr_number[@]:1:4} # 这里分片访问从下标为1开始,元素个数为4,输出的是2 3 4 5 复制代码 数值替换 形式为:${数组名[@或*]/数值/新值} ${arr_number[@]/2/98} #就是把2替换成98,输出:1 98 3 4 5 复制代码 数组遍历 for v in ${arr_number[@]}; do echo $v; done 复制代码 传参 命令行参数一般使用脚本,都需要向脚本传递参数,那shell获取参数的格式是 $n , n代表的是数据,1为执行脚本的第一个参数,2为执行脚本的第二位参数;
echo "Shell 传递参数实例!"; echo "执行的文件名:$0"; echo "第一个参数为:$1"; echo "第二个参数为:$2"; echo "第三个参数为:$3"; 复制代码执行脚本:
$ bash test.sh one two three Shell 传递参数实例! 执行的文件名:test.sh 第一个参数为:one 第二个参数为:two 第三个参数为:three 复制代码 特殊参数还有几个特殊字符,这边不展开说明,自行了解吧;
参数处理 说明 $# 传递到脚本的参数个数 $* 以一个单字符串显示所有向脚本传递的参数。如"$*"用「"」括起来的情况、以"$1 n"的形式输出所有参数。 $$ 脚本运行的当前进程ID号 $! 后台运行的最后一个进程的ID号 $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。如"$@"用「"」括起来的情况、以"$1" " n" 的形式输出所有参数。 $- |显示Shell使用的当前选项,与set命令功能相同。 $? |显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
处理选项如果脚本传参有N个,但是又不想一个一个 $1 、 $2 这样判断,那怎么办?
while [ -n "$1" ] do case "$1" in -jb) echo "jb";; -jb1) echo "jb1";; -jb2) echo "jb2";; -jb3) echo "jb3";; *) echo "over" ;; esac shift done 复制代码运行的结果:
$ bash test.sh -jb -jb2 -jb3 -jb4 jb jb2 jb3 over 复制代码 管道在 Bash 中,管道符使用"丨"代表; 管道符也是用来连接多条命令的,如"命令1丨命令2";
命令 1 的正确输出作为命令 2 的操作对象; 命令 1 必须有正确输出,而命令 2 必须可以处理命令 1 的输出结果; 命令 2 只能处理命令 1 的正确输出,而不能处理错误输出;比如:
adb shell ps | grep com 复制代码就会输出一堆喊出com的内容,也就是利用了管道符号;

条件判断
条件判断的作用判断某需求是否满足;
返回类型 true,返回0 false,返回1 格式 test 判断式 [ 判断式 ] (常用) 按照文件类型进行判断 测试选项 作用 -b 文件 判断该文件是否存在,并且是块设备文件 -c 文件 判断该文件是否存在,并且是字符设备文件 -d 文件 判断该文件是否存在,并且是目录 -e 文件 判断该文件是否存在 -f 文件 判断该文件是否存在,并且是普通文件 -L 文件 判断该文件是否存在,并且是符号链接文件 -p 文件 判断该文件是否存在,并且是管道文件 -s 文件 判断该文件是否存在,并且非空 -S 文件 判断该文件是否存在,并且是套接字文件 利用 && 和 || 判断文件是否是目录 $ [ -d test ] && echo 'yes' || echo 'no' yes $ [ -d test1 ] && echo 'yes' || echo 'no' no 复制代码 按照文件权限进行判断 测试选项 作用 -r 文件 判断该文件是否存在,并且当前用户拥有读权限 -w 文件 判断该文件是否存在,并且当前用户拥有写权限 -x 文件 判断该文件是否存在,并且当前用户拥有执行权限 -u 文件 判断该文件是否存在,并且拥有SUID权限 -g 文件 判断该文件是否存在,并且拥有SGID权限 -k 文件 判断该文件是否存在,并且拥有SBit权限 判断文件权限 $ [ -r test ] && echo yes || echo no yes $ [ -w test ] && echo yes || echo no yes $ [ -x test ] && echo yes || echo no yes 复制代码 两个文件之间进行比较 测试选项 作用 文件1 -nt 文件2 判断文件1的修改时间是否比文件2的新 文件1 -ot 文件2 判断文件1的修改时间是否比文件2的旧 文件1 -ef 文件2 判断文件1和文件2的Inode号是否一致,可以理解为两个文件是否为同一个文件。这适用于判断硬链接很好的方法 比较两个文件的最后修改时间 相隔1秒,创建file1和file2两个文件 $ touch jb1; sleep 5; touch jb22 $ ll jb* -rw-r--r-- 1 jb 197121 0 12月 11 15:17 jb -rw-r--r-- 1 jb 197121 0 12月 11 15:17 jb1 -rw-r--r-- 1 jb 197121 0 12月 11 15:17 jb22 复制代码 比较两个文件的最后修改时间 jb@LAPTOP-2R0431R1 MINGW64 /c $ [ jb1 -ot jb22 ] && echo yes || echo no yes jb@LAPTOP-2R0431R1 MINGW64 /c $ [ jb1 -nt jb22 ] && echo yes || echo no no 复制代码 两个整数之间比较 测试选项 作用 整数1 -eq 整数2 判断整数1是否等于整数2 整数1 -ne 整数2 判断整数1是否不等于整数2 整数1 -gt 整数2 判断整数1是否大于整数2 整数1 -lt 整数2 判断整数1是否小于整数2 整数1 -ge 整数2 判断整数1是否大于等于整数2 整数1 -le 整数2 判断整数1是否小于等于整数2 字符串的判断 测试选项 作用 -z 字符串 判断字符串是否为空 -n 字符串 判断字符串是否为非空 字符串1 == 字符串2 判断字符串1和字符串2是否相等 字符串1 != 字符串2 判断字符串1和字符串2是否不相等 字符串1 == 字符串2:判断字符串1和字符串2是否相等 $ a=111 $ b=222 $ [ "$a" == "$b" ] && echo yes || echo no no 复制代码 多重条件判断 测试选项 作用 判断1 -a 判断2 逻辑与,判断1 和 判断2都为真,最终结果才为真 判断1 -o 判断2 逻辑或,判断1 和 判断2有一个为真,最终结果就为真 ! 判断 逻辑非,使原始的判断式取反 流程控制 if else if if condition then command1 command2 ... commandN fi 复制代码写成一行(适用于终端命令提示符):
if `ps -ef | grep ssh`; then echo hello; fi 复制代码 末尾的fi就是if倒过来拼写,对成对出现的; if else if condition then command1 command2 ... commandN else command fi 复制代码 if else-if else if condition1 then command1 elif condition2 command2 else commandN fi 复制代码 for while for在上面的示例里看到过:
for var in item1 item2 ... itemN do command1 command2 ... commandN done 复制代码写成一行:
for var in item1 item2 ... itemN; do command1; command2… done; 复制代码 whilewhile循环用于不断执行一系列命令,也用于从输入文件中读取数据;
while condition do command done 复制代码 无限循环 while : do command done 复制代码或者
while true do command done 复制代码或者
for (( ; ; )) 复制代码 untiluntil 循环执行一系列命令直至条件为 true 时停止; until 循环与 while 循环在处理方式上刚好相反;
一般 while 循环优于 until 循环,但在某些时候―也只是极少数情况下,until 循环更加有用。
until condition do command done 复制代码 caseShell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
case 值 in 模式1) command1 command2 ... commandN ;; 模式2) command1 command2 ... commandN ;; esac 复制代码需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号用两个分号表示break;
跳出循环 breakbreak命令允许跳出所有循环(终止执行后面的所有循环)。
continueontinue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
函数shell可以自定义函数,然后在shell脚本中随意调用;
定义shell函数定义的格式:
function 函数名 () { to do.. return -n } 复制代码 可以function 函数名()定义,也可以直接函数名()定义,不带任何参数; return是可选,不加则将以最后一条命令运行结果作为返回值; 调用 function jb1 () { #前面的function是声明一个函数 名字叫 jb1 () echo "jb1" # 执行操作,输出jb1 } function jb2 () { echo "jb2" } jb3() { echo "jb3" } jb1 # 调用jb1函数 jb2 jb3 复制代码 带参数函数实例 function jb () { echo 我的名字叫: $1 } jb $1 复制代码运行:
$ bash test.sh hahhahha 我的名字叫: hahhahha 复制代码 使用函数实现菜单脚本 function CDAN(){ cat << yankerp +------------------------------------------------+ | | | _o0o_ 1. 安装Nginx | | 08880 2. 安装Apache | | 88"."88 3. 安装mysql | | (|-_-|) 4. 安装PHP | | 0\=/0 5. 部署LNMP环境 | | __/ \__ 6. 安装zabbix监控 | | ‘\ ///‘ 7. 退出此管理程序 | | / Linux一键 \ 8. 关闭计算机 | | || Server || ====================== | | \ //// 一键安装服务 | | ||| i i i ||| by Yankerp | | ___ ___ ====================== | |___‘. /--.--\ .‘___ | +------------------------------------------------+ yankerp } CDAN LOG_DIR=/usr/local/src read -p "请您输入1-8任意数值:" NUM if [ ${#NUM} -ne 1 ] then echo "请您输入1|2|3|4|5|6|7|8" exit 1 fi expr $NUM + 1 &>/dev/null if [ "$?" -ne 0 ] then echo "请您输入数值!" exit 1 fi if [ "$NUM" -gt 8 ];then echo "请您输入比8小的数值" exit 1 elif [ "$NUM" -eq 0 ];then echo "请您输入比0大的数值" exit 1 fi ###################### function Nginx_DIR() { yum install -y gcc gcc-c++ pcre-devel zlib-devel openssl-devel &>/dev/null if [ $? -eq 0 ] then cd $LOG_DIR && wget http://nginx.org/download/nginx-1.12.2.tar.gz &>/dev/null && useradd -M -s /sbin/nologin nginx && \ tar zxf nginx-1.12.2.tar.gz && cd nginx-1.12.2/ && \ ./configure --prefix=/usr/local/nginx --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module --with-http_flv_module --with-http_mp4_module --with-pcre --with-http_ssl_module --with-http_gzip_static_module --user=nginx &>/dev/null && make &>/dev/null && make install &>/dev/null fi if [ -e /usr/local/nginx/sbin/nginx ];then ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/ && nginx && echo "Nginx安装并启动成功!!!" fi } if [ $NUM -eq 1 ] then echo "开始安装Nginx请稍等..." && Nginx_DIR fi 复制代码输出:

select菜单
上面说了流程,这里就介绍一个比较特殊的流程控制: select
select的格式如下:
select name [in list ] do 循环体命令 done 复制代码从格式上看,跟for的格式类似; select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示PS3 提示符,等待用户输入;
用户输入菜单列表中的某个选项,执行响应的命令,而输入的内容会被保存在 内置变量REPLY 里;
select 是个无限循环,因此要记住用break 命令退出循环,或用exit 命令终止脚本。也可以按ctrl+c 退出循环。例子:
text="Please select a number: " select name in jb1 jb2 jb3 jb4 do case $name in jb1) echo "Hello, jb1." ;; jb2) echo "Hello,jb2." ;; jb3) echo "Hello, jb3." ;; jb4) echo "Hello, jb4." ;; *) echo "Sorry, there is no such person." ;; esac done 复制代码结果:

stdin&stdout&stderr
在Linux系统中,一切设备都看作文件。 而每打开一个文件,就有一个代表该打开文件的文件描述符。 程序启动时默认打开三个I/O设备文件:
标准输入文件stdin,文件描述符0; 标准输出文件stdout,文件描述符1; 标准错误输出文件stderr,文件描述符2; 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
如果希望 stderr 重定向到 file,可以这样写:
$ command 2 > file 复制代码如果希望 stderr 追加到 file 文件末尾,可以这样写:
$ command 2 >> file 复制代码2 表示标准错误文件(stderr)。
如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
$ command > file 2>&1 复制代码或者
$ command >> file 2>&1 复制代码如果希望对 stdin 和 stdout 都重定向,可以这样写:
$ command < file1 >file2 复制代码command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
常用的命令sh脚本结合系统命令便有了强大的威力,在字符处理领域,有grep、awk、sed三剑客, grep负责找出特定的行,awk能将行拆分成多个字段,sed则可以实现更新插入删除等写操作。
ps ps命令用来列出系统中当前运行的那些进程,但ps 提供的hi进程的一次性的查看,它所提供的查看结果并不是动态连续的; 如果想对进程时间监控,应该用 top 工具。对系统中进程进行监测控制,查看状态,内存,CPU的使用情况,使用命令:/bin/ps
ps :是显示瞬间进程的状态,并不动态连续; top:如果想对进程运行时间监控,应该用 top 命令; 作用ps 命令就是最基本同时也是非常强大的进程查看命令; 使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。
命令行格式 使用方式:ps [options] [--help] 说明:显示瞬间行程 (process) 的动态 参数:见下方
参数 命令 含义 ps a 显示现行终端机下的所有程序,包括其他用户的程序 ps -A 显示所有进程 ps c 列出程序时,显示每个程序真正的指令名称 ps -e 此参数的效果和指定"A"参数相同 ps e 列出程序时,显示每个程序所使用的环境变量 ps f 用ASCII字符显示树状结构,表达程序间的相互关系 ps -H 显示树状结构,表示程序间的相互关系 ps -N 显示所有的程序,除了执行ps指令终端机下的程序之外 ps s 采用程序信号的格式显示程序状况 ps S 列出程序时,包括已中断的子程序资料 ps -t<编号> 指定编号,并列出属于该程序的状况 ps u 以用户为主的格式来显示程序状况 ps x 显示所有程序,不以程序来区分
最常用的方法是 ps -aux ,然后再利用一个管道符号导向到grep去查找特定的进程,然后再对特定的进程进行操作。
ps -aux | grep com.jbtest #这样就会把com.jbtest相关的进程信息全部列出; 复制代码 常用 基本PS使用 $ps 复制代码
名称 含义 PID 运行着的命令(CMD)的进程编号 TTY 命令所运行的位置(终端) TIME 运行着的该命令所占用的CPU处理时间 CMD 该进程所运行的命令 列出目前所有的正在内存当中的程序 $ ps -aux 复制代码

名称 含义 USER 用户名 UID 用户ID(User ID) PID 进程ID(Process ID) PPID 父进程的进程ID(Parent Process id) SID 会话ID(Session id) %CPU 进程的cpu占用率 %MEM 进程的内存占用率 VSZ 进程所使用的虚存的大小(Virtual Size) RSS 进程使用的驻留集大小或者是实际内存的大小,Kbytes字节。 TTY 与进程关联的终端(tty) STAT 进程的状态:进程状态使用字符表示的(STAT的状态码) START 进程启动时间和日期 TIME 进程使用的总cpu时间 COMMAND 正在执行的命令行命令
关于STAT,可以通过截图看到有很多字符,具体的含义如下:
名称 含义 R 运行 Runnable (on run queue) 正在运行或在运行队列中等待。 S 睡眠 Sleeping 休眠中, 受阻, 在等待某个条件的形成或接受到信号。 I 空闲 Idle Z 僵死 Zombie(a defunct process) 进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放。 D 不可中断 Uninterruptible sleep (ususally IO) 收到信号不唤醒和不可运行, 进程必须等待直到有中断发生。 T 终止 Terminate 进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行。 P 等待交换页 W 无驻留页 has no resident pages 没有足够的记忆体分页可分配。 X 死掉的进程 < 高优先级进程 高优先序的进程 N 低优先 级进程 低优先序的进程 L 内存锁页 Lock 有记忆体分页分配并缩在记忆体内 s 进程的领导者(在它之下有子进程); l 多进程的(使用 CLONE_THREAD, 类似 NPTL pthreads) + 位于后台的进程组 可以用 | 管道和 more 连接起来分页查看 ps -aux |more 复制代码 可以用 | 管道和 grep 过滤 ps -aux |grep ps 复制代码
把所有进程显示出来,并输出到jb.txt文件 ps -aux > jb.txt 复制代码 输出指定的字段 ps -o pid,ppid,pgrp,session,tpgid,comm 复制代码

根据CPU 使用来升序排序 ps -aux --sort -pcpu | less 复制代码 根据 内存使用 来升序排序 ps -aux --sort -pmem | less 复制代码 树形显示进程 pstree 复制代码

通过进程名和PID过滤使用 -C 参数,后面跟你要找的进程的名字。比如想显示一个名为getty的进程的信息,就可以使用下面的命令:
ps -C getty 复制代码 显示所有进程信息,连同命令行 ps -ef 复制代码 grepgrep命令用于查找文件里符合条件的字符串;
一般是结合管道|来使用,当然也支持单独带参使用;
grep 更适合单纯的查找或匹配文本; 语法 grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数>][-d<进行动作>][-e<范本样式>][-f<范本文件>][--help][范本样式][文件或目录...] 复制代码 参数说明 参数 含义 -a 或 --text 不要忽略二进制的数据。 -A<显示行数> 或 --after-context=<显示行数> 除了显示符合范本样式的那一列之外,并显示该行之后的内容。 -b 或 --byte-offset 在显示符合样式的那一行之前,标示出该行第一个字符的编号。 -B<显示行数> 或 --before-context=<显示行数> 除了显示符合样式的那一行之外,并显示该行之前的内容。 -c 或 --count 计算符合样式的列数。 -C<显示行数> 或 --context=<显示行数>或-<显示行数> 除了显示符合样式的那一行之外,并显示该行之前后的内容。 -d <动作> 或 --directories=<动作> 当指定要查找的是目录而非文件时,必须使用这项参数,否则grep指令将回报信息并停止动作。 -e<范本样式> 或 --regexp=<范本样式> 指定字符串做为查找文件内容的样式。 -E 或 --extended-regexp 将样式为延伸的普通表示法来使用。 -f<规则文件> 或 --file=<规则文件> 指定规则文件,其内容含有一个或多个规则样式,让grep查找符合规则条件的文件内容,格式为每行一个规则样式。 -F 或 --fixed-regexp 将样式视为固定字符串的列表。 -G 或 --basic-regexp 将样式视为普通的表示法来使用。 -h 或 --no-filename 在显示符合样式的那一行之前,不标示该行所属的文件名称。 -H 或 --with-filename 在显示符合样式的那一行之前,表示该行所属的文件名称。 -i 或 --ignore-case 忽略字符大小写的差别。 -l 或 --file-with-matches 列出文件内容符合指定的样式的文件名称。 -L 或 --files-without-match 列出文件内容不符合指定的样式的文件名称。 -n 或 --line-number 在显示符合样式的那一行之前,标示出该行的列数编号。 -q 或 --quiet或--silent 不显示任何信息。 -r 或 --recursive 此参数的效果和指定"-d recurse"参数相同。 -s 或 --no-messages 不显示错误信息。 -v 或 --revert-match 显示不包含匹配文本的所有行。