观察 bash 脚本的输出
Watching the output of a bash script
我正在尝试使用 watch
查看 shell 脚本的输出,执行 /bin/bash
并将脚本本身保存在 heredoc 中。
鼻屎只执行一次。它给出了正确的输出,然后手表刷新并且屏幕变为空白。退出后 watch
没有列出任何错误。
我无法弄清楚问题出在哪里,因为使用 watch
> bash
> heredoc
> ugly code
.[=21= 进行调试变得越来越困难]
好消息是 heredoc 中的 ugly code
工作正常。
function show_users {
[[ -z ]] && watchtime=1 || watchtime=
[[ -z ]] && export userToShow="mydefaultuser" || export userToShow=
echo "Setting up watch for user ${userToShow}"
watch -n $watchtime --no-title /bin/bash <<-'EOF'
#Show finger results of requested user
finger ${userToShow}
#show list of users su'd into requested user
echo "************************************************************************************"
echo "users logged in as ${userToShow}"
#get the parent PIDS of any process belonging to requested user
#into a list that can be read by grep
parentPIDs=$(ps -ef | grep "su - ${userToShow}" | grep -v 'grep\|finger' | awk 'NR>1{printf " %s \|",parentpid}{parentpid=}END{printf " %s\n", parentpid}')
#get usersnames associated to those parent PIDS
parentUsers=$(ps -ef | grep "${parentPIDs}" | grep -v "grep\|${userToShow}" | awk '{print }' | sort | uniq)
#finger each of these users and get their full name
while IFS= read -r line ; do
printf "%s: " $line
parentName=$(finger $line | awk -F":" 'NR==1{print }')
echo $parentName
done <<< "${parentUsers}"
#show tree for all proceses being run by requested user up to root.
echo "************************************************************************************"
ps -ef --forest | egrep -e "sshd:|-ksh|$userToShow" | grep -v grep | awk 'root==1{print ""} NR>1{print line} {line=[=11=];root=(=="root") ? 1 : 0}'
EOF
}
像这样称呼:
show_users 2 "username"
watch
重复 运行 指定命令及其参数。
heredoc 和更普遍的重定向运算符的效果是命令的 not 部分。
所以 watch
无法重新生成 heredoc。
一旦 heredoc 被 bash
的第一个 运行 消耗,
好吧,第二个就没什么了。
您可以在这个答案的底部尝试一个肮脏的技巧。
但我推荐的解决方案是将 heredoc
的内容保存在一个临时文件中。
这相当简单且稳健。
将文件保存在由 mktemp
创建的临时文件中。
设置一个 trap
来捕获中断和其他信号,以确保临时文件得到清理。
运行 watch bash "$tmpfile"
。
这很简单并且会起作用。
脏 "solution" 带有(严重)警告,不要这样做!
您可以将脚本放在变量中,然后 运行 和 watch
像这样:
watch "bash -c '$var'"
或者像这样:
watch "bash -c \"$var\""
但严重的警告是,如果 var
包含 '
,第一个版本将中断,如果 var
包含 "
,第二个版本将中断。
所以这些只适用于最基本的脚本,
当然不是你例子中的那个。
这显然不是一个选项,为了完整起见,我只是在此处添加。
您可以将代码放在函数中。使用 export -f func
可以使函数定义可用于当前脚本的子进程,这样您就可以说
watch bash -c func
在 OP 的示例中:
# don't use bash-only (IMHO ugly) function syntax
get_user_info () {
local userToShow=
# No need for braces around variable names unless disambiguation is required
finger "$userToShow"
echo "lots of ugly asterisks"
echo "users logged in as ${userToShow}"
# avoid grep | grep | awk
# field specifier can probasbly be made more strict
# (match only instead of [=11=])?
parentPIDs=$(ps -ef |
awk -v user="$userToShow" 'NR>1 && ([=11=] ~ "su - " user) {
printf " %s \|",parentpid}
{parentpid=}
END{printf " %s\n", parentpid}')
# Would be better if parentPIDs was a proper regex
# Assumes you are looking for the PPID in column 3
parentUsers=$(ps -ef |
awk -v pids="$parentPIDs" 'parentPIDs ~ {print }' |
# prefer sort -u over sort | uniq
sort -u)
while IFS= read -r line ; do
printf "%s: " "$line"
# No need to capture output just to echo it
finger "$line" | awk -F":" 'NR==1{print }'
done <<< "${parentUsers}"
echo "Another ugly lot of asterisks"
# Again, the regex can probably be applied to just one field
ps -ef --forest |
awk -v re="sshd:|-ksh|$userToShow" '[=11=] !~ re { next }
root==1{print ""}
NR>1{print line}
{line=[=11=];root=(=="root" || ==1) ? 1 : 0}'
}
export -f get_user_info
show_users () {
# Avoid complex [[ -z ... ]]; use defaults with ${var-"value if unset"}
# Mark these as local to avoid polluting global namespace
local watchtime={-1}
local userToShow=${2-mydefaultuser}
# no need to export these variables
echo "$mycommand"
echo "Setting up watch for user ${userToShow}"
watch -n $watchtime --no-title bash -c get_user_info "$userToShow"
}
我正在尝试使用 watch
查看 shell 脚本的输出,执行 /bin/bash
并将脚本本身保存在 heredoc 中。
鼻屎只执行一次。它给出了正确的输出,然后手表刷新并且屏幕变为空白。退出后 watch
没有列出任何错误。
我无法弄清楚问题出在哪里,因为使用 watch
> bash
> heredoc
> ugly code
.[=21= 进行调试变得越来越困难]
好消息是 heredoc 中的 ugly code
工作正常。
function show_users {
[[ -z ]] && watchtime=1 || watchtime=
[[ -z ]] && export userToShow="mydefaultuser" || export userToShow=
echo "Setting up watch for user ${userToShow}"
watch -n $watchtime --no-title /bin/bash <<-'EOF'
#Show finger results of requested user
finger ${userToShow}
#show list of users su'd into requested user
echo "************************************************************************************"
echo "users logged in as ${userToShow}"
#get the parent PIDS of any process belonging to requested user
#into a list that can be read by grep
parentPIDs=$(ps -ef | grep "su - ${userToShow}" | grep -v 'grep\|finger' | awk 'NR>1{printf " %s \|",parentpid}{parentpid=}END{printf " %s\n", parentpid}')
#get usersnames associated to those parent PIDS
parentUsers=$(ps -ef | grep "${parentPIDs}" | grep -v "grep\|${userToShow}" | awk '{print }' | sort | uniq)
#finger each of these users and get their full name
while IFS= read -r line ; do
printf "%s: " $line
parentName=$(finger $line | awk -F":" 'NR==1{print }')
echo $parentName
done <<< "${parentUsers}"
#show tree for all proceses being run by requested user up to root.
echo "************************************************************************************"
ps -ef --forest | egrep -e "sshd:|-ksh|$userToShow" | grep -v grep | awk 'root==1{print ""} NR>1{print line} {line=[=11=];root=(=="root") ? 1 : 0}'
EOF
}
像这样称呼:
show_users 2 "username"
watch
重复 运行 指定命令及其参数。
heredoc 和更普遍的重定向运算符的效果是命令的 not 部分。
所以 watch
无法重新生成 heredoc。
一旦 heredoc 被 bash
的第一个 运行 消耗,
好吧,第二个就没什么了。
您可以在这个答案的底部尝试一个肮脏的技巧。
但我推荐的解决方案是将 heredoc
的内容保存在一个临时文件中。
这相当简单且稳健。
将文件保存在由 mktemp
创建的临时文件中。
设置一个 trap
来捕获中断和其他信号,以确保临时文件得到清理。
运行 watch bash "$tmpfile"
。
这很简单并且会起作用。
脏 "solution" 带有(严重)警告,不要这样做!
您可以将脚本放在变量中,然后 运行 和 watch
像这样:
watch "bash -c '$var'"
或者像这样:
watch "bash -c \"$var\""
但严重的警告是,如果 var
包含 '
,第一个版本将中断,如果 var
包含 "
,第二个版本将中断。
所以这些只适用于最基本的脚本,
当然不是你例子中的那个。
这显然不是一个选项,为了完整起见,我只是在此处添加。
您可以将代码放在函数中。使用 export -f func
可以使函数定义可用于当前脚本的子进程,这样您就可以说
watch bash -c func
在 OP 的示例中:
# don't use bash-only (IMHO ugly) function syntax
get_user_info () {
local userToShow=
# No need for braces around variable names unless disambiguation is required
finger "$userToShow"
echo "lots of ugly asterisks"
echo "users logged in as ${userToShow}"
# avoid grep | grep | awk
# field specifier can probasbly be made more strict
# (match only instead of [=11=])?
parentPIDs=$(ps -ef |
awk -v user="$userToShow" 'NR>1 && ([=11=] ~ "su - " user) {
printf " %s \|",parentpid}
{parentpid=}
END{printf " %s\n", parentpid}')
# Would be better if parentPIDs was a proper regex
# Assumes you are looking for the PPID in column 3
parentUsers=$(ps -ef |
awk -v pids="$parentPIDs" 'parentPIDs ~ {print }' |
# prefer sort -u over sort | uniq
sort -u)
while IFS= read -r line ; do
printf "%s: " "$line"
# No need to capture output just to echo it
finger "$line" | awk -F":" 'NR==1{print }'
done <<< "${parentUsers}"
echo "Another ugly lot of asterisks"
# Again, the regex can probably be applied to just one field
ps -ef --forest |
awk -v re="sshd:|-ksh|$userToShow" '[=11=] !~ re { next }
root==1{print ""}
NR>1{print line}
{line=[=11=];root=(=="root" || ==1) ? 1 : 0}'
}
export -f get_user_info
show_users () {
# Avoid complex [[ -z ... ]]; use defaults with ${var-"value if unset"}
# Mark these as local to avoid polluting global namespace
local watchtime={-1}
local userToShow=${2-mydefaultuser}
# no need to export these variables
echo "$mycommand"
echo "Setting up watch for user ${userToShow}"
watch -n $watchtime --no-title bash -c get_user_info "$userToShow"
}