C语言阶段二·Linuxday4

一、shell中的输入和输出

1.1 输出--echo

类似于 printf

#!/bin/bash 
V1=hello
echo $V1  #echo是自带换行符的
echo "hello world"
V2=beijing
echo $V1 $V2 #可以一行输出多个值
#默认是不识别转义字符 \n 的 如果需要识别 得加 -e 选项
echo -e "hello\nworld"

1.2 输入--read

类似于scanf

#!/bin/bash 
read V1
echo $V1
echo "---------------------------"
read V1 V2 #多个变量输入时 用空格分隔
			#如果要输入的值 本身就有空格
			#建议写多行 read 指令 分别获取  否则有冲突
echo $V1
echo $V2
echo "---------------------------"
#如果要输入数组 需要加 -a 选项  输入时要用空格分隔数组元素
read -a ARR
echo ${ARR[*]}
echo ${ARR[0]}
echo ${ARR[1]}
echo "---------------------------"
# -p 可以加描述字段 用作输入的提示
read -p "请输入一个数:" V1
echo $V1
echo "---------------------------"
#read -n  5 表示最多只获取5个字符 输入够5个字符了
#及时不敲回车 也会继续向下执行
read -n 6 V1
echo $V1
echo "---------------------------"
#read  -t  5  表示等待输入时只等待 5秒
#超过5秒没输入 就不等了 继续向下执行
read -t 5 V1
echo $V1
echo "---------------------------"
#read -s 表示 取消输入的回显
read -s V1
echo $V1
echo "---------------------------"
#多个选项可以同时使用  但是要注意  选项和参数必须成对存在
read -p "请输入密码:" -n 5 -s PASSWD
echo $PASSWD

练习:

编写脚本:

在终端获取一个要下载的软件名

软件名不能超过20个字符

输入时间不能超过10秒

添加适当的提示信息

隐藏回显的功能

如果输入成功,则下载该软件,并运行该软件

#!/bin/bash 
read -n 20 -t 10 -p "请输入要下载的软件名(最多20字符 等待10秒)" -s NAME
sudo apt-get install $NAME #下载
$NAME #执行

二、shell中的运算符

shell本身使用来批量执行命令的,而不是用来做复杂运算的。

如果需要用到复杂的运算,可以使用shell调用C程序来处理问题。


但是有些场景下,简单的运算如果也调用C程序处理的话,就显得有点啰嗦

比如 计数变量的自增。这是,就可以用shell中的运算符来做一些简单的运算了。


shell中如果不使用运算符直接运算,那么默认都会将变量按字符串处理。


shell中的运算符:

(())

$[]

let

expr

上述四种运算符的执行效率从上到下是依次递减的,其中expr可以做字符串处理。

这四种方式至少会用一种(建议记住 (()) 的用法),其他的了解就行。

2.1 (())

((表达式))

((表达式1, 表达式2, ..., 表达式n))

# 当有多个表达式的时候,每个表达式都会参与运算

# 最后一个表达式的结果 就是整个(())运算符表达式的结果

注意事项:

1.在(())内部,引用变量值的时候,$ 可加可不加

2.在(())内部,运算符前后的空格,可加可不加

3.如果想获取表达式的结果 需要在整个(())前加 $ 如 $((A+B))

4.可以做复杂的运算,如 ++ -- for循环

for((i=0; i<100; i++))

例:

#!/bin/bash 
read A B
RET1=$((A+B))
RET1=$((A + B))
RET1=$(($A+$B))
RET1=$(($A + $B))
echo $RET1
#只要是用到表达式的结果 都得加 $
echo $((A+B))
echo A=$A
((++A))  #正确的
echo A=$A
#注意 如果是++运算 表达式中变量前就不能加$了
#因为加了之后 相当于  $A=$A+1
#(($A++))  #错误的
#(())支持复杂的运算 如 for循环  ----后面会详细讲
for((i=0;i<10;i++))
	echo $i
V3=100
V4=200
RET3=$((++V3, ++V4))
echo $V3 $V4  #1

2.2 $[]

$[表达式]

$[表达式1, 表达式2, ..., 表达式n]

# 当有多个表达式的时候,每个表达式都会参与运算

# 最后一个表达式的结果 就是整个$[]运算符表达式的结果

注意事项:

1.在$[]内部,引用变量值的时候,$ 可加可不加

2.在$[]内部,运算符前后的空格,可加可不加

3.$[]表达式的结果必须有变量来接 或者输出到终端 否则报错

$[A+B] #错误的 RET=$[A+B] echo $[A+B]

4.不可以做复杂的运算

$[i=0; i<100; i++] #错误的写法

例:

#!/bin/bash 
V1=10
V2=20
RET1=$[V1+V2]
RET1=$[V1 + V2]
RET1=$[$V1+$V2]
RET1=$[$V1 + $V2]
echo $RET1
RET2=$[++V1, ++V2]
echo $V1 $V2 #11 21
echo $RET2 #21
#表达式的结果必须有变量来接 或者输出到终端
#否则报错
$[V1++]  #虽然 V1的值被 ++ 了 但是 会报域法错误
echo $V1 #12
RET3=$[V1++]
echo $V1 #13
echo $[V1++] #13
#$[]不能左复杂的运算
#$[i=0;i<10;i++]  #没有这种写法

2.3 expr

1.在expr内部,引用变量值的时候,$ 必须加

2.在expr内部,运算符前后的空格,必须加

3.expr会自动将运算的结果输出到终端

4.如果想获取expr的结果,需要用命令置换符 $() 或者 ``

5.expr不能做复杂的运算 如 ++ --

但是可以用其他的写法实现++的功能,如 V1=`expr $V1 + 1`

6.expr在做乘法运算的时候 * 要写成 \*

#!/bin/bash 
v1=10
v2=20
#错误的用法
expr v1+v2   #v1+v2
expr $v1+$v2 #10+20
expr v1 + v2 #非整数参数
#正确的用法
expr $v1 + $v2
RET1=`expr $v1 + $v2`
echo $RET1
RET2=$(expr $v1 + $v2)
echo $RET2
#expr $v1++ #错误的
#可以替换成下面的写法
v1=`expr $v1 + 1`
echo $v1
#RET3=`expr $v1 * $v2`  #错误的
RET3=`expr $v1 \* $v2` #正确的
echo $RET3

expr的字符串处理--了解即可,前面学的字符串处理已经够用了

1.expr  match
		expr  match 源字符串 子字符串
		在源字符串中查找子字符串
		如果源字符串中的,第一个字符和子字符串不相等结果为0
		如果子字符串,有字符和源字符串中的字符不相等,返回的也是0.
		否则返回的就是子字符串的长度。
		#必须是从起点开始匹配   成功才返回长度
			string="www.hqyj.com.hqyj.com.qwer"
        string2="com"
        string3="www.hy"
        string4="www.hq"
        expr match $string $string2  #0
        expr match $string $string3  #0
        expr match $string $string4  #6
		2.expr substr
		string="www.hqyj.com.hqyj.com.qwer"
		expr substr $string 5 4   #hqyj  
		在string中截取从第5个字符开始,向后的4个字符
		3.expr index
		#提取指定字符的下标,单个字符有意义,如配置文件:
			string="my_file=/etc/passwd"
			num=`expr index $string "="`
			file_path=${string:$num}
			echo $file_path
			tail -5 $file_path
		#多个字符意义不大,如下说明
		string="www.hqyj.com.hqyj.com.qwer"
		expr index $string "qy"   #6
		expr index $string "pqy"   #6
		拿p字符在string查找第一次出现的位置,如果
		找到了就返回它在字符串中的下标,然后结束,
		否者找不到接着拿q字符在string查找第一次位置
		如果找到返回q在字符串中的下标,否则返回0
		4.expr length
		expr length $string
		获取string变量字符串的长度   等价于  ${#string}

2.4 let

let 表达式1 表达式2 ... 表达式n #中间用空格分隔

注意:let必须是完整的算术表达式,即有等号两边

如 let C=A+B 正确

C=`let A+B` 错误

1.let会运算所有的表达式

VAR1=30
VAR2=25
let RET=VAR1+VAR2 RET=RET+VAR1
echo $RET  #85

2.let中变量可以加$,也可以不加$

3.运算符的前后不能加空格

三、shell中的 if 语句

3.1 格式

if [ 表达式1 ]  #注意:if和[ 之间 [] 和表达式之间 都必须有空格
elif [ 表达式2 ] #注意:elif和[ 之间 [] 和表达式之间 都必须有空格
else              #else后面没有 then
fi

3.2 对字符串的判断

类似于strcmp。

注意:对字符串的判断最好加双引号 否则如果参与判断的字符串中有空格 就报错了。

-z 判断字符串是否为空

-n 判断字符串是否为非空

==或者= 判断字符串是否相等

!= 判断字符串是否不相等

\> 判断字符串的大小 大于

\< 判断字符串的大小 小于


拓展:

< 和 << 输入重定向符

> 和 >> 输出重定向符

> 覆盖写

>> 追加写

例:

#!/bin/bash 
read -p "input str1:" str1
read -p "input str2:" str2
if [ -z "$str1" ]
	echo "空"
if [ -n "$str3" ]
	echo "非空"
#判断相等时两个等号和一个等号都可以
#if [ "$str1" = "$str2" ]
if [ "$str1" == "$str2" ]
	echo "相等"
	echo "不相等"
# != 用法同理
if [ "$str1" \> "$str2" ]
	echo "yes"
	echo "no"
# \<  用法同理

3.3 将字符串按整型方式判断

-gt 大于 great than

-lt 小于(小写的L) less than

-eq 等于 equal

-ge 大于等于 great equal

-le 小于等于 less equal

-ne 不等于 not equal

逻辑运算

-a 逻辑与 and

-o 逻辑或 or

! 逻辑非

关于逻辑运算符,以逻辑与为例:

在两个 [] 之间要用 &&

在一个 [] 内部要用 -a

例:

#!/bin/bash 
read v1
read v2
if [ $v1 -eq $v2 ]
	echo "=="
	echo "!="
	if [ $v1 -gt $v2 ]
		echo "v1>v2"
		echo "v1<v2"
#if [ $v1 -gt 10 ] -a [ $v1 -lt 20 ] #错误的
#if [ $v1 -gt 10 && $v1 -lt 20 ]     #错误的
#if [ $v1 -gt 10 ] && [ $v1 -lt 20 ] #正确的
if [ $v1 -gt 10 -a $v1 -lt 20 ] #正确的
	echo "yes"
fi

练习:

输入一个成绩 输出成绩对应的等级 用shell实现一次。

[90, 100] A

[80, 90) B

[0, 80) 不及格

#!/bin/bash
read -p "请输入成绩:" NUM
if [ $NUM -ge 0 -a $NUM -le 100 ]
    echo "输入正确"
    echo "输入的成绩有误"
if [ $NUM -ge 90 -a $NUM -le 100 ]
    echo "A"
elif [ $NUM -ge 80 ] && [ $NUM -lt 90 ]
    echo "B"
    echo "不及格"
fi

3.4 判断文件的类型

-e 判断文件是否存在 存在为真 不存在为假

-s 判断文件是否存在,并判断文件是否为非空,非空为真 空为假

-b 判断文件是否存在,并且判断文件是否为块设备文件

-S (大写)判断文件是否存在,并且判断文件是否为套接字文件

-p 判断文件是否存在,并且判断文件是否为管道文件

-f 判断文件是否存在,并且判断文件是否为普通文件

-L (大写)判断文件是否存在,并且判断文件是否为软链接文件

-c 判断文件是否存在,并且判断文件是否为字符设备文件

-d 判断文件是否存在,并且判断文件是否为目录文件


注意:

判断类型时,要先判断链接文件,

因为链接文件的类型既算是链接文件 也算是他链接的文件的类型

所以先判断链接文件,是因为其他文件不会被识别成链接文件

例:

输入一个文件,输出文件的类型

#!/bin/bash 
read -p "input pathname:" FILENAME
if [ -e $FILENAME ]
	echo "存在"
	if [ -L $FILENAME ]
		echo "链接文件"
	elif [ -S $FILENAME ]
		echo "套接子文件"
	elif [ -p $FILENAME ]
		echo "管道文件"
	elif [ -b $FILENAME ]
		echo "块设备文件"
	elif [ -f $FILENAME ]
		echo "普通文件"
	elif [ -c $FILENAME ]
		echo "字符设备文件"
	elif [ -d $FILENAME ]
		echo "目录文件"
	echo "不存在"
fi

3.5 判断文件的权限

-r 判断文件是否存在,并判断是否有读权限

-w 判断文件是否存在,并判断是否有写权限

-x 判断文件是否存在,并判断是否有执行权限


注意: 哪个用户来执行脚本,判断的就是改基本对哪个用户的权限

#!/bin/bash 
read -p "input pathname:" FILENAME
if [ -w $FILENAME ]  #读权限和执行权限判断方式同理
    echo "yes"
    echo "no"
fi

3.6 判断文件的时间戳

时间戳:文件最后一次修改的时间

-nt 判断前面文件的时间戳是否比后面的新 new than

-ot 判断前面文件的时间戳是否比后面的旧 old than

注意:离当前时间越近,时间戳越新。

#!/bin/bash 
read -p "input file1:" NAME1
read -p "input file2:" NAME2
if [ $NAME1 -nt $NAME2 ]
    echo "$NAME1 新"
    echo "$NAME2 新"
fi

练习:

输入一个文件

判断该文件是否存在,

如果存在,判断是否是普通文件

如果是,判断是否有写权限

如果有,将 "hello world" 追加到文件最后(echo "hello world" >> file)

哪个条件不符合,则输出对应的错误。

#!/bin/bash
read -p "input pathname:" NAME
if [ -e $NAME ]
    if [ -f $NAME ]
        if [ -w $NAME ]
            echo "hello world" >> $NAME
            echo "没有写权限"
        echo "不是普通文件"
    echo "不存在"
fi

四、case..in 语句

4.1 格式

case $变量 in
esac

注意:

case 后面一定是变量的值 也就是说变量名前一定有 $

选项中的值就是case后面的变量所有可能的结果

* 选项表示其他分支,相当于switch..case中的 default分支

4.2 关于case的选项

选项是支持通配符的

[0-9] 通配0-9中的任意一个字符

[a-zA-Z] 通配 a-zA-Z中的任意一个字符 注意 涉及本地语序 LC_ALL=C

"aa"|"bb"|"cc" 通过 aa 或者 bb 或者 cc

[abc] 通配[abc]中的任意一个字符

[^abc] 通配除了[abc]中的任意一个字符。。。这种写法逻辑是反的 基本不使用


例:

#!/bin/bash 
read -p "input a value:" VALUE
case $VALUE in
	"beijing"|"shanghai")
		echo "房价贵"
	class[0-9])
		echo "教室名"
	[a-z])
		echo "a-z"
	hqyj[abc])
		echo "hqyja hqyjb hqyjc"
		echo "other"
esac

思考:

编写脚本的部分功能:

获取用户输入的内容,并决定是否下载某个软件

要求:不管用户输入的是 Y y YES yes Yes yEs YEs yeS ... 都识别为 下载

#!/bin/bash
read -p "您真的要下载该软件嘛 [Y/n]" CHOOSE
case $CHOOSE in
    [Yy][Ee][Ss]|[Yy])
        echo "yes"
        echo "no"
esac

五、shell中的while循环

5.1 格式

while 表达式
done

其中表达式的用法和 if 的表达式一样

例:1~10 求和

#!/bin/bash 
VALUE=1
SUM=0
while [ $VALUE -le 10 ]
    SUM=$((SUM+VALUE))
    ((VALUE++))
echo $SUM   #55

5.2 死循环

while [ 1 ]
while true
while ((1))
done

例如:使用shell中死循环每隔1秒输出系统时间

#!/bin/bash 
while [ 1 ]
    sleep 1
while ((1))
    sleep 1
while true
    sleep 1
done

练习:

使用while循环实现一个累加器的功能

可以一直在终端输入数据,直到遇到 "#" 结束

结束后,输出所有数据求和的结果。

#!/bin/bash
read VALUE
SUM=0
while [ $VALUE != "#" ]
    SUM=$[$SUM+$VALUE]
    read VALUE
echo $SUM 

六、shell中的for循环

6.1 C风格的for循环

for ((i=0;i<10;i++))
done

6.2 shell中特有的for循环

格式:

for 变量 in 单词列表
done

关于单词列表:

1.单词列表中的单词用空格分隔

2.如果单词列表是连续的,可以使用 {start..end} 如 {1..100}

也可以使用序列来处理 `seq 1 100` #遍历 [1, 100]

也可以按照一定的间隔取数据 `seq 1 2 100` #从1开始取数据 步长为2 到100结束

3.单词列表也可以是命令的结果(只要命令的结果是以空格分隔的就可以)

4.省略 in 的 for循环,这时 变量的值来自于执行脚本时 命令行的参数

例:

#!/bin/bash 
for i in aa bb cc dd
    echo $i
echo "-------------------"
for i in {1..10}
	echo "hello $i"
echo "-------------------"
for i in `seq 1 20`
	echo "hqyj $i"
echo "-------------------"
for i in `seq 1 2 20`
	echo "hqyj $i"
echo "-------------------"
for i in `ls`   #常用的写法 可以操作多个文件
	chmod a+x $i
echo "-------------------"
#i 的值来自 命令行的参数
for i
	echo $i
done

练习:

编写脚本,实现文件分类的功能。

注意:在脚本中使用家目录时 最好不要使用 ~ 最好使用环境变量 $HOME


1.判断一下当前用户的家目录下是否存在 dir_dir 和 file_dir 两个目录文件

如果存在,则询问用户是否清空

如果用户输入的是:Y 则清空(删除 并 新建)

如果用户输入的是其他字符串,则不清空直接退出程序

如果不存在则直接新建。

2.在终端输入一个 绝对路径

将该目录下的所有普通文件都复制到 file_dir

将该目录下的所有目录文件都复制到 dir_dir

3.将file_dir中所有的 .c 文件 g-w

4.输出复制的 普通文件 和 目录文件 的个数

5.将 file_dir 归档并压缩 file_dir.tar.gz

6.将 dir_dir 归档并压缩 dir_dir.tar.gz

7.列出家目录下所有文件的信息

8.列出dir_dir目录下所有文件的信息

9.列出file_dir目录下所有文件的信息

#!/bin/bash 
MY_DIR=$HOME/dir_dir
MY_FILE=$HOME/file_dir
FILE_COUNT=0
DIR_COUNT=0
if [ -e $MY_DIR ]
	read -p "$MY_DIR 已存在,是否清空[Y/n]" CHOOSE
	if [ $CHOOSE == "Y" ]
		rm -rf $MY_DIR
		mkdir $MY_DIR
		echo "不清空,程序已退出"
	mkdir $MY_DIR
if [ -e $MY_FILE ]
	read -p "$MY_FILE 已存在,是否清空[Y/n]" CHOOSE
	if [ $CHOOSE == "Y" ]
		rm -rf $MY_FILE
		mkdir $MY_FILE
		echo "不清空,程序已退出"
	mkdir $MY_FILE
#输入要操作的路径
read -p "请输入要操作的目录文件的 绝对路径:" PATHNAME
if [ -e $PATHNAME ]
	if [ -d $PATHNAME ]
		for i in `ls $PATHNAME`
			if [ -d $PATHNAME/$i ]
				cp -r $PATHNAME/$i $MY_DIR
				((DIR_COUNT++))
			elif [ -f $PATHNAME/$i ]
				cp $PATHNAME/$i $MY_FILE
				((FILE_COUNT++))
	echo "要操作的目录不存在 程序退出"
cd $MY_FILE
find ./ -type f |grep "\.c"|xargs chmod g-w
echo "共复制了 $DIR_COUNT 个目录文件"
echo "共复制了 $FILE_COUNT 个普通文件"
cd $HOME
tar -zcf dir_dir.tar.gz dir_dir
tar -zcf file_file.tar.gz file_dir
echo "------------HOME LIST--------------"
ls -l $HOME