微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Bash shell脚本在通过stdin传递给LXC容器时不按顺序执行

我将以下简单的 shell脚本传递给LXC容器上的bash:
apt-get update
apt-get install postgresql -y

sudo -u postgres psql -c 'create database dvdrental;'

我用来运行它的实际命令是:

cat sample.sh | lxc-attach -n test-container -- /bin/bash

我之所以这样做,而不是将脚本上传到容器中并执行这种方式,这只是我们正在构建的一个更复杂的应用程序的概念证明,它必须通过stdin接受命令并运行它们在容器中.

它似乎工作得很好,除了一件事.当postgresql仍在安装时,它移动到psql命令,即

[...]
Get:21 http://archive.ubuntu.com/ubuntu/ trusty/main ssl-cert all 1.0.33 [16.6 kB]
Get:22 http://archive.ubuntu.com/ubuntu/ trusty-updates/main postgresql-common all 154ubuntu1 [103 kB]
Get:23 http://archive.ubuntu.com/ubuntu/ trusty-updates/main postgresql-9.3 amd64 9.3.10-0ubuntu0.14.04 [2,669 kB]
Get:24 http://archive.ubuntu.com/ubuntu/ trusty-updates/main postgresql all 9.3+154ubuntu1 [5,038 B]
Fetched 5,834 kB in 28s (207 kB/s)                                             
Preconfiguring packages ...

    sudo -u postgres psql -c 'create database dvdrental;'
Selecting prevIoUsly unselected package libroken18-heimdal:amd64.
(Reading database ... 14599 files and directories currently installed.)
Preparing to unpack .../libroken18-heimdal_1.6~git20131207+dfsg-1ubuntu1.1_amd64.deb ...
Unpacking libroken18-heimdal:amd64 (1.6~git20131207+dfsg-1ubuntu1.1) ...
Selecting prevIoUsly unselected package libasn1-8-heimdal:amd64.
[...]

注意存在sudo -u postgres psql -c’create database dvdrental;’在输出间的行.有趣的是,它总是在apt-get命令的下载部分完成后立即出现……

有人知道是什么原因引起的吗?

哦,这是一个有趣的.

简短的回答:它发生在那里因为apt(或它的东西)在执行中的那一点读取stdin,并且它读取脚本的剩余行,因为那是在那时仍然在stdin中.简短的修复:在apt-get安装行的末尾放置< / dev / null,然后继续你的一天. 答案很长(严肃地说,这是一个大枪):从运行过程的角度来看,stdin / stdout / stderr并没有什么特别之处.它们只是文件描述符,文件描述符在进程分叉时在进程之间共享.那么,会发生什么(或多或少):
>在终端中以交互方式运行的bash副本打开一个新管道(2),然后分叉一个新进程,它关闭现有的stdout,然后使stdout文件描述符(1)成为管道的写入端(参见dup2( 2)).那个子进程然后执行cat sample.sh,它读取文件并将其写入它认为是stdout的内容(但实际上是管道的写入端).
>在终端中以交互方式运行的bash副本分叉另一个新进程,这次关闭现有的stdin,然后使stdin文件描述符(0)成为前面讨论的同一管道的读取器端(再次调用dup2) .然后,此过程将执行您的lxc-attach过程.

如果在整个过程中没有任何东西干扰stdin(在这种情况下它没有),那么从将管道的读取器端作为stdin的那个分叉的每个进程也将具有完全相同的文件描述符,附加到相同的管道,其中包含sample.sh的内容,作为其标准输入.从该文件描述符读取的任何进程现在都将消耗读取的字节,并且从该文件描述符读取的其他进程将不会获得这些特定字节.仔细注意这一点;你会再次看到这个材料.
>当意大利水厕式管道盛会的远端的bash终于开始时,它将从管道中读取“一些”数据,这是stdin(因为这就是bash的作用,当没有参数调用时没有作为stdin的tty).通过strace的魔力,我刚刚确认bash确实一次读取一个字符的输入(而不是读取它,比如4k块),所以每个单独的字符都不是bash命令的一部分已经或现在正在执行,仍将坐在管道中 – 哪个bash-has-as-stdin.
>当bash在你的脚本执行第二个命令时,apt-get install tr​​a la la,它会生成一个新进程.其中继承了bash的所有文件描述符,包括(最重要的)我们的好朋友pipe-which-is-stdin.这也适用于任何apt-get forks的过程(这是让我向你保证的很多).其中一个,或apt-get本身,决定读取stdin并将其读取的内容写入stdout(或可能是stderr).
>当apt-get安装完成后,bash通过再次读取stdin来找出下一步要执行的操作.因为其他东西已经从管道中读出了所有内容,但是没有什么可以留下的,而bash的数字是“哦,好吧,我想我已经完成了”然后退出.同样,管道是空的,因为其他东西已经读取它干,并且共享单个文件描述符的所有内容共享它的赏金.

毫无疑问,“共享标准”问题的解决方案是停止传递stdin,就像在兄弟会派对上一样.既然你无法阻止fork(2)自动给每个人提供相同的文件描述符,那么你需要告诉bash,让apt-get(以及其他任何从那个甜蜜的管道中非法啜饮的东西)给别的东西相反.最简单的事情是/ dev / null – 永远忠实,永不满足的所有“戴夫不在这里,男人”的喜悦.那是“输入重定向”的域,这是< / dev / null的作用 - 它说,“嘿bash,在你执行apt-get之前,用文件描述符换出stdin(文件描述符0)你从打开/ dev / null获得“. 读者练习,完成:尝试在apt-get install命令之后输入< / dev / zero,并解释为什么会发生什么.

原文地址:https://www.jb51.cc/bash/385341.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐