ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux Bash语法总结

2020-02-04 11:55:08  阅读:147  来源: 互联网

标签:shell echo 语法 参数 Bash done Linux fi bash


在学习Linux的过程中,无可避免的会碰到一个既让人喜欢、又令人十分头疼的神奇的东西——bash编程,也就是shell脚本。那么什么是shell脚本呢?shell是一个命令语言解释器,而shell脚本则是Linux命令的集合,按照预设的顺序依次解释执行,来完成特定的、较复杂的系统管理任务,类似于windows中的批处理文件。本篇博文主要介绍bash编程的基础语法讲解。

一、bash编程之变量

1)bash变量类别

本地变量:只对当前shelll进程有效的变量,对其他shell进程无效,包含当前shell进程的子进程。

2)变量赋值:

即向变量的存储空间保存数据,如下

[root@localhost ~]# VAR_NAME=VALUE

3)变量的引用

格式为:${VAR_NAME}

" ":弱引用,里面的变量会被替换;
' ':强引用,里面的所有字符都是字面量,直接输出,所见即所得;

4)环境变量

对当前shell进程及其子shell有效,对其他的shell进程无效!

定义:VAR_NAME=VALUE
导出:export VAR_NAME
撤销变量:unset VAR_NAME
只读变量:readonly VAR_NAME

5)局部变量

在shell脚本中定义,只可以在shell脚本中使用!

6)位置变量

$1,$2...,${10}

7)特殊变量

shell对一些参数做特殊处理,这些参数只能被引用而不能被赋值!

$#:传递到脚本的参数个数
$*:显示所有向脚本传递的参数                  //与位置变量不同,此选项参数可超过9个
$$:获取当前shell的进程号
$!:执行上一个指令的进程号
$?:获取执行的上一个指令的返回值              //0为执行成功,非零为执行失败
$-:显示shell使用的当前选项,与set命令功能相同
$@  与$*相同,但是使用时加引号,并在引号中返回每个参数

8)查看变量

set:查看当前shell进程中的所有变量;
export、printenv、env:查看当前shell进程中的所有环境变量;

9)变量命名规则

1)不能使用程序中的关键字;
2)只能使用数字、字母和下划线,不可使用数字开头;
3)系统变量默认都是大写,自定义变量尽量不要与系统变量冲突;
4)尽量做到见名知意;

10)变量类型

1)数值型:精确数值(整数)、近似数值(浮点数);
2)字符型:char、string;
3)布尔型:true、false;

11)类型转换

1)显示转换;
2)隐式转换;

二、bash的配置文件

功能:设定本地变量、定义命令别名。

1)profile类

profile类:为交互式登录的用户提供配置!

全局:/etc/profile、/etc/profile.d/*.sh
用户:~/.bash_profile

2)bashrc类

bashrc类:为非交互式的用户提供配置!

全局:/etc/bashrc
用户:~/.bashrc

1)bash编程即编写格式及执行方式

1)编写格式

shell脚本第一行必须顶格写,用shabang定义指定的解释器来解释该脚本。

#!/bin/bash       //!即为shebang
//其它的以#开头的行均为注释,会被解释器忽略,可用来注释脚本用途及版本,方便使用管理。

2)执行方式

bash编程属于面向过程编程,执行方式如下:

1)顺序执行:按命令先后顺寻依次执行;
2)选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支;
3)循环执行:将同一段代码反复执行多次,因此,循环必须有退出条件;否则,则陷入死循环;

3)bash执行选项

1)bash -n SHELLNAME  #语法测试,测试是否存在语法错误;
2)bash -x SHELLNAME  #模拟单步执行,显示每一步执行过程;

2)bash之算数运算与逻辑运算

1)算数运算

定义整型变量:

1)et VAR_NAME=INTEGER_VALUE            //例如:let a=3
2)declare -i VAR_NAME=INTEGER_VALUE     //例如:declare -i a=3

实现算术运算的方式:

let VAR_NAME=ARITHMATIC_EXPRESSION
VAR_NAME=$[ARITHMATIC_EXRESSION]
VAR_NAME=$((EXPRESSION))
VAR_NAME=$(expr $num1 + $num2)

算法运算符:

+:加法
-:减法
*:乘法
/:整除
%:取余数
**:乘幂

注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算,bash会执行变量类型的隐式类型转换。

2)逻辑运算

布尔运算:真,假
与运算:真 && 真 = 真
        真 && 假 = 假
        假 && 真 = 假
        假 && 假 = 假
或运算:真 || 真 = 真
  真 || 假 = 真
  假 || 真 = 真
  假 || 假 = 假
非运算:!真=假
        !假=真

3)bash编程之条件测试语句

1)bash条件测试

整型测试:整数比较

例如 [ $num1 -gt $num2 ]

-gt: 大于
-lt: 小于
-ge: 大于等于
-le: 小于等于
-eq: 等于
-ne: 不等于

字符测试:字符串比较

双目:
例如[[ "$str1" > "$str2" ]]

>: 大于则为真
<: 小于则为真
>=:大于等于则为真
<=:小于等于则为真
==:等于则为真
!=:不等于则为真

单目:
-n String: 是否不空,不空则为真,空则为假
-z String: 是否为空,空则为真,不空则假

文件测试:判断文件的存在性及属性等

-a FILE:存在则为真;否则则为假;
-e FILE:存在则为真;否则则为假;
-f FILE: 存在并且为普通文件,则为真;否则为假;
-d FILE: 存在并且为目录文件,则为真;否则为假;
-L/-h FILE: 存在并且为符号链接文件,则为真;否则为假;
-b: 存在并且为块设备,则为真;否则为假;
-c: 存在并且为字符设备,则为真;否则为假
-S: 存在并且为套接字文件,则为真;否则为假
-p: 存在并且为命名管道,则为真;否则为假
-s FILE: 存在并且为非空文件则为值,否则为假;
-r FILE:文件可读为真,否则为假
-w FILE:文件可写为真,否则为假
-x FILE:文件可执行为真,否则为假
file1 -nt file2: file1的mtime新于file2则为真,否则为假;
file1 -ot file2:file1的mtime旧于file2则为真,否则为假;

组合条件测试:在多个条件间实现逻辑运算

与:[ condition1 -a condition2 ]
  condition1 && condition2
或:[ condition1 -o condition2 ]
  condition1 || condition2
非:[ -not condition ]
  ! condition
与:COMMAND1 && COMMAND2
COMMAND1如果为假,则COMMAND2不执行
或:COMMAND1 || COMMAND2
COMMAND1如果为真,则COMMAND2不执行
非:! COMMAND

2)条件测试之if语句

1、if语句之单分支
语句结构:
if 测试条件;then
   选择分支
fi
表示条件测试状态返回值为值,则执行选择分支
例:写一个脚本,接受一个参数,这个参数是用户名;如果此用户不存在,则创建该用户;

#!/bin/bash
if ! id $1 &> /dev/null;then
  useradd $1
fi

2、if语句之双分支
语句结构:
if 测试条件;then
   选择分支1
else
   选择分支2
fi

两个分支仅执行其中之一
例:通过命令行给定一个文件路径,而后判断:如果此文件中存在空白行,则显示其空白行的总数;否则,则显示无空白行;
#!/bin/bash
if grep "^[[:space]]*$" $1 &> /dev/null; then
  echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
else
  echo "No blank lines"
fi

注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。

3、if语句之多分支
语句结构:
if 条件1;then
     分支1
elif 条件2;then
     分支2
elif 条件3;then
     分支3
      ...
else
     分支n
fi

例:传递一个用户名给脚本:如果此用户的id号为0,则显示说这是管理员;如果此用户的id号大于等于500,则显示说这是普通用户;否则,则说这是系统用户。
#!/bin/bash
if [ $# -lt 1 ]; then
  echo "Usage: `basename $0` username"
  exit 1
fi
if ! id -u $1 &> /dev/null; then
  echo "Usage: `basename $0` username"
  echo "No this user $1."
  exit 2
fi

if [ $(id -u $1) -eq 0 ]; then
  echo "Admin"
elif [ $(id -u $1) -ge 500 ]; then
  echo "Common user."
else
  echo "System user."
fi

3)bash交互式编程

read [option] “prompt”-p:直接指定一个变量接受参数
-t timaout:指定等待接受参数的时间
-n:表示不换行

例:输入用户名,可返回其shell

#!/bin/bash
read -p "Plz input a username: " userName
if id $userName &> /dev/null; then
    echo "The shell of $userName is `grep "^$userName\>" /etc/passwd | cut -d: -f7`."
else
    echo "No such user. stupid."
fi  

4)条件测试与case语句

case语句:有多个测试条件时,case语句会使得语法结构更明晰
语句结构:

case 变量引用 in
PATTERN1)
  分支1
;;
PATTERN2)
  分支2
;;
...
*)
  分支n
;;
esac

PATTERN:类同于文件名通配机制,但支持使用|表示或者

 a|b:  a或者b*:匹配任意长度的任意字符
?: 匹配任意单个字符
[]: 指定范围内的任意单个字符

例:写一个脚本,完成如下任务,其使用形式如下所示:
script.sh {start|stop|restart|status}

其中:如果参数为空,则显示帮助信息,并退出脚本;
如果参数为start,则创建空文件/var/lock/subsys/script,并显示“starting script successfully.”
如果参数为stop,则删除文件/var/lock/subsys/script,并显示“Stop script successfully.”
如果参数为restart,则删除文件/var/locksubsys/script并重新创建,而后显示“Restarting script successfully.”
如果参数为status,那么:如果文件/var/lock/subsys/script存在,则显示“Script is running…”,否则,则显示“Script is stopped.”

#!/bin/bash
file='/var/lock/subsys/script'
case $1 in
start)
  if [ -f $file ];then
  echo "Script is running..."
    exit 3
  else
  touch $file
  [ $? -eq 0 ] && echo "Starting script successfully."
  fi
  ;;
stop)
  if [ -f $file ];then
  rm -rf $file
  [ $? -eq 0 ] && echo "Stop script successfully."
  else
  echo "Script is stopped..."
  exit 4
  fi
  ;;
restart)
  if [ -f $file ];then
  rm -rf $file
  [ $? -eq 0 ] && echo "Stop script successfully"
  else 
  echo "Script is stopped..."
  exit 5
  fi
  touch $file
  [ $? -eq 0 ] && echo "Starting script successfully"
  ;;
status)
  if [ -f $file ];then
  echo "Script is running..."
  else
  echo "Script is stopped."
  fi
  ;;
*)
  echo "`basename $0` {start|stop|restart|status}"
  exit 2
  ;;
 esac

4)bash编程之循环语句

1)循环之for循环

1、for语句格式一
语句结构:
for 变量名 in 列表; do
    循环体
done
列表:可包含一个或多个元素
循环体:依赖于调用变量来实现其变化
循环可嵌套
退出条件:遍历元素列表结束
例:求100以内所有正整数之和

#!/bin/bash
declare -i sum=0
for i in {1..100}; do
    let sum+=$i
done
echo $sum

2、for语句格式二
for ((初始条件;测试条件;修改表达式)); do
      循环体
done
先用初始条件和测试条件做判断,如果符合测试条件则执行循环体,再修改表达式,否则直接跳出循环。

例:求100以内所有正整数之和(for二实现)
#!/bin/bash
declare -i sum=0
for ((counter=1;$counter <= 100; counter++)); do
  let sum+=$counter
done
echo $sum

2)循环之while语句

while循环语句适用于循环次数未知,或不适用for直接生成较大的列表!

语句结构:
while 测试条件; do
  循环体
done
测试条件为真,进入循环;测试条件为假,退出循环
例1:求100以内所有偶数之和,要求使用取模方法
#!/bin/bash
declare -i counter=1
declare -i sum=0
while [ $counter -le 100 ]; do
  if [ $[$counter%2] -eq 0 ]; then
     let sum+=$counter
  fi
  let counter++
 done
echo $sum

例2:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止
#!/bin/bash
read -p "Plz enter a username: " userName
while [ "$userName" != 'q' -a "$userName" != 'quit' ]; do
  if id $userName &> /dev/null; then
     grep "^$userName\>" /etc/passwd | cut -d: -f3,7
  else
    echo "No such user."
  fi
read -p "Plz enter a username again: " userName
done

while特殊用法:遍历文本文件
语句结构:
while read 变量名; do
  循环体
done < /path/to/somefile
变量名,每循环一次,记忆了文件中一行文本

例:显示ID号为偶数,且ID号同GID的用户的用户名、ID和SHELL
while read line; do
  userID=`echo $line | cut -d: -f3`
  groupID=`echo $line | cut -d: -f4`
  if [ $[$userID%2] -eq 0 -a $userID -eq $groupID ]; then
     echo $line | cut -d: -f1,3,7
  fi
done < /etc/passwd

3)循环之until语句

语句结构:
until 测试条件; do
      循环体
done
测试条件为假,进入循环;测试条件为真,退出循环

例:求100以内所有偶数之和,要求使用取模方法(until实现)

#!/bin/bash
declare -i counter=1
declare -i sum=0
until [ $counter -gt 100 ]; do
  if [ $[$counter%2] -eq 0 ]; then
     let sum+=$counter
  fi
  let counter++
done
echo $sum
例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止(until实现)

#!/bin/bash
read -p "Plz enter a username: " userName
until [ "$userName" = 'q' -a "$userName" = 'quit' ]; do
  if id $userName &> /dev/null; then
   grep "^$userName\>" /etc/passwd | cut -d: -f3,7
else
echo "No such user."
fi
read -p "Plz enter a username again: " userName
done

4)循环之循环控制和shift

循环控制命令:
1)break:提前退出循环;
2)break [N]: 退出N层循环;N省略时表示退出break语句所在的循环;
3)continue: 提前结束本轮循环,而直接进入下轮循环;
4)continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环;

5)死循环

#while体while true; do
      循环体
done
#until体
until false; do
      循环体
done

例1:写一个脚本,判断给定的用户是否登录了当前系统
(1) 如果登录了,则脚本终止;
(2) 每5秒种,查看一次用户是否登录;
#!/bin/bash
while true; do
    who | grep "gentoo" &> /dev/null
    if [ $? -eq 0 ];then
break
    fi
    sleep 5
done
echo "gentoo is logged."

6)shift

位置参数可以用shift命令左移,比如shift 3 表示原来的$4现在变成$1,原来的$5变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。

我们知道,对于位置变量或命令行参数,其个数必须是确定的,或者当shell程序不知道其个数时,可以把所有参数一起复制给“$*”。若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等。在 shift 命令执行前变量 $1 的值在 shift 命令执行后就不可用了。

实例一如下:

[root@localhost ~]# cat 1.sh
#!/bin/bash
while [ $# -ne 0 ]
do
echo "第一个参数为:$1  参数个数为:$#"
shift
done
[root@localhost ~]# sh 1.sh 1 2 3 4
第一个参数为:1  参数个数为:4
第一个参数为:2  参数个数为:3
第一个参数为:3  参数个数为:2
第一个参数为:4  参数个数为:1

从上面例子中可以看出shift命令每执行一次,变量的个数($#)减1,而变量的值提前一位。

实例二如下:

[root@localhost ~]# cat 2.sh
#!/bin/bash
if [ $# -eq 0 ]
then
echo "Usage:2.sh 参数"
exit 1
fi
sum=0
while [ $# -ne 0 ]
do
sum=`expr ${sum} + $1`
shift
done
echo "sum is:${sum}"
[root@localhost ~]# sh 2.sh 10 20 30
sum is:60

shift命令还有一个重要用途,Bash定义了9个位置变量,从$1到$9,这并不意味这用户在命令行只能使用9个参数,借助shift命令可以访问多于9个的参数。

shift命令一次移动到参数的个数由其所带的参数指定,例如当shell程序处理完前9个命令行参数后,可以使用shift 9命令把$10移动到$1。

5)bash编程之函数

语法结构:

function F_NAME {    
   函数体
  }
或
  F_NAME() {
    函数体
  }
可调用:使用函数名,函数名出现的地方,会被自动替换为函数;
函数的返回值:
函数的执行结果返回值:代码的输出
函数中使用打印语句:echo, printf
函数中调用的系统命令执行后返回的结果
执行状态返回值:
默认取决于函数体执行的最后一个命令状态结果
自定义退出状态码:return [0-255]

注意:函数体运行时,一旦遇到return语句,函数即返回!

1)函数可以接收参数

在函数中调用函数参数的方式同脚本中调用脚本参数的方式:

位置参数
$1, $2, …
$#, $*, $@

实例:
要求如下:
1)提示用户输入一个可执行命令;
2)获取这个命令所依赖的所有库文件(使用ldd命令);
3)复制命令之/mnt/sysroot目录;
4)复制各库文件至/mnt/sysroot对应的目录中;

[root@localhost ~]# cat 1.sh
#!/bin/bash
target=/mnt/sysroot/
[ -d $target ] || mkdir $target
preCommand() {
    if which $1 &> /dev/null; then
  commandPath=`which --skip-alias $1`
  return 0
    else
  echo "No such command."
  return 1
    fi
}

commandCopy() {
    commandDir=`dirname $1`
    [ -d ${target}${commandDir} ] || mkdir -p ${target}${commandDir}
    [ -f ${target}${commandPath} ] || cp $1 ${target}${commandDir}
}

libCopy() {
    for lib in `ldd $1 | egrep -o "/[^[:space:]]+"`; do
libDir=`dirname $lib`
[ -d ${target}${libDir} ] || mkdir -p ${target}${libDir}
[ -f ${target}${lib} ] || cp $lib ${target}${libDir}
    done
} 
read -p "Plz enter a command: " command

until [ "$command" == 'quit' ]; do

  if preCommand $command &> /dev/null; then
    commandCopy $commandPath
    libCopy $commandPath
  fi
    exit 1
done
[root@localhost ~]# sh 1.sh
Plz enter a command: cat
[root@localhost ~]# ls /mnt/sysroot/bin/
cat
[root@localhost ~]# ls /mnt/sysroot/
bin  lib64

2)bash编程之信号捕捉

trap命令用于在shell程序中捕捉到信号,之后可以由三种反应方式:
1)执行一段程序来处理这一信号;
2)接收信号的默认操作;
3)忽略这一信号;

示例:
写一个脚本,能够ping探测指定网络内的所有主机是否在线,当没有执行完时可接收ctrl+c命令退出。

[root@localhost ~]# cat 1.sh
#!/bin/bash
quitScript() {
   echo "Quit..."
}    
trap 'quitScript; exit 5' SIGINT

cnetPing() {
  for i in {1..254}; do
    if ping -c 1 -W 1 $1.$i &> /dev/null; then
      echo "$1.$i is up."
     else
      echo "$1.$i is down."
    fi
    done
}

bnetPing() {
  for j in {0..255}; do
    cnetPing $1.$j 
  done
}
anetPing() {
for m in {0..255}; do
bnetPing $1.$m
done
}

netType=`echo $1 | cut -d"." -f1`

if [ $netType -ge 1 -a $netType -le 126 ]; then
anetPing $netType
elif [ $netType -ge 128 -a $netType -le 191 ]; then
bnetPing $(echo $1 | cut -d'.' -f1,2)
elif [ $netType -ge 192 -a $netType -le 223 ]; then
cnetPing $(echo $1 | cut -d'.' -f1-3)
else
echo "Wrong"
exit 2
fi
[root@localhost ~]# sh 1.sh 192.168.1.1
192.168.1.1 is down.
192.168.1.2 is down.
192.168.1.3 is down.
192.168.1.4 is down.
192.168.1.5 is down.
192.168.1.6 is down.
192.168.1.7 is down.
192.168.1.8 is down.
192.168.1.9 is down.
192.168.1.10 is up.
192.168.1.11 is down.
^CQuit...

——————————本次博文到此结束,感谢阅读——————————

标签:shell,echo,语法,参数,Bash,done,Linux,fi,bash
来源: https://blog.51cto.com/14157628/2469073

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有