Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/bash
- # Written by Manuel Iglesias. [email protected]
- readonly CommandName=${0##*/}
- readonly CommandOptionString="-c"
- readonly RunInBackgroundOptionString="-b"
- readonly KillProcessOptionString="-k"
- readonly ProcessFinishedOptionString="-f"
- readonly TempDir="/tmp"
- readonly KillProcessTimeoutSeconds="1" # Max time for killed process's parent to save child process' info. '$CommandName -k ' fails if parent running after timeout.
- readonly KillSignal="TERM"
- readonly CommandLineVarName="CommandLine" # Process info output.
- readonly ElapsedTime_mSVarName="ElapsedTime_mS" # Process info output.
- readonly ExitCodeVarName="ExitCode" # Process info output.
- readonly StandardOutVarName="StandardOut" # Process info output.
- readonly StandardErrorVarName="StandardError" # Process info output.
- Usage() {
- echo -e "'${CommandName}' can be used to launch/control a process running in background.
- Usage: '$CommandName $CommandOptionString Command [Param [Param...]]'.
- Launches 'Command [Param [Param...]]' in background and outputs its Pid.
- Exitcode 0 if successful.
- Usage: '$CommandName Pid'.
- Outputs background process' information (see below).
- Exitcode 0 if 'Pid' is valid and process no longer running (information valid).
- Exitcode 1 if 'Pid' valid and process still running (information not valid).
- Exitcode non 0 if 'Pid' not valid or any other problem (information not valid).
- Usage: '$CommandName $ProcessFinishedOptionString Pid'.
- Checks if process with 'Pid' is running. Does not output any process information.
- Exitcode 0 if 'Pid' is valid and the process is no longer running
- Exitcode 1 if 'Pid' valid and the process is still running.
- Exitcode non 0 if 'Pid' not valid or any other problem.
- Usage: '$CommandName $KillProcessOptionString Pid'.
- Kills process with 'Pid' (previously launched by '${CommandName}').
- Outputs background process' information (see below).
- Exitcode 0 if 'Pid' is valid and was killed successfully (or had exited already)
- and if the process' information was read successfully.
- Background process' information:
- Text that, when parsed, provides the following variables and values:
- '${CommandLineVarName}': Command and parameters of process when launched.
- '${ElapsedTime_mSVarName}': Process' running time (milliSeconds).
- '${ExitCodeVarName}': Process' exit code.
- '$StandardOutVarName': StdOut generated by process while running.
- '${StandardErrorVarName}': StdErr generated by process while running.
- See System_Docs/System/BashCodeRepository/'Background processes' (parsing).
- Normal usage sequence:
- -Use '$CommandName $CommandOptionString Command ...' to launch a process and get its Pid.
- -Do other tasks.
- -Use '${CommandName} $ProcessFinishedOptionString Pid' to check if the background process has exited.
- -Use '${CommandName} Pid' to read the process' information when it has exited.
- -Parse the background process' information and use Variables' contents.
- -If the background process has not exited in the allotted time, kill it with
- '$CommandName $KillProcessOptionString Pid' (and get the killed process' [partial] information)." 1>&2
- return 0
- }
- CommandOption=false;RunInBackgroundOption=false;KillProcessOption=false;ProcessFinishedOption=false
- if [[ "$1" == "$CommandOptionString" ]]
- then
- CommandOption=true
- shift
- else
- if [[ "$1" == "$RunInBackgroundOptionString" ]]
- then
- RunInBackgroundOption=true
- shift
- else
- if [[ "$1" == "$KillProcessOptionString" ]]
- then
- KillProcessOption=true
- shift
- else
- if [[ "$1" == "$ProcessFinishedOptionString" ]]
- then
- ProcessFinishedOption=true
- shift
- fi
- fi
- fi
- fi
- if [[ $# -lt 1 || "$1" == "-h" || "$1" == "-?" || "$1" == "--help" || "$1" == "-help" ]]
- then
- Usage
- exit 64
- fi
- if $CommandOption
- then
- if ! type "$1" &>/dev/null
- then
- echo -e "${CommandName}: Command '${1}' not in '\$PATH'. Aborting!" 1>&2
- exit 127
- fi
- # 'coproc': Equivalent to ending command with '&'. '&' does not work if shell invoked with `` (ProcessId=`BackgroundProcess -c Command`).
- # &>/dev/null so that no error message is given when killed. Also to allow background processes to be launched from this background process (pipes?).
- if coproc $CommandName $RunInBackgroundOptionString "$@" &>/dev/null
- then
- eval "exec ${COPROC[0]}>&-" # Close coproc stdout pipe (See Books/Science/Computer_Science/Linux/Bash_scripting/Bash_Co_Processes).
- eval "exec ${COPROC[1]}<&-" # Close coproc stdin pipe (See Books/Science/Computer_Science/Linux/Bash_scripting/Bash_Co_Processes).
- echo "$!"
- exit 0
- else
- echo -e "${CommandName}: Launching in background with 'coproc' failed with ExitCode: ${?}. Aborting!" 1>&2 # Should never happen but... just in case ;-).
- exit 70
- fi
- fi
- if $RunInBackgroundOption
- then
- TempFile="${TempDir}/${CommandName}_$$"
- TempErrorFile="${TempFile}.Error"
- >$TempErrorFile # So that other users can not read file.
- chmod 600 $TempErrorFile # So that other users can not read file.
- TempOutFile="${TempFile}.Out"
- >$TempOutFile # So that other users can not read file.
- chmod 600 $TempOutFile # So that other users can not read file.
- cd $TempDir # So that child process uses it as working directory (And does not tie up current wd).
- exec 0<&- # Close stdin.
- exec 1>&- # Close stdout.
- exec 2>&- # Close stderr.
- StartTime=$(date +%s.%N) # Seconds.Nanoseconds since 1970−01−01.
- Decimals=${StartTime##*\.} # Fast Times1000.
- Decimals=${Decimals:0:3} # Fast Times1000.
- StartTime="${StartTime%\.*}$Decimals" # Fast Times1000.
- 1>$TempOutFile 2>$TempErrorFile "$@"
- ExitCode=$?
- EndTime=$(date +%s.%N) # Seconds.Nanoseconds since 1970−01−01.
- Decimals=${EndTime##*\.} # Fast Times1000.
- Decimals=${Decimals:0:3} # Fast Times1000.
- EndTime="${EndTime%\.*}$Decimals" # Fast Times1000.
- ((ElapsedTime_mS = EndTime - StartTime))
- echo -e "${CommandLineVarName}=\"$@\"
- #
- ${ElapsedTime_mSVarName}=${ElapsedTime_mS}
- #
- ${ExitCodeVarName}=${ExitCode}
- #
- ${StandardOutVarName}=\"$(< ${TempOutFile})\"
- #
- ${StandardErrorVarName}=\"$(< ${TempErrorFile})\"
- #" >$TempFile
- chmod 600 $TempFile # So that other users can not read file.
- rm -f $TempErrorFile $TempOutFile
- exit 0
- fi
- if [[ $# -ne 1 ]]
- then
- Usage
- exit 64
- fi
- TempFile="${TempDir}/${CommandName}_$1"
- if ProcessCommandName=$(2>/dev/null ps --pid=$1 h -o comm)
- then
- if [[ "$CommandName" == "${ProcessCommandName}${CommandName#${ProcessCommandName}}" ]] # 'ps' returns only the 15 first characters of command.
- then
- if $KillProcessOption
- then
- for Child in $(ps -o pid --no-headers --ppid $1)
- do
- pkill -${KillSignal} -P $Child &>/dev/null # Kill BackgroundProcess' Child's children (grandchildren).
- done
- pkill -${KillSignal} -P $1 &>/dev/null # -P means kill processes whose parent process ID is listed. Here: kill BackgroundProcess' children.
- ((Timeout = KillProcessTimeoutSeconds * 10)) # Will be checked every 1/10 seconds.
- while &>/dev/null ps --pid=$1 && [[ $Timeout -gt 0 ]]
- do
- sleep 0.1
- ((Timeout--))
- done
- if &>/dev/null ps --pid=$1
- then
- echo -e "${CommandName}: Warning! Process '${1}' could not be killed." 1>&2
- exit 70
- else
- if [[ -f $TempFile ]]
- then
- cat $TempFile
- rm -f $TempFile
- exit 0
- else
- echo -e "${CommandName}: Process killed but temporary file not found. No record of process '${1}'." 1>&2
- exit 70
- fi
- fi
- fi
- echo -e "${CommandName}: Process '${1}' still running." 1>&2
- exit 1
- else
- echo -e "${CommandName}: Process '${1}' running but not an instance of '${CommandName}'. Aborting." 1>&2
- exit 70
- fi
- fi
- if [[ -f $TempFile ]]
- then
- if $ProcessFinishedOption
- then
- exit 0
- fi
- if $KillProcessOption
- then
- echo -e "${CommandName}: Process '${1}' has already exited. Kill request ignored." 1>&2
- fi
- cat $TempFile
- rm -f $TempFile
- else
- echo -e "${CommandName}: Process not running and temporary file not found. No record of process '${1}'." 1>&2
- exit 70
- fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement