I'm trying to use watch
to look at the output of a shell script, executing /bin/bash
and holding the script itself in a heredoc.
我正在尝试使用watch来查看shell脚本的输出,执行/bin/bash,并将脚本本身保存在一个heredoc中。
The booger is only executing once though. It gives the correct output, then watch refreshes and the screen goes blank. Upon exiting watch
there are no errors listed.
booger只执行一次。它给出正确的输出,然后观察刷新,屏幕变为空白。退出时,没有错误列出。
I can't figure out where the issue is since it's getting difficult to debug with watch
> bash
> heredoc
> ugly code
.
我不知道问题出在哪里,因为用watch > bash > bbdoc >丑陋的代码调试变得越来越困难。
The good news is that ugly code
in the heredoc works fine.
好消息是赫里多克的丑陋代码运行良好。
function show_users {
[[ -z $1 ]] && watchtime=1 || watchtime=$1
[[ -z $2 ]] && export userToShow="mydefaultuser" || export userToShow=$2
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=$3}END{printf " %s\n", parentpid}')
#get usersnames associated to those parent PIDS
parentUsers=$(ps -ef | grep "${parentPIDs}" | grep -v "grep\|${userToShow}" | awk '{print $1}' | 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 $3}')
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=$0;root=($1=="root") ? 1 : 0}'
EOF
}
called like:
被称为:
show_users 2 "username"
2 个解决方案
#1
2
You can put the code in a function. With export -f func
you make the function definition available to subprocesses of the current script, so that you can then say
您可以将代码放入函数中。使用export -f func,可以将函数定义提供给当前脚本的子进程,这样就可以说
watch bash -c func
In OP's example:
在OP的例子:
# don't use bash-only (IMHO ugly) function syntax
get_user_info () {
local userToShow=$1
# 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 $2 instead of $0)?
parentPIDs=$(ps -ef |
awk -v user="$userToShow" 'NR>1 && ($0 ~ "su - " user) {
printf " %s \\|",parentpid}
{parentpid=$3}
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 ~$3 {print $1}' |
# 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 $3}'
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" '$0 !~ re { next }
root==1{print ""}
NR>1{print line}
{line=$0;root=($1=="root" || $3==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-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"
}
#2
1
watch
repeatedly runs a specified command with its arguments. The heredoc, and more generally the effect of redirect operators are not part of the command. So watch
cannot re-generate the heredoc. And once the heredoc is consumed by the first run of bash
, well, there will be nothing left for the second.
watch反复运行带有参数的指定命令。赫里多克,更一般地说,重定向操作符的影响不是命令的一部分。因此,watch不能重新生成“异端”。一旦赫里多克被第一次bash消耗掉,那么,第二次就什么都没有了。
There's a dirty hack you could try at the bottom of this answer. But my recommended solution is to save the content of the heredoc
in a temporary file. That's reasonably simple to do and robust.
在这个答案的底部你可以尝试一个肮脏的技巧。但是,我建议的解决方案是将该文档的内容保存在一个临时文件中。这做起来相当简单,而且很健壮。
Save the file in a temporary file, created by mktemp
. Setup a trap
to catch the interrupt and perhaps other signals to make sure the temporary file gets cleaned up. Run watch bash "$tmpfile"
. This is simple and will work.
将文件保存在一个由mktemp创建的临时文件中。设置一个陷阱来捕获中断和其他信号,以确保临时文件被清理干净。观察运行bash“临时美元”。这很简单,也会起作用。
Dirty "solution" with (severe) caveats, don't do this!
You could put a script in a variable and then run with watch
like this:
你可以在变量中放入一个脚本,然后像这样运行watch:
watch "bash -c '$var'"
Or like this:
或者像这样:
watch "bash -c \"$var\""
But the severe caveat is that the first version will break if var
contains '
, and the second version will break if var
contains "
. So these would work with only the most basic kind of scripts, and certainly not the one in your example.
但严重的警告是,如果var包含',第一个版本就会崩溃,如果var包含',第二个版本就会崩溃。这些只适用于最基本的脚本,当然也不适用于你的例子。
This is clearly not an option, I just added here for the sake of completeness.
这显然不是一个选项,我只是为了完整性而添加到这里。
#1
2
You can put the code in a function. With export -f func
you make the function definition available to subprocesses of the current script, so that you can then say
您可以将代码放入函数中。使用export -f func,可以将函数定义提供给当前脚本的子进程,这样就可以说
watch bash -c func
In OP's example:
在OP的例子:
# don't use bash-only (IMHO ugly) function syntax
get_user_info () {
local userToShow=$1
# 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 $2 instead of $0)?
parentPIDs=$(ps -ef |
awk -v user="$userToShow" 'NR>1 && ($0 ~ "su - " user) {
printf " %s \\|",parentpid}
{parentpid=$3}
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 ~$3 {print $1}' |
# 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 $3}'
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" '$0 !~ re { next }
root==1{print ""}
NR>1{print line}
{line=$0;root=($1=="root" || $3==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-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"
}
#2
1
watch
repeatedly runs a specified command with its arguments. The heredoc, and more generally the effect of redirect operators are not part of the command. So watch
cannot re-generate the heredoc. And once the heredoc is consumed by the first run of bash
, well, there will be nothing left for the second.
watch反复运行带有参数的指定命令。赫里多克,更一般地说,重定向操作符的影响不是命令的一部分。因此,watch不能重新生成“异端”。一旦赫里多克被第一次bash消耗掉,那么,第二次就什么都没有了。
There's a dirty hack you could try at the bottom of this answer. But my recommended solution is to save the content of the heredoc
in a temporary file. That's reasonably simple to do and robust.
在这个答案的底部你可以尝试一个肮脏的技巧。但是,我建议的解决方案是将该文档的内容保存在一个临时文件中。这做起来相当简单,而且很健壮。
Save the file in a temporary file, created by mktemp
. Setup a trap
to catch the interrupt and perhaps other signals to make sure the temporary file gets cleaned up. Run watch bash "$tmpfile"
. This is simple and will work.
将文件保存在一个由mktemp创建的临时文件中。设置一个陷阱来捕获中断和其他信号,以确保临时文件被清理干净。观察运行bash“临时美元”。这很简单,也会起作用。
Dirty "solution" with (severe) caveats, don't do this!
You could put a script in a variable and then run with watch
like this:
你可以在变量中放入一个脚本,然后像这样运行watch:
watch "bash -c '$var'"
Or like this:
或者像这样:
watch "bash -c \"$var\""
But the severe caveat is that the first version will break if var
contains '
, and the second version will break if var
contains "
. So these would work with only the most basic kind of scripts, and certainly not the one in your example.
但严重的警告是,如果var包含',第一个版本就会崩溃,如果var包含',第二个版本就会崩溃。这些只适用于最基本的脚本,当然也不适用于你的例子。
This is clearly not an option, I just added here for the sake of completeness.
这显然不是一个选项,我只是为了完整性而添加到这里。