跳至内容
ksh93编译与安装

ksh93编译与安装

October 21, 2026

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 的轻量与现代高级编程语言的特性而备受青睐。

它的一些特性:

  1. 完全的 POSIX 兼容性:ksh93 严格遵循 POSIX 标准,并且向后兼容早期的 Bourne shell 以及旧版 KornShell(如 ksh88),绝大多数旧脚本无需修改即可直接运行。
  2. 强大的算术运算能力:支持类似 C 语言语法的高级整型(支持 2~64 进制)和双精度浮点数运算,无需再額外调用 expr、bc 或 awk 等外部工具。
  3. 丰富的数据结构:支持索引数组(Indexed Arrays)和关联数组(Associative Arrays,即字典)。
  4. 支持复合变量(Compound Variables),可以通过点号(.)构建类似结构体(Struct)的层级命名空间。
  5. 支持名字引用(Nameref,即 typeset -n),极大地便利了函数传参。
  6. 卓越的性能与内建工具:由于许多标准工具被直接实现为 Shell 内建命令(Builtins),ksh93 在执行复杂脚本时的速度往往远超传统 Shell。
  7. 出色的扩展性:支持在运行时通过动态链接加载自定义的内建命令,甚至可以作为共享库被嵌入到 C/C++ 程序中调用。
  8. 典型应用场景大型企业级自动化运维:由于其严密的逻辑控制、丰富的字符串处理(支持正则表达式、子串截取)和函数作用域控制,非常适合编写结构复杂、规模庞大的自动化运维脚本。跨平台脚本移植:广泛应用于各种商业 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$ ls

ksh93 在 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)

欢迎技术交流与转载。转载请注明出处,并保留以下硬核联系方式。


最后更新于