Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

merge process2 to process

  • Loading branch information...
commit 9d2c6f781b36a1fa83c18c689dfc1dbf087732bc 1 parent 0261a5a
@mpapis mpapis authored
View
203 core/api/shell/process/functions
@@ -3,52 +3,32 @@
#
# SM Process API
#
-# process run "command to run"
-# echo $process_pid # this will be set to the command's pid
-#
-# process on exit "some commands to run"
-#
-# if process is running pid $pid
-# then ...
-#
-# if process is running pidfile $pid_file
-# then ...
-#
-# process status pid $pid
-#
-# process status name postgresql pid $pid
-#
-# process status name nginx pidfile /var/run/nginx/nginx.pid
-#
-# process signal $signal pidfile $pid_file
-# process signal $signal pid $pid
-# process $signal pid $pid
-# process $signal pidfile $pid_file
-#
-# where "signal" is one of (all upper or lower case):
+# process start name <name> timeout <timeout> command <command> [params...]
+# process stop name <name> timeout <timeout>
+# process is running name <name>
+# process restart name <name> timeout_start <timeout> timeout_stop <timeout> command <command> [params...]
+# process status <pid>
+# process lock </path/to/myprocess.lock>
+# process send signal <signal> pid <pid>
+# where <signal> is one of (all upper or lower case):
# kill,quit,term,usr2,usr2,ttin,ttou,winch,hup
# OR a hyphen followed by a number ex: "-15"
#
-# Example:
-# process signal hup 1234
-# process signal hup pidfile /var/run/nginx/nginx.pid
-# process term pidfile $HOME/shared/pids/unicorn.pid
-#
-# while : ; do
-# if process lock lockfile /var/run/myprocess.lock
-# then ... # Obtained the lock, continue!
-# fi
-# done
-#
process()
{
trace_filter process || set -o xtrace
- typeset _token _command _process _command _user _flags __process_commands _signal _pid _name _file
- _pid=0
+ typeset _command _token _name _timeout_start _timeout_stop _pid _signal _lock_path
+ typeset -a __process_commands _command_arr
__process_commands=(
- "on exit=exit"
- "is running=running"
+ "start"
+ "stop"
+ "restart"
+ "running=is running"
+ "pid=pid read"
+ "status"
+ "send signal=signal"
+ "lock=lockfile lock"
)
if __sm.command.detect "$*" "${__process_commands[@]}"
@@ -56,37 +36,55 @@ process()
_command="${__sm_command_dotted}"
shift "${__sm_command_words}"
else
- __sm.log.fail "No command given.\nUsage: \npaths <command> ..."
+ __sm.log.fail "No command given.\nUsage: \nprocess <command> ..."
fi
+ case "${_command}" in
+ (status)
+ _pid=$1
+ shift || __sm.log.fail "Process pid must be given after 'status'"
+ ;;
+ (signal)
+ _signal=$1
+ shift || __sm.log.fail "Signal must be given after 'signal' command"
+ ;;
+ (lockfile.lock)
+ _lock_path=$1
+ shift || __sm.log.fail "Signal must be given after 'signal' command"
+ ;;
+ esac
+
while (( $# ))
do
_token="$1" && shift
case ${_token} in
(name)
_name="$1"
- shift || fail "Process name must be given after keyword 'name'"
+ shift || __sm.log.fail "Process name must be given after keyword '${_token}'"
;;
- (pid)
- _pid=$1
- shift || fail "Process id (pid) must be given after keyword 'pid'"
- ;;
- (pidfile|lockfile)
- _file="$1"
- shift || fail "File path/name must follow keyword '${_token}'"
+ (timeout)
+ case "${_command}" in
+ (start) _timeout_start="$1" ;;
+ (stop) _timeout_stop="$1" ;;
+ (*) __sm.log.fail "Keyword '${_token}' can not be used with '${_command}'." ;;
+ esac
+ shift || __sm.log.fail "Timeout must be given after keyword '${_token}'"
;;
- (status|signal|lock)
- _command="${_token}"
+ (timeout_start)
+ _timeout_start="$1"
+ shift || __sm.log.fail "Start timeout must be given after keyword '${_token}'"
;;
- ([[:digit:]]##)
- _pid=${_token}
+ (timeout_stop)
+ _timeout_stop="$1"
+ shift || __sm.log.fail "Stop timeout must be given after keyword '${_token}'"
;;
- (kill|KILL|quit|QUIT|term|TERM|usr1|USR1|usr2|USR2|ttin|TTIN|ttou|TTOU|winch|WINCH|hup|HUP|-[[:digit:]]##)
- _command=signal
- _signal=${_token}
+ (pid)
+ _pid=$1
+ shift || __sm.log.fail "Process pid must be given after 'status'"
;;
- (*.pid)
- _file="${_token}"
+ (command)
+ _command_arr=( "$@" )
+ shift $# || __sm.log.fail "Command must be given after keyword '${_token}'"
;;
(*)
__sm.log.fail "Unknown token '${_token}'"
@@ -95,58 +93,67 @@ process()
done
case "${_command}" in
- (signal)
- if ! (( _pid ))
- then
- __sm.log.fail "pid to signal must be given;"\
- " please check your code (see trace below.)"
+ (start)
+ [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process.${_command}'."
+ [[ -n "${_timeout_start}" ]] || __sm.log.fail "Start timeout must be given for '__sm.process.${_command}'."
+ __sm.array.is.nonempty _command_arr || __sm.log.fail "Command must be given for '__sm.process.${_command}'."
+
+ if __sm.process.${_command} "${_name}" "${_timeout_start}" "${_command_arr[@]}"
+ then return 0
+ else return $?
fi
+ ;;
+ (stop)
+ [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process.${_command}'."
+ [[ -n "${_timeout_stop}" ]] || __sm.log.fail "Stop timeout must be given for '__sm.process.${_command}'."
- case "${_signal}" in
- (KILL|QUIT|TERM|USR1|USR2|TTIN|TTOU|WINCH|HUP|[[:digit:]]##)
- __sm.process.signal ${_signal} ${_pid}
- ;;
- (*)
- __sm.log.fail "Cannot signal service;"\
- " Signal must be given as the first parameter and be one of:"\
- " {KILL,QUIT,TERM,USR1,USR2,TTIN,TTOU,a digit})."
- ;;
- esac
+ if __sm.process.${_command} "${_name}" "${_timeout_stop}"
+ then return 0
+ else return $?
+ fi
;;
- (status)
- if [[ -n "${_file}" || -n "${_pid}" ]]
- then
- __sm.process.status "${_name}" "${_file}" "${_pid}"
- else
- __sm.log.fail "Either a pidfile or a process id (pid) must be given in order to show process status."
+ (restart)
+ [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process.${_command}'."
+ [[ -n "${_timeout_start}" ]] || __sm.log.fail "Start timeout must be given for '__sm.process.${_command}'."
+ [[ -n "${_timeout_stop}" ]] || __sm.log.fail "Stop timeout must be given for '__sm.process.${_command}'."
+ __sm.array.is.nonempty _command_arr || __sm.log.fail "Command must be given for '__sm.process.${_command}'."
+
+ if __sm.process.${_command} "${_name}" "${_timeout_start}" "${_timeout_stop}" "${_command_arr[@]}"
+ then return 0
+ else return $?
fi
;;
- (running)
- __sm.process.is.running "${_pid}" "${_file}"
+ (is.running|pid.read)
+ [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process.${_command}'."
+ if __sm.process.${_command} "${_name}"
+ then return 0
+ else return $?
+ fi
;;
- (lock)
- if [[ -n "${_file}" ]]
- then
- if __sm.process.lockfile.lock "${_file}"
- then return 0
- else return 1
- fi # Let caller deal with the choice of how to react.
- else
- __sm.log.fail "No lockfile was specified for locking."
+ (status)
+ [[ -n "${_pid}" ]] || __sm.log.fail "Process pid must be given for '__sm.process.${_command}'."
+ if __sm.process.${_command} "${_pid}"
+ then return 0
+ else return $?
fi
;;
- (exit)
- NIY "Process on exit"
- __sm.process.on.exit ...
+ (signal)
+ [[ -n "${_pid}" ]] || __sm.log.fail "Process pid must be given for '__sm.process.${_command}'."
+ [[ -n "${_signal}" ]] || __sm.log.fail "Signal must be given for '__sm.process.${_command}'."
+ if __sm.process.${_command} "${_signal}" "${_pid}"
+ then return 0
+ else return $?
+ fi
;;
- (*)
- if [[ -n "${_command}" ]]
- then
- __sm.log.fail "Process API command '${_command}' s not handled."
- else
- __sm.log.fail "Process API command must be given."
+ (lockfile.lock)
+ [[ -n "${_lock_path}" ]] || __sm.log.fail "Lockfile must be given for '__sm.process.${_command}'."
+ if __sm.process.${_command} "${_lock_path}"
+ then return 0
+ else return $?
fi
;;
+ (*)
+ __sm.log.fail "Process2 API command '${_command}' s not handled."
+ ;;
esac
}
-
View
1  core/api/shell/process/includes
@@ -1 +1,2 @@
internal/process
+internal/array
View
101 core/api/shell/process2/functions
@@ -1,101 +0,0 @@
-#!/usr/bin/env zsh
-
-process2()
-{
- trace_filter process2 || set -o xtrace
- typeset _command _token _name _timeout_start _timeout_stop
- typeset -a __process_commands _command_arr
-
- __process_commands=(
- "start"
- "stop"
- "restart"
- "running=is.running"
- "pid=pid.read"
- )
-
- if __sm.command.detect "$*" "${__process_commands[@]}"
- then
- _command="${__sm_command_dotted}"
- shift "${__sm_command_words}"
- else
- __sm.log.fail "No command given.\nUsage: \nprocess2 <command> ..."
- fi
-
- while (( $# ))
- do
- _token="$1" && shift
- case ${_token} in
- (name)
- _name="$1"
- shift || __sm.log.fail "Process name must be given after keyword '${_token}'"
- ;;
- (timeout)
- case "${_command}" in
- (start) _timeout_start="$1" ;;
- (stop) _timeout_stop="$1" ;;
- (*) __sm.log.fail "Keyword '${_token}' can not be used with '${_command}'." ;;
- esac
- shift || __sm.log.fail "Timeout must be given after keyword '${_token}'"
- ;;
- (timeout_start)
- _timeout_start="$1"
- shift || __sm.log.fail "Start timeout must be given after keyword '${_token}'"
- ;;
- (timeout_stop)
- _timeout_stop="$1"
- shift || __sm.log.fail "Stop timeout must be given after keyword '${_token}'"
- ;;
- (command)
- _command_arr=( "$@" )
- shift $# || __sm.log.fail "Command must be given after keyword '${_token}'"
- ;;
- (*)
- __sm.log.fail "Unknown token '${_token}'"
- ;;
- esac
- done
-
- case "${_command}" in
- (start)
- [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process2.${_command}'."
- [[ -n "${_timeout_start}" ]] || __sm.log.fail "Start timeout must be given for '__sm.process2.${_command}'."
- __sm.array.is.nonempty _command_arr || __sm.log.fail "Command must be given for '__sm.process2.${_command}'."
-
- if __sm.process2.${_command} "${_name}" "${_timeout_start}" "${_command_arr[@]}"
- then return 0
- else return $?
- fi
- ;;
- (stop)
- [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process2.${_command}'."
- [[ -n "${_timeout_stop}" ]] || __sm.log.fail "Stop timeout must be given for '__sm.process2.${_command}'."
-
- if __sm.process2.${_command} "${_name}" "${_timeout_stop}"
- then return 0
- else return $?
- fi
- ;;
- (restart)
- [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process2.${_command}'."
- [[ -n "${_timeout_start}" ]] || __sm.log.fail "Start timeout must be given for '__sm.process2.${_command}'."
- [[ -n "${_timeout_stop}" ]] || __sm.log.fail "Stop timeout must be given for '__sm.process2.${_command}'."
- __sm.array.is.nonempty _command_arr || __sm.log.fail "Command must be given for '__sm.process2.${_command}'."
-
- if __sm.process2.${_command} "${_name}" "${_timeout_start}" "${_timeout_stop}" "${_command_arr[@]}"
- then return 0
- else return $?
- fi
- ;;
- (is.running|pid.read)
- [[ -n "${_name}" ]] || __sm.log.fail "Name must be given for '__sm.process2.${_command}'."
- if __sm.process2.${_command} "${_name}"
- then return 0
- else return $?
- fi
- ;;
- (*)
- __sm.log.fail "Process2 API command '${_command}' s not handled."
- ;;
- esac
-}
View
2  core/api/shell/process2/includes
@@ -1,2 +0,0 @@
-internal/process2
-internal/array
View
16 core/cli/map
@@ -89,14 +89,14 @@ package=dev/package
publish=dev/publish
create=dev/create
json/get=json/get
-process2/start/silent=process2/process2_silent_start()
-process2/start=process2/process2_start()
-process2/stop=process2/process2_stop()
-process2/restart=process2/process2_restart()
-process2/running=process2/process2_running()
-process2/shedule=process2/process2_shedule()
-process2/unshedule=process2/process2_unshedule()
-process2/sheduled=process2/process2_sheduled()
+process/start/silent=process/process_silent_start()
+process/start=process/process_start()
+process/stop=process/process_stop()
+process/restart=process/process_restart()
+process/running=process/process_running()
+process/schedule=process/process_schedule()
+process/unschedule=process/process_unschedule()
+process/scheduled=process/process_scheduled()
cron/show=cron/__sm.cron.show()
cron/groups=cron/__sm.cron.list.groups()
cron/group=cron/__sm.cron.show.group()
View
34 core/cli/shell/process2/functions → core/cli/shell/process/functions
@@ -1,17 +1,17 @@
#!/usr/bin/env zsh
-process2_start()
+process_start()
{
log step "Starting $1" \
- process2 start \
+ process start \
name $1 \
timeout 10 \
command "$@"
}
-process2_silent_start()
+process_silent_start()
{
- if process2 start \
+ if process start \
name $1 \
timeout 10 \
command "$@"
@@ -20,32 +20,32 @@ process2_silent_start()
fi
}
-process2_stop()
+process_stop()
{
log step "Stoping $1" \
- process2 stop \
+ process stop \
name $1 \
timeout 10
}
-process2_restart()
+process_restart()
{
log step "Stoping $1" \
- process2 stop \
+ process stop \
name $1 \
timeout 10
log step "Starting $1" \
- process2 start \
+ process start \
name $1 \
timeout 10 \
command "$@"
}
-process2_running()
+process_running()
{
log step "$1 ..."
if
- process2 running name $1
+ process running name $1
then
log_step_message="$1 running."
log step succ
@@ -57,14 +57,14 @@ process2_running()
fi
}
-process2_shedule()
+process_schedule()
{
log step "Adding to cron every 5 minutes check $1"
if {
if which $1 | grep rvm >/dev/null
then rvm env --cron
fi
- echo "*/5 * * * * cd \"$PWD\" ; \"$(which sm)\" process2 start silent $*"
+ echo "*/5 * * * * cd \"$PWD\" ; \"$(which sm)\" process start silent $*"
} | cron edit group $1
then log step succ
else
@@ -74,22 +74,22 @@ process2_shedule()
fi
}
-process2_unshedule()
+process_unschedule()
{
log step "Removing from cron checking $1" \
cron remove group $1
}
-process2_sheduled()
+process_scheduled()
{
log step "$1 ..."
if cron group $1 | grep "" >/dev/null
then
- log_step_message="$1 sheduled."
+ log_step_message="$1 scheduled."
log step succ
else
typeset _status=$?
- log_step_message="$1 not sheduled"
+ log_step_message="$1 not scheduled"
log step fail
return ${_status}
fi
View
2  core/cli/shell/process/includes
@@ -0,0 +1,2 @@
+api/process
+api/cron
View
2  core/cli/shell/process2/includes
@@ -1,2 +0,0 @@
-api/process2
-api/cron
View
167 core/internal/shell/process/functions
@@ -1,37 +1,14 @@
#!/bin/sh
-__sm.process.is.running()
-{
- typeset _pid _pid_file
- _pid=${1:-0} ; _pid_file="$2"
-
- if (( _pid < 1 )) && [[ -s "${_pid_file}" ]]
- then read -r _pid < ${_pid_file}
- fi
-
- kill -0 $_pid
-}
-
-__sm.process.run()
-{
- typeset _command ; _command="$1"
- typeset -g process_pid >/dev/null 2>&1
- ${_command} &
- process_pid=$!
- return 0
-}
-
+#TODO: old code - validate
__sm.process.status()
{
- typeset _name _pid_file _pid _ps
- _name="$1"; _pid_file="$2"; _pid="${3:-0}"
+ typeset _name _pid _ps
+ _name="$1"
+ _pid="$2"
log "${name:-"process"}:"
- if [[ -s "${_pid_file}" ]]
- then read -r _pid < ${_pid_file}
- fi
-
if (( ${_pid} > 0 ))
then
_ps=$(ps -p ${_pid} -ostate,sgi_rss,vsize | tail -1)
@@ -124,16 +101,14 @@ __sm.process.status()
fi
}
+#TODO: old code - validate
__sm.process.signal()
{
- typeset _signal _pid _pid_file
- _signal="${1}" ; _pid="${2:-0}" ; _pid_file="${3:-}"
+ typeset _signal _pid
+ _signal="$1"
+ _pid="$2"
- if (( _pid < 1 )) && [[ -s "${_pid_file}" ]]
- then read -r _pid < ${_pid_file}
- fi
-
- if (( _pid > 0 ))
+ if [[ -n "${_pid}" ]] && (( _pid > 0 ))
then
kill -${_signal} ${_pid}
else
@@ -141,6 +116,8 @@ __sm.process.signal()
fi
}
+#TODO: old code - validate
+#TODO: add lockfile.init, lockfile.unlock, lockfile.finish
__sm.process.lockfile.lock()
{
typeset _lockfile
@@ -166,3 +143,125 @@ __sm.process.lockfile.lock()
return 0 # The lock has been obtained! Proceed with nefarious things!
}
+# start process
+# ex. __sm.process.start NAME TIMEOUT COMMAND ...
+__sm.process.start()
+{
+ typeset _name _pid _counter _counter_value
+ _name="$1"
+ _counter_value="$2"
+ shift 2
+ if __sm.process.is.running "${_name}"
+ then
+ return 0
+ fi
+
+ nohup "$@" </dev/null >"${_name}_out.log" 2>"${_name}_err.log" &
+ _pid=$!
+
+ _counter=${_counter_value}
+ while (( _counter-- )) && __sm.process.pid.running ${_pid}
+ do
+ sleep 1s
+ done
+
+ if __sm.process.pid.running ${_pid}
+ then __sm.process.pid.write "${_name}" ${_pid}
+ else return $?
+ fi
+}
+
+# stop process
+# ex. __sm.process.stop NAME TIMEOUT
+__sm.process.stop()
+{
+ typeset _pid _counter
+ if _pid=$(__sm.process.pid.read "${1}")
+ then
+ _counter=$2
+ while (( _counter-- )) && __sm.process.pid.running ${_pid}
+ do
+ kill ${_pid}
+ sleep 1s
+ done
+ if __sm.process.pid.running ${_pid}
+ then
+ kill -9 ${_pid}
+ fi
+ if ! __sm.process.pid.running ${_pid}
+ then
+ __sm.process.pid.write "${1}"
+ fi
+ else return 0
+ fi
+}
+
+# stop if running ; start
+# ex. __sm.process.restart NAME TIMEOUT_START TIMEOUT_STOP COMMAND ...
+__sm.process.restart()
+{
+ typeset _name _counter_start _counter_stop
+ _name="$1"
+ _counter_start="$2"
+ _counter_stop="$3"
+ shift 3
+
+ if __sm.process.stop "${_name}" ${_counter_stop}
+ then true #continue
+ else return $?
+ fi
+
+ if __sm.process.start "${_name}" ${_counter_start} "$@"
+ then return 0
+ else return $?
+ fi
+}
+
+# Check if a process with a given NAME is working
+# ex. __sm.process.is.running NAME
+__sm.process.is.running()
+{
+ typeset _pid
+ if _pid=$(__sm.process.pid.read "$1")
+ then
+ if __sm.process.pid.running ${_pid}
+ then return 0
+ else return $?
+ fi
+ else return $?
+ fi
+}
+
+## Private?
+
+# read a pid from pid file NAME
+# ex. __sm.process.pid.read NAME
+__sm.process.pid.read()
+{
+ if cat ".${1}.pid" 2>/dev/null
+ then return 0
+ else return $?
+ fi
+}
+
+# write a PID to pid file NAME
+# ex: __sm.process.pid.write NAME PID
+# delete pid file NAME if no PID
+# ex: __sm.process.pid.write NAME
+__sm.process.pid.write()
+{
+ if [[ -n "${2:-}" ]]
+ then echo "$2" > ".${1}.pid" 2>/dev/null
+ else rm -f ".${1}.pid" 2>/dev/null
+ fi
+}
+
+# Check if a process with a given PID is working
+# ex. __sm.process.pid.running PID
+__sm.process.pid.running()
+{
+ if kill -0 $1 2>/dev/null
+ then return 0
+ else return $?
+ fi
+}
View
124 core/internal/shell/process2/functions
@@ -1,124 +0,0 @@
-#!/usr/bin/env zsh
-
-# start process
-# ex. __sm.process2.start NAME TIMEOUT COMMAND ...
-__sm.process2.start()
-{
- typeset _name _pid _counter _counter_value
- _name="$1"
- _counter_value="$2"
- shift 2
- if __sm.process2.is.running "${_name}"
- then
- return 0
- fi
-
- nohup "$@" </dev/null >"${_name}_out.log" 2>"${_name}_err.log" &
- _pid=$!
-
- _counter=${_counter_value}
- while (( _counter-- )) && __sm.process2.pid.running ${_pid}
- do
- sleep 1s
- done
-
- if __sm.process2.pid.running ${_pid}
- then __sm.process2.pid.write "${_name}" ${_pid}
- else return $?
- fi
-}
-
-# stop process
-# ex. __sm.process2.stop NAME TIMEOUT
-__sm.process2.stop()
-{
- typeset _pid _counter
- if _pid=$(__sm.process2.pid.read "${1}")
- then
- _counter=$2
- while (( _counter-- )) && __sm.process2.pid.running ${_pid}
- do
- kill ${_pid}
- sleep 1s
- done
- if __sm.process2.pid.running ${_pid}
- then
- kill -9 ${_pid}
- fi
- if ! __sm.process2.pid.running ${_pid}
- then
- __sm.process2.pid.write "${1}"
- fi
- else return 0
- fi
-}
-
-# stop if running ; start
-# ex. __sm.process2.restart NAME TIMEOUT_START TIMEOUT_STOP COMMAND ...
-__sm.process2.restart()
-{
- typeset _name _counter_start _counter_stop
- _name="$1"
- _counter_start="$2"
- _counter_stop="$3"
- shift 3
-
- if __sm.process2.stop "${_name}" ${_counter_stop}
- then true #continue
- else return $?
- fi
-
- if __sm.process2.start "${_name}" ${_counter_start} "$@"
- then return 0
- else return $?
- fi
-}
-
-# Check if a process with a given NAME is working
-# ex. __sm.process2.is.running NAME
-__sm.process2.is.running()
-{
- typeset _pid
- if _pid=$(__sm.process2.pid.read "$1")
- then
- if __sm.process2.pid.running ${_pid}
- then return 0
- else return $?
- fi
- else return $?
- fi
-}
-
-## Private?
-
-# read a pid from pid file NAME
-# ex. __sm.process2.pid.read NAME
-__sm.process2.pid.read()
-{
- if cat ".${1}.pid" 2>/dev/null
- then return 0
- else return $?
- fi
-}
-
-# write a PID to pid file NAME
-# ex: __sm.process2.pid.write NAME PID
-# delete pid file NAME if no PID
-# ex: __sm.process2.pid.write NAME
-__sm.process2.pid.write()
-{
- if [[ -n "${2:-}" ]]
- then echo "$2" > ".${1}.pid" 2>/dev/null
- else rm -f ".${1}.pid" 2>/dev/null
- fi
-}
-
-# Check if a process with a given PID is working
-# ex. __sm.process2.pid.running PID
-__sm.process2.pid.running()
-{
- if kill -0 $1 2>/dev/null
- then return 0
- else return $?
- fi
-}
Please sign in to comment.
Something went wrong with that request. Please try again.