Linux中()与{}创建后台进程的区别及并行脚本问题咨询
问题分析与解决
原代码的核心问题
你遇到的问题根源在于子shell的变量隔离:(...)&会创建独立的子shell进程,子shell里对ip_addr[$i]的赋值完全不会影响父shell中的数组。哪怕子shell成功获取到IP,父进程里的ip_addr还是初始的0值,所以你会误以为没分配到IP。另外代码里有个明显笔误:1+=1应该改成i+=1,否则while循环会无限执行,wait也没法正常生效。
可行的并行执行方式
要实现并行且让父进程能拿到IP结果,有几种替代方案:
1. 使用命名管道(FIFO)传递结果
子shell把获取到的IP写入管道,父进程从管道读取并更新数组:
mkfifo ip_pipe i=0 while ((i < nic_count)); do if [[ ${#ip_addr[$i]} == 0 ]]; then ( dhclient ${iface[$i]}; sleep 10 current_ip=$(ip -4 addr show ${iface[$i]} | awk '/inet/ {gsub("/..$","",$2); print $2}') if [[ -z $current_ip ]]; then echo -e "Unable to assign an IP address for ${iface[$i]}" fi # 将索引和IP写入管道 echo "$i:$current_ip" > ip_pipe )& fi i+=1 done # 父进程读取管道内容,更新数组 while read line; do idx=$(echo "$line" | cut -d: -f1) ip=$(echo "$line" | cut -d: -f2) ip_addr[$idx]=$ip done < ip_pipe wait rm ip_pipe
2. 使用文件存储临时结果
和管道思路类似,每个子shell把结果写入单独的临时文件,父进程统一读取:
temp_dir=$(mktemp -d) i=0 while ((i < nic_count)); do if [[ ${#ip_addr[$i]} == 0 ]]; then ( dhclient ${iface[$i]}; sleep 10 current_ip=$(ip -4 addr show ${iface[$i]} | awk '/inet/ {gsub("/..$","",$2); print $2}') if [[ -z $current_ip ]]; then echo -e "Unable to assign an IP address for ${iface[$i]}" fi echo "$current_ip" > "$temp_dir/ip_$i" )& fi i+=1 done wait # 读取临时文件更新数组 for ((i=0; i<nic_count; i++)); do if [[ -f "$temp_dir/ip_$i" ]]; then ip_addr[$i]=$(cat "$temp_dir/ip_$i") fi done rm -rf "$temp_dir"
3. 使用进程替换(Process Substitution)
结合read和进程替换,直接在父进程中处理子shell的输出:
i=0 while ((i < nic_count)); do if [[ ${#ip_addr[$i]} == 0 ]]; then # 后台执行并将结果通过fd传递 { dhclient ${iface[$i]}; sleep 10 current_ip=$(ip -4 addr show ${iface[$i]} | awk '/inet/ {gsub("/..$","",$2); print $2}') if [[ -z $current_ip ]]; then echo -e "Unable to assign an IP address for ${iface[$i]}" >&2 fi echo "$i $current_ip" } > >(read idx ip; ip_addr[$idx]=$ip) & fi i+=1 done wait
用大括号{}替代()的结果
如果把(...)&改成{...}&,区别在于:
{...}不会创建子shell,而是在当前shell进程中执行代码块,后台运行(&依然生效)。- 这样代码块里对
ip_addr[$i]的赋值会直接影响当前shell的数组,父进程能拿到IP结果。 - 但要注意语法:
{}内部的最后一条命令必须加;,并且{和代码之间要有空格,比如{ dhclient ...; }&。
修正后的{}版本代码:
i=0 while ((i < nic_count)); do if [[ ${#ip_addr[$i]} == 0 ]]; then { dhclient ${iface[$i]}; sleep 10 ip_addr[$i]=$(ip -4 addr show ${iface[$i]} | awk '/inet/ {gsub("/..$","",$2); print $2}') if [[ ${#ip_addr[$i]} == 0 ]]; then echo -e "Unable to assign an IP address for ${iface[$i]}" fi }& fi i+=1 done wait
这个版本里,后台执行的代码块直接在父shell环境中操作数组,所以你能正常看到IP被赋值,wait也会等待所有后台进程完成。
内容的提问来源于stack exchange,提问作者Kaleem Khattak
相关产品推荐
相关产品推荐

