@ -1,4 +1,4 @@
: "punk MULTISHELL - shebangless polyglot for Tcl Perl sh bash cmd pwsh powershell" + "[rename set S;proc Hide shell_not_supported {proc $shell_not_supported args {}};Hide :]" + "\$(function : {<#pwsh#>})" + "perlhide" + qw^
: "punk MULTISHELL - shebangless polyglot for Tcl Perl sh zsh/ bash cmd pwsh powershell" + "[rename set S;proc Hide shell_not_supported {proc $shell_not_supported args {}};Hide :]" + "\$(function : {<#pwsh#>})" + "perlhide" + qw^
set -- " $@ " " a=[Hide <#;Hide set;S 1 list] " ; set -- : " $@ " ;$1 = @'
set -- " $@ " " a=[Hide <#;Hide set;S 1 list] " ; set -- : " $@ " ;$1 = @'
: heredoc1 - hide from powershell using @ and squote above. close sqote for unix shells + ' \
: heredoc1 - hide from powershell using @ and squote above. close sqote for unix shells + ' \
: .bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl + \
: .bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl + \
@ -13,7 +13,7 @@ set -- "$@" "a=[Hide <#;Hide set;S 1 list]"; set -- : "$@";$1 = @'
: Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \
: Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \
: {
: {
@ REM ############################################################################################################################
@ REM ############################################################################################################################
@ REM THIS IS A POLYGLOT SCRIPT - supporting payloads in Tcl, bash, (some sh ) and/or powershelll (powershell.exe or pwsh.exe)
@ REM THIS IS A POLYGLOT SCRIPT - supporting payloads in Tcl, zsh, bash, (sh diversion ) and/or powershelll (powershell.exe or pwsh.exe)
@ REM It should remain portable between unix-like OSes & windows if the proper structure is maintained.
@ REM It should remain portable between unix-like OSes & windows if the proper structure is maintained.
@ REM ############################################################################################################################
@ REM ############################################################################################################################
@ rem -------------------------------------------------------------------------------------------------------------------------------
@ rem -------------------------------------------------------------------------------------------------------------------------------
@ -266,7 +266,8 @@ set ^"endlocal=for %%# in (1 2) do if %%#==2 (%\n%
set " param= %% L "
set " param= %% L "
@ REM @echo ######### %%L
@ REM @echo ######### %%L
@ rem call :buildcmdline newcommandline param "{" "}"
@ rem call :buildcmdline newcommandline param "{" "}"
call : buildcmdline newcommandline param ' ' %= cmd.exe /c powershell % =
@ rem call :buildcmdline newcommandline param ' ' %= cmd.exe /c powershell ... -c %=
call : buildcmdline newcommandline param %= cmd.exe /c powershell ... -f % =
@ rem @echo .
@ rem @echo .
)
)
) ELSE (
) ELSE (
@ -303,6 +304,7 @@ SETLOCAL EnableDelayedExpansion
@ IF " !selected_shelltype_trimmed! " == " pwsh " (
@ IF " !selected_shelltype_trimmed! " == " pwsh " (
REM pwsh vs powershell hasn't been tested because we didn't need to copy cmd to ps1 this time
REM pwsh vs powershell hasn't been tested because we didn't need to copy cmd to ps1 this time
REM test availability of preferred option of powershell7+ pwsh
REM test availability of preferred option of powershell7+ pwsh
REM when run without cmd.exe - pwsh will receive the semicolon (for cmd.exe unquoted semicolon and comma are separators that aren't seen in positional arguments)
pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted 2 > NUL; write-host " statusmessage: pwsh-found " > NUL
pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted 2 > NUL; write-host " statusmessage: pwsh-found " > NUL
SET pwshtest_exitcode = !errorlevel!
SET pwshtest_exitcode = !errorlevel!
REM ECHO pwshtest_exitcode !pwshtest_exitcode!
REM ECHO pwshtest_exitcode !pwshtest_exitcode!
@ -310,18 +312,18 @@ SETLOCAL EnableDelayedExpansion
IF !pwshtest_exitcode! == 0 (
IF !pwshtest_exitcode! == 0 (
@ rem pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted; "%scriptrootname%.ps1" %arglist%
@ rem pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted; "%scriptrootname%.ps1" %arglist%
@ rem pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted
@ rem pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted
cmd /c pwsh -nop -nol -ExecutionPolicy bypass -c " %scriptrootname% .ps1 " !newcommandline!
cmd /c pwsh -nop -nol -ExecutionPolicy bypass -f " %scriptrootname% .ps1 " !newcommandline!
SET task_exitcode = !errorlevel!
SET task_exitcode = !errorlevel!
) ELSE (
) ELSE (
REM TODO prompt user with option to call script to install pwsh using winget
REM TODO prompt user with option to call script to install pwsh using winget
@ rem powershell -nop -nol -ExecutionPolicy Bypass -c "%scriptrootname%.ps1" %arglist%
@ rem powershell -nop -nol -ExecutionPolicy Bypass -c "%scriptrootname%.ps1" %arglist%
cmd /c powershell -nop -nol -ExecutionPolicy Bypass -c " %scriptrootname% .ps1 " !newcommandline!
cmd /c powershell -nop -nol -ExecutionPolicy Bypass -f " %scriptrootname% .ps1 " !newcommandline!
SET task_exitcode = !errorlevel!
SET task_exitcode = !errorlevel!
)
)
) ELSE (
) ELSE (
IF " !selected_shelltype_trimmed! " == " powershell " (
IF " !selected_shelltype_trimmed! " == " powershell " (
@ rem powershell -nop -nol -ExecutionPolicy Bypass -c "%scriptrootname%.ps1" %arglist%
@ rem powershell -nop -nol -ExecutionPolicy Bypass -c "%scriptrootname%.ps1" %arglist%
cmd /c powershell -nop -nol -ExecutionPolicy Bypass -c " %scriptrootname% .ps1 " !newcommandline!
cmd /c powershell -nop -nol -ExecutionPolicy Bypass -f " %scriptrootname% .ps1 " !newcommandline!
SET task_exitcode = !errorlevel!
SET task_exitcode = !errorlevel!
) ELSE (
) ELSE (
IF " !selected_shelltype_trimmed! " == " wslbash " (
IF " !selected_shelltype_trimmed! " == " wslbash " (
@ -373,6 +375,7 @@ endlocal & set "%~3=%rtn%"
exit /b
exit /b
%= ---------------------------------------------------------------------- =%
%= ---------------------------------------------------------------------- =%
@ REM padding
: buildcmdline cmdlinevar paramvar wrapA wrapB
: buildcmdline cmdlinevar paramvar wrapA wrapB
%= quoting for cmd.exe /c pwsh -nop !args! =%
%= quoting for cmd.exe /c pwsh -nop !args! =%
@ SETLOCAL EnableDelayedExpansion
@ SETLOCAL EnableDelayedExpansion
@ -650,6 +653,8 @@ do if not defined param1 set %%~"param1=%2%%~"
@ REM boundary padding
@ REM boundary padding
@ REM boundary padding
@ REM boundary padding
@ REM boundary padding
@ REM boundary padding
@ REM boundary padding
@ REM boundary padding
: stringTrimTrailingUnderscores
: stringTrimTrailingUnderscores
@ SETLOCAL
@ SETLOCAL
@ SET " rtrn= %~2 "
@ SET " rtrn= %~2 "
@ -842,16 +847,6 @@ namespace eval ::punk::multishell {
puts stderr " No tcl code for this script. Try another program such as zsh or bash or perl "
puts stderr " No tcl code for this script. Try another program such as zsh or bash or perl "
#< /tcl-payload>
#< /tcl-payload>
#< tcl-pre-launch-subprocess>
#< /tcl-pre-launch-subprocess>
#< tcl-launch-subprocess>
#< /tcl-launch-subprocess>
#< tcl-post-launch-subprocess>
#< /tcl-post-launch-subprocess>
# -- --- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- --- ---
# -- Best practice is to always return or exit above, or just by leaving the below defaults in place.
# -- Best practice is to always return or exit above, or just by leaving the below defaults in place.
@ -872,28 +867,61 @@ if {[::punk::multishell::is_main]} {
HEREDOC1B_HIDE_FROM_BASH_AND_SH
HEREDOC1B_HIDE_FROM_BASH_AND_SH
# Be wary of any non-trivial sed/awk etc - can be brittle to maintain across linux,freebsd,macosx due to differing implementations \
# Be wary of any non-trivial sed/awk etc - can be brittle to maintain across linux,freebsd,macosx due to differing implementations \
echo " var0: $0 @: $@ "
echo " var0: $0 @: $@ "
# echo " script: `echo $0 | sed 's/ ^ -//'` "
# use oldschool backticks and sed - lowest common denominator \
# use oldschool backticks and sed - lowest common denominator \
# echo " shell: " `ps -p $$ | awk '$1 != " PID " {print $(NF)}' | tr -d '()' | sed -E 's/^. *\/| ^- //'`
# zsh diversion \
# if [[ " $argv[*] " != " [*] " ]]; then /usr/bin/env bash " $0 " " ${argv[@]:2:$((${#argv[@]}-2))} " ; exit $?; fi
# \
ps_shellname=`ps -p $$ | awk '$1 != " PID " {print $(NF)}' | tr -d '()' | sed -E 's/^. *\/| ^- //'`
ps_shellname=`ps -p $$ | awk '$1 != " PID " {print $(NF)}' | tr -d '()' | sed -E 's/^. *\/| ^- //'`
# \
# \
echo " shell from ps: $ps_shellname argc: ${#@} inner: ${@:2:$((${#@}-2))} "
echo " shell from ps: $ps_shellname "
# non-bash-like diversion \
if [[ " $ p s _ s h e l l n a m e " ! = " b a s h " & & " $ p s _ s h e l l n a m e " ! = " z s h " ] ] ; t h e n / u s r / b i n / e n v b a s h " $ 0 " " $ { @ : 2 : $ ( ( $ { # @ } - 2 ) ) } " ; e x i t $ ? ; f i
# sh/bash (or zsh?) \
shift && set -- " ${@:1:$((${#@}-1))} "
# \
#echo " shell: " `ps -o args= $$ | sed -E 's/^. *\/| ^- //' | awk '{print $1}'`
# \
# \
echo " args: $@ "
echo " args: $@ "
# ------------------------------------------------------------------------------
# -- This if block wraps posix sh diversion section - only needed if Tcl didn't exit or return above.
if false== false # else {
then
: #
# https://gist.github.com/fcard/e26c5a1f7c8b0674c17c7554fb0cd35c0 (MIT lic)
# https://stackoverflow.com/questions/63864755/remove-last-argument-in-shell-script-posix
# posix compliant pop
pop() {
__pop_n=$(($1 - ${2:-1}))
if [ - n " $ Z S H _ V E R S I O N " - o - n " $ B A S H _ V E R S I O N " ] ; t h e n
POP_EXPR='set -- " ${@:1:'$__pop_n'} " '
elif [ $__pop_n -ge 500 ]; then
POP_EXPR=" set -- $(seq -s " " 1 $__pop_n | sed 's/[0-9]\+/ " ${\0}" /g') "
else
__pop_index=0
__pop_arguments=" "
while [ $__pop_index -lt $__pop_n ]; do
__pop_index=$((__pop_index+1))
__pop_arguments=" $__pop_arguments \ " \${$__pop_index}\" "
done
POP_EXPR=" set -- $__pop_arguments "
fi
}
# ------------------------------------------------------------------------------
# non-bash-like posix diversion \
if [ " $ p s _ s h e l l n a m e " ! = " b a s h " ] & & [ " $ p s _ s h e l l n a m e " ! = " z s h " ] ; t h e n
shift
pop $#
eval " $POP_EXPR "
echo " divert to bash $0 $@ "
/usr/bin/env bash " $0 " " $@ "
exit $?
fi
# close false==false block
fi
# close tcl wrap }
# ------------------------------------------------------
# ------------------------------------------------------
# -- This if block only needed if Tcl didn't exit or return above.
# -- This if block wraps whole zsh/bash and perl sections - only needed if Tcl didn't exit or return above.
if false== false # else {
if false== false # else {
then
then
: #
: #
# zsh/bash \
shift && set -- " ${@:1:$((${#@}-1))} "
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# -- sh/bash script section
# -- sh/bash script section
# -- leave as is if all that is required is launching the Tcl payload"
# -- leave as is if all that is required is launching the Tcl payload"
@ -905,7 +933,7 @@ if false==false # else {
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
plat=$(uname -s) #platform/system
plat=$(uname -s) #platform/system
if [[ " $ p l a t " = " L i n u x " * ] ] ; t h e n
if [[ " $ p l a t " == " Linux " * ]]; then
os=" linux "
os=" linux "
elif [[ " $plat " == " Darwin " * ]]; then
elif [[ " $plat " == " Darwin " * ]]; then
os=" macosx "
os=" macosx "
@ -917,11 +945,11 @@ elif [[ "$plat" == "NetBSD"* ]]; then
os=" netbsd "
os=" netbsd "
elif [[ " $plat " == " OpenBSD " * ]]; then
elif [[ " $plat " == " OpenBSD " * ]]; then
os=" openbsd "
os=" openbsd "
elif [[ " $plat " = " MINGW32 " * ]]; then
elif [[ " $plat " == " MINGW32 " * ]]; then
os=" win32 "
os=" win32 "
elif [[ " $plat " = " MINGW64 " * ]]; then
elif [[ " $plat " == " MINGW64 " * ]]; then
os=" win32 "
os=" win32 "
elif [[ " $plat " = " CYGWIN_NT " * ]]; then
elif [[ " $plat " == " CYGWIN_NT " * ]]; then
os=" win32 "
os=" win32 "
elif [[ " $plat " == " MSYS_NT " * ]]; then
elif [[ " $plat " == " MSYS_NT " * ]]; then
#review..
#review..
@ -967,34 +995,34 @@ for ln in "${arr_oslines[@]}"; do
pathraw=" ${splitln %% \ " *}" #take everything before the quote - use %% to get longest match
pathraw=" ${splitln %% \ " *}" #take everything before the quote - use %% to get longest match
#remove trailing underscores (% means must match at end)
#remove trailing underscores (% means must match at end)
nextshellpath=" ${pathraw/ % _*/} "
nextshellpath=" ${pathraw/ % _*/} "
echo " nextshellpath: $nextshellpath "
# echo " nextshellpath: $nextshellpath "
elif [[ " $ln " == *" nextshelltype " * ]]; then
elif [[ " $ln " == *" nextshelltype " * ]]; then
splitln=" ${ln#*=} "
splitln=" ${ln#*=} "
typeraw=" ${splitln %% \ " *}"
typeraw=" ${splitln %% \ " *}"
nextshelltype=" ${typeraw/ % _*/} "
nextshelltype=" ${typeraw/ % _*/} "
echo " nextshelltype: $nextshelltype "
# echo " nextshelltype: $nextshelltype "
fi
fi
done
done
exitcode=0
exitcode=0
#-- sh/bash launches nextscript here instead of shebang line at top
#-- sh/bash launches nextscript here instead of shebang line at top
if [[ " $ n e x t s h e l l t y p e " ! = " b a s h " & & " $ n e x t s h e l l t y p e " ! = " n o n e " ] ] ; t h e n
if [[ " $ n e x t s h e l l t y p e " ! = " b a s h " & & " $ n e x t s h e l l t y p e " ! = " n o n e " ] ] ; t h e n
echo bash launching subshell of type: $nextshelltype shellpath: $nextshellpath on " $0 " with args " $@ "
echo zsh/ bash launching subshell of type: $nextshelltype shellpath: $nextshellpath on " $0 " with args " $@ "
#e.g /usr/bin/env tclsh " $0 " " $@ "
#e.g /usr/bin/env tclsh " $0 " " $@ "
${nextshellpath} " $0 " " $@ "
${nextshellpath} " $0 " " $@ "
exitcode=$?
exitcode=$?
#echo " sh/bash reporting exitcode: ${exitcode} "
#echo " z sh/bash reporting exitcode: ${exitcode}"
exit $exitcode
exit $exitcode
#-- override exitcode example
#-- override exitcode example
#exit 66
#exit 66
else
else
#already in bash - don't launch another process or we would loop
#already in bash - don't launch another process or we would loop
#echo " bash payload "
#echo " zsh/ bash payload"
:
:
fi
fi
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin sh Payload
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin z sh Payload
#printf " start of bash or sh code "
#printf " start of bash or z sh code "
#< shell-payload>
#< shell-payload>
@ -1090,29 +1118,9 @@ cd $launchdir #restore original CWD
#< /shell-payload>
#< /shell-payload>
#< shell-pre-launch-subprocess>
#< /shell-pre-launch-subprocess>
# -- --- --- --- --- --- --- ---
#< shell-launch-subprocess>
#-- sh/bash launches Tcl here instead of shebang line at top
#-- use exec to use exitcode (if any) directly from the tcl script
#exec /usr/bin/env tclsh " $0 " " $@ "
#-- alternative - can run sh/bash script after the tcl call.
#/usr/bin/env tclsh " $0 " " $@ "
#exitcode=$?
#echo " sh/bash reporting tcl exitcode: ${exitcode} "
#-- override exitcode example
#exit 66
#< /shell-launch-subprocess>
# -- --- --- --- --- --- --- ---
#< shell-post-launch-subprocess>
#< /shell-post-launch-subprocess>
#printf " sh/bash done \n "
#printf " zsh/bash done \n "
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end sh Payload
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end zsh Payload
#------------------------------------------------------
#------------------------------------------------------
fi
fi
exit ${exitcode}
exit ${exitcode}
@ -1148,7 +1156,6 @@ print "os $os\n";
# -- --- ---
# -- --- ---
my $i =1;
my $i =1;
foreach my $a(@ARGV) {
foreach my $a(@ARGV) {
print " Arg # $i: $a\n " ;
print " Arg # $i: $a\n " ;
@ -1158,21 +1165,11 @@ foreach my $a(@ARGV) {
print STDERR " No perl code for this script. Try another program such as tcl or bash " ;
print STDERR " No perl code for this script. Try another program such as tcl or bash " ;
#< /perl-payload>
#< /perl-payload>
#< perl-pre-launch-subprocess>
#< /perl-pre-launch-subprocess>
# -- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- ---
#< perl-launch-subprocess>
#$exit_code=system(" tclsh " , $scriptname, @ARGV);
#$exit_code=system(" tclsh " , $scriptname, @ARGV);
#print " perl reporting tcl exitcode: $exit_code " ;
#print " perl reporting tcl exitcode: $exit_code " ;
#< /perl-launch-subprocess>
# -- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- ---
#< perl-post-launch-subprocess>
#< /perl-post-launch-subprocess>
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end perl Payload
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end perl Payload
exit $exit_code;
exit $exit_code;
@ -1540,20 +1537,6 @@ write-host "`e[92m getpunk done `e[m"
#< /powershell-payload>
#< /powershell-payload>
#< powershell-pre-launch-subprocess>
#< /powershell-pre-launch-subprocess>
# -- --- --- --- --- --- --- ---
#< powershell-launch-subprocess>
#tclsh $scriptname $args
#" powershell reporting exitcode: {0} " -f $LASTEXITCODE | write-host
#< /powershell-launch-subprocess>
# -- --- --- --- --- --- --- ---
#< powershell-post-launch-subprocess>
#< /powershell-post-launch-subprocess>
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end powershell Payload
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end powershell Payload
Exit $LASTEXITCODE
Exit $LASTEXITCODE