【Linux软件编程学习笔记】Shell循环、数组、函数 + C语言IO文件操作
📖今日学习内容:shell while、for循环,shell数组,shell函数; C语言IO文件操作。
🔍文章大纲
- shell while循环
- shell for循环
- shell数组
- shell函数
- C语言文件IO操作(4.2 IO)
- ✨学习重点
- ⚠️易错点汇总
- 📝今日学习小结
1. while循环
while属于条件循环,条件为真就循环执行;条件为假退出循环。
语法格式
while 条件
do
# 循环体代码
done
示例1:计数循环
i=1
while [ $i -le 5 ]
do
echo "i = $i"
i=$((i+1))
done
示例2:死循环
while true
do
echo "死循环"
done
退出循环快捷键:
Ctrl + C
拓展:until循环,条件为假执行,条件为真退出,日常开发用的比较少。
2. for循环
shell有两种for写法:遍历集合版 、C语言风格for循环。
写法1:遍历列表(最常用)
for var in 1 2 3 4 5
do
echo $var
done
配合通配符遍历目录文件:
#遍历当前目录所有sh文件
for file in *.sh
do
echo $file
done
写法2:C语言风格for循环
for ((i=0; i<5; i++))
do
echo $i
done
循环控制语句
break:跳出整个循环,直接结束循环continue:跳过本次循环剩余代码,直接进入下一轮循环
3. Shell数组
shell数组下标从0开始,shell数组不限制元素类型,内部全部存储字符串。
3.1 数组定义
#方式1:整体赋值
arr=(apple banana orange)
#方式2:单独给下标赋值
arr[3]="pear"
3.2 数组读取
echo ${arr[0]} #读取下标0元素
echo ${arr[@]} #读取数组全部元素
echo ${arr[*]} #读取数组全部元素
echo ${#arr[@]} #获取数组元素总个数
${arr[@]}和${arr[*]}区别:
@:把每一个元素作为独立个体;*:所有元素合并成一整个字符串。
3.3 数组遍历
arr=(10 20 30 40)
for v in ${arr[@]}
do
echo $v
done
3.4 修改、删除数组元素
arr[1]=200 #修改指定下标元素
unset arr[2] #删除下标为2的元素,**下标不会自动补齐**
unset arr #删除整个数组
注意:
unset arr[2]删除后,数组下标会保留空缺,不会自动重排!
4. Shell函数
把重复代码封装成函数,实现代码复用,提高脚本可读性。
4.1 函数定义语法
方式一
func_name()
{
#函数体
}
方式二
function func_name
{
#函数体
}
4.2 函数调用
直接写函数名调用,不需要括号
func_name
4.3 函数传参
shell函数没有形参列表,依靠位置变量
$1 $2 $3 $# $@ $* $?获取传入参数。
test_func()
{
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "参数总个数:$#"
}
test_func aa bb cc
4.4 函数返回值
- shell函数的
return只能返回0‑255之间数字,用来表示执行状态,0成功,非0失败; - 如果想要返回字符串/大数,不能用return;可以在函数内部
echo输出,调用时用变量捕获输出结果。
示例:捕获函数输出
add()
{
echo $(( $1 + $2 ))
}
res=$(add 10 20)
echo "结果=$res"
注意:
return不能返回字符串,超出255数字会发生取模截断。
4.5 变量作用域
-
脚本直接定义变量:全局变量,函数内外都可以访问。
-
函数内部使用
local修饰:局部变量,仅函数内部有效,函数执行结束销毁。demo()
{
local num=100
echo $num
}
5. C语言文件IO操作( IO)
Linux下一切皆文件。C语言标准IO库
<stdio.h>,是对系统调用open/read/write的封装,带用户缓冲区;文件操作围绕FILE*文件指针完成。
5.1 三个标准流(程序启动自动打开)
| 文件指针 | 描述 | 文件描述符 |
|---|---|---|
stdin |
标准输入,键盘 | 0 |
stdout |
标准输出,终端屏幕 | 1 |
stderr |
标准错误输出,终端屏幕 | 2 |
5.2 文件打开与关闭 fopen / fclose
#include <stdio.h>
FILE *fp;
//打开文件
fp = fopen("test.txt","r");
//必须判断打开是否成功!打开失败返回NULL
if(NULL == fp)
{
perror("fopen"); //打印出错原因
return -1;
}
//关闭文件,用完一定要关闭,刷新缓冲区,释放资源
fclose(fp);
fp = NULL; //置空,防止野指针
打开模式
| 模式 | 含义 |
|---|---|
r |
只读,文件必须存在,否则打开失败 |
w |
只写,文件不存在创建;存在则清空原有内容 |
a |
追加写,文件不存在创建;在文件末尾追加 |
r+ |
可读可写,文件必须存在 |
w+ |
可读可写,不存在创建,存在清空 |
a+ |
可读+追加写 |
b模式:
rb/wb二进制读写;Linux下文本文件和二进制文件无区别,Windows需要区分。
5.3 单字符读写 fgetc / fputc
int ch;
ch = fgetc(fp); //从文件读取一个字符,读到末尾返回EOF(-1)
fputc('A', fp); //向文件写入一个字符
5.4 按行读写 fgets / fputs
char buf[32]={0};
fgets(buf, sizeof(buf), fp); //读取一行,最多读size‑1个字节,会读取换行\n
fputs(buf, fp); //把字符串写入文件,**不会自动加换行**
5.5 格式化读写 fprintf / fscanf
//向文件格式化输出
fprintf(fp,"num = %d\n",100);
//从文件格式化读取
int a;
fscanf(fp,"%d",&a);
5.6 二进制读写 fread / fwrite
用于读写结构体、二进制数据
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
- ptr:数据缓冲区
- size:单个元素字节大小
- nmemb:读写多少个元素
- 返回值:成功读写的元素个数,不是字节数
5.7 文件位置指针(文件偏移)
fseek(fp, offset, whence); //移动文件位置指针
// whence: SEEK_SET 文件开头;SEEK_CUR 当前位置;SEEK_END 文件末尾
ftell(fp); //获取当前距离文件开头的字节偏移量
rewind(fp); //将文件指针重置到文件开头
5.8 缓冲区
标准IO自带用户态缓冲区:
- 行缓冲:stdout,遇到
\n才刷新缓冲区; - 全缓冲:普通磁盘文件,缓冲区满才刷新;
- 无缓冲:stderr,直接输出。
手动刷新缓冲区:
fflush(fp);
5.9 判断文件结束
feof(fp):判断上一次读取是否到达文件末尾,不能作为循环条件直接放最前面!
✨学习重点
Shell部分
- while循环 :条件判断格式,死循环写法,
break continue控制循环。 - for循环两种写法 :遍历集合for、C语言风格
for((;;)),掌握通配符遍历文件。 - shell数组:
${arr[@]}获取全部元素;${#arr[@]}得到数组长度;@与*的区别;unset删除数组元素的行为特点。
- shell函数:
- 函数定义、调用不带括号;
- 函数传参依靠位置变量
$1 $2 $#,没有形参列表; return只能返回0‑255状态码;获取字符串返回值要用$(函数)捕获echo输出;local定义函数局部变量。
C语言文件IO部分
FILE*文件指针;fopen打开,一定要判断返回值是否为NULL ;用完fclose关闭文件。- 分清6种打开模式
r w a r+ w+ a+,重点区分w清空、a追加。 - 文本读写:
fgetc/fputc字符;fgets/fputs行;fprintf/fscanf格式化。 - 二进制读写
fread/fwrite,返回值是成功读写的元素个数,不是字节数。 - 文件指针移动:
fseek、ftell、rewind三个函数。 - 标准IO缓冲区概念:行缓冲、全缓冲,
fflush手动刷新缓冲区。 stdin stdout stderr三个标准流。
⚠️易错点汇总
Shell易错
- while条件的
[],括号内侧必须留空格 :while [ $i -le 5 ],少空格直接语法报错。 - shell算术运算不能直接写
i=i+1,必须写成i=$((i+1))。 - 数组
unset arr[2]只是清空该下标内容,不会重新排列下标,遍历会跳过该下标。 - shell函数调用不能加括号 ,
test_func()是定义;调用写test_func。 - 函数
return只支持0‑255数字,不能返回字符串,不能返回大于255数字 ,大数/字符串用echo+$()捕获。 - 函数内不写
local定义的变量全部是全局变量,容易出现变量污染。
C语言文件IO易错
fopen打开文件之后不判断NULL,直接操作fp,发生段错误。- 打开模式混淆:
w会直接清空原有文件内容;a只能在末尾追加,不能修改中间数据。 fgets会读取换行符\n,读到的字符串末尾可能自带换行,需要手动处理。fread/fwrite返回值是元素个数,不是字节数,判断返回值不要和字节数对比。feof()不能放在循环最开头!只有读操作之后,才能用feof判断是否到文件末尾。- 文件打开之后忘记
fclose,缓冲区数据丢失,资源泄露。 fseek打开"a"追加模式时,部分系统下写操作会强制回到文件末尾,seek修改位置不生效。- 缓冲区未刷新:程序崩溃时,缓冲区中还没写入磁盘的数据直接丢失,可以使用
fflush强制刷盘。
📝今日学习小结
今天分为两大模块:
- shell脚本:掌握
while、for循环;shell数组增删遍历;shell函数定义调用、传参、返回值、局部变量。shell语法对空格符号非常敏感,写脚本要注意语法坑。 - C语言标准文件IO:基于
FILE*完成文件打开关闭、字符/行/格式化/二进制读写;掌握文件指针偏移;理解用户缓冲区机制。>
注意区分:shell是脚本语言;C语言IO是库函数,封装了系统调用,自带缓冲区,操作磁盘文件必须做好判空、关闭,防止内存、资源泄露。