You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WSL的9P挂载中Bash星号通配符展开异常问题

WSL 9P挂载下Shell脚本通配符展开异常原因解析

仅在WSL的9P挂载目录(如/mnt/c/这类Windows磁盘挂载点)中,Shell脚本出现异常行为:当代码中存在写入临时文件的管道操作时,for循环中的*通配符展开会包含临时文件,导致文件计数异常,本该无限运行的测试脚本会随机执行若干次后停止。

测试脚本代码如下:

#!/bin/bash

# save true file number
true_f_num=$(for fn in *; do echo; done | wc -l)

while :; do
    # count of iterations to outer loop pipe
    echo
    
    # save current file list in file
    echo -n '' >.list
    
    for fn in *; do
        echo "$fn" >>.list
    
    # line below causes unexpected behaviour
    # on 9P mounts (like /mnt/c/...)
    # (WSL2, ubuntu 22.04)
    done | cat > .tmp; rm .tmp
    
    # check 
    [ $(cat .list | wc -l ) -ne $true_f_num ] && break

done | wc -l

核心原因:管道并行执行 + 9P文件系统特性

  1. Bash管道的并行执行机制
    Bash中管道的左右两侧命令是并行启动执行的,而非串行等待。在for ... done | cat > .tmp这行代码中,执行for循环的子进程与执行cat > .tmp的子进程会同时启动。cat > .tmp会立即创建.tmp文件(输出重定向会自动创建不存在的文件),这个操作可能比左侧for循环的*通配符展开更快完成。

  2. 9P文件系统的元数据同步特性
    WSL的9P挂载通过网络协议实现Windows与Linux子系统的文件共享,其目录元数据(如文件列表)的同步机制与原生Linux文件系统(如ext4)不同。当.tmp文件被快速创建后,9P文件系统会立即更新目录的文件列表,导致左侧for循环的*通配符展开时,.tmp文件被包含进了遍历列表中。

  3. 计数异常触发循环终止
    初始的true_f_num是在没有.tmp文件时计算的文件总数,而当for循环把.tmp也写入.list后,.list的行数就会比true_f_num多1,触发[ ... -ne $true_f_num ] && break条件,导致循环提前终止。

关于你提到的「隐形FIFO」:不存在隐形FIFO,问题就是.tmp这个普通临时文件因并行执行的竞态条件,被意外包含进了通配符展开的结果中。


解决方案

方案1:改为串行执行临时文件操作

先完成目录遍历和文件列表写入,再执行临时文件操作,避免并行竞态:

#!/bin/bash

true_f_num=$(for fn in *; do echo; done | wc -l)

while :; do
    echo
    echo -n '' >.list
    
    # 先完成遍历和文件列表写入
    for fn in *; do
        echo "$fn" >>.list
    done
    
    # 串行执行临时文件操作,此时通配符展开已完成,不会干扰
    cat .list > .tmp; rm .tmp
    
    [ $(cat .list | wc -l ) -ne $true_f_num ] && break

done | wc -l

方案2:将临时文件放在非9P挂载目录

把临时文件创建在/tmp这类原生Linux文件系统目录下,避免干扰当前9P目录的遍历:

done | cat > /tmp/.tmp; rm /tmp/.tmp

内容的提问来源于stack exchange,提问作者daubmannus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:04:57