嵌入式 Linux 入门(六、Shell 脚本编程下:Shell 脚本语法)

举报
矜辰所致 发表于 2022/09/28 10:23:01 2022/09/28
【摘要】 嵌入式 Linux 入门第六课,继续完成 Shell 脚本学习,本文学习 Shell 脚本语法 ...... 矜辰所致
嵌入式 Linux 入门第六课,继续完成 Shell 脚本学习,本文学习 Shell 脚本语法  ...... 矜辰所致

前言

上文我们初次认识了 Shell 脚本,本文我们就要学习 Shell 脚本的语法 ,争取做到学完本文,你也会写 Shell 脚本。

嵌入式 Linux 入门系列博文:
嵌入式 Linux 入门(一、Linux 基本介绍及文件结构)
嵌入式 Linux 入门(二、Linux 文件系统、文件类型及权限管理)
嵌入式 Linux 入门(三、Linux Shell 及常用命令说明)
嵌入式 Linux 入门(四、Linux 下的编辑器 — 让人爱恨交加的 vi )
嵌入式 Linux 入门(五、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 脚本:

#!/bin/sh
#正点原子@ALIENTEK 
#USB WIFI脚本 
#功能:脚本支持station模式、softap模式、bridge模式相互切换(若相互切换不成功,请重启板子)

#使用方法说明
usage_and_exit() {
	echo ""
# ...使用方法都是打印输出内容被我删除了,节约版面
    return
}

#版本信息
version() {
	echo "version 1.0"
	echo "作者:DZM@ALIENTEK"
}

#获取参数
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

#station Mode(上网模式)
if [ "$mode" == "station" ]; then 
	processkill
	sleep 1
	sync
	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 
    echo -ne "ctrl_interface=/var/run/wpa_supplicant\n
update_config=1\nnetwork={\nssid=\"$ssid\"\npsk=\"$psk\"\n}\n" > $wifi_conf
	rm -rf /var/lib/misc/*
	ifconfig eth0 down
	ifconfig eth1 down
	ifconfig waln0 down
	ifconfig wlan0 up
	sleep 2
	wpa_supplicant -B -D wext -i $device -c $wifi_conf
	udhcpc -R -b -i wlan0
	route add default gw 192.168.1.1
	export WIFI_MODE=station
fi

#删除wifi.conf
rm -rf /$PWD/wifi.conf
case $mode in
    softap|station|bridge)echo "WIFI设置$mode模式完成!"
    ;;

esac

#卸载环境变量
unset device
unset wifi_conf
unset a
unset ethernet
unset mode
unset ssid
unset psk

sync

二、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" 

<3 在 执行命令 或 运算 的时候,都需要使用倒引号。

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 脚本语言是弱类型语言,定义变量时不需要指定类型。

在这里插入图片描述

3种方式定义变量:

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

如果不加双引号或者单引号,如果字符串中有空格或者 TAB 按键,则没法识别出来,如下示例(示例中用到了变量使用方式,就在下文可以看到,这里为了说明这个问题,提前使用):

在这里插入图片描述

双引号,单引号有什么区别呢? 这就要结合上面我们所说的知识类分析了:

在这里插入图片描述

<3 建议变量定义都加双引号。

2.2.2 使用变量

基本使用

使用一个定义过的变量,只要在变量名前面加美元符号$即可:

$variable
${variable}

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

把所有变量加上花括号,这是个好的编程习惯!

在上面我们已经给过使用变量的示例了,这里就不放示例。

重命名变量

重命名变量和 C 语言一样,在后面直接重新赋值即可,如下图:

在这里插入图片描述

将命令的值赋值给变量

变量可以直接赋值,也可以赋值为 命令运行的结果,使用如下方式:

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

在这里插入图片描述

只读变量

variable=“test_only_read” 
readonly variable

删除变量

unset variable

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

在这里插入图片描述

2.2.3 特殊变量

在 Shell 脚本中,有一些特殊的变量,前面我们说过$符号用来表示变量的值,这些特殊变量都是以$开头的,如下:

变量 含义
$0 代表当前脚本的文件名
$n(n>=1) 代表传入脚本或函数的第n个参数
$# 代表传入的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$@ 与$*相同,但是使用时加双引号,并在引号中返回每个参数
$? 显示最后命令的退出状态或者函数的返回值。0表示没有错误,其他任何值表明有错误。
$$ 脚本运行的当前进程号

$*$@ 区别

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

<3 $*$@ 区别在下文 2.7 流控语句中的 2.7.3 for in 语句有示例说明

在这里插入图片描述在这里插入图片描述

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' 等效

看示例:

在这里插入图片描述

获取字符串长度

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

在这里插入图片描述

提取子字符串

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

:

2.3.2 数组

理论的知识引用至 菜鸟教程,我们都会通过实例展示一下。

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

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

定义数组

在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

数组名=(值1 值2 ... 值n)

读取数组

这里和 C 语言数组类似:

${数组名[下标]}

使用 @ 符号可以获取数组中的所有元素

${数组名[@]}

获取数组的长度

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

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

在这里插入图片描述

2.4 注释

在前面我们说 Shell 脚本符号的时候,我们知道了使用 # 号可以表示注释,类似于C语言多种的 // .

多行注释,在C中我们比较简单的使用 /* ...... */,如下:

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

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

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

EOF 也可以使用其他符号:

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

在这里插入图片描述

2.5 读取控制台输入

使用 read 读取控制台输入:

read (选项) (参数)

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

使用直接看示例:

在这里插入图片描述

2.6 运算符

和其他编程语言一样,Shell 脚本也支持多种运算符。

2.6.1 基本算数运算

下图引用菜鸟教程:

在这里插入图片描述
上面的 expr 是一款表达式计算工具,教程解释道原生bash不支持简单的数学运算,所以需要用expr。

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

但是我们除了使用 expr 还可以使用 (( )) ,具体如下示例:

在这里插入图片描述

2.6.2 条件判断

在 Shell 脚本中,检测某个条件是否成立的方式如下:

[ condition ](注意 condition 前后要有空格)
[ $a -eq $b ] 
test condition
test $a $b -eq
运算符 作用 示例
-eq 检测两个数是否相等,相等返回 true [ $a -eq $b ] 返回 false(a=1 b=2 下同)
-ne 检测两个数是否不相等,不相等返回 true [ $a -ne $b ] 返回 true
-gt 检测左边的数是否大于右边的,如果是,则返回 true [ $a -gt $b ] 返回 false
-lt 检测左边的数是否小于右边的 ~ [ $a -lt $b ] 返回 true
-ge 检测左边的数是否大于等于右边的 ~ [ $a -ge $b ] 返回 false
-le 检测左边的数是否小于等于右边的 ~ [ $a -le $b ] 返回 true
下面是字符串关系运算
-z str 检测字符串 str 是否为空,为空返回 true。 [ -z $a ] 返回 false(a=‘abc’ b=‘def’)下同
-n str 检测字符串 str 是否为 非空 ~ [ -n “$a” ] 返回 true
=和== 检测两个字符串是否相等,相等返回 true [ $a = $b ] 返回 falsee
!= 检测两个字符串是否不相等 ~ [ $a != $b ] 返回 true
$ 检测字符串是否不为空 ~ [ $a ] 返回 true
下面是文件判断相关
-d filename 判断文件是否存在,并且是一个目录文件
-f filename 判断文件是否存在,并且是一个普通文件
-e filename 判断文件是否存在
…by 矜辰所致整理,CSDN,公众号同名 …省略后面
文件权限判断
-r filename 检测文件是否可读,如果是,则返回 true
-w filename 检测文件是否可写,如果是,则返回 true
-x filename 检测文件是否可执行
文件类型判断
-b filename 检测文件是否是块设备文件
-c filename 检测文件是否是字符设备文件

关于逻辑判断的符号:

[ ] : 中括号旁边和运算符两边必须添加空格 (可以使用,不推荐)

[[ ]]:中括号旁边和运算符两边必须添加空格 (字符串验证时,推荐使用)

(()) : 中括号旁边和运算符两边必须添加空格 (数字验证时,推荐使用)

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

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

2.6.3 布尔运算

布尔运算也和 C 语言意思类似,好理解,主要在于如何使用:

运算符 含义
! 非运算,表达式为 true 则返回 false,否则返回 true
-o 或运算
-a 与运算

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

在这里插入图片描述

在上面的示例中,我加了条件判断的 if 语句,这个在下文我们讲解条件语句的时候会说明。

2.6.4 逻辑运算

逻辑运算有与 && 和 或|| ,和 C 语言一样这个好理解。

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

在这里插入图片描述

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

在这里插入图片描述

这个中括号上面只需要一个,这里需要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

直接做个测试:

在这里插入图片描述

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,示例如下:

在这里插入图片描述

2.7.3 for in 语句

for in 语句这里就和 C 区别大了,记住格式把:

for var in item1 item2 ... itemN  #后面这些叫in列表
do
    #执行语句
done

当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。

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

in 列表是可选的,如果不用它,for循环使用命令行的位置参数。

在这里插入图片描述

再看 $*$@ 区别 ,通过下面的示例说明:

没有双引号时候效果一样:

在这里插入图片描述

有双引号时候有了区别:

在这里插入图片描述

2.7.4 while 循环

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

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

在这里插入图片描述

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 返回,如果不加,将以最后一条命令运行结果作为返回值 。

函数示例如下:

在这里插入图片描述

2.9 输入/输出重定向

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

2.9.1 重定向的定义和用途

我用简单的白话说明一下,就是你在 Linux 下打开一个终端,输入 Shell 命令(这个你输入的命令就输入,这很好理解),输入 Shell 命令以后,会根据你输入的命令显示结果(这个结果,就是你的输出,是直接在你打开的这个终端下面显示,就是输出就在这个终端显示)。

我们说重定向,比如输出重定向,就是本来你输入的 Shell 命令结果会在你这个终端中显示的,但是你给他 重新定了一个方向,让他输出到其他地方,比如某个文件,就叫做输出重定向。

举个简单的例子,比如你敲 ls 指令查看当前文件夹下面的目录,使用 ls 输出重定向以后,它就不会在你这个终端中直接显示出 ls 的结果,而是到你重定向的那个地方去了,比如:

在这里插入图片描述

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

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

☆ 输出重定向可以把命令的结果保存到文件中,目的在于可以给管理员做记录分析,或者对其进行更进一步的处理。☆

2.9.2 Linux 下的标准输入输出

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

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

一般情况下,每个 Linux 命令运行时都会打开三个文件:

设备 设备文件名 文件描述符 类型
键盘 /dev/stdin 0 标准输入
显示器 /dev/sdtout 1 标准输出
显示器 /dev/sdterr 2 标准错误输出

2.9.3 重定向命令

重定向使用如下命令操作:

类型 命令 说明
标准输出重定向 command > file 以覆盖的方式,将输出重定向到 file
标准输出重定向 command >> file 将输出以追加的方式重定向到 file
标准错误输出重定向 command 2>file 以覆盖的方式,将错误输出输出到指定的file,即吧 stderr 重定向到 file, 2>是一体的,中间不能有空格
标准错误输出重定向 command 2>>file 追加方式将错误重定向
输入重定向 command < file 将输入重定向到 file
矜辰所致 SCDN、公众号 文件描述符表示的重定向 文件描述符的理解可通过上面的标准输出输出设备理解
n > file 覆盖方式,将文件描述符为 n 的文件重定向到 file
n >> file 追加方式 ,将文件描述符为 n 的文件重定向到 file
n >& m 将输出文件 m 和 n 合并

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

上面列表中那么多,要想理解需要自己试一试,结合实例才更加直观的可以理解,这里我举几个简单的例子。

标准输出重定向示例
在这里插入图片描述

标准错误输出重定向示例

在这里插入图片描述

那么问题来了,正确命令和错误命令的输出格式不一样,我们需要人为判断一下正确命令还是错误命令?

我要是能知道这条命令是错的(有时候粗心也会写错),我直接改正不就好了,所以我们要讲如何把正确的输出和错误的输出同时保存在指定的文件之中的方式。

正确和错误输出同时保存

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

命令 说明
command > file 2>&1 以覆盖方式,把正确输出和错误输出都保存到同一个文件当中,2和1是文件描述符,可通过上面的标准输出输出设备理解
command >> file 2>&1 追加方式,都保存到同一个文件
command &> file 覆盖方式,都保存到同一个文件
command &>> file 追加 …

在这里插入图片描述
在这里插入图片描述

正确和错误输出分开保存

命令 说明
command >> file1 2>>file2 把正确的输出追加到file1,把错误的输出追加到file2

直接看示例:

在这里插入图片描述

分开保存的方式也是常见的应用,这样我们在查看脚本运行出错的时候查找问题可以直接查看错误输出以便快速定位问题。

2.9.4 /dev/null 文件

/dev/null 文件,相当于垃圾箱,是在我们希望某个命令执行但是又不需要他的输出的时候使用的。

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

$ command > /dev/null

再根据我们上面的学习,我们想要把正确和错误信息都不需要输出,可以使用下面的指令:

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

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

简单举个例子说明,我们在写 Shell 脚本的时候,有些命令行会有一些输出,这个输出对我们整个 Shell 脚本没有任何意义,Shell 脚本批量执行的时候会有很多输出,这种没有意义的输出我们就需要把它们给屏蔽了,就可以使用上面的方式给它“丢到垃圾箱”。

结语

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

文章首发发于 CSDN 矜辰所致,矜辰所致,CSDN、公众号同名,手动防抄袭……

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

另外提一下,Shell 脚本对命令中多余的空格不进行任何的处理,所以我们在平时编写程序的时候为了是程序可读性强,可以合理的使用空格 。

好了,本文就到这里,在以后如果在使用 Shell 脚本的时候有新的注意事项,文章会保持更新!

谢谢大家!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。