Bash拼接ncap2命令存入变量执行触发段错误 手动粘贴正常
需要使用ncap2处理netcdf文件,批量将大量变量重定义为关联时间维度的格式。参考日常使用cdo的调用习惯,计划通过循环拼接命令参数存入bash变量command后直接调用执行,日常cdo的调用方式如下:
cdo $command in.nc out.nc
最初使用如下代码拼接ncap2的执行参数:
varlist="hfls hfss prlr prw rlds rlus rlut rsds rsdt rsus rsut clt evspsbl" command="" for var in $varlist ; do command+=" -s '${var}[time,lat,lon]=${var}'" done echo $command
拼接完成后执行如下命令,直接触发segmentation fault(段错误)并生成核心转储文件:
ncap2 $command in.nc out.nc
但如果将echo命令输出的拼接结果手动复制粘贴到ncap2执行语句中,命令可以完全正常运行,手动执行的命令示例如下:
ncap2 -s 'hfls[time,lat,lon]=hfls' -s 'hfss[time,lat,lon]=hfss' -s 'prlr[time,lat,lon]=prlr' -s 'prw[time,lat,lon]=prw' -s 'rlds[time,lat,lon]=rlds' -s 'rlus[time,lat,lon]=rlus' -s 'rlut[time,lat,lon]=rlut' -s 'rsds[time,lat,lon]=rsds' -s 'rsdt[time,lat,lon]=rsdt' -s 'rsus[time,lat,lon]=rsus' -s 'rsut[time,lat,lon]=rsut' -s 'clt[time,lat,lon]=clt' -s 'evspsbl[time,lat,lon]=evspsbl' $dir/$file /scratch/b/$USER/${file%???}_corrected.nc
经排查判断问题与bash的字符串处理逻辑有关,多次调整引号写法均未解决。
bash对无引号包裹的变量做展开时,只会按空白字符拆分参数,不会解析变量值内部的单引号、双引号作为shell语法标记。
存储在command变量里的单引号会被当成参数内容的字面量传给ncap2,而不是用来包裹脚本字符串的shell语法符号:比如变量里存储的-s 'hfls[time,lat,lon]=hfls'段,展开后实际传给ncap2的两个参数是-s和'hfls[time,lat,lon]=hfls'(第二个参数首尾带字面量单引号),和手动输入命令时shell解析去掉单引号后传递的hfls[time,lat,lon]=hfls完全不一致。ncap2收到带多余单引号的错误脚本内容后解析失败,就会触发段错误。
注:cdo用字符串拼接参数偶尔能正常运行,只是因为cdo对参数格式容错性更高,这种写法本身就是bash的错误用法。
bash中存储多参数命令不要用普通字符串拼接,必须用数组存储,数组可以严格保留每个参数的边界,不会出现引号解析错误的问题。
正确实现代码如下:
varlist="hfls hfss prlr prw rlds rlus rlut rsds rsdt rsus rsut clt evspsbl" # 初始化参数数组 cmd_args=() for var in $varlist ; do # 每个-s标识、对应的ncap2处理脚本分别作为独立元素存入数组 cmd_args+=(-s "${var}[time,lat,lon]=${var}") done # 调用时用[@]展开数组,加双引号保证每个参数边界不被破坏 ncap2 "${cmd_args[@]}" "$dir/$file" "/scratch/b/$USER/${file%???}_corrected.nc"
注意事项:
- 不需要在拼接的脚本参数里额外加单/双引号,数组本身会保证整段脚本作为单个参数传递,不会被空白字符拆分
- 所有路径、文件名变量调用时也要加双引号,避免路径含空格时出现参数拆分错误
- 该写法的参数传递效果和手动逐字输入命令完全一致,不会出现多余字符的问题
内容的提问来源于stack exchange,提问作者ClimateUnboxed

