Shell高级编程学习笔记(基础篇)

目录

  1.shell脚本的执行方法   2.shell的变量类型   3.shell特殊变量


4.变量子串的常用操作   5.批量修改文件名实践    6.变量替换


7.在shell中计算字符串长度的方法    8.变量的数值计算:(()) 的用法


9. 变量的数值计算:let的用法    10.变量的数值计算:expr的用法


11.变量的数值计算:bc的用法      12.条件测试的多种方法


13.字符串测试操作符          14.整数二元比较操作符


15.逻辑操作符              16.利用shell知识制作单级及多级菜单


17.case结构条件语句       18. 当型循环和直到型循环


19.for循环

 

 

1.shell脚本的执行方法 

当shell脚本以非交互的方式运行时,它会先查找环境变量ENV,该变量指定了一个环境文件(通常是.bashrc),然后从该环境变量文件开始执行,当读取了ENV文件后,SHELL才开始执行shell脚本中的内容。

shell脚本的执行通常可以采用以下三种方法:

  1. bash script-name 或者 sh script-name
  2. path/script-name 或者 ./script-name(当前路径下执行)
  3. source script-name 或 . script-name(注意符号“.”与 script-name之间有空格)

第一种方法是脚本文件本身没有可执行权限时,通常使用的方法;

第二种方法需要先将脚本文件的权限改为可执行,然后通过脚本路径就可以直接执行;

第三中方法是在当前shell环境中直接执行,而不是新创建一个子shell,当我们需要在一个脚本中使用另一个脚本中的变量的时候就使用第三种方法,例如创建如下脚本,其中定义了chars变量:

 

通过上面三种方法执行:

 

可以看出方法1,2执行脚本是系统会自动新创建一个子shell,执行之后,其变量不会在当前shell有效,而第三种方法是告诉系统让这个脚本直接在当前shell执行,显然其定义的变量chars在当前shell中也是有效的,所以需要在某个脚本中使用另外一个脚本中的变量时,需要使用第3中方法执行脚本。

2.shell的变量类型 

shell中的变量分为环境变量和全局变量

  环境变量也称为全局变量,可以在创建他们的shell及其派生的任意子进程shell中使用。局部变量只能使用在创建它们的shell函数或脚本中使用。

  环境变量用于定义Shell的运行环境,保证shell命令的正确执行,shell通过环境变量来确定登录用户名,命令路径,终端类型,登录目录。环境变量可以在命令行中设置,但用户退出时这些变量值也会丢失,因此最好在用户家目录下的.bash_ptofile文件会全局配置/etc/profile文件或者/etc/profile.d中定义,将环境变量放入profile中,每次用户登录,这些变量值都将被初始化。

  传统上,所有环境变量均为大写。环境变量用于用户进程前,必须用export命令导出。

  有一些环境变量,比如HOME,PATH,SHELL,UID,USER等在用户登录之前就已经被/bin/login程序设置了,通常环境变量的定义并保存在用户的家目录下的./bash_profile文件中。

  已经定义的变量可以用unset在取消定义。

本地变量定义的三种方式:

var_name=value

var_name='value'

var_name="value"

下面看看这三种方式的区别

 

第一种方法是直接定义变量内容,但是内容必须连续,中间不能有空格字符;

接着看下面:

 

第二种方法通过单引号定义变量,只会原样输出字符串内容,而不会对字符串中的变量和特殊字符进行解析,适合定义单纯字符显示;

第一种和第三种,输出时,会对内容进行解析,输出解析后的内容,有于第一种方法不能定义不连续的变量,所以通常在需要解析的时候都加上双引号,而很少使用第一种方法,第一种方法一般仅仅用来定义连续的数字或者路径。

3.shell的特殊变量 

  位置变量

$0 $0 获取当前执行脚本的文件名,包括路径;

$n $n,获取当前执行的shell脚本的第n个参数,n=1,...,9,如果 n 大于9, 使用大括号${10};

$ $∗ 获取当前shell的所有参数,将命令行参数视为单个字符串,相当于 "$1$2$3...$n" "$1$2$3...$n"

$ $# 获取当前shell脚本或者命令行参数的总个数;

$ $@ 这个程序的所有参数  "$1""$2""$3""...", "$1""$2""$3""...",这是将参数传递给其他程序的最佳方式,因为它会保留所有内嵌在每个参数里的任何空白。

  进程状态变量

$$ $$ 获取当前shell的进程号(PID)

$ $? 获取上一个指令的返回值(0 为成功,非零为失败)

4.变量子串的常用操作 

常用操作
编号 表达式 说明
1 ${#string} 返回$string的长度
2 ${string:position} 在$string中,从位置position之后开始提取子串
3 ${string:position:length} stringposition string中,从位置position之后开始提取长度为length的子串
4 ${string#substring} 从变量 string string开头删除最短匹配substring子串
5 ${string##substring} 从变量 string string开头删除最长匹配substring子串
6 ${string%substring} 从变量 string string结尾删除最短匹配substring子串
7 ${string%%substring} 从变量 string string结尾删除最长匹配substring子串
8 ${string/substring/replace} 使用 replace replace来代替第一个匹配的substring
9 ${string//substring/replace} 使用 replace replace来代替所有匹配的substring
10 ${string/#substring/replace} 如果 string string前缀匹配substring,就用 replace replace来代替匹配的字符串substring
11 ${string/%substring/replace} 如果 string string后缀匹配substring,就用 replace replace来代替匹配的字符串substring

 

举例说明如下:

 

5.批量修改文件名实践 

例如有下面的文件:

 

现在想要将后缀前部改为大写JPG:

方法一:

#!/bin/bash
for obj in $(ls *.jpg)
do
    mv ${obj} $(echo ${obj/%jpg/JPG})
done

方法二:

rename 's/jpg$/JPG/' *.jpg

 

 

6.变量替换 

运算符号 作用
${value:-word}

如果变量名存在且非null,返回变量值;否则,返回word字符串

用途:如果变量未定义,则返回默认值

${value:=word}

如果变量名存在且非null,返回变量值;否则,设置这个变量值为word

用途:如果变量未定义,,则设置变量为默认值,并返回默认值

${value:?"not defined"}

如果变量名存在且非null,返回变量值;否则显示“not defined”,并退出当前命令或脚本。

用途:用于捕捉由于变量未定义而导致的错误,并退出程序。

${value:+word}

如果变量名存在且非null,返回word,否则返回null。

用途:测试变量是否存在。

示例如下:

 

 

7.在shell中计算字符串长度的方法 

建立一个字符串:

注意:使用 wc -m 的方法会计算换行符,因为echo会在输出完字符串之后自动加上一个换行符,使用echo -n 会去掉换行符:

 

比较上面三种方法的效率:

 

time :

获取一个程序的执行时间,可以获取到实际运行时间以及程序在用户态和内核态分别的时间,大部分的性能测试,可能只需要关注实际(real)时间

看到上面三种获取字符串长度的速度相差几十倍到上百倍,一般调用外部命令处理,与内置功能性能相差很大。所以在shell编程,尽量使用内置操作或函数完成。 

 8.变量的数值计算:(())的用法 

(1) (())用法:(此法很常用,且效率高)

用于执行的整数运算,支持的运算符与C语言中的基本一致,除了特别注意 "**" 在 (()) 中表示幂运算,而C语言不支持,如 ((2**3)) 结果是 8.

 示例:

 

注意:1)**表示幂运算

   2)上面涉及的变量元素必须为整型,不能是小数和字符,涉及小数的后面使用 bc 可以实现。

 (()) 内部的变量可以不用加  $ $ 符号

 各种shell运算的脚本例子:

 实践:用shell脚本编写一个实现加,减,乘,除的计算器:

9. 变量的数值计算:let的用法 

与 ((表达式)) 功能一致,但是没有 (()) 的计算效率高,所以常用 (()),而不去使用let.

 

10.变量的数值计算:expr的用法

expr命令一般用于整数计算,但也可用于字符串,用来求表达式变量的值,同时expr也是一个手工命令计算器。

 

expr 的语法不是很好,所以不常用,这里标记,保证看到别人使用的时候,自己能够看懂。

下面介绍expr比较好用两个技巧

  1)检查某个文件是否是某一类型或扩展名:

  

  2)判断一个变量是否是整数:

  

  写出判断一个输入是否为整数的脚本:

  

  

 11.变量的数值计算:bc的用法 

bc支持科学计算

  

 

  使用shell编写输出杨辉三角的脚本:

  

复制代码
#!/bin/bash

export LANG="zh_CN.GB18030"

#本文内容来自《老男孩linux运维实战培训中心》shell 编程部分学生练习资料

#如果脚本后面没有参数,提示输入一个参数
if ( test -z $1 );then
    read -p "Input max int lines:" MAX 
else
    MAX=$1
fi

#判断参数的合法性
#将参数中的数字全部替换为空,然后判断替换后的内容是否为空
#若不为空,表明参数中含有非数字的其他非数字内容,则不合法
#“[ ]”的功能与test一致,可以 man test 查看
[ -n "$(echo ${MAX}|sed 's/[0-9]//g')" ] && \
echo "The number you input must be int(1-9)" && exit 1

#将输入限制在小于10
[ ! ${MAX} -lt 10 ] && \
echo "The number you input must be int(1-9)" && exit 1

#start
a[0]=1
for ((i=0;i<MAX;i++))
do
    for ((j=$i;j>0;j--))
    do
        ((a[$j]+=a[$j-1]))
    done

    for ((j=0;j<=MAX-i;j++))
    do
        if ((MAX<=6));then
            echo -en "\t"
        else
            echo -n "    "
        fi
    done

    for ((j=0;j<=i;j++))
    do
        if [ ${MAX} -le 6 ];then
            echo -en "\t\t"${a[$j]}
        else
            echo -en ${a[$j]}
        fi
    done
echo
done
复制代码

 

运行结果:

 12.条件测试的多种方法 

语法格式:

格式1:test expression 

格式2:[ expression ]

格式3:[[ 测试表达式 ]]

注:中括号与表达式之间前后都有空格

格式1与格式2是等价的;格式3为扩展的test命令,在 [[  ]] 中可以使用通配符进行模式匹配,如&&,||,>, <等操作符可以应用于[[ ]]中,但不能应用于[ ]中;对整数的关系运算,也可以使用Shell的算术运算符 (()).

关于各种测试操作符只需要在终端man test 即可得到详细描述:

常用的文件测试操作符号 说明
-f file 若文件存在且为普通文件则真
-d file 若文件存在且为目录则真
-s file 若文件存在且不为空(文件大小非0)则真
-e file   若文件存在则真,区别于-f
-r file 若文件存在且可读则真
-w file   若文件存在且可写则真
-x file 若文件存在且可执行则真
-L file   若文件存在且为链接文件则真
f1 -nt f2   若文件f1 比文件 f2 新则真
f1 -ot f2 若文件f1 比文件 f2 旧则真

13.字符串测试操作符 

  字符串测试操作符的作用:比较两个字符串是否相同、字符串长度是否为0,字符串是否为null等

  “=”比较两个字符串是否相同,与 “==”等价,如 if [ " $a $a"=" $b $b" ],其中  $a $a 这样的变量最好用双引号括起来。

字符串测试操作符
常用字符串测试操作符 说明
-z "string" 若string长度为0,则真
-n "string" 若string长度步为0,则真
“string1”= ”string2“ 若string1等于string2则真,可以使用“==”代替“=”
“string1”!= ”string2“ 若string1不等于string2则真

 

  注意:测试符“==”,“=”,“!=”好前后均需与测试字符串之间留空格!!!

14.整数二元比较操作符 

 

在[  ]中使用的比较符 在(())和[[]]中使用的比较符 说明
-eq ==   相等
-ne != 不等
-gt > 大于
-ge >= 大于或等于
-lt < 小于
-le   <= 小于或等于

 

15.逻辑操作符 

在[]中的使用 在[[]]中的使用 说明
-a && 与,两端均为真则真
-o || 或,两端有一个为真则真
! ! 非,相反则真

16.利用shell知识制作单级及多级菜单 

 

 17.case结构条件语句 

 

语法:

case "字符串变量" in

  值1)指令1...

;;

  值2)指令2...

;;

  *)指令

;;

esca

 给字符串显示不同的颜色:

shell脚本中echo可以显示不同颜色的字符,格式如下:

  echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"
  例如:
  echo -e "\033[41;36m something here \033[0m"
  其中41的位置代表底色, 36的位置是代表字的颜色
  1、字背景颜色和文字颜色之间是英文的";"
  2、文字颜色后面有个m
  3、字符串前后可以没有空格,如果有的话,输出也是同样有空格

下面利用case来该给定的字符串加上不同的颜色:

复制代码
 1 #!/bin/bash
 2 #Paint given chars with given color
 3 usage="Usage:chars {black|red|green|yellow|blue|purple|white}"
 4  [[ $# -ne 2 ]] &&{ 
 5      echo "${usage}" 
 6      exit 1
 7 }
 8 BLACK="\033[1;30m"; RED="\033[1;31m" ;GREEN="\033[1;32m"; YELLOW="\033[1;33m"
 9 BLUE="\033[1;34m"; PRUPLE="\033[1;35m"; WHITE="\033[1;37m"
10 COLOR_END="\033[0m"
11 
12 chars=$1
13 selected_color=$2
14 
15 case ${selected_color} in
16     black) res=${BLACK}
17 ;;
18     red) res=${RED}
19 ;;
20     green) res=${GREEN}
21 ;;
22     yellow) res=${YELLOW}
23 ;;
24     blue) res=${BLUE}
25 ;;
26     purple) res=${PURPLE}
27 ;;
28     white) res=${WHITE}
29 ;;
30     *) echo 'Input color is not supported'
31        echo ${usage}
32        exit 1
33 ;;
34 esac
35 
36 echo -e "${res}"$1"${COLOR_END}"
复制代码

18. 当型循环和直到型循环  

 

while条件语句,条件满足一直执行command

while condition
    do
    command
done

 

until语法,条件满足就退出,目前很少用,作为了解

until condition
    do
    command
done

 

复制代码
#!/bin/bash
i=10
while (($i>=1))
do
    echo $i
    ((i--))
done
复制代码

 

使用until实现上面同样的功能:

复制代码
#!/bin/bash

i=10
until [ $i -eq 0 ] 
do
    echo $i
    (( i-- ))
done
复制代码

 在实践中经常需要使用while来处理日志文件,下面实际中使用while读取文本的方法:

复制代码
#1
exec <FILE
while read line
do
    cmd
done

#2
cat ${FILE} | while read line
do
    cmd
done

#3
while read line
do
    cmd
done<FILE
复制代码

 

19.for循环 

 

for循环语法结构1:

复制代码
for var in var_list
do
    cmd1
    cmd2
    ...
done
复制代码

 

在此结构中, “in var_list”可以省略,省略时,相当于in " @",foriforiin" @",例如fori就相当于foriin"@".

for循环语法结构2:

复制代码
for ((exp1;exp2;exp3)) #与C语言一样
do
    cmd1
    cmd2
    ...
done
复制代码
#!/bin/bash
for i in 10 9 8 7 6 
do
    echo $i
done

下面代码实现同样的功能:

#!/bin/bash
for i in {10..6}
do
    echo $i
done

 

#!/bin/bash
for i in $(seq -s " " 10 -1 6)
do
    echo $i
done

 

获取当前目录下的所有文件名,打印

#!/bin/bash
for i in $(ls) 
do
    echo $i
done

 

打印9*9乘法表:

复制代码
 1 #!/bin/bash
 2 for a in $( seq 1 9 )
 3 do
 4     for b in $( seq 1 9 )
 5     do  
 6         if [ $a -ge $b ];then
 7             echo -en "$a x $b = $((a*b))  "
 8         fi  
 9     done
10 echo " "
11 done
12 
13 echo
复制代码

 

转载:http://www.cnblogs.com/90zeng/ 作者:博客园-90Zeng
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Shell脚本高级编程教程,希望对你有所帮助。 Example 10-23. Using continue N in an actual task: 1 # Albert Reiner gives an example of how to use "continue N": 2 # --------------------------------------------------------- 3 4 # Suppose I have a large number of jobs that need to be run, with 5 #+ any data that is to be treated in files of a given name pattern in a 6 #+ directory. There are several machines that access this directory, and 7 #+ I want to distribute the work over these different boxen. Then I 8 #+ usually nohup something like the following on every box: 9 10 while true 11 do 12 for n in .iso.* 13 do 14 [ "$n" = ".iso.opts" ] && continue 15 beta=${n#.iso.} 16 [ -r .Iso.$beta ] && continue 17 [ -r .lock.$beta ] && sleep 10 && continue 18 lockfile -r0 .lock.$beta || continue 19 echo -n "$beta: " `date` 20 run-isotherm $beta 21 date 22 ls -alF .Iso.$beta 23 [ -r .Iso.$beta ] && rm -f .lock.$beta 24 continue 2 25 done 26 break 27 done 28 29 # The details, in particular the sleep N, are particular to my 30 #+ application, but the general pattern is: 31 32 while true 33 do 34 for job in {pattern} 35 do 36 {job already done or running} && continue 37 {mark job as running, do job, mark job as done} 38 continue 2 39 done 40 break # Or something like `sleep 600' to avoid termination. 41 done 42 43 # This way the script will stop only when there are no more jobs to do 44 #+ (including jobs that were added during runtime). Through the use 45 #+ of appropriate lockfiles it can be run on several machines 46 #+ concurrently without duplication of calculations [which run a couple 47 #+ of hours in my case, so I really want to avoid this]. Also, as search 48 #+ always starts again from the beginning, one can encode priorities in 49 #+ the file names. Of course, one could also do this without `continue 2', 50 #+ but then one would have to actually check whether or not some job 51 #+ was done (so that we should immediately look for the next job) or not 52 #+ (in which case we terminate or sleep for a long time before checking 53 #+ for a new job).

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值