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

2023-10-26

嵌入式 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" 

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

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 按键,则没法识别出来,如下示例(示例中用到了变量使用方式,就在下文可以看到,这里为了说明这个问题,提前使用):

在这里插入图片描述

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

在这里插入图片描述

❤️ 建议变量定义都加双引号。

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”(传递了三个参数)。

❤️ $*$@ 区别在下文 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 脚本的时候有新的注意事项,文章会保持更新!

谢谢大家!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式 Linux 入门(六、Shell 脚本编程下:Shell 脚本语法) 的相关文章

  • Linux shell 根据第二列对文件进行排序?

    我有一个这样的文件 FirstName FamilyName Address PhoneNumber 如何按 FamilyName 排序 如果这是 UNIX sort k 2 file txt 您可以使用多个 k用于对多列进行排序的标志 例
  • Xvfb 冻结初始化 GLX 扩展

    我正在尝试运行无头 Xvfb 服务器来捕获 Amazon EC2 micro 上的屏幕截图 但它在 GLX 上陷入了困境 我使用此脚本安装了 GLX Xvfb 和所有库 https gist github com joekiller 414
  • 如何通过ssh获取远程命令的退出代码

    我正在通过 ssh 从远程计算机运行脚本 ssh some cmd my script 现在 我想在本地计算机上存储 shell 脚本的退出状态 我该怎么做 假设没有任何问题ssh其本身 其退出状态是在远程主机上执行的最后一个命令的退出状态
  • 我想在 Red Hat Linux 服务器中执行 .ps1 powershell 脚本

    我有一个在窗口中执行的 ps1 powershell 脚本 但我的整个数据都在 Linux 服务器中 有什么可能的方法可以让我在红帽服务器中执行 powershell 脚本 powershell脚本是 Clear Host path D D
  • 使用 shell 脚本发送 HTML 邮件

    如何使用 shell 脚本发送 HTML 电子邮件 首先 您需要撰写消息 最低限度由这两个标头组成 MIME Version 1 0 Content Type text html 以及适当的消息正文 p Hello world p 获得后
  • 使用 systemctl 获取 systemd 进程的正常运行时间或停机时间?

    喜欢使用systemctl is active
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • Bash:将字符串添加到文件末尾而不换行

    如何将字符串添加到文件末尾而不换行 例如 如果我使用 gt gt 它将添加到文件末尾并换行 cat list txt yourText1 root host 37 echo yourText2 gt gt list txt root hos
  • 链接错误:命令行中缺少 DSO

    我对 Linux 使用 Ubuntu 14 04 LTS 64 位 相当陌生 来自 Windows 并且正在尝试移植我现有的 CUDA 项目 当通过链接时 usr local cuda bin nvcc arch compute 30 co
  • Crontab 每 5 分钟一次 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我如何告诉 crontab 每 5 分钟运行一次 但从每小时的第二分钟开始 换句话说 我想在以下时间执行我的脚本minute 5 2 例如 我的脚本应
  • /sys/device/ 和 dmidecode 报告的不同 CPU 缓存大小

    我正在尝试获取系统中不同缓存级别的大小 我尝试了两种技术 a 使用 sys device 中的信息 这是输出 cat sys devices system cpu cpu0 cache index1 size 32K cat sys dev
  • 嵌入清单文件以要求具有 mingw32 的管理员执行级别

    我正在 ubuntu 下使用 i586 mingw32msvc 交叉编译应用程序 我很难理解如何嵌入清单文件以要求 mingw32 具有管理员执行级别 对于我的例子 我使用了这个hello c int main return 0 这个资源文
  • 如何从 C++ 程序中重新启动 Linux?

    我有一个 Qt 4 GUI 我需要在下拉菜单中提供一个选项 允许用户选择重新启动计算机 我意识到这对于以其他方式重新启动计算机的能力来说似乎是多余的 但选择需要保留在那里 我尝试使用 system 来调用以下内容 suid root she
  • 为 Linux 编译 Objective-C 应用程序(API 覆盖范围)

    我可能在这里问一些奇怪的问题 但我不确定从哪里开始 问题是我正在考虑使用 Obj C 和 Foundation 类在 Mac 上编写一个命令行工具 但存在一个非常大的风险 那就是我希望能够为不同的 Linux 发行版编译它 以便将来作为服务
  • Python 3.4.3 subprocess.Popen 在没有管道的情况下获取命令的输出?

    我试图将命令的输出分配给变量 而不让命令认为它正在通过管道传输 原因是 如果正在通过管道传输 则相关命令会给出未格式化的文本作为输出 但如果从终端运行 则会给出颜色格式化的文本 我需要获取这种颜色格式的文本 到目前为止我已经尝试了一些事情
  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • 如何在 Linux 上通过 FTP 递归下载文件夹 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 在 docker 中重定向命令输出

    我想为我的服务器做一些简单的日志记录 它是一个在 Docker 容器中运行的小型 Flask 应用程序 这是 Dockerfile Dockerfile FROM dreen flask MAINTAINER dreen WORKDIR s
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in

随机推荐

  • 矩阵卷积运算过程讲解

    写了那么久的博客 始于Python爬虫 目前专于Java学习 终于有了属于自己的小窝 欢迎各位访问我的个人网站 未来我们一起交流进步 在爬虫处理验证码的过程中接触到矩阵卷积运算 关于该类运算 记录一下自己的心得 理论知识 在讲述卷积过程前
  • windows 串口中断编程_51单片机的中断及其使用方法

    51单片机采用中断方式的串口通信过程及程序分析 所谓中断方式 就是串口收 发标志位出发中断后 在中断中执行既定操作 可通过函数调用来实现 接收数据时 等待中断 gt 然后在中断中接收数据 发送数据时 发送数据 gt 等待中断 gt 然后在中
  • 3-linux集群搭建-Hive

    1 导入包并配置环境变量 导入需要的包 如hive mysql等 解压上传的包到指定文件 tar zxvf apache hive 2 0 0 bin tar gz C opt software 然后进入文件夹下改名 mv apache h
  • Springboot日志系统工作原理

    通过日志信息查找日志框架 当我们启动一个springboot项目时 我们会在控制台看到很多日志信息 如下图所示 那这些日志信息究竟是怎么打印出来的呢 就让我们一起来看看吧 2020 10 13 22 17 41 010 INFO 4569
  • three.js学习之环境贴图

    设置cube纹理加载器 设置cube纹理加载器 const cubeTextureLoader new THREE CubeTextureLoader const envMapTexture cubeTextureLoader load s
  • DS18B20_单总线协议

    H文件 ifndef ONEWIRE H define ONEWIRE H include STC15F2K60S2 H include
  • 腾讯员工人均年薪84.7万,再次冲上热搜

    腾讯2020年员工平均月薪6 76万元 2021年人均月薪7 06万元 消息一出立马登上热搜 由财经网科技刚已发出两分钟的时间 转发和赞同人数超过了1万 2021年腾讯营收5601 18亿元 同比去年增长了16 净利润1237 88亿元 同
  • nodejs:非docker下设置NODE_ENV

    参考 使用process env NODE ENV的正确姿势 掘金
  • Linux Server安全配置基线(等保)

    第1章 概述 1 1 目的 本文档规定了所有维护管理的Linux操作系统的主机应当遵循的操作系统安全性设置标准 本文档旨在指导系统管理人员进行Linux操作系统的安全合规性检查和配置 1 2 适用范围 本配置标准的使用者包括 服务器管理员
  • springmvc组件HandleMapping源码-RequestMappingHandlerMapping

    Copyright 2002 2019 the original author or authors Licensed under the Apache License Version 2 0 the License you may not
  • Java中接口的多态

    多态参数 就像我们现实生活中电脑的usb接口 我们既可以接受手机对象 又可以接受相机对象 等等 体现了接口的多态 查看以下代码 接口 package InterfaceM public interface Interface public
  • tftp服务器权限配置文件,tftp服务器权限配置

    tftp服务器权限配置 内容精选 换一换 华为云帮助中心 为用户提供产品简介 价格说明 购买指南 用户指南 API参考 最佳实践 常见问题 视频帮助等技术文档 帮助您快速上手使用华为云服务 本课程主要针对openEuler操作系统工程师在基
  • VUE-element-admin之配置多级路由菜单

    步骤 routers js中添加如下代码 path usermanagement alwaysShow true 是否显示父级 如果为false则只显示最内层菜单 默认false component Layout hidden false
  • NIO、AIO、BIO的区别

    一 同步阻塞I O BIO 同步阻塞I O 服务器实现模式为一个连接一个线程 即客户端有连接请求时服务器就需要启动一个线程进行处理 如果这个连接不做任何事情会造成不必要的线程开销 可以通过线程池机制来改善 BIO方式适用于连接数目比较少且固
  • Springboot配置的端口号不起作用

    在application yml文件中 进行了如下配置 server port 32088 spring profiles active dev spring application name consul client 启动项目后发现 端
  • ElasticSearch基础(一)

    ElasticSearch 适用场景 日志可视化 ELK组合 方便查询定位业务问题 存储非结构化数据 有些场景存储复杂嵌套的关系类型 使用关系型数据库联合查询将会很繁琐 并且影响性能 这时ElasticSearch是个不错的选择 全文搜索引
  • 分频电路的实现:奇数分频、偶数分频和小数分频

    目录 偶数分频 奇数分频 N 0 5分频 任意小数分频 偶数分频 偶数分频是最简单的 N分频需要计数到 N 1 并在 N 2 1 和 N 1 处更改输出的取值即可 只需要单一时钟沿计数 下面是四分频电路的实现 代码 module div4
  • 【满分】【华为OD机试真题2023B卷 JAVA&JS】数据分类

    华为OD2023 B卷 机试题库全覆盖 刷题指南点这里 数据分类 知识点位运算 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 对一个数据a进行分类 分类方法为 此数据a 四个字节大小 的四个字节相加对一个给定的值b取模 如
  • 地理信息安全在线培训考试系统题库-填空题

    第1题 依据 中华人民共和国保守国家秘密法实施条例 定密授权应当以 书面 形式作出 第2题 机关 单位对符合保密法的规定 但保密事项范围没有规定的不明确事项 应当先行拟定密级 保密期限和知悉范围 采取相应的保密措施 并自拟定之日起 10 日
  • 嵌入式 Linux 入门(六、Shell 脚本编程下:Shell 脚本语法)

    嵌入式 Linux 入门第六课 继续完成 Shell 脚本学习 本文学习 Shell 脚本语法 矜辰所致 前言 上文我们初次认识了 Shell 脚本 本文我们就要学习 Shell 脚本的语法 争取做到学完本文 你也会写 Shell 脚本 g