Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # Written by Manuel Iglesias. glesialo@gmail.com
- CommandName=${0##*/}
- CommandOptionString="-c"
- RunInBackgroundOptionString="-b"
- KillProcessOptionString="-k"
- ProcessFinishedOptionString="-f"
- TempDir="/tmp"
- KillProcessTimeout="1" # Max time for killed process's parent to save child process' info. '$CommandName -k ' fails if parent still running after timeout.
- KillSignal="TERM"
- CommandLineVarName="CommandLine" # Process info output.
- ElapsedTime_mSVarName="ElapsedTime_mS" # Process info output.
- ExitCodeVarName="ExitCode" # Process info output.
- StandardOutVarName="StandardOut" # Process info output.
- 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...]]'.
- \tLaunches 'Command [Param [Param...]]' in background.
- \tOutputs background process Id. Exitcode 0 if successful.
- Usage: '$CommandName ProcessPid'.
- \tOutputs background process' info (See below).
- \tExitcode 0 if process is valid and no longer running (Info valid).
- \tExitcode 1 if process valid and still running (Info not valid).
- \tExitcode non 0 if not a valid process or any other problems
- \t (Info not valid).
- Usage: '$CommandName $ProcessFinishedOptionString ProcessPid'.
- \tChecks if process with 'ProcessPid' Pid has finished running.
- \tExitcode 0 if process is valid and no longer running
- \tExitcode 1 if process valid and still running.
- \tExitcode non 0 if not a valid process or any other problems.
- \tDoes not output any info.
- Usage: '$CommandName $KillProcessOptionString ProcessPid'.
- \tKills process with 'ProcessPid' Pid (Previously
- \t launched by '$CommandName').
- \tOutputs background process' info (See below).
- \tExitcode 0 if valid process and killed successfully
- \t (Or had exited already) and info read successfully.
- Background process' info:
- \tText that, when parsed, provides the following variables and values:
- \t '$CommandLineVarName': Command and parameters of process when launched.
- \t '$ElapsedTime_mSVarName': Process' running Time (MilliSeconds).
- \t '$ExitCodeVarName': Exit code of process.
- \t '$StandardOutVarName': Output generated by process while running.
- \t '$StandardErrorVarName': Error output generated by process while running.
- \tNote: Parsing notes in: System_Docs/System/BashCodeRepository.
- Note: Normal usage sequence:
- \tLaunch a process in background and get its Pid.
- \tDo other tasks.
- \tCheck if backgroung process has finished running
- \t using '$CommandName' and Pid.
- \tRead info when backgroung process has finished.
- \tParse background process' info and use Variables' contents.
- \tIf background process has not finished in the allotted time
- \t kill it with '$CommandName' (And get info of killed process).
- Please read System_Docs/System/BashCodeRepository for examples of usage.
- " 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}=\"$(cat $TempOutFile)\"
- #
- ${StandardErrorVarName}=\"$(cat $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.
- ((KillProcessTimeout *= 10)) # Will be checked every 1/10 seconds.
- while &>/dev/null ps --pid=$1 && [[ $KillProcessTimeout -gt 0 ]]
- do
- sleep 0.1
- ((KillProcessTimeout--))
- 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