ksh93编译与安装
ksh93编译与安装
什么是ksh93
ksh93(KornShell 93)是由 David Korn 在贝尔实验室(AT&T Bell Laboratories)开发的经典 Unix Shell —— KornShell 的一个重大版本(93系列)。它既是一个功能强大的交互式命令解释器,也是一种高效的脚本编程语言。
在传统的 Unix/Linux 生态中,ksh93 常常被用来替代标准的 Bourne shell (sh) 或 C shell (csh),因其兼具传统 Shell 的轻量与现代高级编程语言的特性而备受青睐。
它的一些特性:
- 完全的 POSIX 兼容性:ksh93 严格遵循 POSIX 标准,并且向后兼容早期的 Bourne shell 以及旧版 KornShell(如 ksh88),绝大多数旧脚本无需修改即可直接运行。
- 强大的算术运算能力:支持类似 C 语言语法的高级整型(支持 2~64 进制)和双精度浮点数运算,无需再額外调用 expr、bc 或 awk 等外部工具。
- 丰富的数据结构:支持索引数组(Indexed Arrays)和关联数组(Associative Arrays,即字典)。
- 支持复合变量(Compound Variables),可以通过点号(.)构建类似结构体(Struct)的层级命名空间。
- 支持名字引用(Nameref,即 typeset -n),极大地便利了函数传参。
- 卓越的性能与内建工具:由于许多标准工具被直接实现为 Shell 内建命令(Builtins),ksh93 在执行复杂脚本时的速度往往远超传统 Shell。
- 出色的扩展性:支持在运行时通过动态链接加载自定义的内建命令,甚至可以作为共享库被嵌入到 C/C++ 程序中调用。
- 典型应用场景大型企业级自动化运维:由于其严密的逻辑控制、丰富的字符串处理(支持正则表达式、子串截取)和函数作用域控制,非常适合编写结构复杂、规模庞大的自动化运维脚本。跨平台脚本移植:广泛应用于各种商业 UNIX 系统(如 AIX、Solaris、HP-UX)以及现代 Linux / 嵌入式系统和 Windows 兼容环境(如 MSYS2、Cygwin)中。
ksh93当前的活跃分支
当前的活跃分支是这个:ksh93 u+m 。
在 Linux 系统和 Cygwin 下的安装和编译遵循官方的说明和指引即可,但是在 Windows 的 msys2 环境下的编译,目前还没有官方的解决方案。目前还有一个 BUG 还没有解决。
ksh93在windows的msys2环境下的编译与安装
根据 BUG#801 的讨论,我们尝试着来在 msys2 下进行编译和构建。
build.sh :
#!/bin/sh
set -e
# [Soft link defense] Force MSYS2 to pull up real symbolic links at the Windows permission level
# Force MSYS2 to try to create a real Windows system-level soft link to avoid the build system reporting errors due to link failure
# Requires Windows Developer Mode or administrator rights to create real symbolic links
export MSYS="winsymlinks:sys"
# Enable POSIX and Windows NT permission security mapping to prevent permission denial when reading, writing and executing files.
export CYGWIN="ntsec"
# [Path Search] Automatically grab the real path of cc1.exe inside gcc and inject it into PATH
# Automatically capture the current internal core installation directory of gcc (including cc1.exe, collect2 and other back-end components)
export GCC_DIR="$(gcc -print-search-dirs | sed -ne '/^install: /s///p')"
# Append it to PATH to prevent old build systems from prompting "command not found" when calling the compiler component under the hood.
export PATH="$PATH:$GCC_DIR"
# [Compilation Environment Specification] Standardized C language compiler environment variables
# Explicitly specify the search paths for standard header files and library files to ensure that the compiler can accurately locate basic dependencies.
export CFLAGS="-I${GCC_DIR}/include -L${GCC_DIR} -L/usr/lib"
# Extended compilation flags specific to the AST/Mamfile build system
# core key:-fno-use-linker-plugin
# Function: Turn off the modern GCC linker plug-in (LTO plug-in).
# Reason: To prevent modern linker plugins from colliding with older, non-standard build system packaging/linking processes and causing crashes.
export CCFLAGS="${CFLAGS} -fno-use-linker-plugin"
# Specifies a private directory where the GCC linker searches for library files at compile time
export LIBRARY_PATH="${GCC_DIR}"
# [Global Shebang added] Traverse all .sh scripts in the src directory, and insert the header safely into #!/bin/sh
if [ -d src ]; then
echo "Safely padding the /bin/sh boot header for all .sh scripts in the src directory..."
find src -name "*.sh" -type f | while read -r sh_file; do
if ! head -n 1 "$sh_file" | grep -q "/bin/sh"; then
sed -i '1s/^/#!\/bin\/sh\n/' "$sh_file"
fi
done
fi
# [UNC Compatible] Inject macro isolation patch into astconf.c in place before compilation
if [ -f src/lib/libast/port/astconf.c ]; then
patch -p1 <ksh-unc-paths.patch
fi
# [Replace the old cygwin interface]
# src/lib/libast/path/pathnative.c
# src/lib/libast/path/pathposix.c
sed -Ei '/^\s*extern\s+void\s+.*cygwin_conv_to_win32_path/c\#include <sys/cygwin.h>' "src/lib/libast/path/pathnative.c"
sed -i 's/cygwin_conv_to_win32_path(path, tmp);/cygwin_conv_path(CCP_POSIX_TO_WIN_A, path, tmp, PATH_MAX);/g' "src/lib/libast/path/pathnative.c"
sed -i 's/cygwin_conv_to_win32_path(path, buf);/cygwin_conv_path(CCP_POSIX_TO_WIN_A, path, buf, siz);/g' "src/lib/libast/path/pathnative.c"
sed -Ei '/^\s*extern\s+void\s+.*cygwin_conv_to_posix_path/c\#include <sys/cygwin.h>' "src/lib/libast/path/pathposix.c"
sed -i 's/cygwin_conv_to_posix_path(path, tmp);/cygwin_conv_path(CCP_WIN_A_TO_POSIX, path, tmp, PATH_MAX);/g' "src/lib/libast/path/pathposix.c"
sed -i 's/cygwin_conv_to_posix_path(path, buf);/cygwin_conv_path(CCP_WIN_A_TO_POSIX, path, buf, siz);/g' "src/lib/libast/path/pathposix.c"
# [Macro Conflict Interception] Forcibly remove system iconv macro definition confusion
if [ -f src/lib/libast/features/iconv ]; then
sed -i '6s/^/#undef _lib_iconv_open\n/' src/lib/libast/features/iconv
fi
# [Environment variables ready] Add the local target compilation bin directory to PATH
TARGET_BIN="$PWD/arch/cygwin.i386/bin"
mkdir -p bin "$TARGET_BIN"
export PATH="$PWD/bin:$TARGET_BIN:$PATH"
echo "[Phase 1] Start the first round of compilation (it is normal if interruption is caused by the fragile fork mechanism of Windows)..."
CC="/usr/bin/gcc" /bin/sh bin/package make PACKAGEROOT="$PWD" || true
# [Detection] Check whether the target ksh binary file has been successfully generated
KSH_BIN=$(ls arch/*/bin/ksh 2>/dev/null || true)
if [ -n "$KSH_BIN" ] && [ -f "$KSH_BIN" ]; then
echo "[Skip] Detected ksh binary successfully generated in first round!"
else
echo "[Phase 2] It is found that the binary is not ready yet, and the second round of in-situ resurrection compilation is started..."
CC="/usr/bin/gcc" /bin/sh bin/package make PACKAGEROOT="$PWD"
fi
echo "=========================================================="
echo "Congratulations, man! Rigorous [complete body] Ksh93 u+m binary file compiled successfully!"
echo "Your native ksh.exe is already safely located in the following path:"
ls -l arch/*/bin/ksh
echo "=========================================================="
# Automatically find the compiled ksh and install it to /usr/local/bin/ksh
KSH_SRC=$(ls arch/*/bin/ksh 2>/dev/null)
if [ -f "$KSH_SRC" ]; then
echo "[Standardized Installation] Deploying ksh to /usr/local/bin/..."
install -Dm755 "$KSH_SRC" /usr/local/bin/ksh
echo "Installation successful! Now you can call it directly by typing ksh anywhere."
else
echo "The compiled ksh binary file was not found, please compile first!"
exit 1
fi
# Check and write safely to /etc/shells
KSH_PATH="/usr/local/bin/ksh"
if ! grep -qxF "$KSH_PATH" /etc/shells; then
echo "[Registration Shell] Writing $KSH_PATH to /etc/shells..."
echo "$KSH_PATH" >> /etc/shells
echo "Registration completed!"
else
echo "/etc/shells the path already exists in the directory and registration is skipped."
fi
# test
./arch/cygwin.i386/bin/ksh.exe --version
./arch/cygwin.i386/bin/ksh.exe -c 'typeset -A user=( [name]="qindapao" [status]="Legendary" );print -r -- "Author: ${user[name]}, Build Status: ${user[status]}"; print -r -- "Math test: $(( 3.14159 * 2 ))"'ksh-unc-paths.patch :
diff --git a/src/lib/libast/port/astconf.c b/src/lib/libast/port/astconf.c
index b6b58027..396cd46d 100644
--- a/src/lib/libast/port/astconf.c
+++ b/src/lib/libast/port/astconf.c
@@ -1355,6 +1355,24 @@ astgetconf(const char* name, const char* path, const char* value, int flags, Err
Lookup_t look;
Sfio_t* tmp;
+#ifdef __CYGWIN__
+ /*
+ * hack for Cygwin to make Cygwin UNC paths (with leading
+ * two slashes) work.
+ *
+ * Testcase:
+ * cd into UNC path, print "pwd -P", result must start with
+ * "//<hostname>/<path>"
+ * ---- snip ----
+ * $ bash -c 'cd "//derfw1fs10.global.loc/rfw" ; path_to_kshdir/ksh.exe -c "pwd -P ; true" ; true'
+ * //derfw1fs10.global.loc/rfw
+ * ---- snip ----
+ *
+ */
+ if (name && (!strcmp(name, "PATH_LEADING_SLASHES"))) {
+ return "1";
+ }
+#endif /* __CYGWIN__ */
if (!name)
{
if (path)
最终编译出来的产物有 1.4 M,看起来能运行。
version sh (AT&T Research) 93u+m/1.1.0-alpha 2026-10-22
Author: qindapao, Build Status: Legendary
Math test: 6.28318
admin@DESKTOP-AQBA715 UCRT64 /d/temp/2026-10/ksh-dev$ lsksh93 在 msys2上执行缓慢的问题
PATH 里混入大量 Windows 原生路径时,MSYS2 在进程启动阶段要对每条路径做 POSIX 转换和挂载表匹配。冷启动时这部分开销会和磁盘读取、安全软件扫描叠加,明显拖慢首次启动;路径精简后,首次启动的开销也会随之下降。
注意:首次运行慢、后续变快是正常现象,通常与 Windows 文件缓存和安全软件首次扫描有关,不代表 PATH 优化无效。
# 保留 MSYS2 核心路径,去掉 Windows 原生冗余路径
export PATH=/ucrt64/bin:/usr/bin:/usr/local/bin定位这个问题的方法是这样的:
strace -f -o /tmp/ksh_full.log /usr/bin/ksh --version使用 -o 选项把 strace 跟踪过程中的信息导入到文件中,方便分析程序启动缓慢问题。不受 ksh 自身打印的干扰。
✍️ 本文作者:秦大炮 (Qindapao)
欢迎技术交流与转载。转载请注明出处,并保留以下硬核联系方式。
- 🔗 独立博客: qindapao.com
- 💻 GitHub: github.com/qindapao
- 📧 Email: nothisland2017@gmail.com