如何筛选目录中不含指定文本的文件名?脚本异常求助
问题
我正在运行若干实验,结果会输出到对应文件里。实验成功的话,文件内会包含ChampSim completed all CPUs文本,我需要筛选出未成功的案例——也就是不含这句话的文件,不同实验结果靠文件名区分。
我的思路是用ls拿所有文件名,grep筛出成功的文件,再通过数组差集得到失败文件名,但运行脚本时总是输出所有文件名。
已经做了这些排查:
- 用简单数组验证差集逻辑,结果是对的
- 单独输出所有文件名或成功文件名,结果也都正确
- 但完整运行下面的脚本时,始终输出所有文件名:
CURRENT_DIR=$(cd $(dirname $0); pwd) ChampSim_path=$CURRENT_DIR/.. cd $CURRENT_DIR/$1 all=$(ls -q * | wc -l) good=$(grep "ChampSim completed all CPUs" * | wc -l) good_trace_prefetcher=($(grep -Rl "ChampSim completed all CPUs" * )) all_trace_prefetcher=($(ls -q * )) # echo ${good_trace_prefetcher[@]} # echo ${all_trace_prefetcher[@]} # exit # arry_list1=(1 2 3 4 5 6 7 8 9) # arry_list2=(3 5 8) arry_list1=${all_trace_prefetcher[@]} arry_list2=${good_trace_prefetcher[@]} declare -a diff_list t=0 flag=0 # echo arry_list1=${arry_list1[@]} # echo arry_list2=${arry_list2[@]} for list1_num in "${arry_list1[@]}" do # echo list1_num is ${list1_num} for list2_num in "${arry_list2[@]}" do # echo list2_num is ${list2_num} if [[ "${list1_num}" == "${list2_num}" ]]; then flag=1 break fi done if [[ $flag -eq 0 ]]; then diff_list[t]=$list1_num t=$((t+1)) else flag=0 fi done echo diff_list=${diff_list[@]}
问题原因
核心错误是数组赋值时丢了数组结构:
arry_list1=${all_trace_prefetcher[@]} arry_list2=${good_trace_prefetcher[@]}
不加双引号直接用${array[@]}赋值,数组会被展开成一个长字符串,所有元素用空格拼接。后续遍历"${arry_list1[@]}"时,其实是在遍历这一个长字符串,而非数组的每个单独元素。内层循环的比较永远匹配不上,所有元素都被塞进了差集里。
修复方案
1. 修正数组赋值方式
把数组赋值改成保留数组结构的写法,加双引号和括号:
arry_list1=("${all_trace_prefetcher[@]}") arry_list2=("${good_trace_prefetcher[@]}")
这样arry_list1和arry_list2才会被正确识别为数组,遍历的时候能拿到每个单独的文件名。
2. 优化后的完整脚本
CURRENT_DIR=$(cd $(dirname $0); pwd) ChampSim_path=$CURRENT_DIR/.. # 增加目录切换失败的退出逻辑,避免后续命令跑错目录 cd "$CURRENT_DIR/$1" || exit # 获取所有文件数组 all_trace_prefetcher=($(ls -q *)) # 获取成功的文件数组 good_trace_prefetcher=($(grep -Rl "ChampSim completed all CPUs" *)) # 正确复制数组结构 arry_list1=("${all_trace_prefetcher[@]}") arry_list2=("${good_trace_prefetcher[@]}") declare -a diff_list t=0 for list1_num in "${arry_list1[@]}" do flag=0 for list2_num in "${arry_list2[@]}" do if [[ "${list1_num}" == "${list2_num}" ]]; then flag=1 break fi done if [[ $flag -eq 0 ]]; then diff_list[t]="$list1_num" t=$((t+1)) fi done echo "未成功的实验文件:" echo "${diff_list[@]}"
3. 更简洁的实现方式
其实不用手动做数组差集,grep本身就支持反向筛选,效率更高:
CURRENT_DIR=$(cd $(dirname $0); pwd) ChampSim_path=$CURRENT_DIR/.. cd "$CURRENT_DIR/$1" || exit # 直接输出不包含目标字符串的文件 grep -RL "ChampSim completed all CPUs" *
-L参数会直接返回不匹配的文件名,比手动处理数组简单得多。
内容的提问来源于stack exchange,提问作者Gerrie
相关产品推荐
相关产品推荐

