shell简介 shell脚本不过是一些文件,我们将一系列需要执行的命令写入其中,然后通过shell来执行这些命令。 shell脚本的执行方式 1. sh script.sh # 脚本在当前目录下 2. ./script.sh #在当前目录下直接执行,不过需要赋予用户script的可执行权限 当打开一个终端的时候,终端会执行一组命令来定义提示文本,颜色等设置。这组命令来自位于用户home目录中的.bashrc脚本文件(~/.bashrc) bash 还维护了一个历史记录文件~/.bash_history,用于保存用户运行过的命令。 在bash中,每个命令或是命令序列都是使用分号或换行符来分隔的。1、关于打印和输出的shell脚本1.1 echo是终端打印的基本命令echo默认每次调用都会添加一个换行符echo输出命令,在echo命令后加双引号,单引号,不加引号都能输出但是这三种情况各有不同1.不带引号的echo ,没法在所需要显示的文本中使用,因为在bash shell中被用做命令界定符。(相当于直接输出一个变量) 当你输入 echo hello;hello 时,因为;是命令界定符,无法输出后面的一个hello 2.使用单引号echo ,bash不会对变量求值,只是按照原样显示 3.使用双引号echo ,一些特殊字符想要输出需要加转义字符 4.使用三引号echo , 所见即所得,输如什么输出就是什么。 5.在变量输出时还有反引号,存储命令输出,echo输出的是反引号中命令执行的结果1.2 printf是另一个可以在终端打印的命令printf不像echo命令自动添加换行符printf的使用方法和C语言中使用方法是一样的同时需要注意printf中格式化的输出2、 变量和环境变量2.1 变量的赋值操作注意:var = value 和 var=value,前者是相等操作,后者是赋值操作。如果value不包含任何空白字符,那么这个变量的值就不需要使用引号进行引用,反之必须使用单引号或双引号2.2 环境变量环境变量是没有在当前进程中定义,而是从父进程中继承来的变量。export 命令用来设置环境变量。(临时作用,就是关机之后环境变量就会消失,或者说是切换用户或者shell这个变量就会消失)export var="value"PATH就是一个环境变量,我们可以用echo $PATH 查看变量的值$PATH 通常定义在/etc/environment或/etc/profile或~/.bashrc中常见环境变量: HOME PWD USER UID SHELL 等注:环境变量以[:]为分隔路径2.3 环境变量的配置方法1.修改/etc/profile 文件(系统级) 用vim打开文件,在文件末尾加上你想要加入的环境变量,然后重新登录 (这种方法是修改成为永久性环境变量,全局性的,对所有用户都有效)(1)/etc/profile:该文件是用户登录时,操作系统定制用户环境时使用的第一个文件,应用于登录到系统的每一个用户。该文件一般 是调用/etc/bash.bashrc文件。 /etc/bash.bashrc:系统级的bashrc文件。 (2)/etc/environment:在登录时操作系统使用的第二个文件,系统在读取你自己的profile前,设置环境文件的环境变量。2.修改~/.bashrc (用户家目录下) 同样打开当前用户家目录下的文件.bashrc,在文件末尾加入你需要加入的环境变量,然后重新登录 (这种修改方法是修改成为永久性的环境变量,但是只是针对当前shell和用户,就是说其他shell此环境变量失效)3.修改~/.profile (用户家目录下)同样使用vim打开当前用户家目录下的文件.profile,在文件末尾加入需要加入的环境变量,重新登录下面这几个文件同样也可以修改环境变量~/.bash_profile or ~./bash_login:这里没有引用作者的,下面会提到 ~/.pam_environment:用户级的环境变量设置文件,没有做测试,不知道管不管用。4.直接在shell下设置 使用export设置环境变量(临时性的设置环境变量) 换个shell这个设置就会失效2.4 查看进程的环境变量 首先获取应用程序的PID pgrep 进程/应用名 查看进程的环境变量 cat /proc/$PID/environ2.5 关于变量的其他使用方法1. 获得字符串长度2. 识别当前shell[root@air ~]# echo $SHELL/bin/bash3.修改bash提示符字符串直接使用修改 PS1 环境变量[root@air ~]# echo $PS1[\u@\h \W]\$4. 使用函数添加环境变量环境变量通常用于存储路径列表,这些路径用于搜索可执行文件、库文件等。例如$PATH 、$LD_LIBRARY_PATH假设我们要将myapp安装到/opt/myapp,它的二进制文件在bin目录中,库文件在lib目录中。实现方法如下:export PATH=/opt/myapp/bin:$PATHexport LD_LIBRARY_PATH=/opt/myapp/lib;$LD_LIBRARY_PATHPATH 和 LD_LIBRARY_PATH 现在看起来应该像这样:PATH=/opt/myapp/bin:/usr/bin:/binLD_LIBRARY_PATH=/opt/myapp/lib:/usr/lib;/lib不过我们可以把下面的函数加入 .bashrc- ,让一切变得更轻松些:prepend() { [ -d "$2" ] && eval $1=\"$2':'\$$1\" && export $1; }像下面这样来使用该函数:prepend PATH /opt/myapp/binprepend LD_LIBRARY_PATH /opt/myapp/lib3、通过shell进行数学运算在bash shell环境中,可以利用 let、(())、[]执行基本的算数操作在执行高级操作时,expr和bc两个工具也经常用到3.1 let命令可以直接执行基本的算数操作3.2 操作符[] 使用方法和let命令类似 r=$[ no + no2 ] 在[]中也可以使用$符号来提取变量的值 r=$[ $no + 5 ]3.3 同样可以使用(()),但是使用(())时候,变量之前需要加上$ r=$(( no + no2 ))这个$符号不能省略,如不不加就会报错3.4 expr同样可以用于基本的算数操作result=`expr 3 + 4` ### 注意空格的使用,不加空格这是输出后面的字符串result=$(expr $no + 5)在进行数学运算的时候,需要注意变量之间的空格是否需要加上空格,其中操作符[]有很多其他的作用所以有时不需要加$符号,但是(())则必须要加上$符号,否则就会报错。3.5 浮点数中数学运算bc是一个用于数学运算的高级工具,借助他执行浮点数运算并应用一些高级函数通常是和管道功能结合使用,同样也可以使用bc 进行普通运算 1.可以设定小数精度echo “scale=2;3/8” | bcscale=小数位个数 2.进行进制的转换使用bc将一种进制转换成另一种进制obase=想要转换的进制ibase=转换之前的进制在系统中默认的是10进制数,所以在10进制转换2进制时不需要注明转换之前的进制当你想要把一个已知的二进制数转换为十进制数就需要使用ibase(如图),否则系统认为你输入的二进制数为十进制 3.计算平方以及平方根[root@air ~]# echo "sqrt(100)" | bc10[root@air ~]# echo "10^10" | bc100000000004、文件描述符和重定向文件描述符是与文件输入、输出相关联的整数。文件描述符0 1 2 是系统预留的。常见的文件描述符是stdin(标准输入 代号:0) ;stdout(标准输出 代号:1) ;stderr(标准错误 代号:2) 2016/11/16 21:54当一个命令发生错误并退回时,他会返回一个非0的退出状态;而当命令执行成功完成后,返回数字0就输出重定向而言,> 和>> 并不相同。前者会先清除空文件,然后再写入内容,后者会把内容直接追加现有的文件的尾部。使用重定向操作符的时候,输出内容不会再终端打印,而是直接导向文件。4.1 将脚本内部的文本块进行重定向#!/bin/bashcatLOG FILE HEADERThis is a test log fileFunction: System statisticsEOFcat文本内容EOF在 cat文件的内容打印如下:$ cat log.txtLOG FILE HEADERThis is a test log fileFunction: System statistics4.2 自定义文件描述符文件描述符是一种用于访问文件的抽象指示器。存取文件离不开被称为文件描述符的特殊数字可以使用exec命令创建自己的文件描述符。文件的打开模式:1.只读模式2.截断写入模式3.追加写入模式$ echo this is a test line > input.txt$ exec 3现在你就可以在命令中使用文件描述符 3 了。例如:$ catthis is a test line如果需要再次进行读取就不能再使用文件描述符3了,而是需要用exec重新分配文件描述符3以便用于读取5、数组和关联数组数组是shell中重要的组成部分,他借助索引将多个独立的数据存储为一个集合。普通数组只能使用整数作为数组索引。bash也支持关联数组,可以使用字符串作为数组索引。5.1 数组的定义1.可以直接在单行中使用一列值来定义一个数组#注意数组的输出2. 可以将数组定义成一组“索引-值”的模式列出所有值echo ${array_var[*]} 或者 echo ${array_var[@]}打印数组长度echo ${#array_var[*]}回顾打印字符串长度echo ${#var}3.定义关联数组declare命令用来声明变量类型(在for循环以及if语句中会用到)利用内嵌“索引 - 值”列表的方法,提供一个“索引 - 值”列表:$ ass_array=([index1]=val1 [index2]=val2)? 使用独立的“索引 - 值”进行赋值:$ ass_array[index1]=val1$ ass_array'index2]=val2举个例子,试想如何用关联数组为水果制定价格:$ declare -A fruits_value$ fruits_value=([apple]='100dollars' [orange]='150 dollars')用下面的方法显示数组内容:$ echo "Apple costs ${fruits_value[apple]}"Apple costs 100 dollars4.列出数组索引echo ${!array_var[*]} 或者 echo ${!array_var[@]}6、 命令别名的使用别名实际上就是一种便捷方式,以省去一长串命令序列的麻烦。alias命令的作用只是暂时的,一旦关闭终端,所有设置的命令别名就失效了,为了使这些命令别名一直保持作用,可以将它放入~/.bashrc文件中$ echo 'alias cmd="command seq"' >> ~/.bashrc 每次使用alias命令这样使用都会直接写入~/.bashrc文件中如果需要删除命令别名就需要在~/.bashrc文件中删除,或者使用unalias命令,或者alias example=,取消example的别名注:创建别名时,如果已经有同名的别名存在,原有的别名设置将会被取代对别名进行转义并不是一直都想要用别名执行命令,可以使用在执行命令别名的时候加上一个转义字符$ \command7、获取终端信息tput和stty是两款终端处理工具1.打印出当前终端名tput longname2.设置终端背景色tputsetp n其中n可以在0-7之间取值3.设置文本样式为粗体tput boldecho 允许输出到终端-echo 禁止输出到终端可以使用stty命令不让输入的内容显示到终端,详细脚本见shell脚本收录—18、获取,设置日期和延时1.读取日期$ date#查看系统时间以及日期2.打印纪元时$ date +%s#来自世界标准时间(UTC)1970/01/01 00:00起所流逝的秒数3.用格式串结合+ 作为date的参数 按照你的选择打印出对应格式的日期$ date "+%d %B %Y"[root@air script]# date "+%d %B %Y"19 十一月 20164.设置日期和时间# date -s "格式化的日期字符串"5.有时候需要检查一组命令所需要的时间 可以使用以下脚本 --脚本收录--26.在脚本中生成延时编写一循环方式运行的监视脚本时,设置时间间隔是必不可少的。为了在脚本中推迟执行一段时间,可以使用sleep$ sleep no_of_seconds#!/bin/bash#文件名: sleep.shecho -n Count:tput sccount=0;while true;doif [ $count -lt 40 ];thenlet count++;sleep 1;tput rctput edecho -n $count;else exit 0;fidone9、调试脚本1.使用选项-x,启用Shell脚本的跟踪调试功能$ bash -x script.sh 或者 sh -x script.sh-x 选项可以打印出所执行的没一行命令以及当前的状态2.使用set -x 和 set +x 对脚本进行部分调试#!/bin/bash#文件名: debug.shfor i in {1..6};doset -xecho $iset +xdoneecho "Script executed"在上面的脚本中,只会打印出 echo $i 的调试信息,因为使用了 -x 和 +x 对调试区域进行了限制。3.以上两种方法都是Bash内建的,他们以固定的格式生成调试信息,但是当需要自定义格式显示调试信息,通过_DEBUG环境变量来建立这类调试风格#!/bin/bashfunction DEBUG(){[ "$_DEBUG" == "on" ] && $@ || :}for i in {1..10}doDEBUG echo $idone可以将调试功能置为 "on" 来运行上面的脚本:$ _DEBUG=on ./script.sh我们在每一个需要打印调试信息的语句前加上 DEBUG 。如果没有把 _DEBUG=on 传递给脚本,那么调试信息就不会打印出来。在Bash中,命令“ : ”告诉shell不要进行任何操作。4.-x工作原理-x标识将脚本中执行过的每一行都输出到stdout,不过,当只想关注脚本某些 部分的命令及参数的打印输出set -x #在执行的时候显示参数和命令set +x#禁止调试set -v#当命令进行读取时显示输入set +v#禁止打印输入5. 其他方法调试脚本把#! /bin/bash 改成#! /bin/bash -xv 不用其他选项就可以启用调试功能10、函数和参数和其他脚本语言一样,bash同样支持函数1.定义函数function fname(){statements;}或者是fname(){statements;}2.只需要使用函数名就可以调用某个函数$ fname; #执行函数3. 参数可以传递给函数,并由脚本进行访问fname(){echo $1,$2#访问参数1,参数2echo "$@"# 以列表的方式一次性打印所有参数echo "$*"#类似于$@ ,但是参数被作为单个实体retutn 0 # 返回值}类似的参数可以传递给脚本并通过script : $0 (脚本名)访问$1是第一个参数$2是第二个参数$n是第三个参数"$@"被扩展为“$1”“$2”“$3”等"$*"被扩展为“$1c$2c$3”,其中c是IFS的第一个字符“$@”比"$*"使用 的多。由于“$*”将所有的参数当做单个字符串,因此它很少被使用函数传递参数的时候,在执行脚本的时候输入你想要传递的值然后通过上述传递方式就把参数传递给了脚本4.递归函数在Bash中,函数同样支持递归调用。5.导出函数函数可以像环境变量一样用export导出,这样函数的作用域就可以扩展到子进程中export -f fname6.向命令传递参数命令的参数能够以不同的格式进行传递。假设-p -v是可用选项,-k N 是另一个可以接受数字的选项同时命令还接受一个文件名作为参数11、将命令序列的输出读入变量shell脚本最棒的特性之一就是可以将多个命令或工具集合起来生成输出。一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递至另一个命令。1.使用下面的方法读取由管道相连的命令序列的输出cmd_output=$(commands) #这种方法和命令表名是不同的,命令别名设定之后,直接就可以用,而 子shell,必须使用echo $cmd 才能输出这种方法称为子shell,还有一种方法是反引用就是使用反引号用来存储命令的输出1.利用子shell生成一个独立的子shell子shell本身就是一个独立的进程,可以使用()操作符来定义一个子shell当命令在子shell中执行时,不会对当前shell有任何影响;所有的改变仅限于子shell内。也就是说 ,当你想要不改变当前的工作环境,却需要在另一个环境中执行一些命令的时候,就需要生成独立的子shell,然后在子shell中执行时,不会影响当前shell2.通过引用子shell的方式保留空格和换行符假设我们使用子shell或反引用的方法将命令输出读入到一个变量中,可以将它放入双引号中以保留空格和换行符(\n)$ cat text.txt123$ out=$(cat text.txt)$ echo $out1 2 3 # Lost \n spacing in 1,2,3$ out="$(cat tex.txt)"$ echo$out12312. 不使用回车键来读取n个字符read是一个重要的Bash命令,它用于从键盘或者标准输入中读取文本。可以使用read以交互式读取来自用户的输入任何编程语言的输入库大多都是从键盘读取输入;但是只有当回车键按下的时候才标志输入完毕1.从输入中读取n个字符,并存入变量表示在读取2个字符2.用无回显的方式读取密码read -s var3.显示提示信息read -p "Enter input: " var4.在特定的时限内读取输入read -t timeout var$ read -t 2 var# 在2 秒内将键入的字符串读入变量var5.用特定的定界符做为输入行的结束read -d delim_char var$ read -d ":" varhello: #var 被设置为 hello,只有输入:的时候才结束输入 没有设置之前是使用【enter】键作为结束的输入符13、运行命令直至执行成功在日常的工作中使用shell时,有时候命令只有满足某些条件或是某种外部事件操作才能被执行1.创建函数 repeat ,它包含了一个无限 while 循环,该循环执行以参数形式(通过 $@访问)传入函数的命令。如果命令执行成功,则返回,进而退出循环。repeat() { while true; do $@ && return; done }在大多数现代系统中, true 是作为/bin中的一个二进制文件来实现的。这就意味着每执行一次 while 循环,shell就不得不生成一个进程。如果不想这样,可以使用shell内建的“ : ”命令,它总是会返回为0的退出码:repeat() { while :; do $@ && return; done }尽管可读性不高,但是肯定比第一种方法快。2.增加延时我们可以修改函数,加入一段短暂的延时:repeat() { while :; do $@ && return; sleep 30; done }这使得命令每30秒运行一次。14、字段分隔符和迭代器内部字符按分隔符(Internal Field Separator ,IFS)是shell脚本编程的一个重要概念用于特定用途的定界符。IFS是存储定界符的环境变量,是当前shell环境使用的默认的定界字符串 2016/11/20 12:53IFS 的默认值为空白字符(换行符,制表符或者空格)在进行一系列值进行迭代时,循环非常有用。1.for循环for var in listdocommands;# 使用变量$vardonelist可以是一个字符串,也可以是一个序列for循环也可以采用C语言中for循环的格式for((i=0;i{commands;#使用变量$i}2.while循环while conditiondocommands;done使用true作为循环条件的时候能够产生无限循环3.until循环until循环是指他会一直执行循环,知道给定的条件为真x=0;until [ $x -eq 9 ]; #条件是[$x -eq 9 ]dolet x++; echo $x;done15、比较与测试程序中的流程控制是由比较语句和测试语句处理的。1.if语句if condition;thencommands;fielse if和elseif condition;thencommands;elif condition;thencommands;elsecommands;fiif语句和else语句可以进行嵌套使用。if的条件判断部分可能会变得很长,但是可以使用逻辑运算符是他们变得简洁[ condition ] && action; #如果为真,就会直接执行action[ condition ] || action;#如果condition为假,则执行action2.算数比较条件通常被放置在封闭的中括号内。一定注意在[或]与操作数之间有一个空格。[ $var -eq 0 ] #当 $var等于0的时候,返回真-ne#非o-gt#大于-lt#小于-ge #大于或等于-le#小于或等于可以按照下面的方法结合多个条件进行测试:[ $var1 -ne 0 -a $var2 -gt 2 ] #使用逻辑与-a[ $var1 -ne 0 -o var2 -gt 2 ] #逻辑或 -o3.文件系统的相关测试使用不同的条件标志测试不同的文件系统-f#测试变量是否是文件-d#测试变量是否为目录文件-e#测试变量文件是否存在-c#测试变量是否为字符设备文件-b#测试变量是否为块设备文件-l#测试变量是否为符号链接-x #测试变量是否是可执行文件-w #测试变量文件权限是否为可写-r #测试变量文件权限是否为可读4.字符串比较[[ $var1 = $var2 ]]#测试字符串是否相等 或者是[ [ $var1 == $var2 ] ][[ $var1 != $var2 ]]#测试字符串是否不等还可以检查字符串的字母序情况,具体如下[[ $str1 > $str2 ]] :如果 str1 的字母序比 str2 大,则返回真。[[ $str1[[ -z $str1 ]] :如果 str1 包含的是空字符串,则返回真。[[ -n $str1 ]] :如果 str1 包含的是非空字符串,则返回真。在比较语句中,等号前后都是要有一个空格,忘记加空格,就是赋值语句了5.test命令test命令可以用来执行条件检测,用test可以避免使用过多的括号例如:if [ $var -eq 0 ]; then echo "True"; fi也可以写成:if test $var -eq 0 ; then echo "True"; fi 2016/11/20 17:44 12-26 17:28