bug in tcsh v6.22.03 breaks @parse_afni_name

AFNI version info (afni -ver):

Precompiled binary linux_centos_7_64: Nov  7 2023 (Version AFNI_23.3.07 'Septimius Severus')


I am running AFNI on a shared compute cluster which has recently been updated to RHEL 9.2. Our system administrators have gotten AFNI mostly functional, but unfortunately the installed version of tcsh (tcsh 6.22.03 (Astron) 2020-11-18 (x86_64-unknown-linux)) contains a bug that breaks the @parse_afni_name utility. This script relies on some specific behavior of the tcsh :h modifier, which if given a string without a / in it, returns the string. The bug in tcsh v6.22.03 makes it instead return an empty string, which causes AFNI to fail to find files in the current directory (as in @SUMA_AlignToExperiment). This bug was fixed in a subsequent release (6.22.04), but unfortunately our system administrators are unwilling/unable to upgrade tcsh system-wide. I do have a newer version of tcsh I can add to my path; afni_system_check.py reports
which tcsh : /oscar/rt/9.2/software/0.20-generic/0.20.1/opt/spack/linux-rhel9-x86_64_v3/gcc-11.3.1/tcsh-6.24.10-dtqo5kycnu6m3c6xl6sr7iuvg4hrv3bj/bin/tcsh
However, all of the AFNI tcsh scripts point to /bin/tcsh in the shebang, so the buggy version is used as the interpreter regardless of what's in my path. Do you have any suggestions for a way to point AFNI to a version of tcsh not in /bin? Worst case, we can make a copy of AFNI and modify the shebang in every script, but I'm wondering if you can think of a simpler way.

Thanks very much!

Hi Elizabeth,

This is something a collaborator (Tom H) mentioned many years back in some Fedora package, and it is surprising to see Red Hat shipping tcsh with that bug.

After some pondering, we have still not come up with any nice solution here. Many programs invoke tcsh with a path, especially if options like -f or -e are applied, which do not work when invoking via 'env'.

On the plus side, it probably would not be too difficult to script a substitution. The hardest part is that using something like sed to change /bin/tcsh to /some/other/path/to/tcsh would mean every '/' would need to be escaped. Or it could be done via python. That would be more work but would probably be more reliable.

To find all files that seem to have /bin/tcsh in the first line, consider something like:

find ~/abin/ -type f -exec awk '/bin.tcsh/ {if ( FNR==1 ) print FILENAME}' {} \;

We can then test to see what lines are being matched, to make sure they look like the starts of scripts:

set flist = ( `find ~/abin/ -type f -exec \
               awk '/bin.tcsh/ {if ( FNR==1 ) print FILENAME}' {} \; ` )
head -q -n 1 $flist | sort | uniq

That list could then be used to modify the files (and then re-test the result):

sed -i 's/\/bin\/tcsh/\/some\/other\/bin\/dir\/tcsh/g' $flist
head -q -n 1 $flist | sort | uniq

This should be done on a local copy of the binaries, so as not to conflict with a system package.

Does that seem reasonable enough? I am sorry that it would mean always making your own copy of the binaries, as long as you are stuck on that system. We have not heard about other RH9 people having this problem though. We may have to look harder...

Thanks for bringing it up!

  • rick

Hi Rick,

Thanks for such a thoughtful reply! It's definitely messy, but modifying my own copy of the binaries seems to have worked.

With some help from chatGPT on syntax, I made the following script. This replaces every instance of /bin/tcsh in ASCII text executable files (to avoid corrupting binary files that have it in their help text). It isn't limited to the first line, which catches things like new script generation by afni_proc.py.


# Check if arguments are provided
if [ "$#" -ne 3 ]; then
    echo "Usage: $0 <abin> <orig_tcsh_path> <new_tcsh_path>"
    exit 1

# Assign input arguments to variables
orig_tcsh_path="$(echo "$2" | sed 's/\//\\\//g')"  # Escape forward slashes in the orig_tcsh_path
new_tcsh_path="$(echo "$3" | sed 's/\//\\\//g')"  # Escape forward slashes in the new_tcsh_path

# Find all ASCII text executable files
find "$abin" -type f -exec sh -c '

    # Check if the file is an ASCII text executable
    if file "$1" | grep -q "ASCII text executable"; then

        # Search for the orig_tcsh_path pattern in the file
        if grep -n "$2" "$1" >/dev/null; then

            # Print the file name
            echo "File: $1"

            # Print the line number and line containing the orig_tcsh_path pattern
            grep -n "$2" "$1"

            # Perform the replacement
            sed -i "s#$2#$3#g" "$1"

' _ "{}" "$orig_tcsh_path" "$new_tcsh_path" \;

I ran afni_system_check.py and test_afni_prog_help.tcsh, but is there anything else you'd recommend to doubly verify I haven't broken anything?

Thank you!

Oh my, I forgot about doing it within scripts like afni_proc.py (though even in that case, such scripts are usually invoked with tcsh, rather than using direct exec).

Pease let me know if you run into further issues.
This is really nice, thanks for posting it!

  • rick