Skip to content

Conversation

@clelange
Copy link

I'm using zsh for most of my work, which is currently not supported by the call_host.sh script. I've made it work by identifying the bash-specific parts and changing those to functions. I've also introduced a function to identify if zsh is used and another one to prevent duplicating the bind paths when accidentally sourcing the call_host.sh script more than once.

@kpedro88
Copy link
Contributor

@clelange thanks for this contribution! tagging @NJManganelli here because he had also expressed interest in zsh support.

I won't have time to review this in depth until next week, but I wanted to acknowledge receipt.

@kpedro88
Copy link
Contributor

In the meantime, you can try to placate shellcheck...

@NJManganelli
Copy link

I had hacked together a minimal translation and had no time to clean it up, but even at a glance this is better and more functional. I can hopefully test it as a replacement for my hacked version later

@clelange
Copy link
Author

clelange commented Nov 4, 2025

In the meantime, you can try to placate shellcheck...

This should be OK now.

@@ -1,4 +1,5 @@
#!/bin/bash
# Print each command before executing it (for debugging)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this comment accurate / needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, a leftover from debugging, will remove

# get last path component if ps returns full path
p="$(ps -p $$ -o comm= 2>/dev/null | awk -F/ '{print $NF}')"
case "$p" in
zsh) return 0 ;;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because there is only a zsh case here, in the bash case, this will proceed through the fallback below before returning non-zero. I would prefer stopping here if bash is directly detected.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, will add

bash) return 1 ;;

# bash
export_func(){
[ -n "$1" ] || return
eval "export -f $1" 2>/dev/null || true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eval is not necessary here. The shellcheck warning should be handled in either of the following ways:

  1. put # shellcheck disable=SC2163 above this line
  2. use export -f ${1?} as noted at https://www.shellcheck.net/wiki/SC2163

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's already a check for the existence of ${1}$, I'll go with the former

}
declare_assoc(){
# create named associative array in bash
eval "declare -A $1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't seem like eval should be necessary here either

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's needed because declare expects a literal variable name and not a variable containing the name. Are you able to test this quickly? Otherwise, I can check next week.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the following works fine for me:

TEST1=TEST2
declare -A $TEST1
TEST2[foo]=bar
echo "${TEST2[@]}"

output:

bar

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, thanks, I'll remove the eval. Just wanted to make sure I'm not breaking the bash part.

}
# declare an associative array (zsh) - create a named array using eval so dynamic name works
declare_assoc(){
eval "typeset -A $1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if eval necessary here

echo "Warning: unsupported value ${!VAR_TO_VALIDATE} for $VAR_TO_VALIDATE; disabling"
# retrieve the value of the named variable in a way that works in bash and zsh
VARVAL="$(eval "printf '%s' \"\${$VAR_TO_VALIDATE}\"")"
# check allowed values in a POSIX-compatible way
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zsh really doesn't support =~?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(the benefit of this construct is scaling: more allowed values can be added without repeating "$VARVAL" !=)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does support it, but it's a bit tricky because of differences in quoting and word-splitting, so this is a more robust implementation. One could switch to case or loop/grep to avoid repeating "$VARVAL" !=. What's your take?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Case would be okay with me.

# args: varname value [sep]
varname="$1"; val="$2"; sep="${3:-:}"
# retrieve current value portably
cur="$(eval "printf '%s' \"\${${varname}:-}\"")"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe should be a function, since it's used repeatedly (also for $VAR_TO_VALIDATE above)

APPTAINERENV_HOSTFNS="$( ( IFS=:
for d in $PATH; do
[ -d "$d" ] || continue
for f in "$d"/condor_* "$d"/eos*; do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would prefer to avoid repetition here (specifying condor and eos twice, since the list could grow in the future). put these in an array, then either create the grep expression (first case) or use a nested loop (second case)?

Comment on lines +242 to +257
# portable retrieval of function source and re-definition under a new name
if is_zsh; then
# zsh: use `functions` to get the definition
if functions "$1" >/dev/null 2>&1; then
fnsrc="$(functions "$1" 2>/dev/null)"
else
return
fi
else
# bash: use declare -f
if declare -f "$1" >/dev/null 2>&1; then
fnsrc="$(declare -f "$1")"
else
return
fi
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fairly repetitive. suggestion:

  1. make get_function() functions for both bash and zsh (using declare -f and functions, resp.) in the "compatibility helpers" section at the top
  2. here, just call:
    fnsrc="$(get_function "$1")"
    if [ -z "$fnsrc" ]; then return; fi
    then the remaining lines below

@clelange
Copy link
Author

clelange commented Nov 6, 2025

Thanks for the careful review. I'm not sure I'll be able to address everything this week, but if not, I'll pick this up in the second half of next week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants