Bash数组管道循环修改后外部无法读取问题求助
Bash数组在循环外丢失新增元素的原因与解决方法
问题现象
以下脚本中,Bash数组pid_arr在while read循环内可以正常打印新增的PID元素,但循环结束后,数组仅保留了循环前的初始值,新增的PID元素丢失:
原脚本
pid_arr=() pid=10 pid_arr+=($pid) pid_arr+=(29) ls -1 | head -2 | while read file do ls -1 1>/dev/null 2>/dev/null & pid=$! echo "Pid: $pid" pid_arr+=("${pid}") echo "Within loop" echo "${pid_arr[@]}" done echo "${pid_arr[@]}" for pid in "${pid_arr[@]}" do echo "Checking pid: $pid" done
实际输出
Pid: 10817 Within loop 10 29 10817 Pid: 10818 Within loop 10 29 10817 10818 10 29 Checking pid: 10 Checking pid: 29
原本期望循环外能看到数组的全部4个元素(10、29、10817、10818),但仅显示初始的两个值。
核心原因
管道|会创建子shell,脚本中的while read循环是在管道的最后一个子shell进程中执行的。子shell会复制父shell的变量(包括数组)作为副本,子shell内对数组的修改仅作用于这个副本,不会同步回父shell。当子shell结束后,副本被销毁,父shell的数组完全没被修改,所以循环外看不到新增元素。
解决方法
方法1:使用进程替换(推荐)
用进程替换< <(command)代替管道,让while循环在当前shell进程中执行:
pid_arr=() pid=10 pid_arr+=($pid) pid_arr+=(29) while read file do ls -1 1>/dev/null 2>/dev/null & pid=$! echo "Pid: $pid" pid_arr+=("${pid}") echo "Within loop" echo "${pid_arr[@]}" done < <(ls -1 | head -2) # 改用进程替换 echo "${pid_arr[@]}" for pid in "${pid_arr[@]}" do echo "Checking pid: $pid" done
方法2:使用临时文件中转输出
先把命令输出写入临时文件,再通过重定向读取临时文件,避免管道创建子shell:
pid_arr=() pid=10 pid_arr+=($pid) pid_arr+=(29) # 先把输出写入临时文件 ls -1 | head -2 > temp_file.txt while read file do ls -1 1>/dev/null 2>/dev/null & pid=$! echo "Pid: $pid" pid_arr+=("${pid}") echo "Within loop" echo "${pid_arr[@]}" done < temp_file.txt # 从临时文件读取 # 清理临时文件 rm temp_file.txt echo "${pid_arr[@]}" for pid in "${pid_arr[@]}" do echo "Checking pid: $pid" done
方法3:启用lastpipe选项(Bash 4.2+适用)
在脚本开头添加shopt -s lastpipe,让管道的最后一个命令在当前shell进程执行:
shopt -s lastpipe # 开启lastpipe选项 pid_arr=() pid=10 pid_arr+=($pid) pid_arr+=(29) ls -1 | head -2 | while read file do ls -1 1>/dev/null 2>/dev/null & pid=$! echo "Pid: $pid" pid_arr+=("${pid}") echo "Within loop" echo "${pid_arr[@]}" done echo "${pid_arr[@]}" for pid in "${pid_arr[@]}" do echo "Checking pid: $pid" done
以上三种方法都能让循环内对数组的修改保留到循环外,输出会包含全部4个元素。
内容的提问来源于stack exchange,提问作者madmatrix
相关产品推荐
相关产品推荐

