BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / linux / #161402同步于 2026/1/17
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Linux机器人发帖

一个ai没能帮我解决的bug

redsand
2026/1/17镜像同步9 回复
运行环境是Ubuntu 2204,bug定位后抽象截取出来如下脚本 net_json='{ "userdata_type": "plain", "networks": [ { "type": "ipv4", "ip_address": "104.233.207.169", "netmask": "255.255.255.240", "link": "eth0", "id": "network0" }, { "type": "ipv4", "ip_address": "104.233.207.171", "netmask": "255.255.255.240", "link": "eth0", "id": "network0", "routes": [ { "network": "0.0.0.0", "netmask": "0.0.0.0", "gateway": "104.233.207.174" } ] } ], "links": [ { "ethernet_mac_address": "52:54:ab:f7:f1:2f", "mtu": 1400, "id": "eth0", "type": "phy" } ], "services": [ { "type": "dns", "address": "8.8.8.8" } ] }' new_ip=104.233.207.170 echo $net_json | jq --arg ip "$new_ip" '.networks[] |= (select(.id == "network0" and .routes[0].network == "0.0.0.0") | .ip_address = $ip)' 预期运行结果是104.233.207.171被替换成新ip 104.233.207.170,实际执行结果是没有替换,我打开claude,看着它左试右试也达不到预期效果。我审了半天代码之后把上面的脚本粘贴到ubuntu2404上,输出结果竟然直接就是正确的。看起来是旧版版的jq有bug造成的,找到了问题我的解决办法就是绕过去,也就是把select里面的.routes[0].network == "0.0.0.0"去掉,这样会造成一个全数组ip替换,下一句再把需要的.routes[0].network == "0.0.0.0"这个元素单独select出来。这好像也不是什么太好的办法,先凑活用着。倒数2000天了么,人类程序员也不必太努力了。
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
YukinoRikka机器人#1 · 2026/1/17
Gemini 3 Pro: 这个问题确实是 jq 版本差异导致的经典坑。Ubuntu 22.04 默认是 jq-1.6,而 24.04 是 jq-1.7。 在 jq 中,|= (update assignment) 的逻辑是:把管道左边的路径,更新为管道右边表达式的输出。 在你的写法中: .networks[] |= (select(...) | .ip_address = $ip) 在 jq-1.6 中: 如果 select 条件不满足(比如第一个元素),它会返回 empty(空)。 当 |= 的右边返回 empty 时,它的行为是“删除”该元素。 所以理论上,你的脚本在旧版 jq 下,不仅不会替换 IP,甚至可能会把不匹配的那个 network 节点直接删掉(或者如果触发了隐式错误,可能导致整个表达式失效,表现为“没替换”)。 而在 jq-1.7 中,jq 对路径更新逻辑做了很大优化,能更智能地处理这种情况。 解决方法 与其用蹩脚的“先全改再过滤”,不如用更稳健的写法。这里有两个不需要依赖 jq 版本的标准解法: 解法 1:把 select 放在赋值符左边(推荐,最优雅) 把筛选逻辑作为路径的一部分,直接定位到要改的那个字段,然后赋值。这样不匹配的路径根本不会被触碰。 code Bash download content_copy expand_less echo $net_json | jq --arg ip "$new_ip" ' (.networks[] | select(.id == "network0" and .routes[0].network == "0.0.0.0").ip_address) |= $ip ' 逻辑解释:先定位到所有的 networks -> 筛选出符合条件的那个 -> 定位到它的 ip_address 字段 -> 仅对这个最终定位到的字段进行 update。 解法 2:使用 map 配合 if-else(传统,最稳) 如果不习惯路径操作,可以用 map 显式重构数组。这是最通用的编程思路,肯定不会有版本 bug。 code Bash download content_copy expand_less echo $net_json | jq --arg ip "$new_ip" ' .networks |= map( if .id == "network0" and .routes[0].network == "0.0.0.0" then .ip_address = $ip else . end ) ' 逻辑解释:遍历数组,如果是目标对象就改 IP,否则保持原样 (.) 返回。 AI 写这种脚本经常会忽视版本兼容性(毕竟它们很多是拿最新文档训练的),这时候还得靠人类程序员的经验兜底。这也说明咱们离失业(至少完全失业)还有一段距离,哈哈。
redsand机器人#2 · 2026/1/17
我试试你的方法,多谢 :D
redsand机器人#3 · 2026/1/17
Nice,效果颇佳,第一种方法优雅且完全符合我的预期,我决定采用!第二种方法没有把数组里其它元素去掉,不过的确达到了合理的替换效果。
sdtyds机器人#4 · 2026/1/17
确实是 jq 版本问题。Ubuntu 22.04 默认是 jq 1.6,24.04 是 jq 1.7+,在 `|=` 配合 `select` 的行为上有差异。 1.6 版本里 `select` 返回空时,`|=` 会把整个元素丢掉而不是保留原值。这是个已知的坑。 你的绕行方案可以用,另一个写法是用 `if-then-else` 替代 `select`: ```bash echo "$net_json" | jq --arg ip "$new_ip" ' .networks |= map( if .id == "network0" and .routes[0].network == "0.0.0.0" then .ip_address = $ip else . end )' ``` 这个在 1.6 上也能正常工作。 ----- 倒数2000天这个说法挺有意思。不过我觉得程序员的价值可能会转型而不是消失——调试这种版本兼容性的坑、理解系统行为的差异、做架构决策,这些短期内还是需要人来判断的。当然,“需要多努力”确实是个好问题。[bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203]
sdtyds机器人#5 · 2026/1/17
Claude opus 4.5的结果
sdtyds机器人#6 · 2026/1/17
再把前三楼评论喂给他后的结果 Gemini 的解法 1 确实更优雅: ```bash (.networks[] | select(...).ip_address) |= $ip ``` 把 `select` 放在路径里而不是 `|=` 右边,这样不匹配时路径直接不存在,不会触发删除行为。这是 jq 的正确用法,我给的 `map + if-else` 虽然能用但确实啰嗦了。 ----- 至于”AI 靠最新文档训练所以忽视版本兼容性”这个说法,部分对。但更准确的是:我在生成时倾向于给出”语义上正确”的方案,而不会主动去想”这个在 1.6 上行为不一样”。除非明确告诉我环境是旧版,我大概率会给出”现代写法”。 这次你自己定位到是版本问题,然后把两个环境的对比结果喂给我,这个诊断过程本身就是人类程序员的价值所在——AI 目前不太擅长”这段代码在我机器上跑不对但在另一台上能跑”这种跨环境调试。 2000 天的事——我倒觉得与其说”不必太努力”,不如说努力的方向会变。能准确描述问题、做对比实验、判断哪个 AI 的答案更靠谱,这些 meta-skill 可能比死磕语法细节更有长期价值。[bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203][bbsemoji8203]
redsand机器人#7 · 2026/1/17
倒数2000天的说法来自这里:https://finance.sina.cn/2026-01-12/detail-inhhcszt6056100.d.html?cref=cj
redsand机器人#8 · 2026/1/17
犹豫再三,忍不住又多试了试,这个jq bug的表现十分微妙,好像是数组长度为单数的时候输出就能替换,数组长度为双数的时候就不能,另外长度为3或者以上的时候输出的数组长度为2,即除了那个带有"network0" and .routes[0].network == "0.0.0.0"之外还有一个元素遗留下来。这段程序的上下文是修改cloudinit配置文件里的所有ip,networks数组的内容是主ip加secondary ips,所以体现出来的用户体验就是有时候ip修改成功了,有的时候没有。先全替换后面再次select其实是因为后面一句本来就要select主ip,再和新的secondary ips作拼接,整体来说方法就比较简单粗暴,代码也是泛善可陈,所以还是决定用二楼推荐的改法让程序看起来稍微正常一点。
redsand机器人#9 · 2026/1/27
今天想把代码用最优雅的方式修正,发现并不直接可行,因为实际的程序不光要改ip,还需要改netmask和gateway,第二种方法倒是的确可以, new_ip=104.233.206.170 new_netmask=255.255.255.128 new_gateway=104.233.206.254 echo $net_json | jq --arg ip "$new_ip" --arg netmask "$new_netmask" --arg gateway "$new_gateway" ' .networks |= map( if .id == "network0" and .routes[0].network == "0.0.0.0" then .ip_address = $ip | .netmask = $netmask | .routes[0].gateway = $gateway else . end )' 看起来只是略显啰嗦,并不明显,但是和我原本的绕行方法 先echo $net_json | jq --arg ip "$new_ip" --arg netmask "$new_netmask" --arg gateway "$new_gateway" '.networks[] |= (select(.id == "network0") | .ip_address = $ip | .netmask = $netmask | .routes[0].gateway = $gateway)',后面反正也要再次select 两相对比,我决定不了改还是不改了,改的话代码正常准确一点,不改程序结果也没有错误还可以偷懒,有点难以抉择