如何改变后台函数的变量?

如何改变后台函数的变量?

我一直在为我父亲编写一个脚本,该脚本通过一个包含许多链接在一起的功能的脚本来管理多项任务(组织目的)。该脚本完全依赖于zenity所有用户交互(我父亲真的不懂技术{60 岁以上})。

我遇到的问题是,一旦我调用该函数,即使循环所依赖的变量在函数外部发生变化,transprog它也不会退出循环。while

循环在这里调用:

copystat="1"
transprog & cp -rf "$netlocdirFIN/" "$transdir2/" # "transprog" is called here
copystat="0"
sleep 1
ret="$?"
echo "$ret"
transnew #calls next step/function

这是transprog函数:

transprog ()
{
i2="Im Working On It!"

(
while [ $copystat = 1 ]; do
# =================================================================
echo "25"
echo "# ^_^ $i2" ; sleep 1

# =================================================================
echo "50"
echo "# ^-^ $i2" ; sleep 1

# =================================================================
echo "75"
echo "# ^_^ $i2" ; sleep 1


# =================================================================
echo "99"
echo "# ^-^ $i2" ; sleep 1

done
echo "# Your files are DONE TRANSFERING!" ; sleep 1
echo "100"
) |
zenity --progress \
  --title="File Transfer In Progress" \
  --text="# Im Getting Warmed Up" \
  --percentage=0 \
  --auto-close \
  --auto-kill
}

复制完成后,脚本继续运行:zenity进度条在后台滴答作响,这确实让他感到困惑。

我如何告诉后台函数退出?

(zenity 使用echo25、50、75 和 99 的值来改变进度条的“完整性”。并echo "# (whatever)"改变文本。)

你只需要告诉他“嘿,达弗斯,我正在复印东西,别打扰我”

我已经尝试过 Guss 的解决方案:(为了测试目的而分离的脚本)

#!/bin/bash

transtest ()
{

i2="Im Working On It!"

var=0
trap 'var=1' 2

(
while [ "$var" = 0 ]; do
# =================================================================
echo "25"
echo "# ^_^ $i2" ; sleep 1

# =================================================================
echo "50"
echo "# ^-^ $i2" ; sleep 1

# =================================================================
echo "75"
echo "# ^_^ $i2" ; sleep 1

# =================================================================
echo "99"
echo "# ^-^ $i2" ; sleep 1


done
echo "# Your files are DONE TRANSFERING!" ; sleep 1
echo "100"
) |
zenity --progress \
  --title="File Transfer In Progress" \
  --text="# Im Getting Warmed Up" \
  --percentage=0 \
  --auto-close \
  --auto-kill
}

transtest &
pid=$!
echo "$pid"
sleep 20 #code to copy? or just for this test?
echo "Killing Child: $pid"
kill -2 "$pid"
echo "wait"
wait
exit

我看到了结果echo "wait"并且它仍然在循环。

答案1

您遇到的问题是由调用transprog在后台运行时生成的子 shell 引起的(&符号的作用)。您必须了解您的 bash 脚本不是程序,而是按照 shell 规则运行的 shell 脚本 - shell 一次只能运行一个进程并等待它终止。

这 ”工作系统“允许您运行另一个进程而不等待它,但是当该进程是 bash 中定义的函数时,bash 将生成一个“子 shell”,该“副本“原始 shell 的所有变量和函数,并将其作为作业进行跟踪。复制的 shell 不知道其父 shell 所做的更改 - 因为它没有直接引用父 shell 变量 - 只是它们的副本。

您需要为后台循环执行的操作是使用进程间通信机制(工业控制计算机) 让父 shell 通知子 shell 中的循环中止。对于这种简单的信号,通常使用“POSIX 信号”是合适的。

最合适的信号是“中断信号”SIGINT(信号编号 2),这是按下 CTRL+C 时产生的信号,通常会停止脚本。但我们可以使用trap内置命令捕获此信号并更新本地变体。

这是一个简单的例子:

#!/bin/bash

function test() {
  var=0
  trap 'var=1' 2
  while [ "$var" = 0 ]; do
    sleep 3
    echo 'still sleeping'
  done
  echo "done sleeping"
}

test &
pid=$!
echo $pid;
sleep 5
echo "Killing $pid"
kill -2 "$pid"
wait

这将输出:

$ ./test.sh 
8359
still sleeping
Killing 8359
still sleeping
done sleeping

这里发生的事情是,5 秒后,循环完成一轮,现在在第二轮中休眠,此时父级使用kill内置函数向子 shell 发出信号。子 shell 的休眠被中断,它接收信号并更新变量,然后恢复休眠。当循环被唤醒时,它会输出文本,然后返回并看到变量已更改并离开循环。

我使用wait父进程中的内置函数只是为了确保父进程不会在子 shell 之前退出。它本质上是“等待”子 shell 结束。

[更新] 在进一步查看了 OP 之后,我发现还有几个子 shell 正在运行,我不想反对这一点 - 子 shell(除了函数)是管理脚本复杂性的好方法 - 但这确实使得找到正确的 PID 来将信号发送到少量难的。

相反,我们可以使用另一种称为“共享内存段”的 IPC 机制。这允许一个进程分配一批内存,为其分配一个名称,并让多个进程访问同一内存(上面链接的 IPC 文档对此进行了更详细的介绍)。虽然在 Bash 中我们无法直接实现该功能(例如,与 C 程序可以做到的不同),但我们可以使用 Linux 机制,通过设备/dev/shm类别将共享内存 IPC 公开给任何程序。因此,OP 脚本可能看起来像这样:

#!/bin/bash

transtest () {
  local sync="$1"
  i2="Im Working On It!"
  var=0
  (
    while [ -z "$(cat $sync)" ]; do sleep 1; done
    echo "# Your files are DONE TRANSFERING!"
    sleep 1
    echo "100"
  ) | zenity --progress --title="File Transfer In Progress" --text="# Im Getting Warmed Up" \
    --percentage=0 --pulsate --auto-close --auto-kill
}

sync=/dev/shm/syntest-$$ # allocate a shared memory segment name for this process
echo '' > $sync # init the shm
transtest $sync &
sleep 20
echo "Signaling done"
echo "done" > $sync
echo "waiting"
wait
# remove the shm segment. While not actually necessary (it will clear
# when you reboot), I like to keep a clean system
rm $sync 
exit

基本上,我们将 SHM 段视为一个文件,并在其中存储一些数据。循环不断读取“文件”,当其中有内容时,就会中断。

还有一个问题,为什么不直接分配一个临时文件(使用mktemp)并用它来同步呢?好吧 - 效果几乎一样!主要区别在于,这也是我更喜欢使用 SHM 的原因,因为它实际上不是一个文件,不受文件系统 IO 操作、磁盘错误等的影响,如果我无法清理它,它将在系统下次启动时消失。

答案2

copystat您在循环外部(在子 shell 中运行)更改了条件变量 ( )的值while,这超出了循环的范围while。除非您在循环内部设置了一些条件来触发循环退出条件,或者使用工业控制计算机,循环将永远持续下去。

因此,解决方案是在循环本身内加入复制或任何其他条件逻辑来while触发退出,或者您可以考虑完全改变设计,使其在这种情况下更加健壮,或者使用 IPC这里提到

相关内容