2

We are running: Red Hat Enterprise Linux release 8.5 (Ootpa). We allow the server to update weekly using "yum -y update". We recently began receiving the following errors when executing shell commands:

  • sh: which: line 1: syntax error: unexpected end of file
  • sh: error importing function definition for `which'

I am not aware of anything we changed that could cause this error -- this why I am pointing to the yum system update. Much of our code is written in php where we use passthru() to run shell commands. When we rarely write a shell script we generally use bash.

I have the ability to prevent these errors using "unset which" prior to running a php script that uses passthru(). We run many shell commands from many scripts. Updating each of these scripts is not an effective solution -- time consuming and potentially introduces defects.

It is interesting to note that the following code Does Not cause an error:

#!/bin/sh
echo hello

Is there a way to 'unset which' once at the system or user level so it would be executed each time we run the shell? Perhaps there is a shell profile that I can change or something similar.

Update 1

I found the following in /etc/profile.d/which2.sh

# shellcheck shell=sh
# Initialization script for bash, sh, mksh and ksh

which_declare="declare -f"
which_opt="-f"
which_shell="$(cat /proc/$$/comm)"

if [ "$which_shell" = "ksh" ] || [ "$which_shell" = "mksh" ] || [ "$which_shell" = "zsh" ] ; then
  which_declare="typeset -f"
  which_opt=""
fi
 
which ()
{
(alias; eval ${which_declare}) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@"
}

export which_declare
export ${which_opt} which

The syntax looks correct here. I can tell that it exports 'which' -- but I'm not sure why. If I knew what this is doing, I could evaluate the risk of unset which.

Update 2

Created a small php script:

#!/usr/bin/php
<?php
passthru('echo hello');
?>

This Does create the error.

2
3

The error message hints at the Bash feature of exporting and importing shell functions to child shells through the environment. It reads environment variables with names like BASH_FUNC_funcname%%, that have values beginning with () { and tries to parse them as function definitions.

The error says the definition is faulty, you could get that by e.g. missing the closing }. Here, the environment variable would contain the string () { echo hi; when Bash sees it:

$ perl -e '$ENV{"BASH_FUNC_which%%"} = "() { echo hi;"; exec("/bin/bash")'
bash: which: line 1: syntax error: unexpected end of file
bash: error importing function definition for `which'

unset which would undefine the function, so it doesn't get exported, but that doesn't explain why the definition would be broken when the shell is started from PHP. I'm not sure what PHP, or your PHP program does to environment variables, but a possible reason is that Bash uses newlines in the envvars for exported functions. E.g.:

$ which() { echo hi; } 
$ export -f which
$ env |grep which -A1
BASH_FUNC_which%%=() {  echo hi
}

If something were to just look at the first line, the definition would be broken as in my Perl script above. You may want to look into that.

As for where the which function comes, look into the shell's initialization files, i.e. /etc/bash.bashrc etc. See the ideas in How to determine where an environment variable came from?, they should work for functions too. (Lazy first attempt: grep -re which /etc)

3
  • Great feedback! Please see my update to the original question.
    – Greg Sims
    Dec 14 '21 at 23:09
  • @GregSims, re. exporting the function, the scripts in profile.d only get run by login shells, and exporting makes it available to child shells too, even scripts. That's not a very good way to define the command though, since it's only going to be available in Bash (though RHEL has Bash as /bin/sh too). It would better be made a tool in PATH. I would expect it's only meant for interactive use since it's defined like that, but it's hard to be sure.
    – ilkkachu
    Dec 15 '21 at 10:17
  • You could of course also unset it in PHP, it should be BASH_FUNC_which%% (putenv("BASH_FUNC_which%%"), I guess. Better still, unset all BASH_FUNC_ vars), though that'd be a bit of an ugly workaround too. And as for the PHP example, well yeah, it looks like PHP is broken. Some people might not be surprised.
    – ilkkachu
    Dec 15 '21 at 10:19
0

I placed unset which in .bashrc as a work around. This worked for both bash and php. This morning I commented the unset which from .bashrc. The issue no longer exists. I believe the issue was created and resolved by upstream code changes. Thank you @ilkkachu!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.