【问题标题】:Watching the output of a bash script观察 bash 脚本的输出
【发布时间】:2018-05-18 05:53:28
【问题描述】:

我正在尝试使用 watch 查看 shell 脚本的输出,执行 /bin/bash 并将脚本本身保存在 heredoc 中。

鼻屎只执行一次。它给出了正确的输出,然后观察刷新并且屏幕变为空白。退出 watch 后没有列出任何错误。

我不知道问题出在哪里,因为使用 watch 调试变得越来越困难 > bash > heredoc > ugly code

好消息是heredoc 中的ugly code 工作正常。

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
}

像这样称呼:

show_users 2 "username"

【问题讨论】:

  • 基本问题是heredoc附加到只执行一次的watch的stdin。第一个bash 继承stdin 并消耗所有here doc,当调用下一个bash 时,stdin 中没有任何内容。 (只是评论,因为我没有好的解决方案。)
  • @CharlesBailey 我在想这样的事情就是这样。也许如果我将catheredoc 变成一个变量,然后将bash -c 变量放在watch... 我会继续修修补补。
  • MCVE 中的 M 是“最小”。你可以用watch /bin/bash &lt;&lt; 'EOF'dateEOF替换整篇文章
  • 定义一个函数并export -f它。然后你可以简单地watch bash -c yourfunc
  • ps 解析是一种常见且广泛使用的反模式。如果您仍然在运行 Awk,您也可以轻松地将 grep 分解到 Awk 脚本中。见useless use of grep.

标签: linux bash watch heredoc


【解决方案1】:

您可以将代码放入函数中。使用export -f func,您可以将函数定义提供给当前脚本的子进程,这样您就可以说

watch bash -c func

在 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】:

watch 重复运行带有参数的指定命令。 heredoc,更一般地说,重定向运算符的效果是命令的不是的一部分。 所以watch 不能重新生成heredoc。 并且一旦第一次运行bash 消耗了heredoc, 好吧,第二个就什么都没有了。

您可以在此答案的底部尝试一个肮脏的技巧。 但我推荐的解决方案是将heredoc 的内容保存在一个临时文件中。 这样做相当简单且功能强大。

将文件保存在由mktemp 创建的临时文件中。 设置一个trap 来捕捉中断和其他信号,以确保临时文件得到清理。 运行watch bash "$tmpfile"。 这很简单,而且会奏效。

带有(严重)警告的肮脏“解决方案”,不要这样做!

您可以将脚本放入变量中,然后使用watch 运行,如下所示:

watch "bash -c '$var'"

或者像这样:

watch "bash -c \"$var\""

但需要注意的是,如果var 包含',第一个版本将中断,如果var 包含",第二个版本将中断。 所以这些只适用于最基本的脚本, 当然不是你的例子。

这显然不是一个选项,我只是为了完整起见在这里添加。

【讨论】:

  • 您的脏变量解决方法很好,但显然问题是我在脚本中都有两个引号,甚至是嵌套的。同样在这一点上,我还不如直接将整个脚本推入观看并处理相同的引号转义噩梦。
  • @JNevill 为什么不将脚本保存到文件中?
  • 我可以看到将完整的解决方案拆分为两个脚本会很麻烦。将所有内容保存在一个文件中消除了分发物流的许多问题(如果脚本使用noexec 挂载,则无法将脚本保存在/tmp 中,避免I/O 开销和处理临时文件的陷阱等)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-15
  • 2017-11-21
  • 1970-01-01
  • 2010-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多