【问题标题】:Job Control with Multiple Shells in bash?在 bash 中使用多个 Shell 进行作业控制?
【发布时间】:2011-11-04 12:53:13
【问题描述】:

首先,我对 bash 脚本非常陌生。

嗯,我有一个 bash 脚本来开始创建环回服务器。它执行一些 bash 命令,然后运行“expect”,然后启动一个名为 wadm 的程序(expect 处理 wadm 提示的密码)。

这里有一个快速概述:

  • 执行一些 bash cmds(提示输入用户名/密码)
  • 计算一些东西
  • 在 bash 中开始期待 shell
    • 期望使用特定用户名启动 wadm
      • wadm 提示输入密码
      • 期待输入密码
      • 期望运行特定于 wadm 的 cmds
      • 退出 wadm(期望向 wadm 发送“退出”)
    • 退出期望(期望在 bash 脚本中结束)
  • 编辑上述 wadm cmds 创建的一些文件
  • 在 bash 中开始期待 shell
    • 期望使用特定用户名启动 wadm
      • wadm 提示输入密码
      • 期待输入密码
      • 期望运行特定于 wadm 的 cmds(依赖于以前的 wadm cmds 的不同 cmds)
      • 退出 wadm(期望向 wadm 发送“退出”)
    • 退出期望(期望在 bash 脚本中结束)

我想要做的是让 expect 和 wadm 在后台运行(因为每次我需要在其中做某事时都不会启动/退出 wadm),而我在 bash 中做一些其他事情。

作为 bash 脚本的新手(另外,我在 linux/unix 方面也不是那么先进)我曾想过使用作业控制来解决这个问题,但根据这篇文章(http://stackoverflow.com/questions/690266/为什么我不能在 bash 脚本中使用作业控制) 工作控制可能不是要走的路。这种流程还有哪些其他选择?

【问题讨论】:

  • 抱歉格式错误。不知道会发生这种情况。

标签: linux bash


【解决方案1】:

创建两个命名管道:

mkfifo wadm_stdin
mkfifo wadm_stdout

在后台启动wadm:

wadm <wadm_stdin >wadm_stdout &
wadm_id=$!

使用expect 编写任意次数的脚本(不要忘记第一次登录并从最后删除退出)

expect ... <wadm_stdout >wadm_stdin

使用完 wadm 后,等待它退出:

cat wadm_stdout >/dev/null &  # Read any output to prevent blocking on a full pipe
echo quit >wadm_stdin
wait $wadm_id

【讨论】:

  • 我不太确定我是否遵循这一行:wadm wadm_stdout 和我运行 expect/wadm 的 sn-p 看起来像这样:expect -c " stty -echo set password $PASSW spawn $WORKDIR/bin/wadm --user=$yourUID expect \"Please enter admin-user-password&gt;\" send \"$password\r\" expect { -re \"Invalid user or password\" { exit 3 } \"wadm&gt;\" { send \"copy-config --config=$oldUID-$MAXPORT $1-$NP\r\" } } expect \"wadm&gt;\" send \"quit\r\" "
  • wadm &lt;wadm_stdin &gt;wadm_stdout &amp; 表示wadm_stdin 的所有输入都通过管道传输到wadm,wadm 的所有输出都通过管道传输到wadm_stdout
  • 是的,就是这个意思。
【解决方案2】:

通常您不会与脚本交互,然后将其发送到后台 - 您可以选择一个。但是当然在 linux 上一切皆有可能。

这里的主要问题是管道(stdout/stderr):你不能只是关闭,脚本很可能会在写入管道端退出本身时出错。因此,您需要重定向现有管道。我不知道任何可以做到这一点的标准工具,但可以使用 gdb 完成。

工作控制也是一个有争议的话题,恕我直言。如果您不是在编写商业脚本,那么在每台服务器上强制执行作业控制可能会更容易,然后进行一些变通方法。但是在这种情况下,您必须知道的唯一一件事是父shell将在退出时向所有子进程发送SIGHUP,因此在这种情况下最好将其关闭,或者使用陷阱忽略它(就像我一样)。

现在,我将描述一种方法来做你想做的事。

首先,确保 gdb 可以附加到从同一用户运行的正在运行的进程。默认情况下,它在许多系统上启用,但有些系统只允许 root 执行此操作。通常您可以通过将 /proc/sys/kernel/yama/ptrace_scope 设置为 0 来更改它(这样做可能不安全)。运行“gdb -p some_pid”检查它是否有效,“some_pid” - 用户运行的任何进程的 PID。

由于 gdb 似乎在从 stding 读取时出现问题,请在系统某处设置一个文件(在我的示例中为 /usr/share/gdb_null_descr),其内容如下:

p dup2(open("/dev/null",0),1)
p dup2(open("/dev/null",0),2)

这将告诉 gdb 在它所附加的进程中将 STDOUT 和 STDERR 重定向到 /dev/null(如果你想保存它,你可以将它更改为任何其他文件,但要小心权限)。

现在,一切都很简单。为了测试,在这个例子中创建一些简单的守护进程,比如 daemon.sh:

#!/bin/bash
success=0;
while [ "$success" -lt 1 ]
do
    echo "Give me username!";
    read username;
    echo "Give me password!";
    read password;
    if [[ "$username" = "root" && "$password" = "rootpass" ]]
    then
        success=1;
    else
        echo "Invalid username/password!";
    fi;
done;
echo "Logged in succesfully!";
echo -n > test_file;
loop_count=0;
while [ 1 ]
do
    echo "Still working";
    if [ "$loop_count" -eq 100 ]
    then
        loop_count=0;
        echo "Please, kill me, I am tired!";
    fi;
    let loop_count++;
    echo "$loop_count" >> test_file;
    sleep 1;
done;

现在,我们的期望脚本:

#!/usr/bin/expect
#Just never timeout
set timeout -1;
#Start bash process - we need it only to set trap
spawn bash;
#Set trap on SIGHUP to nothing - bash will just ignore this signal.
send "trap '' HUP;\n";
#Start our half-daemon by replacing this bash process (using exec).
send "exec ./daemon.sh;\n";
#ineract with half-daemon
expect *username!;
send "root\n";
expect *password!;
send "rootpass\n";
#Ok, interaction ends, redirect pipes
system gdb -p [exp_pid] --batch -x /usr/share/gdb_null_descr;

现在,运行 expect 脚本并查看 test_file - 如果每秒都有新条目出现,则妖魔化已成功完成!

【讨论】:

    猜你喜欢
    • 2012-09-27
    • 2012-08-03
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 1970-01-01
    • 2018-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多