嵌入式——Linux软件编程2

【Linux软件编程学习笔记】Shell循环、数组、函数 + C语言IO文件操作

📖今日学习内容:shell while、for循环,shell数组,shell函数; C语言IO文件操作。

🔍文章大纲

  1. shell while循环
  2. shell for循环
  3. shell数组
  4. shell函数
  5. C语言文件IO操作(4.2 IO)
  6. ✨学习重点
  7. ⚠️易错点汇总
  8. 📝今日学习小结

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 变量作用域

  1. 脚本直接定义变量:全局变量,函数内外都可以访问。

  2. 函数内部使用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自带用户态缓冲区

  1. 行缓冲:stdout,遇到\n才刷新缓冲区;
  2. 全缓冲:普通磁盘文件,缓冲区满才刷新;
  3. 无缓冲:stderr,直接输出。

手动刷新缓冲区:

复制代码
fflush(fp);

5.9 判断文件结束

feof(fp)判断上一次读取是否到达文件末尾,不能作为循环条件直接放最前面!


✨学习重点

Shell部分

  1. while循环 :条件判断格式,死循环写法,break continue控制循环。
  2. for循环两种写法 :遍历集合for、C语言风格for((;;)),掌握通配符遍历文件。
  3. shell数组:
    • ${arr[@]}获取全部元素;${#arr[@]}得到数组长度;
    • @*的区别;
    • unset删除数组元素的行为特点。
  4. shell函数:
    • 函数定义、调用不带括号
    • 函数传参依靠位置变量$1 $2 $#,没有形参列表;
    • return只能返回0‑255状态码;获取字符串返回值要用$(函数)捕获echo输出;
    • local定义函数局部变量。

C语言文件IO部分

  1. FILE*文件指针;fopen打开,一定要判断返回值是否为NULL ;用完fclose关闭文件。
  2. 分清6种打开模式 r w a r+ w+ a+,重点区分w清空、a追加。
  3. 文本读写:fgetc/fputc字符;fgets/fputs行;fprintf/fscanf格式化。
  4. 二进制读写fread/fwrite,返回值是成功读写的元素个数,不是字节数。
  5. 文件指针移动:fseekftellrewind三个函数。
  6. 标准IO缓冲区概念:行缓冲、全缓冲,fflush手动刷新缓冲区。
  7. stdin stdout stderr三个标准流。

⚠️易错点汇总

Shell易错

  1. while条件的[]括号内侧必须留空格while [ $i -le 5 ],少空格直接语法报错。
  2. shell算术运算不能直接写i=i+1,必须写成 i=$((i+1))
  3. 数组unset arr[2]只是清空该下标内容,不会重新排列下标,遍历会跳过该下标。
  4. shell函数调用不能加括号test_func()是定义;调用写test_func
  5. 函数return只支持0‑255数字,不能返回字符串,不能返回大于255数字 ,大数/字符串用echo+$()捕获。
  6. 函数内不写local定义的变量全部是全局变量,容易出现变量污染。

C语言文件IO易错

  1. fopen打开文件之后不判断NULL,直接操作fp,发生段错误。
  2. 打开模式混淆:w会直接清空原有文件内容;a只能在末尾追加,不能修改中间数据。
  3. fgets会读取换行符\n,读到的字符串末尾可能自带换行,需要手动处理。
  4. fread/fwrite返回值是元素个数,不是字节数,判断返回值不要和字节数对比。
  5. feof()不能放在循环最开头!只有读操作之后,才能用feof判断是否到文件末尾。
  6. 文件打开之后忘记fclose,缓冲区数据丢失,资源泄露。
  7. fseek打开"a"追加模式时,部分系统下写操作会强制回到文件末尾,seek修改位置不生效。
  8. 缓冲区未刷新:程序崩溃时,缓冲区中还没写入磁盘的数据直接丢失,可以使用fflush强制刷盘。

📝今日学习小结

今天分为两大模块:

  1. shell脚本:掌握whilefor循环;shell数组增删遍历;shell函数定义调用、传参、返回值、局部变量。shell语法对空格符号非常敏感,写脚本要注意语法坑。
  2. C语言标准文件IO:基于FILE*完成文件打开关闭、字符/行/格式化/二进制读写;掌握文件指针偏移;理解用户缓冲区机制。>

注意区分:shell是脚本语言;C语言IO是库函数,封装了系统调用,自带缓冲区,操作磁盘文件必须做好判空、关闭,防止内存、资源泄露。

相关推荐
Shell运维手记1 小时前
Linux (挂载|rpm|yum|源码编译)
linux·运维·笔记·网络协议·bash
-今昭-1 小时前
MFS高可用负载均衡
运维·负载均衡
ITyunwei09871 小时前
SSL 证书过期排查实战:openssl + Nginx 4步从发现到续签
运维·网络
郝开2 小时前
Docker Compose 本地环境搭建:kkFileView
运维·docker·容器
QYR_112 小时前
2032年气相分解设备市场规模达2.25亿美元:VPD向高灵敏度、自动化晶圆污染检测升级
运维·自动化
RisunJan2 小时前
Linux命令-vgchange(修改 LVM 卷组属性)
linux·运维·服务器
ZMacros2 小时前
Buildroot构建文件系统
linux·服务器
herinspace2 小时前
管家婆财贸ERP如何进行基本信息批量搬移
服务器·数据库·管家婆软件·财务软件
仓储管理员20252 小时前
不同业务场景下,如何匹配适配的WMS系统?
大数据·运维·云计算·精选