require_once 用于 bash,但如何用于旧的 bash 和 POSIX-shell?
require_once for bash, but how to for older bash and POSIX-shell?
受到 Php require_once
的启发,我想出了如何在此处使用关联数组为现代 Bash 实现它:
a.sh
#!/usr/bin/env bash
declare -gAi __REQUIRES
require_once() {
for p; do
r=$(readlink --canonicalize-existing "$p")
# shellcheck disable=SC1090 # Dynamic source
[[ -r "$r" && 1 -ne "${__REQUIRES[$r]}" ]] && __REQUIRES[$r]=1 && . "$r"
done
}
require_once ./b.sh
require_once ./b.sh
hello
b.sh
hello() {
printf 'I am the hello function in b.sh.\n'
}
这按预期工作:
- 获取
source
的真实路径。
- 检查它是否可读,通过查找全局关联数组键检查它是否已经加载。
- 如果是,将其注册到全局关联数组中并获取它。
现在我还在想如何:
- 以更 portable/standard 的方式获取源的真实路径而不依赖于 Linux Gnu 工具?
- 让它与旧的 Bash 一起工作,比如 3.2
- 让它在没有数组的情况下使用 POSIX-shell 语法。
Get the real path of the source in a more portable/standard way not depending on Linux Gnu Tools?
readlink -f
在 Busybox 上可用。检查路径是否存在后readlink
无论如何,https://www.google.com/search?q=readlink+POSIX -> https://medium.com/mkdir-awesome/posix-alternatives-for-readlink-21a4bfe0455c , https://github.com/ko1nksm/readlinkf .
Have it work with older Bash like 3.2
Have it work with POSIX-shell grammar without array.
POSIX 无论如何都不喜欢文件名中的换行符,所以只需将文件存储为行:
__REQUIRES=""
if ! printf "%s" "$__REQUIRES" | grep -Fq "$r"; then
__REQUIRES="$__REQUIRES""$r
"
. "$r"
fi
或者使用案例,这样你就不会 fork
:
__REQUIRES="
"
case "$__REQUIRES" in
*"
$r
"*) ;;
*)
__REQUIRES="$__REQUIRES""$r
"
. "$r"
;;
esac
如果你想处理文件名中的换行符,通过 xxd
或 od
转换文件名(两者都在 Busybox 上可用,od
是 POSIX)并存储十六进制文件名表示为变量中的行。
POSIX-shell 语法方法使用临时目录中的符号链接作为已经需要的源标记:
a.sh
#!/usr/bin/env sh
unset __REQUIRES;
trap 'rm -fr -- "$__REQUIRES"' EXIT INT
__REQUIRES=$(mktemp -d)
require_once() {
for p; do
# Path is readable or continue
[ -r "$p" ] || continue
# Get real path
r=$(readlink -f -- "$p")
# New real path in __REQUIRES temp dir
nr="$__REQUIRES$r"
# If path __REQUIRES temp dir exists, file is already sourced
[ -r "$nr" ] && continue
# Make equivalent path for file in __REQUIRES temp dir
mkdir -p -- "${nr%/*}"
# Create symbolic link to __REQUIRES temp dir
ln -sf -- "$r" "$nr"
# shellcheck disable=SC1090 # Dynamic source
. "$r"
done
}
counter=0
require_once ./b.sh
require_once ./b.sh
hello
printf 'counter=%d\n' "$counter"
b.sh
counter=$((counter + 1))
hello() {
printf 'I am the hello function in b.sh.\n'
}
bash 3.2 没有实现关联数组,所以我们可以为每个脚本创建单独的“必需”变量名。
不要忘记 readlink -e
或 -f
失败 如果符号链接被破坏,那么让我们将其工作到函数中。
MacOS readlink
根本没有“规范化”选项,所以我不知道如何将其移植。
也许这样:在我的 Mac 上工作,GNU readlink 安装了自制软件。
require_once() {
local script canonical varname val
for script; do
if canonical=$(readlink -f -- "$script") && [[ -r "$canonical" ]]; then
varname=$(printf '__REQUIRED__%s' "${canonical//[^[:alnum:]]/__}")
val=$(eval "echo $$varname")
if [[ -z $val ]]; then
eval "$varname=1"
. "$canonical"
fi
fi
done
}
受到 Php require_once
的启发,我想出了如何在此处使用关联数组为现代 Bash 实现它:
a.sh
#!/usr/bin/env bash
declare -gAi __REQUIRES
require_once() {
for p; do
r=$(readlink --canonicalize-existing "$p")
# shellcheck disable=SC1090 # Dynamic source
[[ -r "$r" && 1 -ne "${__REQUIRES[$r]}" ]] && __REQUIRES[$r]=1 && . "$r"
done
}
require_once ./b.sh
require_once ./b.sh
hello
b.sh
hello() {
printf 'I am the hello function in b.sh.\n'
}
这按预期工作:
- 获取
source
的真实路径。 - 检查它是否可读,通过查找全局关联数组键检查它是否已经加载。
- 如果是,将其注册到全局关联数组中并获取它。
现在我还在想如何:
- 以更 portable/standard 的方式获取源的真实路径而不依赖于 Linux Gnu 工具?
- 让它与旧的 Bash 一起工作,比如 3.2
- 让它在没有数组的情况下使用 POSIX-shell 语法。
Get the real path of the source in a more portable/standard way not depending on Linux Gnu Tools?
readlink -f
在 Busybox 上可用。检查路径是否存在后readlink
无论如何,https://www.google.com/search?q=readlink+POSIX -> https://medium.com/mkdir-awesome/posix-alternatives-for-readlink-21a4bfe0455c , https://github.com/ko1nksm/readlinkf .
Have it work with older Bash like 3.2
Have it work with POSIX-shell grammar without array.
POSIX 无论如何都不喜欢文件名中的换行符,所以只需将文件存储为行:
__REQUIRES=""
if ! printf "%s" "$__REQUIRES" | grep -Fq "$r"; then
__REQUIRES="$__REQUIRES""$r
"
. "$r"
fi
或者使用案例,这样你就不会 fork
:
__REQUIRES="
"
case "$__REQUIRES" in
*"
$r
"*) ;;
*)
__REQUIRES="$__REQUIRES""$r
"
. "$r"
;;
esac
如果你想处理文件名中的换行符,通过 xxd
或 od
转换文件名(两者都在 Busybox 上可用,od
是 POSIX)并存储十六进制文件名表示为变量中的行。
POSIX-shell 语法方法使用临时目录中的符号链接作为已经需要的源标记:
a.sh
#!/usr/bin/env sh
unset __REQUIRES;
trap 'rm -fr -- "$__REQUIRES"' EXIT INT
__REQUIRES=$(mktemp -d)
require_once() {
for p; do
# Path is readable or continue
[ -r "$p" ] || continue
# Get real path
r=$(readlink -f -- "$p")
# New real path in __REQUIRES temp dir
nr="$__REQUIRES$r"
# If path __REQUIRES temp dir exists, file is already sourced
[ -r "$nr" ] && continue
# Make equivalent path for file in __REQUIRES temp dir
mkdir -p -- "${nr%/*}"
# Create symbolic link to __REQUIRES temp dir
ln -sf -- "$r" "$nr"
# shellcheck disable=SC1090 # Dynamic source
. "$r"
done
}
counter=0
require_once ./b.sh
require_once ./b.sh
hello
printf 'counter=%d\n' "$counter"
b.sh
counter=$((counter + 1))
hello() {
printf 'I am the hello function in b.sh.\n'
}
bash 3.2 没有实现关联数组,所以我们可以为每个脚本创建单独的“必需”变量名。
不要忘记 readlink -e
或 -f
失败 如果符号链接被破坏,那么让我们将其工作到函数中。
MacOS readlink
根本没有“规范化”选项,所以我不知道如何将其移植。
也许这样:在我的 Mac 上工作,GNU readlink 安装了自制软件。
require_once() {
local script canonical varname val
for script; do
if canonical=$(readlink -f -- "$script") && [[ -r "$canonical" ]]; then
varname=$(printf '__REQUIRED__%s' "${canonical//[^[:alnum:]]/__}")
val=$(eval "echo $$varname")
if [[ -z $val ]]; then
eval "$varname=1"
. "$canonical"
fi
fi
done
}