请注明文章来源:http://blog.zjiecode.com/2019/04/15/shell/html
shell 俗称叫作壳,计算机的壳层,和内核是相对的,用于和用户交互,接收用户指令,调用相应的程序。 linux
所以,把shell分为2大类git
也就是用户使用GUI和计算机核交互的shell,好比Windows下使用最普遍的Windows Explorer(Windows资源管理器),Linux下的X Window,以及各类更强大的CDE、GNOME、KDE、 XFCE。github
他们都是GUI Shell。正则表达式
也就是经过命令行和计算机交互的shell。 Windows NT 系统下有 cmd.exe(命令提示字符)和近年来微软大力推广的 Windows PowerShell。 Linux下有bash / sh / ksh / csh/zsh等 通常状况下,习惯把命令行shell(CLI shell)直接称作shell,之后,若是没有特别说明,shell就是指 CLI shell,后文也是主要讲Linux下的 CLI shell。shell
根据交互方式的不同,命令行式shell(CLI shell),又分为交互式shell和非交互式shell。编程
交互式模式就是shell等待你的输入,而且执行你提交的命令,而后立刻给你反馈。这种也是咱们大多数时候使用的。segmentfault
非交互式shell,就是把shell放在写在一个文件里面,执行的时候,不与用户交互,从前日后依次执行,执行到文件结尾时,shell也就终止了。数组
在Linux下 ,各类shell百花齐放,种类繁多,不一样的shell,也有不一样的优缺点。 咱们要查看当前系统下支持的shell,能够读取/etc/shells文件。bash
Bourne Again Shell 用来替代Bourne shell,也是目前大多数Linux系统默认的shell。
Bourne Shell 是一个比较老的shell,目前已经被/bin/bash所取代,在不少linux系统上,sh已是一个指向bash的连接了。 下面是CentOS release 6.5 的系统
C shell 使用的是“类C”语法,csh是具备C语言风格的一种shell,tcsh是加强版本的csh,目前csh已经不多使用了。
最先,bash交互体验很好,csh做为非交互式使用很爽,ksh就吸收了2者的优势。
zsh网上说的目前使用的人不多,可是感受使用的人比较多。 zsh自己是不兼容bash的,可是他可使用仿真模式(emulation mode)来模拟bash等,基本能够实现兼容。 在交互式的使用中,目前不少人都是zsh,由于zsh拥有很强大的提示和插件功能,炫酷吊炸天。推荐在终端的交互式使用中使用zsh,再安利一个插件Oh My Zsh 其实我我的的理解是,在终端中使用shell,基本上只是调用各类命令,好比:curl cat ls等等,基本不会使用到zsh的编程,因此终端中使用zsh是能够的。可是在写shell脚本的时候,须要考虑兼容性, 最主流的仍是bash shell,因此,后文咱们介绍的shell脚本也是bash shell的。
#!/bin/bash echo "Hello World !"
#!:是一个特殊的标记,代表使用啥解释器来执行,好比这里使用了:/bin/bash 来执行这个脚本。 #:只用一个#,就是注释 echo:输出 咱们把上面的脚本保存成一个文件, 1.sh 后面的这个sh是shell脚本的扩展名。 而后要怎嚒来执行呢?执行一个shell脚本有不少种方式:
这个是由于没有这个脚本没有执行权限,运行 chmod a+x 1.sh 加上执行权限便可。 这里顺带说一下,为啥直接运行1.sh不行呢?由于他默认是去PATH里面找程序,当前目录,通常都不在PATH里面。因此直接运行1.sh就回报找不到文件。
根据测试,#!/bin/bash 的标记,只是针对第二种方式 ./xxx.sh的方式有效。本文中代码,第一行均为这个标记,为了节约篇幅,已经省略.
执行并获取返回结果,有点相似JavaScript 的eval函数。
#!/bin/bash dt=`date` #反引号内的字符串会看成shell执行 ,而且返回结果。 echo "dt=${dt}"
shell的使用比较简单,就像这样,而且没有数据类型的概念,全部的变量均可以当成字符串来处理:
#!/bin/bash myName="tom" youName="cat"
不须要申明,直接写就能够了,可是有几个点须要特别注意:
使用变量
ABC="tom" echo $ABC #使用变量前面加$美圆符号 echo "ABC=$ABC" #能够直接在字符串里面引用 echo "ABC=${ABC}" #可是建议把变量名字用{}包起来
只读变量
ABC="tom" echo "ABC=${ABC}" readOnly ABC #设置只读 ABC="CAT" #会报错,由于设置了只读,不能修改
删除变量
ABC="tom" echo "ABC=${ABC}" unset ABC #删除 echo "ABC=$ABC" echo "ABC=${ABC}"
从这个例子当中,咱们也发现,使用一个不存在的变量,shell不会报错,只是看成空来处理。
使用字符串
NAME="tom" A=my #你甚至能够不用引号,可是字符串当中不能有空格,这种方式也不推荐 B='my name is ${NAME}' #变量不会被解析 C="my name is ${NAME}" #变量会解析 echo $A echo $B echo $C
执行结果
咱们能够发现,这个字符串的单双号和PHP的处理很是相似,单引号不解析变量,双引号能够解析变量。可是均可以处理转义符号。
A='my\nname\nis\ntom' B="my\nname\nis\ntom" echo $A echo $B
执行结果
拼接字符串 其实shell拼接字符串,大概就是2种
NAME="TOM" # 使用双引号拼接 echo "hello, "$NAME" !" #直接写在一块儿,没有字符串链接符 echo "hello, ${NAME} !" #填充模版 # 使用单引号拼接 echo 'hello, '$NAME' !' #直接写在一块儿,没有字符串链接符 echo 'hello, ${NAME} !' #上面已经提升过,单引号里面的变量是不会解析的
强大的字符串处理 shell中简单的处理字符串,能够直接使用各类标记,只是比较难记忆,要用的时候,能够查一下。
ABC="my name is tom,his name is cat" echo "字符串长度=${#ABC}" # 取字符串长度 echo "截取=${ABC:11}" # 截取字符串, 从11开始到结束 echo "截取=${ABC:11:3}" # 截取字符串, 从11开始3个字符串 echo "默认值=${XXX-default}" #若是XXX不存在,默认值是default echo "默认值=${XXX-$ABC}" #若是XXX不存在,默认值是变量ABC echo "从开头删除最短匹配=${ABC#my}" # 从开头删除 my 匹配的最短字符串 echo "从开头删除最长匹配=${ABC##my*tom}" # 从开头删除 my 匹配的最长字符串 echo "从结尾删除最短匹配=${ABC%cat}" # 从结尾删除 cat 匹配的最短字符串 echo "从结尾删除最长匹配=${ABC%%,*t}" # 从结尾删除 ,*t 匹配的最长字符串 echo "替换第一个=${ABC/is/are}" #替换第一个is echo "替换全部=${ABC//is/are}" #替换全部的is
运行结构
这里只是介绍了比较经常使用的一些字符串处理,实际shell支持的还有不少。
Bash Shell 也是支持数组的,与绝大部分语言同样,数组下标从0开始。不过须要注意的是,它只支持一维数组。 定义一个数组,用小括号阔气来,当中用“空格”分割,就像下面这样:
array=("item0" "item1" "item2")
也能够根据下标来定义元素
array[0]="new_item0" array[1]="new_item1" array[2]="new_item2" array[4]="new_item4" #数组下标能够是不连续的
读取数组元素,和变量相似
echo ${array[0]} echo "array[0]=${array[0]}"
获取数组全部的元素
echo "数组的元素为: ${array[*]}" echo "数组的元素为: ${array[@]}"
获取数组的长度
echo "数组的长度为: ${#array[*]}" echo "数组的长度为: ${#array[@]}"
在上文中,其实咱们已经到屡次,就是:echo “字符串” 来输出,一个很简单的例子
echo "Hello world!"
若是当中包含特殊符号,可使用转义等:
echo "Hello \nworld!" echo "\"Hello\"" echo '"Hello"' #固然,也能够这样,单引号不转义,上文提到过 echo `date` #打印执行date的结果 echo -n "123" #加-n 表示不在末尾输出换行 echo "456" echo -e "\a处理特殊符号" #-e 处理特殊符号
-n 让echo输出结束之后,在默认不输出换行符 -e 让echo处理特殊符号,好比:
符号 | 做用 |
---|---|
\a | 发出警告声 |
\b | 删除前一个字符 |
\c | 后不加上换行符号 |
\f | 换行但光标仍旧停留在原来的位置 |
\n | 换行且光标移至行首 |
\r | 光标移至行首,但不换行 |
\t | 插入tab |
上面的特殊符号,写到mac的shell脚本里面要注意,执行的时候,要用bash执行才有效 ,sh无效。 |
固然,你也能够玩一点更有趣的,就是咱们随时在终端中看到的五光十色的文字:
echo -e "\033[31m 红色前景 \033[0m 缺省颜色" echo -e "\033[41m 红色背景 \033[0m 缺省颜色"
其中 \033[是一个特殊标记,表示终端转义开始, 31m表示使用红色字体,你也可使用其余颜色,[30-39]是前景颜色,[40-49]是背景颜色。 \033[0m回复到缺省设置 还能够有一些其余的动做
echo -e "\033[2J" #清除屏幕 echo -e "\033[0q" #关闭全部的键盘指示灯 echo -e "\033[1q" #设置"滚动锁定"指示灯(Scroll Lock) echo -e "\033[2q" #设置"数值锁定"指示灯(Num Lock) echo -e "\033[1m" #设置高亮度 echo -e "\033[4m" #下划线 echo -e "\033[7m" #反显 echo -e "\033[y;xH" #设置光标位置
其余更多的特殊码请自行查询。
有输出,必然有输入,read命令接收标准输入的输入。
read name echo "my name is ${name}"
可使用-p给一个输入提示
read -p "please input your name:" name echo "my name is ${name}"
若是没有指定输入的变量,会把输入放在环境标量REPLY中
read -p "please input your name:" echo "my name is ${REPLY}"
计时输入,若是一段时间没有输入 ,就直接返回,使用-t 加时间
read -t 3 -p "please input your name in 3 senconds:" name
指定输入字符个数,使用-n ,后面的是输入字符个数
read -n 1 -p "Are you sure [Y/N]?" isYes
默读(输入再也不监视器上显示),加一个-s参数。
read -s -p "Enter your password:" password
echo已经比较强大,可是有的时候,咱们须要用到字符串模版输出,printf就比较好用了,他相似C里面的printf程序。 语法是:printf format-string [arguments...] 好比咱们要输出一个表格
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543 printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
运行结果
%s %c %d %f都是格式替代符 %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),至少显示10字符宽度,若是不足则自动以空格填充,超过不限制。 %-4.2f 指格式化为小数,其中.2指保留2位小数。
大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回到您的终端。一个命令一般从一个叫标准输入的地方读取输入,默认状况下,这刚好是你的终端。一样,一个命令一般将其输出写入到标准输出,默认状况下,这也是你的终端。
命令 | 做用 |
---|---|
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 之间的内容做为输入。 |
须要注意的是文件描述符 0 一般是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
输出到文件
echo "test">text.txt #直接输出 echo "test">>text.txt #追加在text.txt后面
重定向输入
read a <<EOF "测试" EOF echo "a=$a"
来个比较过度的
cat < 1.sh > text.txt
把1.sh文件的内容出入到cat,而后cat在输出到text.txt中,至关于,把1.sh的内容输出到text.txt中了
还有一种用法,把标准错误直接输出到标准输出,而且输出到文件file
command > file 2>&1
/dev/null 文件 这个是一个特殊文件,他是一个黑洞,写入到它的内容都会被丢弃,若是咱们不关心程序的输出,能够这样
command > /dev/null 2>&1
和其余语言同样,shell也有条件判断
单分支:
if condition then command1 command2 ... fi
双分支:
if condition then command1 command2 ... else command fi
多分支:
if condition1 then command1 elif condition2 then command2 else commandN fi
好比
if [ "2" == "2" ]; then # "2" 的2边都有空格,不能省略 ,写在一行,条件后面加一个分号 echo "2==2" else echo "2!=2" fi
须要特别注意:[ "2" == "2" ] 其中的"=="两边都有空格,不能省略,不然结果不正确。 判断普通文件是否存在
if [ -f "1.sh" ]; then # 判断一个普通文件是否存在 echo "1.sh 存在" fi
判断目录是否存在
if [ -d "1.sh" ]; then # 判断一个目录是否存在 echo "1.sh 存在" fi
判断字符串长度为0
a="" if [ -z $a ]; then echo "a为空" fi
在shell中,有几个符号要很是注意,用的也比较多,不要搞混了,搞混了,逻辑运算很容易出错
命令组 括号中的命令将会新开一个子shell顺序执行,因此括号中的变量不可以被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令能够没有分号,各命令和括号之间没必要有空格。
a="123" (echo "123";a="456";echo "a=$a") echo "a=$a
命令替换 发现了$(cmd)结构,便将$(cmd)中的cmd执行一次,获得其标准输出,再将此输出放到原来命令。
用于初始化数组 如:array=(a b c d)
a=$((4+5)) echo "a=$a"
a=5 ((a++)) echo "a=$a"
if ((1+1>1));then echo "1+1>1" fi
用于字符串比较 须要注意,用于字符串比较,运算符只能是 ==和!=,须要注意,运算符号2边必须有空格,否则结果不正确!!!好比:
if [ "2" == "2" ]; then # "2" 的2边都有空格,不能省略 echo "2==2" else echo "2!=2" fi
用于整数比较 须要注意,整数比较,只能用-eq,-gt这种形式,不能直接使用大于(>)小于(<)符号。只能用于整形。
if [ 2 -eq 2 ]; then echo "2==2" else echo "2!=2" fi
符号表
符号 | 运算 |
---|---|
-eq | 等于 |
-ne | 不等于 |
-gt | 大于 |
-ge | 大于等于 |
-lt | 小于 |
-le | 小于等于 |
多个逻辑组合 -a 表示and 与运算 -o 表示or 或运算
if [ "2" == "2" -a "1" == "1" ]; then #注意,在这里,不能是[ "2" == "2" ] -a [ "1" == "1" ] 会报错 echo "ok" fi
[[是 bash 程序语言的关键字。并非一个命令,[[ ]] 结构比[ ]结构更加通用。
if [[ "123" == 12* ]]; then #右边是正则不须要引号 echo "ok" fi
if [[ 2.1 > 1.1 ]]; then echo "ok" fi
if [[ 1.1 > 1.1 ]] || [[ 1.1 == 1.1 ]]; then echo "ok" fi
touch new_{1..5}.txt #建立new_1.txt new_2.txt new_3.txt new_4.txt new_5.txt 5个文件
语法格式为:
for a in "item1" "item2" "item3" do echo $a done
输出当前目录下 .sh结尾的文件
for a in `ls ./` do if [[ $a == *.sh ]] then echo $a fi done
语法
while condition do command done
咱们要输出1-10000
int=1; while(($int<=10000)) do echo $int ((int++)) done
语法
until condition do command done
用法相似,这里再也不赘述。 循环中 continue命令与break做用和其余语言中相似。
case和其余语言switch类型,多分支,选择一个匹配。匹配发现取值符合某一模式后,其间全部命令开始执行直至 ;;,有点类型Java的break。若是无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
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
shell也能够用户定义函数,而后在shell脚本中能够随便调用。 注意:全部函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可使用。调用函数仅使用其函数名便可。 语法格式以下:
[function] funname() { cmd.... [return int] }
一个最简单的函数
Line(){ echo "--------分割线--------" } echo "123" Line echo "456"
在Shell中,调用函数时能够向其传递参数。 在函数体内部,经过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数... 调用的时候 ,函数名,参数直接用空格分割开。 带参数的函数示例:
out(){ echo "1-->$1" echo "2-->$2" } out 1 2 #调用的以后
还有一些其余的特殊符号须要注意
符号 | 做用 |
---|---|
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示全部向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,可是使用时加引号,并在引号中返回每一个参数。 |
$? | 显示最后命令的退出状态。0表示没有错误,其余任何值代表有错误。 |
因此咱们能够写一个代码参数,返回值的函数
out(){ echo "所有参数$*" for item in $* do echo "$item" done return $# #这类返回参数个数,返回值必须是整数 } out this is perfect echo "函数返回值:$?"
咱们能够在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 表明一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 除了参数可使用特殊符号,也可使用上文中函数所使用的特殊符号,这里再也不赘述
echo "执行的文件名:$0"; echo "所有参数:$*" echo "参数个数:$#" echo "第一个参数为:$1"; echo "第二个参数为:$2"; echo "第三个参数为:$3";
shell脚本,他自己的功能并不强大,强大的是他能够调用其余程序,而在Linux下,系统自带的就有很是多的强大工具能够调用。
后台执行一个脚本只须要在后面加上&符号便可,咱们先用以前学习的,写一个脚本,1s输出一个数字
#!/bin/bash int=1 while : do echo $int ((int++)) sleep 1s #睡眠一秒 done
咱们执行sh d.sh & 咱们发现,的确会后台输出,可是会输出到当前控制台,咱们能够用以前学的重定向,把输出重定向到文件
sh d.sh > out.log 2>&1 &
这样就把输出和错误从新定向到out.log文件了 可是,咱们发现,关闭终端之后,文件就不输出了。 当咱们端口链接远程主机的session或者关闭当前终端的时候, 会产生一个SIGHUP信号 ,致使程序退出,咱们可使用nuhup来忽略这个信号 ,达到真正的后台。
nuhup sh d.sh > out.log 2>&1 &
这样启动程序,就能够打到真正后台运行了。 那么问题来了,咱们验证程序在后台运行呢?要怎嚒结束后台程序呢?请继续看。
在本文中,咱们已经屡次用到cat,他的做用就是读取文件输出到标准输出上,也就是咱们的终端。 语法是:
cat [option] file
咱们也可使用:cat -n file ,来输出行号。
相似上面的例子,咱们要验证程序是否是在后台,每一秒输出一个数字到文件,使用cat读取,须要不断的屡次查看,一次cat只能输出一次。 tail很是适合查看这种日志类文件,他的做用是读取文件末尾几行输出到标准输出上。 tail out.log 默认显示10行,可使用参数-n指定行数 tail -20 out.log 显示文件末尾20行 tail -f out.log 持续监控文件out.log,若是有变化,他会试试的显示在咱们的屏幕上面。
ps,查询进程 这个命令参数比较多,列举几个比较经常使用的
参数 | 做用 |
---|---|
a | 显示终端上的全部进程,包括其余用户的进程。 |
u | 显示面向用户的格式信息。 |
x | 显示没有控制终端的进程。 |
通常查询,使用 ps aux就能够了,查询出来比较多,能够筛选一下。 这里咱们使用 ps u 就能够查询出咱们刚才开启的后台进程了。
咱们看到咱们刚才启动的程序PID为7523, 使用kill命令就能够杀死他了
kill命令比较简单,就是根据PID结束一个程序,好比咱们已经查询到,咱们开的后台进行是7523,要结束他可使用: kill 7523 以上是经常使用用法,其实kill是给程序发送一个信号,上面的程序给会程序发送一个SIGTERM信号,程序收到这个信号,完成资源的释放,就退出了。 可是也有程序不听话,收到信号就是不退出,这个时候,就要强制他退出,使用9号命令(SIGKILL),强制杀死他。 简单的说 kill PID 是告诉程序,你应该退出了,请本身退出。 kill -9 PID ,是直接告诉程序,你被终结了,这个命令信号,不能被抓取或者忽略。
推荐
推荐一个库,经过API把消息推送到我的微信上,SDK接入:https://github.com/zjiecode/wxpusher-client。
关注公众号开发者服务(wxpusher),万一有须要的时候呢?
http://c.biancheng.net/shell/ https://baike.baidu.com/item/shell/99702?fr=aladdin https://blog.csdn.net/lixinze779/article/details/81012318 http://www.javashuo.com/article/p-chvewjpu-bc.html https://blog.csdn.net/felix_f/article/details/12433171 http://www.runoob.com/linux/linux-shell-printf.html http://www.runoob.com/linux/linux-shell-process-control.html https://www.jb51.net/article/123081.htm http://www.runoob.com/linux/linux-shell-io-redirections.html https://www.cnblogs.com/mfryf/p/3336804.html http://www.javashuo.com/article/p-tnhezbqz-bh.html