Skip to content

Instantly share code, notes, and snippets.

@ormaaj
Created March 10, 2025 19:35
Show Gist options
  • Save ormaaj/dbfb4e4ecd567ea51a4d1374e4e8a2d5 to your computer and use it in GitHub Desktop.
Save ormaaj/dbfb4e4ecd567ea51a4d1374e4e8a2d5 to your computer and use it in GitHub Desktop.
Silly bash typeset builtin wrapper

This script exploits bash's weird interpretation of POSIX's requirement that declaration builtins overloaded by functions must not modify the parsing of the arguments. Bash even extends this requirement to nonstandard extensions such as arrays. This typeset wrapper parses options by stealing the (approximately correct) optstring from ksh. It modifies each assignment then prints the effect of the command as a side-effect.

Code:

#!/usr/bin/env bash

shopt -s lastpipe extglob expand_aliases

function unset2 {
	\command unset "$@"
}

alias 'typeset=\command typeset'

function typeset {
	local -; set +m
	typeset -A opts
	typeset -i OPTIND=1
	typeset REPLY os OPTARG
	{
		printf :
		ksh -c 'typeset --\?\?api' 2>&1
	} | awk '/^\.OP . -/ { printf "%s", ($2 ($4 ~ /^flag|[^:]+:optional/ ? "" : ":")) }' |
		IFS= read -rd '' os

	while getopts "$os" REPLY; do
		case $REPLY in
			[:?]) return 1 ;;
			*) opts+=([$REPLY]=$OPTARG)
		esac
		unset -v OPTARG
	done

	shift "$((OPTIND - 1))"
	unset2 -v os OPTIND OPTARG
	unset -v REPLY
	typeset compound scalar IFS=
	printf 'typeset %.*s%s -- ' "$((!!${#opts[@]}))" - "${!opts[*]}"
	[[ ${!opts[*]} == *!(n|!([Aa]))* ]] && compound= || scalar=
	typeset ${compound+\-}${scalar+\+}n REPLY
	IFS=' '
	for REPLY do
		${compound+\:} printf -v "${REPLY%%=*}" %q "${REPLY#*=}"
		REPLY=${REPLY%%=*} ${compound+\:}
		${compound+\:} command typeset -n REPLY
		REPLY+=([${#REPLY[@]}${scalar+ - 1}]+=${scalar+\\ }lol!)
		set -- ${compound+"$(printf '[%s]=%q ' "${REPLY[@]@k}")"} ${scalar+"$REPLY"}
		${scalar+\:} set -- "${1%%?}"
		printf %s "${!REPLY}" =${compound+\(} "${1}${compound+)} "
		${compound+\:} command typeset +n REPLY
	done
	echo
}

unalias typeset

function lol {
	typeset -ua lolarray=(moo! blerg!)
	typeset -x herp=hello! derp='hi there'
}

# set -x
lol "$@"

# vim: set ft=bash fenc=utf-8 ff=unix ts=4 sw=4 noet :

Script output:

typeset -ua -- lolarray=([0]=MOO\! [1]=BLERG\! [2]=LOL\!)
typeset -x -- herp=hello\!\ lol! derp=hi\ there\ lol!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment