持续创作,加速成长!这是我参与「日新方案 10 月更文应战」的第30天,点击检查活动概况

嵌入式 Linux 入门第六课,持续完结 Shell 脚本学习,本文学习 Shell 脚本语法  ...... 矜辰所造成的

前言

之前咱们经过文章《嵌入式 Linux 入门(五、Shell 脚本编程上:知道 Shell 脚本)》初次知道了 Shell 脚本,本文咱们就要学习 Shell 脚本的语法 ,争取做到学完本文,你也会写 Shell 脚本。

一、先看几个脚本

学习一门言语,咱们要知道咱们将来完结的程序是什么姿态的,所以有必要先来看几个脚本。

1.1 启动某个应用程序

一个简略的脚本,根本上都是咱们了解的 Shell 指令组成的:

#!/bin/bash
export LD_LIBRARY_PATH=/app/test_app/lib/
echo $LD_LIBRARY_PATH
cd /app/test_app/bin/
./test_app

1.2 USB WIFI 脚本

此脚本为 阿尔法 Linux 开发板 资料中的 USB WIFI 脚本片断:


#获取参数
while [ $# -gt 0 ]; do
  case $1 in
    --help | -h)
      usage_and_exit $0;break;;
    -m) shift; mode=$1; shift; ;;
    -i) shift; ssid=$1; shift; ;;
  -p) shift; psk=$1; shift; ;;
  -d) shift; device=$1; shift; ;;
  -e) shift; ethernet=$1; shift; ;;
    --version) version $0; break;;
    *) usage_and_exit; break; ;;
  esac
done
#判别参数
if [ -n "$mode" ]; then
echo "您的WIFI配置信息是:"
echo "mode  : $mode"
fi
if [ -n "$ssid" ]; then
echo "ssid  : $ssid"
echo "psk   : $psk"
fi
if [ -n "$device" ]; then
echo "device: $device"
fi
if [ -n "$ethernet" ]; then
echo "ethernet  : $ethernet"
fi
#kill掉相关进程
processkill()
{
a=$(ps -aux |grep -E "hostapd" | grep -v grep | awk '{print $2}')
if [ -n "$a" ];then
  kill -9 $a
fi
# ...重复相似if句子被我删去了,节约版面
}
#创建配置wifi的信息
touch /$PWD/wifi.conf
wifi_conf="/$PWD/wifi.conf"
#bridge Mode(桥接形式)
if [ "$mode" == "bridge" ]; then 
  ifconfig $device down
  ifconfig $device up
  ifconfig $ethernet down
  ifconfig $ethernet up 
  sleep 2
  if [ "$WIFI_MODE" == "station" ]; then 
    wpa_supplicant -B -D wext -i $device -c /etc/wpa_supplicant.conf
  fi
  processkill
  sleep 1
  sync
    echo -ne "interface=wlan0\nssid=alientek_bridge\ndriver=rtl871xdrv\nchannel=6\nhw_mode=g\nignore_broadcast_ssid=0\n
auth_algs=1\nwpa=3\nwpa_passphrase=12345678\nwpa_key_mgmt=WPA-PSK\nwpa_pairwise=TKIP\nrsn_pairwise=CCMP\nbridge=br0" > $wifi_conf
  rm -rf /var/lib/misc/*
  touch /var/lib/misc/udhcpd.leases
  udhcpd -fS /etc/udhcpd.conf & 
  ifconfig $device 0.0.0.0
  brctl addbr br0       
  ifconfig $ethernet 0.0.0.0
  brctl addif br0 $ethernet 
  brctl addif br0 $device         
  ifconfig br0 192.168.1.39 netmask 255.255.255.0
  hostapd $wifi_conf -B
  export WIFI_MODE=bridge
fi
#softap Mode(热点形式)
if [ "$mode" == "softap" ]; then
  processkill
  sleep 1
  sync
    echo -ne "interface=wlan0\nssid=alientek_softap\ndriver=rtl871xdrv\nchannel=6\nhw_mode=g\nignore_broadcast_ssid=0\n
auth_algs=1\nwpa=3\nwpa_passphrase=12345678\nwpa_key_mgmt=WPA-PSK\nwpa_pairwise=TKIP\nrsn_pairwise=CCMP" > $wifi_conf
  a=$(ifconfig |grep -E "br0" | grep -v grep | awk '{print $0}')
  if [ -n "$a" ];then
    brctl delif br0 $device
    brctl delif br0 $device
    ifconfig br0 down
  fi   
  rm -rf /var/lib/misc/*
  touch /var/lib/misc/udhcpd.leases
  ifconfig $device up
  sleep 2
  ifconfig $device 192.168.1.38 netmask 255.255.255.0
  udhcpd -fS /etc/udhcpd.conf     &
  hostapd $wifi_conf -B
  export WIFI_MODE=softap
fi

二、Shell 脚本语法

上面脚本看完,有编程言语根底的应该都勉勉强强能看懂一二,但是也有许多符号啊,关键字啊与咱们从前嵌入式开发的 C 言语都不相同,带着这些疑问,开端咱们的 Shell 脚本语法学习。

2.1 Shell 脚本中的符号

首要,咱们先了解一下 Shell 脚本中的一些常用符号,然后再去阐明 Shell 的根本语法。

Shell 脚本中的符号有许多,咱们这儿只讲解一些常用的符号,假如后期用到再来更新。

2.1.1 #

# 符号: 注释

最初的 #! 是一个约定的符号,它告诉系统这个脚本需求什么解说器来履行,即运用哪一种 Shell。

出最初外的 # 号作为注释的最初字母,每一行句子中,从#号开端的部分就不履行了,相似于C 言语的//

#!/bin/sh
#正点原子@ALIENTEK 
#USB WIFI脚本 
#功用:脚本支撑station形式、softap形式、bridge形式相互切换(若相互切换不成功,请重启板子)
#运用办法阐明
usage_and_exit() {

2.1.2 $

$符号(美元符号):变量替换(Variable Substitution)的代表符号,用来表明变量的值。

比方一个变量 TESTA 的值为 456 ,运用 $TESTA 就能够得到 456 这个值。

if [ -n "$a" ];then

2.1.3 ‘ ‘

'' 单引号:被单引号用括住的内容,将被视为单一字串,shell不会将一对单引号之间的任何字符做特别解说。

在引号内的代表变量的$符号,也没有作用,他被视为一般符号处理,避免任何变量替换。

a=$(ps -aux |grep -E "hostapd" | grep -v grep | awk '{print $2}')

2.1.4 ” “

" "双引号:被双引号用括住的内容,将被视为单一字串。它避免通配符扩展,但答应变量扩展。

不能辨认指令,能够辨认变量。如#不再是注释的最初,它只表明一个井号“#”。但 $ 仍然坚持特别含义。

if [ -n "$a" ];then

2.1.5 “

“ 倒引号:指令替换,在倒引号内部的shell指令首要被履行,其成果输出代替用倒引号括起来的文本。

在前面的单双引号,括住的是字串,但假如该字串是一列想要履行的指令,该怎样处理?要处理这种状况,咱们得用倒单引号来做。

倒引号估量许多人都不知道在哪里,便是键盘 ESC 下面,字母这边的数字键 1 的左面,运用英文输入法,就能打出倒引号。

test=`date +%F`echo "Today $test"

在 履行指令 或 运算 的时分,都需求运用倒引号。

2.1.6 \

“ 反斜杠 :

  1. 转义字符,和 C 言语相似;
  2. 续行符,放在指令的最末端,表明指令衔接下一行,也和 C 相似;

在文本中,跟在\后边的一个字符不会被shell特别解说。

2.1.7 {}

{} 大括号 :这儿只说一个和$符号配合,作为字符串衔接来运用。

2.1.8 ()

() 小括号:

  1. 指令组,用括号将一串连续指令括起来,这种用法对 shell 来说,称为指令群组, 括号中的指令将会新开一个子shell次序履行,所以括号中的变量不能够被脚本余下的部分运用。
  2. 指令替换,等价于cmd
  3. 初始化数组

估量要实践测验才能够了解 = =!

processkill()
{
a=$(ps -aux |grep -E "hostapd" | grep -v grep | awk '{print $2}')
if [ -n "$a" ];then
  kill -9 $a
fi

2.1.9 [ ]

[ ] 中括号:这儿也只说一个,常出现在流程操控中,扮演括住判别式的作用。

比方:

if [ "$mode" == "bridge" ]; then

2.1.10 ;

; 分号:首要用于差异在同一行的不同句子。

在 Shell 编程中分段的句子,假如写成单行,需求用分号进行差异,假如写成块,那么则用换行符代替了分号。

比方下面示例:

if [条件1]
then
  echo "1"
else     
  echo "2"
fi

if [条件1]  ; then echo "1" ; else echo "2"; fi

2.2 变量

运转shell时,会一起存在三种变量:

  • 局部变量:局部变量在脚本或指令中界说,仅在当时shell实例中有效。本小结即将阐明的变量界说与引证首要讲的便是这个局部变量。
  • 环境变量:一切的程序,包含shell启动的程序,都能拜访环境变量,有些程序需求环境变量来确保其正常运转。必要的时分shell脚本也能够界说环境变量。
  • shell变量:shell 变量是由 shell 程序设置的特别变量。shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量确保了shell 的正常运转

关于环境变量,咱们会单独开一篇文章阐明,大概就在本文完毕的下一篇文章。

2.2.1 界说变量

运用=进行变量界说, 在shell中赋值的=两头是不能有空格的!

变量名的命名须遵从如下规矩:

  • 命名只能运用英文字母,数字和下划线,首个字符不能以数字最初。
  • 中间不能有空格,能够运用下划线 _。
  • 不能运用标点符号。
  • 不能运用bash里的关键字(可用help指令检查保留关键字)。

shell 脚本言语是弱类型言语,界说变量时不需求指定类型。

一篇文章带你上手 Shell 脚本编程

3种办法界说变量:

variable=value
variable='value'
variable="value"    

假如不加双引号或许单引号,假如字符串中有空格或许 TAB 按键,则无法辨认出来,如下示例(示例中用到了变量运用办法,就在下文能够看到,这儿为了阐明这个问题,提前运用):

一篇文章带你上手 Shell 脚本编程

双引号,单引号有什么差异呢? 这就要结合上面咱们所说的常识类剖析了:

一篇文章带你上手 Shell 脚本编程

主张变量界说都加双引号。

2.2.2 运用变量

根本运用

运用一个界说过的变量,只要在变量名前面加美元符号$即可:

$variable
${variable}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解说器辨认变量的边界 。

把一切变量加上花括号,这是个好的编程习气!

在上面咱们已经给过运用变量的示例了,这儿就不放示例。

重命名变量

重命名变量和 C 言语相同,在后边直接从头赋值即可,如下图:

一篇文章带你上手 Shell 脚本编程

将指令的值赋值给变量

变量能够直接赋值,也能够赋值为 指令运转的成果,运用如下办法:

#cmd 为shell 指令
var1=`cmd`
var1=${cmd}

一篇文章带你上手 Shell 脚本编程

只读变量

variable=“test_only_read”
readonly variable

删去变量

unset variable

删去之后不可拜访,删去不掉只读变量,示例如下图:

一篇文章带你上手 Shell 脚本编程

2.2.3 特别变量

在 Shell 脚本中,有一些特别的变量,前面咱们说过$符号用来表明变量的值,这些特别变量都是以$最初的,如下:

一篇文章带你上手 Shell 脚本编程

$*$@ 差异

  • 相同点:都是引证一切参数
  • 不同点:只要在双引号中体现出来。假设在脚本运转时写了三个参数 1、2、3,则 ” * ” 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。

$*$@ 差异在下文 2.7 流控句子中的 2.7.3 for in 句子有示例阐明

一篇文章带你上手 Shell 脚本编程
一篇文章带你上手 Shell 脚本编程

2.3 数据类型

介绍一下shell 脚本中的数据类型: 字符串 与 数组;

2.3.1 字符串

字符串是 shell 编程中最常用最有用的数据类型,字符串能够用单引号,也能够用双引号,也能够不必引号。

拼接字符串

拼接字符串直接连在一起就能够,具体能够自己体会一下

name=“this is”" my name"; name=“this is my name”; name=“this” is “my name” 等效

name=‘this is'' my nam'; name=‘this is my name'; name=‘this' is ‘my name' 等效

看示例:

一篇文章带你上手 Shell 脚本编程

获取字符串长度

${}中运用#获取长度:

一篇文章带你上手 Shell 脚本编程

提取子字符串

变量后边跟一个:第几个开端:取几个数字,字符串从 0 开端计数:

一篇文章带你上手 Shell 脚本编程

2.3.2 数组

理论的常识引证至 菜鸟教程,咱们都会经过实例展示一下。

bash支撑一维数组(不支撑多维数组),并且没有限制数组的大小。

相似于 C 言语,数组元素的下标由 0 开端编号。获取数组中的元素要利用下标,下标能够是整数或算术表达式,其值应大于或等于 0。

界说数组

在 Shell 中,用括号来表明数组,数组元素用”空格”符号分割开。界说数组的一般形式为:

数组名=(值12 ... 值n)

读取数组

这儿和 C 言语数组相似:

${数组名[下标]}

运用 @ 符号能够获取数组中的一切元素

${数组名[@]}

获取数组的长度

获取数组长度的办法与获取字符串长度的办法相同,在${}中运用#获取长度:

# 获得数组元素的个数
length=${#array_name[@]}
# 或许
length=${#array_name[*]}
# 获得数组单个元素的长度
lengthn=${#array_name[n]}

一篇文章带你上手 Shell 脚本编程

2.4 注释

在前面咱们说 Shell 脚本符号的时分,咱们知道了运用 # 号能够表明注释,相似于C言语多种的 // .

多行注释,在C中咱们比较简略的运用 /* ...... */,如下:

/*
hjksagdfsjkf asdfhasdg fasdfdasfasdf asdfl;;sdgh
sdhjkasgd akl
*/

那么 Shell 脚本中的多行注释怎样处理呢?

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF 也能够运用其他符号:

:<<!
注释内容...
注释内容...
注释内容...
!

一篇文章带你上手 Shell 脚本编程

2.5 读取操控台输入

运用 read 读取操控台输入:

read (选项) (参数)

选项: -p:指定读取值时的提示符;-t:指定读取值时等候的时刻(秒)。参数:变量:指定读取值的变量名

运用直接看示例:

一篇文章带你上手 Shell 脚本编程

2.6 运算符

和其他编程言语相同,Shell 脚本也支撑多种运算符。

2.6.1 根本算数运算

下图引证菜鸟教程:

一篇文章带你上手 Shell 脚本编程

上面的 expr 是一款表达式核算东西,教程解说道原生bash不支撑简略的数学运算,所以需求用expr。

其间数字运算的时分,运用乘法符号 " * " ,要用转义字符 " \ " 进行转义。

但是咱们除了运用 expr 还能够运用 (( )) ,具体如下示例:

一篇文章带你上手 Shell 脚本编程

2.6.2 条件判别

在 Shell 脚本中,检测某个条件是否建立的办法如下:

[ condition ](注意 condition 前后要有空格)
[ $a -eq $b ] 
test condition
test $a $b -eq

一篇文章带你上手 Shell 脚本编程
一篇文章带你上手 Shell 脚本编程

关于逻辑判别的符号:

[ ] : 中括号周围和运算符两头有必要增加空格 (能够运用,不引荐)

[[ ]]:中括号周围和运算符两头有必要增加空格 (字符串验证时,引荐运用)

(()) : 中括号周围和运算符两头有必要增加空格 (数字验证时,引荐运用)

[[]] 和 (()) 分别是[ ]的针对数学比较表达式和字符串表达式的加强版。运用[[ … ]]条件判别结构,而不是[ … ],能够避免脚本中的许多逻辑过错。

对整数进行数学运算用 (( )),不需求运用上面的 -eq 那些运算符,直接 ((a < b)) 即可,还能够做加减法,比方 a=$((a+1)) 就表明 a 加1 。

2.6.3 布尔运算

布尔运算也和 C 言语意思相似,好了解,首要在于怎么运用:

一篇文章带你上手 Shell 脚本编程

(在写这个示例的时分把自己搞蒙圈了,疏忽打印的字符串阐明,具体见下面逻辑运算阐明 = =!)

一篇文章带你上手 Shell 脚本编程

在上面的示例中,我加了条件判别的 if 句子,这个在下文咱们讲解条件句子的时分会阐明。

2.6.4 逻辑运算

逻辑运算有与 && 和 或|| ,和 C 言语相同这个好了解。

但是我在写文章的时分,写示例,把自己高蒙圈了,如下图:

一篇文章带你上手 Shell 脚本编程

搞得我自己一下子都蒙了,尴尬,搞得我写了一个 c 理一理思路:

一篇文章带你上手 Shell 脚本编程

这个中括号上面只需求一个,这儿需求2个,这儿注意一下!

2.7 流控句子

流控句子,便是 那些 if,while,for 等循环挑选句子。

2.7.1 if 句子

if 句子相对比较简略的,记住他的根本格局就好:

if else

if [条件1]  #我是注释,假如条件为真履行 then 后边的
then
  #条件建立程序
else       #我是注释,假如条件1位假履行 then 后边的
  #条件不建立程序
fi

if else-if else

if [条件1]
then      #我是注释,假如条件1为真履行第一个 then 后边的
  #条件1建立程序
elif [条件2] 
then      #我是注释,假如条件2为真履行第二个 then 后边的
  #条件2建立程序
else
  #条件1和2都不建立程序
fi

直接做个测验:

一篇文章带你上手 Shell 脚本编程

2.7.2 case in 句子

相似于 C 言语中的 swtich 句子。

每个 case 分支用右圆括号)开端,用两个分号 ;; 表明 break,即履行完毕,跳出整个 case … esac 句子,esac(便是 case 反过来)作为完毕符号。

根本格局如下:

casein
形式1)
    #形式1程序
    #形式1程序
    ...
    #形式1程序
    ;;
形式2)
    #形式2程序
    #形式2程序
    ...
    #形式2程序
    ;;
esac

取值可认为变量或常数,匹配发现取值契合某一形式后,其间一切指令开端履行直至 ;;

假如无一匹配形式,运用星号 * 捕获该值,再履行后边的指令,相似于 C 言语中的 default,示例如下:

一篇文章带你上手 Shell 脚本编程

2.7.3 for in 句子

for in 句子这儿就和 C 差异大了,记住格局把:

for var in item1 item2 ... itemN  #后边这些叫in列表
do
    #履行句子
done

当变量值在列表里,for 循环即履行一次一切指令,运用变量名获取列表中的当时取值。

in 列表能够包含替换、字符串和文件名。

in 列表是可选的,假如不必它,for循环运用指令行的位置参数。

一篇文章带你上手 Shell 脚本编程

再看 $*$@ 差异 ,经过下面的示例阐明:

没有双引号时分作用相同:

一篇文章带你上手 Shell 脚本编程

有双引号时分有了差异:

一篇文章带你上手 Shell 脚本编程

2.7.4 while 循环

while 循环相对也比较简略,其语法格局为:

while 条件
do
    #满足条件履行的程序
done

一篇文章带你上手 Shell 脚本编程

2.7.5 跳出循环

相似 C 言语中的用法,Shell 运用两个指令来实现该功用:break 和 continue 。

break 跳出一切循环(停止履行后边的一切循环)。

continue 跳出当时循环。

2.7.6 sleep

sleep 就相当于 C 言语的延时函数,有时分在履行指令的时分需求等候上一条指令履行,咱们需求加入延时, sheep 用法如下:

sleep 1 #睡觉1秒
sleep 1s #睡觉1秒
sleep 1m #睡觉1分
sleep 1h #睡觉1小时

2.8 函数

和 C 言语相同,shell 能够用户界说函数,在 shell 脚本中调用。

函数的格局如下:

function  name (){
    action;
    [return int;] #可加可不加
}

或许不带 function 也能够:

name (){
    action;
    [return int;] #可加可不加
}

return 返回,假如不加,将以最终一条指令运转成果作为返回值 。

函数示例如下:

一篇文章带你上手 Shell 脚本编程

2.9 输入/输出重定向

输入/ 输出重定向是什么?

2.9.1 重定向的界说和用途

我用简略的白话阐明一下,便是你在 Linux 下翻开一个终端,输入 Shell 指令(这个你输入的指令就输入,这很好了解),输入 Shell 指令今后,会依据你输入的指令显现成果(这个成果,便是你的输出,是直接在你翻开的这个终端下面显现,便是输出就在这个终端显现)。

咱们说重定向,比方输出重定向,便是本来你输入的 Shell 指令成果会在你这个终端中显现的,但是你给他 从头定了一个方向,让他输出到其他地方,比方某个文件,就叫做输出重定向。

举个简略的比方,比方你敲 ls 指令检查当时文件夹下面的目录,运用 ls 输出重定向今后,它就不会在你这个终端中直接显现出 ls 的成果,而是到你重定向的那个地方去了,比方:

一篇文章带你上手 Shell 脚本编程

在一般状况下,咱们很少用到输入重定向,所以本文剖析输入重定向,知道一下即可,输出重定向咱们倒是用得比较多。

那么这个重定向有什么含义?

☆ 输出重定向能够把指令的成果保存到文件中,意图在于能够给管理员做记载剖析,或许对其进行更进一步的处理。☆

2.9.2 Linux 下的标准输入输出

这一末节即使暂时不明白也没什么关系,了解记一下就好。

咱们在核算机中,标准的输入输出设备为 键盘 和显现器,咱们知道在Linux 下,一切都是文件,在 Linux 下 把输出设备分为了2个文件,一个正确输出,一个过错输出。

一般状况下,每个 Linux 指令运转时都会翻开三个文件:

一篇文章带你上手 Shell 脚本编程

2.9.3 重定向指令

重定向运用如下指令操作:

一篇文章带你上手 Shell 脚本编程

默许状况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

上面列表中那么多,要想了解需求自己试一试,结合实例才更加直观的能够了解,这儿我举几个简略的比方。

标准输出重定向示例

一篇文章带你上手 Shell 脚本编程

标准过错输出重定向示例

一篇文章带你上手 Shell 脚本编程

那么问题来了,正确指令和过错指令的输出格局不相同,咱们需求人为判别一下正确指令还是过错指令?

我要是能知道这条指令是错的(有时分粗心也会写错),我直接改正不就好了,所以咱们要讲怎么把正确的输出和过错的输出一起保存在指定的文件之中的办法。

正确和过错输出一起保存

先来看下正确过错一起保存的指令,下面的指令都是一起保存至同一个文件:

一篇文章带你上手 Shell 脚本编程
一篇文章带你上手 Shell 脚本编程
一篇文章带你上手 Shell 脚本编程

正确和过错输出分隔保存

一篇文章带你上手 Shell 脚本编程

直接看示例:

一篇文章带你上手 Shell 脚本编程

分隔保存的办法也是常见的应用,这样咱们在检查脚本运转出错的时分查找问题能够直接检查过错输出以便快速定位问题。

2.9.4 /dev/null 文件

/dev/null 文件,相当于垃圾箱,是在咱们希望某个指令履行但是又不需求他的输出的时分运用的。

依据咱们上面重定向的学习,他的根本指令为:

$ command > /dev/null

再依据咱们上面的学习,咱们想要把正确和过错信息都不需求输出,能够运用下面的指令:

command > /dev/null 2>&1
command &> /dev/null

那么这儿又有一个问题,这样做的含义在哪里?

简略举个比方阐明,咱们在写 Shell 脚本的时分,有些指令行会有一些输出,这个输出对咱们整个 Shell 脚本没有任何含义,Shell 脚本批量履行的时分会有许多输出,这种没有含义的输出咱们就需求把它们给屏蔽了,就能够运用上面的办法给它“丢到垃圾箱”。

结语

作为一门言语的学习,本文的内容相对还是许多的,对于本文的学习,一定要自己动手练习。

假如你未来的工作是做运维,那么有必要得熟练的学会 Shell 脚本编程,假如是嵌入式 Linux 学习,那么相对来说一般用不到特别复杂的脚本,不要一开端就被劝退,那么咱们一起加油吧 ヾ(◍∇◍)ノ゙ !

别的提一下,Shell 脚本对指令中多余的空格不进行任何的处理,所以咱们在平常编写程序的时分为了是程序可读性强,能够合理的运用空格 。

好了,本文就到这儿,在今后假如在运用 Shell 脚本的时分有新的注意事项,文章会坚持更新!

谢谢咱们!