# Boilerplate functions to get consistent shell execution # across various gawk interpreter builds on windows. # Write a command as if you were at a shell prompt, # and whatever happens under the hood, it should run. # Klabaster 4.0.1 # http://www.klabaster.com/freeware.htm#dl # Pipe:Bat, System:Bat # Klabaster 4.0.0 # # Pipe:Bat, System:Quotes # GnuWin32 3.1.6 # http://gnuwin32.sourceforge.net/packages/gawk.htm # Pipe:Quotes, System:Quotes # kbk Japanese 3.1.7, with |& net support. # http://www.4shared.com/zip/3JEMkSE-/gawk-mbcs-win32-20091124.html # Pipe:Quotes, System:Bat BEGIN { initGlobals(); # A demo command... # %1 Should be visible # OS should be expanded to env value # That last bit should be visible command = "ECHO x%1x %OS% %~dp$PATH:1"; shellExec(command); } ### # initGlobals(command) # Sets global vars for shellExec functions. # _debug: 0,1 # _os: windows, linux # _execPipeAsCmdBatch: 0,1 # _execPipeExtraQuotes: 0,1 # _execSystemAsCmdBatch: 0,1 # _execSystemExtraQuotes: 0,1 # tmp.txt will briefly be created and deleted in the current directory. function initGlobals( command, tmp, tmpFile) { tmpFile = "tmp.txt"; # No quotes or spaces in the path. _debug = 1; # Set 0 to disable feedback. _os = ""; # Leave blank to autodetect. _execPipeAsCmdBatch = 0; _execPipeExtraQuotes = 0; _execSystemAsCmdBatch = 0; _execSystemExtraQuotes = 0; if (_os == "" && ENVIRON["OS"] ~ /[Ww]indows/) _os = "windows"; if (_os == "" && ENVIRON["OSTYPE"] ~ /[Ll]inux/) _os = "linux"; if (_os == "") { print "Error: Couldn't guess the OS. Set it manually." > "/dev/stderr"; exit 1; } if (_os == "windows" && isShellUnixy() == 0) { # Test for temp batch. command = "ECHO x%1x"; if ((command | getline tmp) > 0) { if (tmp ~ /^xx */) _execPipeAsCmdBatch = 1; } close(command); system(command " > " tmpFile); if ((getline tmp < tmpFile) > 0) { tmp = gensub(/\r/, "", "g", tmp); # CR-LF vs LF if (tmp ~ /^xx */) _execSystemAsCmdBatch = 1; } close(tmpFile); system("DEL /Q " tmpFile " >NUL"); # Test for CMD quote munching. command = "\"ECHO hi\" 2>&1"; if ((command | getline tmp) > 0) { if (tmp ~ /^hi *$/) _execPipeExtraQuotes = 1; # If quotes weren't eaten, you'd get "... is not recognized". } close(command); # This test breaks when tmpFile's quoted, # It may be fragile in other circumstances. command = "\"ECHO hi\" > " tmpFile " 2>&1"; system(command); if ((getline tmp < tmpFile) > 0) { tmp = gensub(/\r/, "", "g", tmp); # CR-LF vs LF if (tmp ~ /^hi *$/) _execSystemExtraQuotes = 1; } close(tmpFile); system("DEL /Q " tmpFile " >NUL"); } if (_debug == 1) { print "DEBUG Init ShellExec: _execPipeAsCmdBatch = " _execPipeAsCmdBatch > "/dev/stderr"; print "DEBUG Init ShellExec: _execPipeExtraQuotes = " _execPipeExtraQuotes > "/dev/stderr"; print "DEBUG Init ShellExec: _execSystemAsCmdBatch = " _execSystemAsCmdBatch > "/dev/stderr"; print "DEBUG Init ShellExec: _execSystemExtraQuotes = " _execSystemExtraQuotes > "/dev/stderr"; print "" > "/dev/stderr"; } } ### # isShellUnixy() # Returns 1 if the SHELL env var is set and unixy. # Only relevant on windows to test for non CMD.exe interpreters. # No need to run this directly. # See gawk_src/pc/popen.c: unixshell(char *p) function isShellUnixy( unixShellsList, unixShellsCount, unixShells, u) { if (ENVIRON["SHELL"] != "") { unixShellsList = "sh,bash,csh,tcsh,sh32,sh16,ksh"; unixShellsCount = split(unixShellsList, unixShells, ","); for (u=1; u <= unixShellsCount; u++) { if (ENVIRON["SHELL"] == unixShells[u]) return 1; } } return 0; } ### # getCommandWithExtraQuotes(command) # Surrounds a string with fodder quotes, for the shell to eat. # No need to run this directly. function getCommandWithExtraQuotes(command) { command = "\"" command "\""; return command; } ### # getCommandAsCmdBatch(command) # Escapes a string to keep batch parsing from mangling it. # No need to run this directly. function getCommandAsCmdBatch(command, newCommand, chunksCount, chunks, c, isEnv, env) { # A temp bat will be created, instead of CMD /C WHATEVER. # When parsed in batch-mode, vars that aren't set are blank, # instead of literals. So %'s need doubling. Real vars keep # their single percents however. # http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts newCommand = ""; chunksCount = split(command, chunks, "%"); for (c=1; c <= chunksCount; c++) { if (c == 1) {newCommand = chunks[c]; continue;} if (chunks[c] == "") {newCommand = newCommand "%%"; continue;} if (chunks[c] ~ /^([*0-9]|~([fdpnxsatz]|[$]PATH:)*[0-9])/) {newCommand = newCommand "%%" chunks[c]; continue;} isEnv = 0; for (env in ENVIRON) { if (tolower(chunks[c]) == tolower(env)) {isEnv = 1; break;} } if (isEnv) {newCommand = newCommand "%" chunks[c] "%";} else {newCommand = newCommand "%%" chunks[c] "%%";} # In either case, append the next chunk, sans-percent. if (c < chunksCount) newCommand = newCommand chunks[++c]; } return newCommand; } ### # getPipeCommand(command) # Prepares a string, written as in a shell prompt, for pipes. # Call this before loops and such. function getPipeCommand(command) { if (_execPipeExtraQuotes == 1) command = getCommandWithExtraQuotes(command); if (_execPipeAsCmdBatch == 1) command = getCommandAsCmdBatch(command); return command; } ### # getSystemCommand(command) # Prepares a string, written as in a shell prompt, for system(). # Call this before system(), or just use shellExec() instead. function getSystemCommand(command) { if (_execSystemExtraQuotes == 1) command = getCommandWithExtraQuotes(command); if (_execSystemAsCmdBatch == 1) command = getCommandAsCmdBatch(command); return command; } ### # shellExec(command) # Runs a command on the native shell and waits. function shellExec(command) { command = getSystemCommand(command); if (_debug == 1) print "DEBUG ShellExec: " command > "/dev/stderr"; system(command); }