From 027eeaa0a4001403afe6ef1ba8d64887010b04d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=BCgler?= Date: Fri, 13 Mar 2026 17:44:59 +0100 Subject: [PATCH 1/2] This Commit fixes an error in run_fastsurfer.sh, where $subject_dir/stats/aseg+DKT.stats was not pointing to the correct file. aseg+DKT.stats is usually a symlink to aseg+DKT.VINN.stats, but the target was previously an absolute path, which would create invalid paths if created inside a docker or singularity image that mounted $subject_dir somewhere else. Another failure "opportunity" was when files were moved. Furthermore, the copy option of softlink_or_copy would fail, if a relative path was used, but the current working directory was not the directory of the link. --- recon_surf/functions.sh | 38 +++++++++++++++++++++++++++++--------- run_fastsurfer.sh | 8 +++++++- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/recon_surf/functions.sh b/recon_surf/functions.sh index 801be99b7..09c1309aa 100644 --- a/recon_surf/functions.sh +++ b/recon_surf/functions.sh @@ -225,15 +225,16 @@ function check_allow_root() function softlink_or_copy() { + # Creates a symlink at file pointing to target; if that fails, for example because symlinks are not supported by the + # file system, copy the file instead. The baseline call is `ln -sf $1 $2 >> $3`. # params - # 1: file - # 2: target + # 1: link target (or file copy source) -> path to target of link / absolute or relative but relative to **file**!! + # 2: link (or file copy destination) -> path to link file / absolute or relative to cwd # 3: logfile # 4: cmdf - local LF="$3" - local ln_cmd=(ln -sf "$1" "$2") - local cp_cmd=(cp "$1" "$2") + local LF="$3" link_tgt_cp_src="$1" link_cp_dest="$2" if [[ $# -lt 3 ]] || [[ -z "$LF" ]] ; then echo "WARNING: Parameter 3 of softlink_or_copy missing!" ; fi + local ln_cmd=(ln -sf "$link_tgt_cp_src" "$link_cp_dest") if [[ $# -eq 4 ]] then local CMDF=$4 @@ -242,8 +243,12 @@ function softlink_or_copy() echo "$timecmd $(echo_quoted "${ln_cmd[@]}")" echo "if [[ \${PIPESTATUS[0]} != 0 ]]" echo "then" - echo " echo $(echo_quoted "${cp_cmd[@]}")" - echo " $timecmd $(echo_quoted "${cp_cmd[@]}")" + if [[ "$link_tgt_cp_src" != /* ]] ; then # relative path, defined with respect to $link_cp_dest + echo "dest=\$(dirname $(echo_quoted "$link_cp_dest"))/$(echo_quoted "$link_tgt_cp_src#"))" + else echo "dest=$(echo_quoted "$link_cp_dest")" + fi + echo " echo \"cp $(echo_quoted "$link_tgt_cp_src") \\\"\$dest\\\"\"" + echo " $timecmd cp $(echo_quoted "$link_tgt_cp_src") \"\$dest\"" echo " if [[ \${PIPESTATUS[0]} != 0 ]] ; then exit 1 ; fi" echo "fi" } | tee -a "$CMDF" @@ -253,8 +258,9 @@ function softlink_or_copy() $timecmd "${ln_cmd[@]}" 2>&1 if [[ "${PIPESTATUS[0]}" != 0 ]] then - echo_quoted "${cp_cmd[@]}" - $timecmd "${cp_cmd[@]}" 2>&1 + if [[ "$link_tgt_cp_src" != /* ]] ; then link_tgt_cp_src="$(dirname "$2")/$link_tgt_cp_src" ; fi # relative path + echo_quoted "cp" "$link_tgt_cp_src" "$link_cp_dest" + $timecmd "cp" "$link_tgt_cp_src" "$link_cp_dest" 2>&1 if [[ "${PIPESTATUS[0]}" != 0 ]] ; then exit 1 ; fi fi } | tee -a "$LF" @@ -262,6 +268,20 @@ function softlink_or_copy() fi } +function relative_to() +{ + # Generate a relative path from $2 to $3, so `ln -s $(relative_to python /path/src /path/target) /path/src` is valid. + # params + # python executable + # base to compute path from, must be the folder (no double quotes!) + # target to compute path to (no double quotes!) + script=('import sys' + 'from pathlib import Path' + 'base, target = sys.argv[sys.argv.index("-c")+1:]' + 'print(Path(target).relative_to(Path(base).parent, walk_up=True))') + $1 -c "$(printf "%s\n" "${script[@]}")" "$2" "$3" +} + function echo_quoted() { # params ... 1-N diff --git a/run_fastsurfer.sh b/run_fastsurfer.sh index 471790a3e..9a18156dc 100755 --- a/run_fastsurfer.sh +++ b/run_fastsurfer.sh @@ -1102,7 +1102,13 @@ then exit 1 fi # create a symlink of the stats file for the old file name - softlink_or_copy "$asegdkt_vinn_statsfile" "$asegdkt_statsfile" "$seg_log" + # at this point, $asegdkt_vinn_statsfile might be an absolute path, which causes problems in containers, so make + # the path relative, if both statsfiles are in $subject_dir (which will be almost always). + if [[ "$asegdkt_vinn_statsfile" == "$subject_dir/"* ]] && [[ "$asegdkt_statsfile" == "$subject_dir/"* ]] + then asegdkt_vinn_statsfile_=$(relative_to "$python" "$asegdkt_statsfile" "$asegdkt_vinn_statsfile") + else asegdkt_vinn_statsfile_=$asegdkt_vinn_statsfile + fi + softlink_or_copy "$asegdkt_vinn_statsfile_" "$asegdkt_statsfile" "$seg_log" # create the aseg only statsfile mask_name_manedit=$(add_file_suffix "$mask_name" "manedit") if [[ -e "$mask_name_manedit" ]] ; then mask_name="$mask_name_manedit" ; fi From 93b5b6309ef0b1f014f14695ad9769bcb0e4c821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=BCgler?= Date: Tue, 17 Mar 2026 16:08:25 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- recon_surf/functions.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/recon_surf/functions.sh b/recon_surf/functions.sh index 09c1309aa..5e03aee64 100644 --- a/recon_surf/functions.sh +++ b/recon_surf/functions.sh @@ -244,11 +244,12 @@ function softlink_or_copy() echo "if [[ \${PIPESTATUS[0]} != 0 ]]" echo "then" if [[ "$link_tgt_cp_src" != /* ]] ; then # relative path, defined with respect to $link_cp_dest - echo "dest=\$(dirname $(echo_quoted "$link_cp_dest"))/$(echo_quoted "$link_tgt_cp_src#"))" - else echo "dest=$(echo_quoted "$link_cp_dest")" + echo " src=\$(dirname $(echo_quoted "$link_cp_dest"))/$(echo_quoted "$link_tgt_cp_src")" + else + echo " src=$(echo_quoted "$link_tgt_cp_src")" fi - echo " echo \"cp $(echo_quoted "$link_tgt_cp_src") \\\"\$dest\\\"\"" - echo " $timecmd cp $(echo_quoted "$link_tgt_cp_src") \"\$dest\"" + echo " echo \"cp \\\"\$src\\\" $(echo_quoted "$link_cp_dest")\"" + echo " $timecmd cp \"\$src\" $(echo_quoted "$link_cp_dest")" echo " if [[ \${PIPESTATUS[0]} != 0 ]] ; then exit 1 ; fi" echo "fi" } | tee -a "$CMDF" @@ -272,13 +273,12 @@ function relative_to() { # Generate a relative path from $2 to $3, so `ln -s $(relative_to python /path/src /path/target) /path/src` is valid. # params - # python executable - # base to compute path from, must be the folder (no double quotes!) - # target to compute path to (no double quotes!) - script=('import sys' - 'from pathlib import Path' + # $1: python executable + # $2: base path whose directory is used as the starting point (e.g., the link path; start dir is dirname($2)) + # $3: target path to compute the relative path to (normal shell quoting is allowed) + script=('import sys, os' 'base, target = sys.argv[sys.argv.index("-c")+1:]' - 'print(Path(target).relative_to(Path(base).parent, walk_up=True))') + 'print(os.path.relpath(target, start=os.path.dirname(base)))') $1 -c "$(printf "%s\n" "${script[@]}")" "$2" "$3" }