向Bash传字符串参数引发语法错误:AFNI 3dcalc fMRI数据缩放问题
解决Bash调用AFNI 3dcalc时的语法错误问题
问题背景
使用Bash脚本调用AFNI的3dcalc工具进行fMRI数据缩放,-expr参数需要传入带函数调用的表达式(含括号),提交任务时触发bash语法错误。
错误信息
-bash: -c: line 0: syntax error near unexpected token `(' -bash: -c: line 0: `apptainer run --cleanenv /Users/user44/fMRI/my_images/afni-binaries.sif 3dcalc -a /Users/user44/fMRI/PPMI/derivatives/afni_blur/sub-3102/sub-3102+tlrc.BRIK -b /Users/user44/fMRI/PPMI/derivatives/afni_scale/sub-3102/sub-3102_desc-scalestats+tlrc.BRIK -c /Users/user44/fMRI/PPMI/derivatives/fmriprep/sub-3102/func/sub-3102_task-rest_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz -expr c*min(200,a/b*100)*step(a)*step(b) -prefix /Users/user44/fMRI/PPMI/derivatives/afni_scale/sub-3102/sub-3102_task-rest_space-MNI152NLin2009cAsym_desc-preprocScaled_bold.nii '
原脚本内容
#!/bin/bash for fn in /Users/user44/fMRI/PPMI/derivatives/fmriprep/sub*/; do id=$(echo $fn | cut -d '/' -f 8) qsub -b y -q $QUEUE_NAME -pe orte 8 -l h_vmem=8G -N $id apptainer run --cleanenv ~/fMRI/my_images/afni-binaries.sif 3dcalc \ -a ~/fMRI/PPMI/derivatives/afni_blur/${id}/${id}+tlrc.BRIK \ -b ~/fMRI/PPMI/derivatives/afni_scale/${id}/${id}_desc-scalestats+tlrc.BRIK \ -c ~/fMRI/PPMI/derivatives/fmriprep/${id}/func/${id}_task-rest_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz \ -expr "c*min(200,a/b*100)*step(a)*step(b)" \ -prefix ~/fMRI/PPMI/derivatives/afni_scale/${id}/${id}_task-rest_space-MNI152NLin2009cAsym_desc-preprocScaled_bold.nii break done
尝试过的无效写法
-expr "'c*min(200,a/b*100)*step(a)*step(b)'"-expr "$('c*min(200,a/b*100)*step(a)*step(b)')"
问题根源
错误的核心是qsub会将整个命令作为bash -c的参数执行,原脚本中用双引号包裹的-expr表达式,在传递给qsub的过程中双引号会被bash剥离,导致表达式里的括号()暴露给bash解释器——括号在bash中是子命令/分组的语法符号,未经转义会触发语法错误。
解决方案
方式1:正确转义引号与括号
在qsub的命令行中,需要确保-expr的表达式被完整传递,不被bash解析。可以通过转义单引号或者括号实现:
转义单引号传递表达式
将表达式用单引号包裹,同时在bash中用\'转义单引号,确保qsub能完整接收:
qsub -b y -q $QUEUE_NAME -pe orte 8 -l h_vmem=8G -N $id \ apptainer run --cleanenv ~/fMRI/my_images/afni-binaries.sif 3dcalc \ -a ~/fMRI/PPMI/derivatives/afni_blur/${id}/${id}+tlrc.BRIK \ -b ~/fMRI/PPMI/derivatives/afni_scale/${id}/${id}_desc-scalestats+tlrc.BRIK \ -c ~/fMRI/PPMI/derivatives/fmriprep/${id}/func/${id}_task-rest_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz \ -expr '\''c*min(200,a/b*100)*step(a)*step(b)'\'' \ -prefix ~/fMRI/PPMI/derivatives/afni_scale/${id}/${id}_task-rest_space-MNI152NLin2009cAsym_desc-preprocScaled_bold.nii
转义表达式中的括号
用双引号包裹表达式,同时对每个括号加反斜杠转义:
-expr "c*min\(200,a/b*100\)*step\(a\)*step\(b\)" \
方式2:使用独立脚本(更推荐)
多层命令传递的引号转义容易出错,更可靠的方式是把3dcalc命令单独写成脚本:
- 创建
run_3dcalc.sh脚本:
#!/bin/bash id=$1 apptainer run --cleanenv ~/fMRI/my_images/afni-binaries.sif 3dcalc \ -a ~/fMRI/PPMI/derivatives/afni_blur/${id}/${id}+tlrc.BRIK \ -b ~/fMRI/PPMI/derivatives/afni_scale/${id}/${id}_desc-scalestats+tlrc.BRIK \ -c ~/fMRI/PPMI/derivatives/fmriprep/${id}/func/${id}_task-rest_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz \ -expr "c*min(200,a/b*100)*step(a)*step(b)" \ -prefix ~/fMRI/PPMI/derivatives/afni_scale/${id}/${id}_task-rest_space-MNI152NLin2009cAsym_desc-preprocScaled_bold.nii
- 给脚本添加执行权限:
chmod +x run_3dcalc.sh
- 修改原循环脚本:
#!/bin/bash for fn in /Users/user44/fMRI/PPMI/derivatives/fmriprep/sub*/; do id=$(echo $fn | cut -d '/' -f 8) qsub -b y -q $QUEUE_NAME -pe orte 8 -l h_vmem=8G -N $id ./run_3dcalc.sh $id break done
这种方式彻底避免了多层命令传递的引号问题,代码更清晰易维护。
内容的提问来源于stack exchange,提问作者Connor Johnson
相关产品推荐
相关产品推荐

