linux shell编程

Posted 野生java研究僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux shell编程相关的知识,希望对你有一定的参考价值。

linux shell编程

1.shell脚本概述

hell 是一种脚本语言
脚本:本质是一个文件,文件里面存放的是 特定格式的指令,系统可以使用脚本解析器 翻译或解析 指令 并执行(它不需要编译)
shell 既是应用程序 又是一种脚本语言(应用程序 解析 脚本语言)
shell命令解析器:
系统提供 shell命令解析器: sh ash bash
查看自己linux系统的默认解析:echo $SHELL

2.脚本的调用形式

打开终端时系统自动调用:/etc/profile 或 ~/.bashrc

/etc/profile
此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行,系统的公共环境变量在这里设置
开机自启动的程序,一般也在这里设置
~/.bashrc
用户自己的家目录中的.bashrc
登录时会自动调用,打开任意终端时也会自动调用
这个文件一般设置与个人用户有关的环境变量,如交叉编译器的路径等等
用户手动调用:用户实现的脚本

3.shell脚本语法

3.1 脚本开头标识

定义以开头:#!/bin/bash ,#!用来声明脚本由什么shell解释,否则使用默认shell , shell脚本文件名默认设置为 sh,用于标识这是一个脚本

#!/bin/bash

3.2 脚本注释

单个"#"号代表注释当前行

3.3 给脚本加上可执行权限

chmod 774 shell.sh

3.4 shell脚本的运行方式

三种执行方式 (./xxx.sh bash xxx.sh . xxx.sh)

三种执行方式的不同点(./xxx.sh bash xxx.sh . xxx.sh)
./xxx.sh :先按照 文件中#!指定的解析器解析

如果#!指定指定的解析器不存在 才会使用系统默认的解析器
bash xxx.sh:指明先用bash解析器解析

如果bash不存在 才会使用默认解析器
. xxx.sh 直接使用默认解析器解析(不会执行第一行的#!指定的解析器)但是第一行还是要写的

三种执行情况:
打开终端就会有以后个解释器,我们称为当前解释器
我们指定解析器的时候(使用 ./xxx.sh 或 bash xxx.sh)时会创建一个子shell解析 脚本

3.5 检测脚本是否正确

bash -n shellName.sh (不在当前目录下加绝对路径)   检查语法错误
bash -x shellName.sh (不在当前目录下加绝对路径)   检查逻辑错误

3.6 重定向的使用

类型操作符用途
重定向输入<从指定文件读取数据
重定向输出>将标准输出结果保存到指定的文件,并且覆盖原有文件
>>将标准输出追加到指定的文件的尾部,不覆盖原有内容
标准错误输出2>将错误信息保存到指定文件,并且覆盖原有文件
2>>将错误信息追加到指定文件的尾部,不覆盖原有内容
混合输出&>和2>&1将标准输出,标准错误保存到同—文件中

3.7 获取上一条命令的执行结果

$?

案例:

#!/bin/bash

test  "hello"
echo $? # $?= 0

test -z "admin"
echo $? # $?=1

4.变量

4.1 变量的定义和使用

定义变量: 变量名=变量值  
如:num=20
引用变量: $变量名
unset :清除变量值
只读变量: readonly 变量名=变量值

4.2 从键盘上读取变量:

Read可以带有-a, -d, -e, -n, -p, -r, -t, 和 -s八个选项。

-d :表示delimiter,即定界符,一般情况下是以IFS为参数的间隔,但是通过-d,我们可以定义一直读到出现执行的字符位置。例如read –d madfds value,读到有m的字符的时候就不在继续向后读,例如输入为 hello m,有效值为“hello”,请注意m前面的空格等会被删除。这种方式可以输入多个字符串,例如定义“.”作为结符号等等。
-e :只用于互相交互的脚本,它将readline用于收集输入行。读到这几句话不太明白什么意思,先跳过。
-n :用于限定最多可以有多少字符可以作为有效读入。例如echo –n 4 value1 value2,如果我们试图输入12 34,则只有前面有效的12 3,作为输入,实际上在你输入第4个字符‘3’后,就自动结束输入。这里结果是value为12,value2为3。
-p :用于给出提示符,在前面的例子中我们使用了echo –n “…“来给出提示符,可以使用read –p ‘… my promt?’value的方式只需一个语句来表示。
-r :在参数输入中,我们可以使用’/’表示没有输入完,换行继续输入,如果我们需要行最后的’/’作为有效的字符,可以通过-r来进行。此外在输入字符中,我们希望/n这类特殊字符生效,也应采用-r选项。
-s :对于一些特殊的符号,例如箭头号,不将他们在terminal上打印,例如read –s key,我们按光标,在回车之后,如果我们要求显示,即echo,光标向上,如果不使用-s,在输入的时候,输入处显示^[[A,即在terminal上 打印,之后如果要求echo,光标会上移。
-t :用于表示等待输入的时间,单位为秒,等待时间超过,将继续执行后面的脚本,注意不作为null输入,参数将保留原有的值

read -p "请输入number的值" number
echo "$number"

4.3 读取多个变量的值:

#!/bin/bash

read height width
echo "height=$height"
echo "width=$width"

4.4 查看环境变量:

env

导出为环境变量:让其他shell脚本识别该变量,设为全局变量

source 脚本文件
source命令用法:
source FileName
作用:在当前bash环境下读取并执行FileName中的命令。
注:该命令通常用命令“.”来替代。
如:source .bash_rc 与 . .bash_rc 是等效的。
注意:source命令与shell scripts的区别是,
source在当前bash环境下执行命令,而scripts是启动一个子shell来执行命令。这样如果把设置环境变量(或alias等等)的命令写进scripts中,就只会影响子shell,无法改变当前的BASH,所以通过文件(命令列)设置环境变量时,要用source 命令。

4.5 注意事项

  • 变量名只能包含英文字母下划线,不能以数字开头
    • 1_num=10 错误
    • num_1=20 正确
  • 等号两边不能直接接空格符,若变量中本身就包含了空格,则整个字符串都要用双引号、或单引号括起来
  • 双引号 单引号的区别
  • 双引号:可以解析变量的值
  • 单引号:不能解析变量的值

如果想在PATH变量中 追加一个路径写法如下:(重要!!!!)

export PATH=$PATH:/需要添加的路径

4.6 判断变量是否存在

$变量名:变量值 如果变量名存在,则为该变量名的值,否则为变量值

案例: 这种方式判断只支持数字 包含小数
echo $val:100

4.7 字符串操作

1.测量字符串长度

 使用方式: $#字符串或字符串变量
 str='admin'
 echo $#str # 输出5

2.字符串截取

1.从指定位置一直截取到末尾 索引从0开始
使用方式: $字符串变量去掉$符号:截取的索引
例如: 
#!/bin/bash
str='admin'
echo $str:2 # 最终输出 min

2.从指定位置截取n位字符
使用方式: $字符串变量去掉$符号:截取的索引:截取的长度
例如: 
#!/bin/bash
str='admin'
echo $str:2:2 # 最终输出 mi

3.字符串替换 用新的字符串替换遇到的第一个旧字符串
使用方式:$原始字符串/需要替换的字符串/待替换的新字符串
#!/bin/bash
str='hello admin'
echo $str/"admin"/"root" # 最终输出 hello admin

4.字符串替换 用新的字符串替换遇到的蓑鲉旧字符串
使用方式:$原始字符串//需要替换的字符串/待替换的新字符串
#!/bin/bash
str='hello admin,hou are you admin'
echo $str//"admin"/"root" # 最终输出 hello admin

5.使用# 至左向右截取
案例:
#!/bin/bash
str='http://baidu.com/index.htm'
echo $str#*// # 输出 /baidu.com/index.htm 删除遇到的#*的字符,并且从左到右截取到最后

6.##号截取(自左向右)
案例:
#!/bin/bash
str='http://baidu.com/index.htm'
echo $str##*/ 输出: index.html #  ##*/ 表示从左边开始,删除##*后匹配的最后一个字符及其后面的字符

7.%号截取(自右向左)
案例:
#!/bin/bash
str='http://baidu.com/index.htm' # 输出 http://baidu.com 删除 %匹配的字符以及匹配字符串后面的字符
echo $str%/*

8.%%号截取(自右向左)
 #!/bin/bash
str='http://baidu.com/index.htm'
echo $str%%/* # 输出:http: 删除第一个匹配的字符及右边的所有字符




4.8 数组

​ 我们可以用一个单一的阵列来存储所有上述提及的名称。以下是最简单的方法创建一个数组变量分配一个值,其索引之一。

# 1. 数组的初始化
#!/bin/bash
array=("value1" "value2")
echo $array[*]

# 2. 数组的赋值 
array_name[index]=value

# 3. 数组的取值
array_name[index]
# 4. 特殊取值 
 		array_name[*] # 取整个数组的全部value 
 		array_name[@] # 取整个数组的全部value 
 		array_name[0] # 获取数组的第一个元素
 		array_name[-1] # 获取数组的最后一个元素
# 5.获取数组的长度 
 #方式1: $#array[*]
 #方式2: $#array[@]

# 6.删除数组和删除数组元素
unset array_name[index]			#删除索引数组的第三个元素
unset array_name[key]	        #删除关联数组中索引为key的元素
unset array_name			   #删除数组

# 7.遍历数组

# 直接取值
for i in "$array_name[@]"
do
	echo $i
done

#C语言风格遍历

for((i=0;i<$#array_name[@];i++))
do
	echo $array_name[i]
done

# 8.字符串以冒号分隔存入数组
string="12:34:56"
array=($string/:/ )

# 9.$*和$@的区别
  # 1. 当直接通过echo获取数组所有元素时,它们是一样的效果
  # 2. $array_name[*]会将数组元素视为一个整体
  # 3. $array_name[*]会将数组元素拆分为单独的个体 

# 10.打印数组的下标值
echo $!array_name[@]

​ array_name 是数组名,索引是在阵列中,你要设置的项目索引,值是你想要的值设置该项目。

5.预设变量

预定义变量即Shell已经定义的变量,用户可根据Shell的定义直接使用这些变量,无需自己定义。所有预定义的变量都由$符和其他符号组成,常用的预定义变量如下所示。

(1)$#:表示命令行参数的个数。
(2)$@:包含所有的命令行参数,即“$1$2$3......”。
(3)$?:前一个命令的退出状态,正常退出返回0,反之为非0值。
(4)$*:包含所有的命令行参数,即“$1$2$3......”。
(5)$$:正在执行的进程的ID号。

脚本标量的特殊用法:

""[双引号:里面包含的变量会被解释]
''[单引号:包含的变量会当做字符串接收]
``[反引号:反引号的内容作为系统命令进行执行]
转义字符[\\t,\\n,\\r等..和c语言的一致]

6.if控制语句

6.0 shell 运算符

( )Change precedence
~1’s complement
!Logical negation
*Multiply
/Divide
%Modulo
+Add
-Subtract
<<Left shift
>>Right shift
==String comparison for equality
!=String comparison for non equality
=~Pattern matching
&Bitwise “and”
^Bitwise “exclusive or”
|Bitwise “inclusive or”
&&Logical “and”
||Logical “or”
++Increment
Decrement
=Assignment
*=Multiply left side by right side and update left side
/=Divide left side by right side and update left side
+=Add left side to right side and update left side
-=Subtract left side from right side and update left side
^=“Exclusive or” left side to right side and update left side
%=Divide left by right side and update left side with remainder

6.1 Shell if…fi语句

if [ expression ]
then
   Statement(s) to be executed if expression is true
fi

案例:

#!/bin/bash
# 当if条件成立的时候,then 到 fi 之间的语句才会被执行
if [ 1 = 1 ] 

  then
       echo "条件成立"  
	
fi

6.2 Shell if…else…fi 语句

语法:

if [ expression ]
then
   Statement(s) to be executed if expression is true
else
   Statement(s) to be executed if expression is not true
fi

案例:

#!/bin/bash
# 当if条件成立的时候,then 到 fi 之间的语句才会被执行,当条件不成立的时候执行 else 到 if 之间的语句
if [ 1 == 2 ] 
  then
       echo "条件成立"  
  else
      echo "条件不成立"	
fi

6.3 Shell if…elif…fi 语句

语法:

if [ expression 1 ]
then
  echo " Statement(s) to be executed if expression 1 is true"
elif [ expression 2 ]
then
   echo "Statement(s) to be executed if expression 2 is true"
elif [ expression 3 ]
then
   echo "Statement(s) to be executed if expression 3 is true"
else
   echo "Statement(s) to be executed if no expression is true"
fi

案例:

#!/bin/bash
number=$1
if [ $number == 10 ]
	then
  		echo " Statement(s) to be executed if expression 1 is true"
elif [ $number -gt 10 ]
	then
   		echo "Statement(s) to be executed if expression 2 is true"
elif [ $number -lt 10 ]
	then
   		echo "Statement(s) to be executed if expression 3 is true"
else
  		echo "Statement(s) to be executed if no expression is true"
fi

6.4 判断某个字符串中是否出现指定字串

#!/bin/bash
str='http://baidu.com/index.htm'
target="baidu"
if [[ $str =~ $target ]]
then
  echo "包含"
else
  echo "不包含"
fi

6.5 文件测试

-f : 判断文件是否为普通文件
-d :判断是否目录

-b :判断是都块设备文件
-c :判断是都字符设备文件
-S :判断是否socket文件
-p: 判断是否管道文件
-h: 判断是都为符号链接

-L: 文件存在且有符号链接
文件大小存在性:
-e : 判断文件或者目录是否存在
-s : 文件或者或者目录存在且大小大于0

文件读写特性
-r: 判断文件是否有可读权限
-w:判断文件是否具有可写权限
-x: 判断文件是否具有可执行的权限

-g:判断文件是否具有sgid位
-u:判断文件是否有suid位

-k:判断是否设置了粘滞位,文件设置粘滞位后会被写入缓存

文件修改时间:
file1 -nt file2 : 判断file1是否 比file2 新
file1 -ot file2 : 判断file1是否 比file2 旧

这里就不一个一个举例 写一个demo即可

#!/bin/bash

if [ -f $1  ]
 then 
     echo "$1 是文件"
 else
     echo "$1 是目录"
fi 

6.6 数值测试

含义英文单词shell比较符
相等equal-eq
不相等not equal-ne
大于greater than-gt
大于等于greater equal-ge
小于等于less equal-le
小于less than-lt

案例:猜数字游戏

#!/bin/bash
number=$1
if [ $number -gt 50  ]
	then 
		echo "猜大了"
elif [ $number -lt 50 ]
	then
		echo "猜小了"
else
		echo "恭喜你猜到了,幸运数字就是: 50"
fi

6.7 条件测试

命令执行控制:

command1 && command2 左边的命令执行成功才执行右边的命令
command1 || command2 左边的命令执不成功才执行右边的命令

案例:

test  1 == 1 && echo "ture"
test  1 == 2 || echo "ture"

6.8 多重条件判定

判定符解释
-a两个条件同时成立
-o只需要一个条件成即可
!取反,如果条件true,就返回false

案例:

test 1==1 -a 2 == 2 && echo "true"
test ! [  1==3 -a 2 == 2 ] && echo "true"

6.9 字符串测试

语法:

test expression
[ expression ]
运算符说明
string判断指定的字符串是否为空,不为空返回0
string1 = string2判断两个字符串是否相等,相等返回0
string1 != string2判断两个字符串是否不相等,不相等返回0
-n string判断string是否为非空串,非空返回0
-z string判断string是否为空串,空串返回0

7.算术运算

7.1 使用let命令

使用let命令可以执行一个或者多个算术表达式,其中的变量名无需使用$符号。如果表达式中含有空格或者其他特殊字符,需要引用起

#!/bin/bash
n=15
let n=n+1
echo "$n"

7.2 expr命令

在使用运算符时,需要对运算符进行转义,如果有括号,也需要对括号进行转义。不能使用紧凑格式,要使用松散格式,举例:

#!/bin/bash
result=`expr 2 + 100`
echo "$result"

7.3 $[expression]

#!/bin/bash
#采用紧凑格式
result=$[3+7]
echo "$result"
 
#采用松散格式
result=$[ 3 - 7 ]
echo "$result"

7.4 使用$((expression))

使用这种形式进行算术运算写法比较自由,无需对运算符和括号做转义处理,可以采用松散或者紧凑的格式来书写表达式。

#!/bin/bash
#采用紧凑格式
result=$((3+7))
echo "$result"
 
#采用松散格式
result=$(( 3 - 7 ))
echo "$result"

7.5 数字常量的进制

数字常量的进制

默认情况下,Shell总是以十进制来表示数字。但是用户也可以在Shell中使用其他进制来表示数字,比如二进制,八进制,十六进制。在Shell中,用户可以使用两种语法来表示不同的进制

1.增加前缀:0开头的数字表示0进制,0x开头的数字表示十六进制

2.使用#号:2#1001表示二进制,8#20表示八进制 

案例:

#!/bin/bash
a=$((2#1000))
((b=8#20))
((c=16#10))
 
d=$[0x10]
e=$[020]
f=20
((g=0x10))
 
((h=(1+3)*5))
echo "$a $b $c $d $e $f $g $h"

8.Shell case…esac 语句

可以使用多个if…elif 语句执行多分支。然而,这并不总是最佳的解决方案,尤其是当所有的分支依赖于一个单一的变量的值。

Shell支持 case…esac 语句处理正是这种情况下,它这样做比 if…elif 语句更有效。

case…esac 语句基本语法 是为了给一个表达式计算和几种不同的语句来执行基于表达式的值。

解释器检查每一种情况下对表达式的值,直到找到一个匹配。如果没有匹配,默认情况下会被使用。

case word in
  pattern1)
     Statement(s) to be executed if pattern1 matches
     ;;
  pattern2)
     Statement(s) to be executed if pattern2 matches
     ;;
  pattern3)
     Statement(s) to be executed if pattern3 matches
     ;;
esac

​ 这里的字符串字每个模式进行比较,直到找到一个匹配。执行语句匹配模式。如果没有找到匹配,声明退出的情况下不执行任何动作。

​ 没有最大数量的模式,但最小是一个。

​ 当语句部分执行,命令;; 表明程序流程跳转到结束整个 case 语句。和C编程语言的 break 类似。

案例:输入数字 1-7 输出对应的星期几?

#!/bin/bash

key=$1

case "$key" in
   "1") echo "星期一" 
   ;;
   "2") echo "星期二" 
   ;;
   "3") echo "星期三" 
   ;;
   "4") echo "星期四"
   ;;
   "5") echo "星期五"
   ;;
   "6") echo "星期六"
   ;;
   "7") echo "周日"
   ;;
esac

9. 循环

9.1 while循环

while command
do
   Statement(s) to be executed if command is true
done

里Shell命令进行计算。如果结果值是 true,给定语句被执行。如果命令为 false,那么没有语句将不执行,程序将跳转到done语句后的下一行。

案例:打印 1-10

#!/bin/bash
number=$1

while [ $number -lt 10 ]
  do
   let number=number+1
   echo "number=$number"
done

执行: ./demo.sh 0

9.2 Shell for循环

循环操作项目清单。重复一组命令列表中的每个项目。

语法:

for var in word1 word2 ... wordN
do
   Statement(s) to be executed for every word.
done

var是一个变量,word1 到 wordN 是由空格分隔的字符(字)序列的名称。每次for 循环的执行,变量var的值被设置为下一个单词的列表中的字,word1 到 wordN 。

案例:

#!bin/bash
read -p "input dir path" dir
for item in `ls $dir`; 
do
	echo $item
done

遍历某个文件夹下的所有文件和目录

#!/bin/bash
function getdir()
    for file in $1/*
    do
    if test -f $file
    then
        echo $file
        arr=($arr[*] $file)
    else
        getdir $file
    fi
    done

getdir $1

# 调用方式: ./demo.sh /web/jenkins/dir

9.3 循环中的break 和 continue

  1. break 语句 : 用于跳出循环
  2. continue 语句 : 跳过当前本次循环,继续下一次循环

9.4 until循环

until和while循环相反,until是当条件为假的时候就执行循环,一旦条件满足就退出循环

until condition
dountil 后面的条件不成立的时候,这里写需要做的事情
done 

10.函数

函数允许您对分解成更小的,逻辑子部分,然后可以被要求执行各项任务时,它需要一个脚本的整体功能。

使用函数来执行重复性的任务,是一个很好的方式来创建代码的重用。代码重用是现代面向对象编程的原则的重要组成部分。

Shell函数是类似于其他编程语言中的子程序,过程和函数。

10.1 声明函数:

function_name ()  
   list of commands

声明一个简单的函数:

#!/bin/bash
hello()
	echo "hello world"


# 调用hello函数
hello

10.2 传递参数给函数:

你可以定义一个函数,它接受参数,而调用这些函数。将这些参数代表$1,$2,依此类推。

案例: 输入两个数字,计算他们的和

#!/bin/bash

add()
  let sum=$1+$2
  echo $sum

# 函数外的$1 和 $2 有脚本参数传递,函数内的参数由 add后面的$1 和 $2 传递
add $1 $2

10.3 函数的返回值

​ 如果你执行一个exit命令从一个函数内部,其效果不仅是终止执行的功能,而且Shell 程序中调用该函数。

​ 如果你不是想,只是终止执行该函数,再有就是退出来的一个定义的函数。

​ 根据实际情况,你可以从你的函数返回任何值,使用返回的命令,其语法如下

return code;

案例: 输入两个数,计算他们的乘积

#!/bin/bash

hello () 
   echo "hello args: $1 $2"
   let result=

以上是关于linux shell编程的主要内容,如果未能解决你的问题,请参考以下文章

linux使用bash shell命令行常用快捷键 (转载)

为啥在 URL 中缺少尾部斜杠 (/) 时会发生重定向?

mac shell终端编辑命令行快捷键

Linux中高效编写Bash脚本的10个技巧

linux 怎么进入shell编程模式

linux shell编程问题