在运行时编辑shell脚本


91

您可以在运行过程中编辑Shell脚本,并使更改影响正在运行的脚本吗?

我对csh脚本的具体情况感到好奇,因为我有该批处理运行一系列不同的构建风格并整夜运行。如果在操作过程中发生什么事情,我想加入并添加其他命令,或注释掉未执行的命令。

如果不可能,是否有任何外壳或批处理机制可以使我做到这一点?

当然,我已经尝试过了,但是要知道它是否有效还需要几个小时,而且我对幕后正在发生或未发生的事情感到好奇。


1
我已经看到了为运行中的脚本编辑脚本文件得到的两个结果:1)更改被忽略,就好像它已将整个内容读入内存一样; 2)脚本崩溃并出现了错误,就像它已读取命令的一部分一样。我不知道这是否取决于脚本的大小。无论哪种方式,我都不会尝试。
Paul Tomblin

简而言之:否,除非它是自引用/调用的,否则在这种情况下,主脚本仍将是旧脚本。
2010年

这里有两个重要的问题。1)如何正确安全地将命令添加到正在运行的脚本中?2)修改正在运行的脚本时,会发生什么?
克里斯·奎纳

3
问题是外壳程序是通过读取整个脚本文件然后执行它来执行脚本,还是在执行时部分读取它来执行脚本。我不知道那是什么 甚至可能没有指定。您应避免依赖于任何一种行为。
基思·汤普森

Answers:


-67

脚本不能那样工作;执行副本独立于您正在编辑的源文件。下次运行脚本时,它将基于源文件的最新保存版本。

将这个脚本分成多个文件,然后分别运行它们可能是明智的。这样可以减少执行失败的时间。(即,将批处理分成一个构建风味脚本,分别运行每个脚本以查看引起问题的脚本)。


66
我观察到相反的情况。被编辑的正在运行的bash脚本可能导致正在运行的脚本崩溃,因为该文件似乎在bash的脚本读取文件位置下移动。
Tilman Vogel,2012年

10
根据我在多个系统上的经验,正在执行的副本并非独立于磁盘文件,这就是为什么此问题如此令人惊讶并且在Shell脚本编程中很重要的原因。
克里斯·奎纳

6
它绝对不是独立于光盘文件的。Shell通常仅以例如128字节或4096字节或16384字节的块读取脚本,并且仅在需要新输入时才读取下一个块。(您可以在运行脚本的shell上执行lsof之类的操作,然后查看它仍然打开了文件。)
mirabilos 2013年

5
否。实际上,如果您编辑脚本,则会导致该过程失败。
Erik Aronesty

8
你说的不对。它会根据实现和脚本中调用的实际命令进行缓冲,是否将stdout重定向到文件,有很多因素,您的答案并不完全正确。
GL2014年

50

确实影响了至少我环境中的重击,但是以非常不愉快的方式。请参阅这些代码。首先a.sh

#!/bin/sh

echo "First echo"
read y

echo "$y"

echo "That's all."

b.sh

#!/bin/sh

echo "First echo"
read y

echo "Inserted"

echo "$y"

# echo "That's all."

$ cp a.sh run.sh
$ ./run.sh
$ # open another terminal
$ cp b.sh run.sh  # while 'read' is in effect
$ # Then type "hello."

就我而言,输出始终为:

你好
你好
就这样。
就这样。

(当然,自动化它要好得多,但是上面的示例是可读的。)

[编辑]这是不可预测的,因此很危险。在最好的解决方法是这里所描述 都放在一撑,而右括号之前,把“退出”仔细阅读链接的答案,避免陷入陷阱。

[添加]确切的行为取决于一个额外的换行符,并且可能还取决于您的Unix风格,文件系统等。如果您仅想查看一些影响,只需在b.sh之前和/或之后添加“ echo foo / bar” “读取”行。


3
嗯,我看不到这种感情。我想念什么吗?
用户未知

确切的行为取决于一个额外的换行符,也许还取决于Unix风格,文件系统等,但我还不确定。如果您只想查看任何影响,只需b.sh添加10行echo foo / bar / baz 即可扩大。dave4220和我的回答的要点是效果不容易预测。(顺便说一句,名词“喜爱”是指“爱” =)
teika kazura 2012年

是的,它很坏。我有一个解决方案(如下)。更危险的是svn / rsync / git更新
Erik Aronesty

39

试试这个...创建一个名为的文件bash-is-odd.sh

#!/bin/bash
echo "echo yes i do odd things" >> bash-is-odd.sh

这表明bash确实是在“随行”解释脚本。确实,编辑长时间运行的脚本会产生不可预测的结果,插入随机字符等。为什么?因为bash从最后一个字节位置读取,所以编辑会移动当前正在读取字符的位置。

总之,由于这种“功能”,Bash非常非常不安全。svn以及rsync与bash脚本一起使用时特别麻烦,因为默认情况下,它们会“合并”结果...就地编辑。rsync具有解决此问题的模式。svn和git没有。

我提出一个解决方案。创建一个名为/bin/bashx

#!/bin/bash
source "$1"

现在使用#!/bin/bashx您的脚本,并始终使用bashx而不是来运行它们bash。这解决了该问题-您可以安全地rsync编写脚本。

@ AF7提出/测试的替代(在线)解决方案:

{
   # your script
} 
exit $?

大括号可防止编辑,退出可防止附加。当然,如果bash带有-w(整个文件)之类的选项,或者这样做的话,我们所有人都会好很多。


1
顺便说一句; 这是抵消负号的加号,因为我喜欢您编辑后的答案。
安德鲁·巴伯

1
我不推荐这个。在这种解决方法中,位置参数会移位一个。还要记住,您不能为$ 0赋值。这意味着如果仅将“ / bin / bash”更改为“ / bin / bashx”,则许多脚本都会失败。
teika kazura 2013年

1
请告诉我,这样的选项已经实施!
AF7

10
我的朋友Giulio(应交欠额)建议我一个简单的解决方案是在脚本的开头插入{,在末尾插入}。Bash被迫读取内存中的所有内容。
AF7 2015年

1
@ AF7改善了您朋友的解决方案:{your_code; } && 出口; 也将阻止附加到末尾的行被执行。
korkman's

17

将您的脚本分解为函数,每次调用函数时,都会source从单独的文件中对其进行调用。然后,您可以随时编辑文件,正在运行的脚本将在下次获取源代码时进行更改。

foo() {
  source foo.sh
}
foo

我已经有一段时间有效地使用了这项技术,以在我长期运行的构建脚本运行时对其进行更新。我很想学习一种使当前文件读取到文件末尾的技术,这样我就不必为每个Shell脚本都拥有两个文件。
克里斯·奎纳

3

好问题!希望这个简单的脚本有帮助

#!/bin/sh
echo "Waiting..."
echo "echo \"Success! Edits to a .sh while it executes do affect the executing script! I added this line to myself during execution\"  " >> ${0}
sleep 5
echo "When I was run, this was the last line"

在Linux下,如果您键入得足够快,似乎对正在执行的.sh所做的更改是由正在执行的脚本制定的!


2

一个有趣的旁注-如果您正在运行Python脚本,则它不会更改。(这对于任何了解shell如何运行Python脚本的人来说都是显而易见的,但是认为这对于寻找该功能的人可能是一个有用的提醒。)

我创建:

#!/usr/bin/env python3
import time
print('Starts')
time.sleep(10)
print('Finishes unchanged')

然后,在另一个外壳中(此时正在休眠),编辑最后一行。完成此操作后,它会显示未更改的行,可能是因为它正在运行.pyc?。在Ubuntu和macOS上也是如此。


1

我没有安装csh,但是

#!/bin/sh
echo Waiting...
sleep 60
echo Change didn't happen

运行该命令,快速编辑最后一行以阅读

echo Change happened

输出是

Waiting...
/home/dave/tmp/change.sh: 4: Syntax error: Unterminated quoted string

r

我想对Shell脚本的编辑要等到重新运行后才能生效。


2
您应该将要显示的字符串放在引号中。
user1463308

2
实际上,这证明您的编辑器无法按照您的想法工作。许多许多编辑器(包括vim,emacs)都对“ tmp”文件而不是实时文件进行操作。尝试使用“ echo'echo uh oh'>> myshell.sh”而不是vi / emacs ...,并观察其输出新内容。更糟糕的是... svn和rsync也用这种方式编辑!
Erik Aronesty

3
-1。该错误与正在编辑的文件无关:这是因为您使用的是单引号!那充当单引号,导致错误。将整个字符串放在双引号中,然后重试。
匿名企鹅

5
发生错误的事实表明编辑没有达到预期的效果。
danmcardle

@danmcardle谁知道?也许bash看到了Change didn'ned
Kirill Bulygin

1

如果全部都在一个脚本中,那么否将不起作用。但是,如果将其设置为调用子脚本的驱动程序脚本,则可以在调用子脚本之前或在循环播放之前更改子脚本,在这种情况下,我相信这些更改将反映在执行中。


0

我没有听到...但是关于间接的情况:

BatchRunner.sh

Command1.sh
Command2.sh

Command1.sh

runSomething

Command2.sh

runSomethingElse

然后,您应该能够在BatchRunner处理正确之前编辑每个命令文件的内容?

要么

较干净的版本将使BatchRunner查找单个文件,该文件将一次连续运行一行。然后,当第一个文件运行正确时,您应该能够编辑第二个文件?


我想知道是否将它们加载到内存中以运行它们,并且在启动主流程后进行更改就无关紧要了……
Eric Hodonsky

0

使用Zsh代替脚本。

在AFAICT中,Zsh不会表现出这种令人沮丧的行为。


这就是为什么#473更喜欢Zsh而不是bash的原因。我最近一直在研究运行10m的旧bash脚本,在等待它完成时无法编辑它!
Micah Elliott

-5

通常,在脚本运行时很少会对其进行编辑。您要做的就是对您的操作进行控制检查。使用if / else语句检查条件。如果某件事失败,则执行此操作,否则执行该操作。那是要走的路。


实际上,脚本失败的原因要多于决定修改批处理作业的中期操作。IE意识到我还有更多要编译的内容,或者我不需要排队的某些作业。
确认

1
如果您严格追加脚本,那么bash会完成您所期望的!
Erik Aronesty
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.