【Linux学习】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} stringpositionstring中,从位置position之后开始提取长度为length的子串
4 ${string#substring} 从变量stringstring开头删除最短匹配substring子串
5 ${string##substring} 从变量stringstring开头删除最长匹配substring子串
6 ${string%substring} 从变量stringstring结尾删除最短匹配substring子串
7 ${string%%substring} 从变量stringstring结尾删除最长匹配substring子串
8 ${string/substring/replace} 使用replacereplace来代替第一个匹配的substring
9 ${string//substring/replace} 使用replacereplace来代替所有匹配的substring
10 ${string/#substring/replace} 如果stringstring前缀匹配substring,就用replacereplace来代替匹配的字符串substring
11 ${string/%substring/replace} 如果stringstring后缀匹配substring,就用replacereplace来代替匹配的字符串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
复制代码

 

posted @ 2020-04-09 18:27  gtea  阅读(747)  评论(0编辑  收藏  举报