static int backtick_exitValue; static boolean backtick_verbose, backtick_keepScript; static new ThreadLocal backtick_scriptFile; static new ThreadLocal backtick_uninterruptable; // Great trick, thanks to Tim Bunce @ http://stackoverflow.com/questions/12856620/how-to-handle-signals-in-bash-during-synchronous-execution sbool backtick_win_cmd; // bugfixing static String backtick(String cmd) ctex { ping(); File outFile = File.createTempFile("_backtick", ""); backtickToFile(cmd, outFile); S result = loadTextFile(outFile.getPath(), ""); if (backtick_verbose) { //print("backtick: script length after=" + backtick_scriptFile->length()); print("[[\n" + result + "]]"); } outFile.delete(); ret result; } static java.lang.Process backtickToFile(String cmd, File outFile) ctex { try { java.lang.Process process = backtickToFile_noWait(cmd, outFile); process.waitFor(); backtick_exitValue = process.exitValue(); if (backtick_verbose) System.out.println("Process return code: " + backtick_exitValue); ret process; } finally { if (!backtick_keepScript) deleteFile(backtick_scriptFile.get()); backtick_scriptFile.set(null); } } static java.lang.Process backtickToFile_noWait(String cmd, File outFile) ctex { ping(); File scriptFile; S ext = isWindows() ? ".bat" : ""; if (backtick_keepScript) scriptFile = makeFileNameUnique_withExtension(javaxCachesDir("Cmd Scripts/backtick"), ".bat"); else scriptFile = File.createTempFile("_backtick", ext); backtick_scriptFile.set(scriptFile); if (backtick_verbose) print("backtick: scriptFile " + f2s(scriptFile)); bool makeInterruptable = !isTrue(backtick_uninterruptable!) && !isWindows(); cmd = trim(cmd); if (makeInterruptable && numLines(cmd) > 1) fail("No multi-line commands allowed when making interruptable"); S command = cmd + " >" + bashQuote(outFile.getPath()) + " 2>&1"; if (makeInterruptable) command = fixNewLines([[ interruptable() { # handle options local setsid="" local debug=false while true; do case "${1:-}" in --killall) setsid=setsid; shift ;; --debug) debug=true; shift ;; --*) echo "Invalid option: $1" 1>&2; exit 1;; *) break;; # no more options esac done # start the specified command $setsid "$@" & local child_pid=$! # arrange to propagate a signal to the child process trap ' exec 1>&2 set +e trap "" SIGPIPE # ensure a possible sigpipe from the echo does not prevent the kill echo "${BASH_SOURCE[0]} caught SIGTERM while executing $* (pid $child_pid), sending SIGTERM to it" # (race) child may have exited in which case kill will report an error # if setsid is used then prefix the pid with a "-" to indicate that the signal # should be sent to the entire process group kill ${setsid:+-}$child_pid exit 143 ' SIGTERM # ensure that the trap doesn't persist after we return trap 'trap - SIGTERM' RETURN $debug && echo "interruptable wait (child $child_pid, self $$) for: $*" # An error status from the child process will trigger an exception (via set -e) # here unless the caller is checking the return status wait $child_pid # last command, so status of waited for command is returned } interruptable ]]) + command; //Log.info("[Backtick] " + command); if (backtick_verbose) { print("backtick: command " + command); print("backtick: saving to " + scriptFile.getPath()); } saveTextFile(scriptFile.getPath(), command); if (backtick_verbose) print("backtick: command length=" + l(command) + ", file length=" + scriptFile.length()); String[] command2; if (isWindows()) if (backtick_win_cmd) command2 = new S[] { "cmd", "/c", scriptFile.getPath() }; else command2 = new S[] { scriptFile.getPath() }; else command2 = new String[] { "/bin/bash", scriptFile.getPath() }; if (backtick_verbose) print("backtick: command2 " + structure(command2)); ret Runtime.getRuntime().exec(command2); }