/ add-intended-for
add-intended-for
1 #!/usr/bin/env bash 2 # 3 # 20221020WF - init. lncd "habit" project 4 # 20231110WF - into lncdtools 5 # 20231221WF - takes multiple patterns 6 # 7 8 usage(){ 9 cat <<HEREDOC 10 'add-intended-for' adds an element like 11 'IntendedFor': ['sub-1/ses-1/func/sub-1_ses-1_task-rest_bold.nii.gz'] 12 to the json files matching \`-fmap 'pattern.json'\` 13 14 IntendedFor is used by fmriprep's susceptibility distortion correction (SDC). 15 16 USAGE: 17 $(basename "$0") -fmap '*PA_run-1_epi.json' [-for '*_bold.nii.gz'] [-for '*dwi.nii.gz'] subj-1/ses-1/ [subj-2/ses-1/ ...] 18 19 OPTIONS: 20 -help this message 21 22 -fmap pattern to find fmap json file. where to insert IntendedFor 23 24 -for pattern to find bold or dwi file(s). 25 default is '*_bold.nii.gz'. An alternative: '*dwi.nii.gz' 26 Can repeat "-for 'pattern.nii.gz'" 27 to put multiple patterns into the 'IntendedFor' array 28 29 -me_okay include multiecho files in search. 30 default is to exclude all files matching '*echo-*' 31 32 sesdir any number of session dirs 33 each should be session root dir with fmap/, func/ +/- dwi/ 34 repeat for as many sessions as needed. Consider using a glob 35 36 NOTE: 37 * skips files where 'IntendedFor' already exists 38 * test with 'DRYRUN=1 add-intended-for ...' 39 40 HEREDOC 41 } 42 43 #shellcheck disable=SC2089 # intentionally not an array 44 FIND_EXCLUDE=" -not -name *echo-*" 45 46 csv_niifiles(){ 47 local sesdir="$1"; shift 48 #local boldpat="${1:?need at least one bold or dwi file pattern}" 49 local find_names 50 # build a list of acceptable names 51 find_names=$(printf " -iname %s -or" "$@"|sed 's/-or$//') 52 # shellcheck disable=SC2086,SC2090 # sending multiple args in ea. var 53 (cd "$sesdir" && 54 find func/ dwi/ \( $find_names \) $FIND_EXCLUDE ) | 55 sed 's/^\|$/"/g'| 56 paste -sd, 57 } 58 find_se_file(){ 59 local sesdir="${1:?find_se_file requires session dir}" 60 local pattern="${2:?find_se_file requires a json pattern}" 61 mapfile -t sefiles < <(find "$sesdir/fmap" -name "$pattern") 62 if [[ -z "${sefiles[*]}" || ! -r "${sefiles[0]}" ]]; then 63 warn "no like '$sesdir/fmap/$pattern'?" 64 exit 1 65 fi 66 [ ${#sefiles[@]} -gt 1 ] && warn ">1 match for '$pattern' found: ${sefiles[*]}" 67 printf "%s\n" "${sefiles[@]}" 68 } 69 70 add_intended_for(){ 71 local sefile forfilescsv str 72 sefile="$1"; shift 73 forfilescsv="$1"; shift 74 # (DANGER) inline replace on 75 # matching files without an 'IntendedFor' line 76 str="\"IntendedFor\": [$forfilescsv]," 77 grep -L IntendedFor "$sefile" | 78 xargs -r dryrun sed "s;{;{\n$str;" -i || : 79 return 0 80 } 81 82 _intendedFor() { 83 local boldpatt=() 84 local sepatt="" 85 local sesdirs=() 86 [ $# -eq 0 ] && usage && exit 1 87 while [ $# -gt 0 ]; do 88 case "$1" in 89 -for|-bold|-dwi) boldpatt=("${boldpatt[@]}" "$2"); shift 2;; 90 -fmap) sepatt="$2"; shift 2;; 91 -me_okay) FIND_EXCLUDE=""; shift 1;; 92 -help) usage; exit 0;; 93 *) sesdirs+=("$1"); shift 1;; 94 esac 95 done 96 [ -z "${boldpatt[*]}" ] && boldpatt=('*_bold.nii.gz') 97 98 # check inputs 99 [ -z "$sepatt" ] && 100 echo "ERROR: no -fmap pattern; see -help" && 101 exit 1 102 ! [[ "$sepatt" =~ .json$ ]] && 103 echo "-fmap pattern must end with .json" && 104 exit 2 105 106 for sesdir in "${sesdirs[@]}"; do 107 mapfile -t sefiles < <(find_se_file "$sesdir" "$sepatt") 108 for sefile in "${sefiles[@]}"; do 109 forfilescsv=$(csv_niifiles "$sesdir" "${boldpatt[@]}") 110 [ -z "$forfilescsv" ] && 111 warn "no matching files in $sesdir/{func,dwi}/ matching ${boldpatt[*]}" && return 1 112 add_intended_for "$sefile" "$forfilescsv" || : 113 done 114 done 115 return 0 116 } 117 118 # if not sourced (testing), run as command 119 eval "$(iffmain "_intendedFor")"