shell 中,为什么 while + read 能逐行读取输入

例如:需要获取容器的 ID 和 Name,并输出

containers=$(docker ps --format "{{.ID}}:{{.Names}}")

while IFS=':' read -r cid cname ;do
    echo "container_id: ${cid}  container_name: $cname"
done <<< "$containers"

1、while 循环体

while 循环标准结构:

while [ 条件判断 ]  ; do
 # 满足条件执行的语句
done

######################
# 1、执行“条件命令”
# 2、看退出码
	0:进入 do
	非0:跳出循环
# 3、 回到第1步
######################

重点注意:SHELL 没有布尔值(true/false),只有退出码。判断条件是否满足,while 是否继续,取决于「条件命令」的退出码([ 条件判断 ] 本质是一个命令)


2、理解 read 命令

read 的默认行为就是从「标准输入」读取一整行内容,遇到换行符 \n 就停。

read 将读取到的内容赋值给后面指定的变量,可以指定多个变量,按 IFS 定义的分隔符 "空格 tab 换行" 将这一整行内容进行词分割,从而按顺序给对应的变量赋值。

read 成功读取到一行内容后,退出码为 0;当遇到 EOF(读不到内容)时,退出码为非 0。


所以将 while 和 read 结合后是这样的

while read variable ;
     # 满足条件执行的语句
done

3、给 read 喂输入

read 默认是从标准输入读取一行内容,如:命令行交互式输入。在非交互式场景下有两种方式给 read 喂输入。

方式1:管道符

echo "xxx" | while read x; do
  ...
done

重点注意:但是使用管道符,while ... done 是运行在子 shell 中,环里对变量的修改,外部是看不到的,所以不大合适。


方式二:内联字符串重定向

<<<here-string(内联字符串重定向),把右边的字符串,作为“标准输入”,喂给左边的命令,不会因为管道而额外创建子 shell

while read x; do
  ...
done <<< "xxx"

# 为什么 <<< 要写在 done 后面?因为前面是一个完整的语句,搞成一行就能理解
while read var; do :; done <<< "$data"

4、变量前为什么可以定义变量

这是 bash 的 临时环境变量赋值语法 ,因为 shell 执行一条命令前,会先 解析前缀

[变量赋值] [变量赋值] … 命令 参数 参数 …

也就是说,命令前面允许出现一串变量赋值,而且这个变量属于临时赋值,仅本次循环生效。意思就是这个 read 命令执行结束,变量赋值也就没了。

所以此时就能理解:

while IFS=':' read -r cid cname ;do
    echo "container_id: ${cid}  container_name: $cname"
done <<< "$containers"

整体总结:

while + read 能逐行读取输入,是因为 read 以 为单位从标准输入读取内容,并通过 退出码 与 while 的循环条件契合;

here-string(<<<)只是提供了一种无子 shell 的方式,将字符串作为标准输入喂给整个 while 语句块。

posted on 2025-12-23 11:10  一直小爪子  阅读(0)  评论(0)    收藏  举报