shell programming tutorial

可以直接 man bash 学习语法和相关命令。

一、什么是shell程序

以文件形式存放批量的Linux命令集合,该文件能够被Shell解释执行,这种文件就是Shell脚本程序

通常由一段Linux命令、Shell命令、控制语句以及注释语句构成

Shell 脚本的编写

Shell 脚本是纯文本文件,可以使用任何文本编辑器编写 Shell 脚本通常是以 .sh 作为后缀名 第一行:指定用哪个程序来编译和执行脚本。#!/bin/bash(shell 变量里面含 ! x 的话,要转义一下 \! x)

注释行:使用(#)符号;多行注释  <<EOD ... EOD

二、shell编程的主要内容

变量

本地变量、环境变量、位置参量

输入输出

read/echo或printf

条件测试

整数测试、逻辑测试、字符串测试

控制语句

条件/循环/分支/

函数 常用Shell程序内置指令

declare/export/eval/trap等

三、变量

(一)变量概述

(二)变量常见操作

(三)环境变量和只读变量

(四)位置参量(命令行参数)

 位置参量是一组特殊的内置变量,通常被 shell 脚本用来从命令行接受参数,或被函数用来保存传递给它的参数。

 执行 shell 脚本时,用户可以通过命令行向脚本传递信息,跟在脚本名后面的用空格隔开的每个字符串都称为位置参量。

在脚本中使用这些参数时,需通过位置参量来引用。例如: $1 表示第一个参数,$2 表示第二个参数,以此类推。 $9 以后需要用花

括号把数字括起来,如第 10 个位置参量以  ${10} 的方式来访问。

$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。但是当它们被双引号(" ")包含

时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。

注意 “$*” 受 $IFS 变量值的影响,如 

$ IFS=:;   $ set x y z   $ echo $*   x y z   $ echo "$*"   x:y:z   $ echo $@   x y z   $ echo "$@"   x y z  

使用$?判断管道的执行结果是否成功,不可信。因此在使用管道获取的参数,我们一定要增加对于参数的判断,或者,我们在sh和bash的解释器中,

增加set –o pipefail 的设置,可以让管道的执行结果是否成功,变得可信。

#例1:shell_test.sh  echo “the count of parameters:$#” echo “first param=$1” echo “second param=$2” echo “params’ string=$*”

shell_test.sh This is Peter

shell_test.sh This is "Peter Piper"    //如果位置参量中含有空格,则需要使用双引号

(五)数组

# 字典必须先声明

declare -A dic

dic=([key1]="value1" [key2]="value2" [key3]="value3")

#打印指定key的value

echo ${dic["key1"]}

#打印所有key值

echo ${!dic[*]}

#打印所有value

echo ${dic[*]}

四、输入输出

(一)输入--read命令

read var

从标准输入读取一行并赋值给变量var

read

标准输入读取一行并赋值给内置变量REPLY

read -a arr

读入一组词,依次赋值给数组arr

read -p "please input 5 digits:" -t 10 -a arr3  // -p 提示符 -t 超时

read -s // 输入不回显

stty -echo // 输入不回显    

stty echo // 输入回显

(二)输出--echo 命令

echo $num 或 echo ${num}   //输出一行文本

echo -n “Hello World”  // -n 去掉换行符 echo -e “\t” “Hello World”   // -e 支持转义

(三)echo输出颜色与光标定位:

\33[30m -- \33[37m   设置前景色(字体颜色) \33[40m -- \33[47m   设置背景色 \33[y;xH设置光标位置

echo –e “\033[31mthis is a test” echo -e "\033[10;5H\033[31m\033[46m this is a test“ echo -e "\033[0m" // 取消全部设置

五、算术扩展

(一)单引号、双引号、反引号区别

单引号 忽略所有特殊字符 双引号 忽略大部分特殊字符($,`, \ 等字符除外) 参考这里 或者尝试  X=*;   echo $X;   echo '$X';  echo "$X"; 的区别 反引号 命令替换(将一个命令的标准输出插入到命令的任何位置) $()     同上 命令替换可以嵌套  如果使用反引号,则内部的反引号必须用反斜杠来转义。

echo  `basename \`pwd\``

echo  $(basename  $(pwd))

算术运算符:

+、-、*、/(四则运算)

**、%      (幂运算 和 模运算,取余数)

<<、>>     (按位左移 和 按位右移)

&、^、|   (按位与 、按位异或 和 按位或)

=、+=、-= 、*=、/=、%= <<= 、>>=、&=、^=、|=   (赋值运算)

<、>、<=、>=、==、!=    (比较操作符)

&&、||    (逻辑与 和 逻辑或)

算术扩展:

$[] n=5;  echo $[$n+1] $(())        n=5;  echo $(($n+1)) (()) ((n+=1));  echo $n expr expr 4 + 5 注意+号两边要有空格      r=`expr 4 + 5` r=`expr 4 \* 5` let   // 内置命令  type let    let n=n+1    let “n = n + 1”    let “n=n+abc”    // abc 当做0

六、条件测试

任何一种测试中,都要有退出状态(返回值),退出状态为 0 表示命令成功或表达式为真,非0 则表示命令失败或表达式为假。

状态变量 $? 中保存命令退出状态的值

grep  study /etc/passwd; echo $?

grep   hello  /etc/passwd;echo  $?

(一)测试表达式的值

表达式测试包括字符串测试、整数测试和文件测试。

通常用 test 命令来测试表达式的值

x=5; y=10 test $x -gt $y echo $?

test 命令可以用 方括号 来代替(方括号前后要留空格)

x=5; y=10 [ $x -gt $y ]  echo $?

2.x 版本以上的 Bash 中可以用双方括号来测试表达式的值,此时可以使用通配符进行模式匹配。

name=Tom

[[ $name = [Tt]?? ]]  echo $?

(二)字符串测试   //字符串测试最好加上"  "

[ -z $str ]

如果字符串 str 长度为0,返回真

[ -n $str ]

如果字符串 str 长度不为0,返回真

[ $str1 ==  $str2 ]

两字符串相等

[ $str1 != $str2 ]

两字符串不等

name=Tom; [ -z $name ]; echo $?

name2=Andy; [ $name == $name2 ] ; echo $?

(三)整数测试 //操作符两边必须留空格!

[ int1 -eq int2 ]

int1等于int2

[ int1 -ne int2 ]

int1不等于int2

[ int1 -gt int2 ]

int1大于int2

[ int1 -ge int2 ]

int1大于或等于int2

[ int1 -lt int2 ]

int1小于int2

[ int1 -le int2 ]

int1小于或等于int2

x=1; [ $x -eq 1 ]; echo $?

整数测试也可以使用 let 命令或双圆括号:

相应的操作符为:== 、!= 、> 、>= 、< 、<=

x=1; let "$x == 1"; echo $?

x=1; (($x+1 >= 2 )); echo $? 

let 和 双圆括号中可以使用算术表达式,而方括号不能 let 和 双圆括号中,操作符两边可以不留空格

(四)逻辑测试

[ expr1 -a expr2 ]

逻辑与,都为真时,结果为真

[ expr1 -o expr2 ]

逻辑或,有一个为真时,结果为真

[ !expr ]

逻辑非

x=1; name=Tom; [ $x -eq 1 –a –n $name ]; echo $?

[ ( $x -eq 1 ) –a ( –n $name ) ]; echo $?  //error, 不能随便添加括号

可以使用模式的逻辑测试:

[[ pattern1 && pattern2 ]]

逻辑与

[[ pattern1 || pattern2 ]]

逻辑或

[[ !pattern ]]

逻辑非

x=1; name=Tom;

[[ $x -eq 1 && $name = To? ]]; echo $?

(五)检查空值

[ "$name" =  "" ]           [ ! "$name" ]                  [ "X${name}"  =  "X" ]

(六)文件测试

文件测试:文件是否存在,文件属性,访问权限等。更多文件测试符参见 man test

-f fname

fname 存在且是普通文件时,返回真 ( 即返回0 )

-L fname

fname 存在且是链接文件时,返回真

-d fname

fname 存在且是一个目录时,返回真

-e fname

fname(文件或目录)存在时,返回真

-s fname

fname 存在且大小大于0 时,返回真

-r fname

fname(文件或目录)存在且可读时,返回真

-w fname

fname(文件或目录)存在且可写时,返回真

-x fname

fname(文件或目录)存在且可执行时,返回真

(七)括号总结

${...}

获取变量值,${BASH:0:1} 可以代替 /

$(...)

命令替换

$[...]

让无类型的变量参与算术运算

$((...))

同上

((…))

算术运算

[ ... ]

条件测试,等价于test命令

[[ ... ]]

条件测试,支持模式匹配与通配符

七、条件与分支语句

(一)if条件语句

if expr1      #如果expr1为真(返回值为0)

then          #那么

   commands1  #执行语句块commands1

elif  expr2    #若expr1不真,而expr2为真

then          #那么

   commands2  #执行语句块commands2

 ... ... #可以有多个elif语句

else          # else最多只能有一个

   commands4  #执行语句块commands4

fi            #if语句必须以单词fi终止

几点说明:

 elif 可以有任意多个(0 个或多个); else 最多只能有一个(0 个或 1 个); if 语句必须以 fi 表示结束

 expr 通常为条件测试表达式;也可以是多个命令,以分号分隔,以最后一个命令的退出状态为条件值。

 commands 为可执行语句块,如果为空,需使用 shell 提供的空命令 “ : ”,即冒号。该命令不做任何事情,只返回一个退出状态 0

 if 语句可以嵌套使用。

(二)case 选择语句

case expr in # expr为表达式,关键词in不要忘!

pattern1) # 若expr与pattern1匹配,注意括号

   commands1 #  执行语句块commands1

   ;;        # 跳出case结构

pattern2) # 若expr与pattern2匹配

   commands2 #执行语句块commands2

   ;;        # 跳出case结构 ...... # 可以有任意多个模式匹配

*)        # 若expr与上面的模式都不匹配

   commands  # 执行语句块commands

   ;;        # 跳出case结构 esac # case语句必须以esac终止

几点说明:

表达式 expr 按顺序匹配每个模式,一旦有一个模式匹配成功,则执行该模式后面的所有命令,然后退出 case。

 如果 expr 没有找到匹配的模式,则执行缺省值 “ *) ” 后面的命令块 ( 类似于 if  中的 else ); “ *) ” 可以不出现。

 所给的匹配模式 pattern 中可以含有通配符和“ | ”。

 每个命令块的最后必须有一个双分号,可以独占一行,或放在最后一个命令的后面。

八、循环语句

(一)for 循环

for variable  in  list

# 每一次循环,依次把列表list中的一个值赋给循环变量

do          #循环开始的标志

  commands  #循环变量每取一次值,循环体就执行一遍

done        #循环结束的标志

几点说明:

列表 list 可以是命令替换、变量名替换、字符串和文件名列表 ( 可包含通配符 )

list 里面的分隔符可以是空格、换行等等。

 for 循环执行的次数取决于列表 list 中单词的个数

 for 循环体中一般要出现循环变量,但也可以不出现

可以省略  in list ,此时使用“$@”

for ((exp1;exp2;exp3)  //类似于C语言的for 循环 do done

LIST="rootfs usr data data2" for d in $LIST; do  done

for d in seq_* ; do done

for i in ${arr[@]} ; do  done

for k in $( seq 1 $count ); do done

for file in $( ls $dir); do done

(二)while 和until循环

while expr  #执行expr

do #若expr的退出状态为0,进入循环,否则退出while

  commands  #循环体

done        #循环结束标志,返回循环顶部

eg.

#使用read命令读取一行数据   cat datafile.txt | while read myline   do        echo "LINE:"$myline   done  

while read name cn_name risk  #字段直接tab 分割,每行 \n 结束

do

    echo $name, $cn_name, $risk

done < $filename

read 命令默认的分隔符是空白符(如空格,tab等),我们也可以使用IFS(内部字段分隔符)指定的的字符作为分隔符;

需要注意的是多个空白被当做一个空白处理,当某一行中某个字段不存在,这样会导致读到的数据对应不到正确的变量

用 perl or python 读取一行数据时,需要去掉换行符,perl 用 chomp,python 用 strip('\n')

until expr  #执行expr

do #若expr的退出状态非0,进入循环,否则退出until

  commands  #循环体

done        #循环结束标志,返回循环顶部

(三)break和continue、exit和sleep

break  [n]

用于强行退出当前循环。

 如果是嵌套循环,则 break 命令后面可以跟一数字 n,表示退出第 n 重循环(最里面的为第一重循环)。

continue [n]

 用于忽略本次循环的剩余部分,回到循环的顶部,继续下一次循环。  如果是嵌套循环,continue 命令后面也可跟一数字 n,表示回到第 n 重循环的顶部。

exit n

exit 命令用于退出脚本或当前进程。n 是一个从 0 到 255 的整数,0 表示成功退出,非零表示遇到某种失败而非正常退出。该整数被保存在状态变量 $? 中。

sleep n  暂停 n 秒钟

(四)select循环和菜单

select variable in  list

do          #循环开始的标志

  commands  #循环变量每取一次值,循环体就执行一遍

done        #循环结束的标志

 select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示 PS3 提示符,等待用户输入

 用户输入菜单列表中的某个数字,执行相应的命令

 用户输入被保存在内置变量 REPLY 中。

 select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环。

 select 经常和 case 联合使用

 与 for 循环类似,可以省略 in list ,此时使用位置参量

九、函数

 一个函数就是一个子程序,用于完成特定的任务,当有重复代码,或者一个任务只需要很少的修改就被重复几次执行时, 这时你应考虑使用函数。

local X   //  X 是局部变量

function function_name {

   commands

}

function_name() {

   commands

}

只需输入函数名即可调用该函数

 函数必须在调用之前定义

如果要调用其他文件的函数,可以在开头  . 文件名  //类似文件包含,也可以使用source。

这两个命令都以一个脚本为参数,该脚本将作为当前shell的环境执行,即不会启动一个新的子进程。所有在脚本中设置的变量将成为当前Shell的一部

分。同样的,当前脚本中设置的变量也将作为脚本的环境。

sh -x xx.sh 是在一个脚本中,调用另一个脚本执行,启动一个新的子进程,-x 会输出所有的执行信息。

脚本调用脚本,要对被调用脚本的执行返回值进行判断。参数同理,需要对脚本计算的参数进行合理判断,提前发现错误,避免走入不可控制的分支。

#!/bin/bash

fun2()

   echo "This is fun2."

   echo "Now exitingfun2."

}

fun2 #调用函数fun2

#! /bin/bash

func1()

{

        echo "the parameter's count:$#"

        echo "the firstparameter:$1"

        echo "the secondparameter:$2"

}

func1 a b

(一)字符串操作:

m 的取值从0 到${#var}-1

注:pattern,old中可以使用通配符。

${#var}

返回字符串变量 var的长度

${var:m}

返回${var}中从第m+1个字符到最后的部分

${var:m:len}

返回${var}中从第m+1个字符开始,长度为len的部分

${var#pattern}

删除${var}中开头部分与pattern匹配的最小部分

${var##pattern}

删除${var}中开头部分与pattern匹配的最大部分

${var%pattern}

删除${var}中结尾部分与pattern匹配的最小部分

${var%%pattern}

删除${var}中结尾部分与pattern匹配的最大部分

${var/old/new}

用new替换${var}中第一次出现的old

${var//old/new}

用new替换${var}中所有的old(全局替换)

字符串拼接:

value1=home

value2=${value1}"="

value3=${value1}${value2}

(二)随机数和 expr 命令

echo $RANDOM  // 生成随机数的特殊变量

expr:通用的表达式计算命令

表达式中参数与操作符必须以空格分开,表达式中的运算可以是算术运算,比较运算,字符串运算和逻辑运算。

expr 5 % 3

expr 5 \* 3 #乘法符号必须被转义

注意:目前比较少使用,可用$[ ... ]替换

(三)shift 命令

一般用于函数或者脚本程序参数处理,特别是参数多于10以上的时候 将所有参数变量向下移动一个位置,$2变成$1,$3变成$2,依次递进,但$0保持不变

例如:

while [ "$1" != "" ]

do

    echo $1

    shift

done

(四)eval 命令

eval arg1 [arg2] ... [argN]

将所有的参数连接成一个表达式,并计算或执行该表达式,参数中的任何变量都将被展开。

listpage="ls -l | more"

eval $listpage

(五)trap命令

trap command signal

command 一般情况下是Linux命令 ’ ’表示发生陷阱时为空指令,不做任何动作 ’-’表示发生陷阱时采用缺省指令 signal HUP(1) 挂起;一般因终端掉线或用户退出而引发 INT(2) 中断;一般因按下”Ctrl+C”组合键引发 QUIT(3) 退出;一般因按下”Ctrl+\”组合键引发 ABRT(6) 异常中止;一般因某些严重的执行错误而引发 ALRM(14) 闹钟;一般是超时时钟到来而引发 TREM(15) 中止;一般由系统在关机的时候发出

#!/bin/bash

#安装2、3号信号

#处理代码为"rm-ftmp$$; exit0"

trap "rm -ftmp$$; exit 0"  2 3

#生成文件,文件名为tmp+当前进程号

touch tmp$$

#睡眠60秒,以便向当前进程发送信号

sleep 60

(六)declare 命令

declare或typeset内建命令(它们是完全相同的)可以用来限定变量的属性.这是在某些编程语言中使用的定义类型不严格的方式。命令declare是bash版本2之后才有的。命令typeset也可以在ksh脚本中运行。

-r只读

declare -r var1

-i整数

declare -i number  # 脚本余下的部分会把"number"当作整数看待.

-a数组

declare -a indices

-f函数

declare -f

-x export

declare -x var3

-x var=$value

declare -x var3=373

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C/C++基础

shell编程知识点集锦

shell脚本是按行分隔每一条shell语句。如果每一条shell语句写在单独一行,此时可以加分号,也可以不加,没有什么区别。如果多条shell写在同一行,那么...

741
来自专栏决胜机器学习

Shell基本操作与命令

Shell基本操作与命令 (原创内容,转载请注明来源,谢谢) 本文主要是我最近学习shell语言的学习笔记,主要在于通过学习这些内容,达到看得懂shell脚...

3155
来自专栏挖掘大数据

Spark整合Mongodb(附实例代码)

环境准备 mongodb下载 解压安装 启动mongodb服务 $MONGODB_HOME/bin/mongod --fork --dbpath=/root/...

3060
来自专栏白驹过隙

Redis - string类型操作

3156
来自专栏张俊红

零基础学习爬虫并实战

总第63篇 本篇主要从爬虫是什么、爬虫的一般流程、爬虫各个流程的实现方法、爬虫实例四个方面分享零基础了解爬虫,并进行简单的实战。 在阅读下面之前,我们...

2.4K10
来自专栏C/C++基础

模板与分离编译模式

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件连接起来形成单一的可执行文件的过程成为分离编译模式。

702
来自专栏Python小屋

Python编程一定要注意的那些“坑”(八):赋值运算符=

原始问题:下面的代码执行后为什么x的值是[2, 2]呢? >>> x = [3, 5, 7] >>> x = x[1:] = [2] >>> x [2, 2]...

2716
来自专栏专注 Java 基础分享

Struts2框架的基本使用(二)

     上一篇 Struts2框架的基本使用 我们限于篇幅,最后简单介绍了Action的配置问题,本篇接着介绍有关框架的一些其他基本用法,主要内容如下: Ac...

20010
来自专栏何俊林

JNI开发中,你需要知道的一些建议

本文原文是:http://developer.android.com/training/articles/perf-jni.html,翻译费了我不少功夫,但是我...

813
来自专栏架构师小秘圈

shell极简教程(二)

一,题记 不懂shell的程序员不是好程序员,学习shell是为了自动化,使用自动化可以非常有效的提高工作效率。没有一个大公司不要求linux的基本技能的,只是...

3667

扫码关注云+社区