如果编辑正在运行的bash脚本,请小心

2020-05-06 12:55:23

假设我编写了以下bash脚本,并将其命名为delay.sh。您认为当我运行./delay.sh时会发生什么?

看起来它将等待30秒,然后在屏幕上打印一条消息。这里没有什么花招--这正是它要做的。中间有一个看起来很危险的命令,但它不会有任何效果,因为它已被注释掉。

想象一下,我又运行了一次,我已经厌倦了等待睡眠。三十秒太长了。我打开第二个终端,将休眠30更改为休眠3,然后保存文件。你认为现在会发生什么?

嗯,在30秒之后,运行的脚本将删除我的所有文件。之所以会发生这种情况,是因为bash在执行时以块的形式读取脚本内容,并使用字节偏移量跟踪脚本的内容。当我从休眠行删除一个字符时,“NEXT COMMAND”偏移量指向#rm中的r,而不是#。从解释器的角度来看,#向后移到前一行,因此它运行不幸的命令。

可以通过观察bash在Linux上进行的系统调用来确认这一点。下面是经过注释和修剪的strace bash delay.sh的输出。

#打开scriptopenat(AT_FDCWD,";delay.sh";,O_RDONLY)=3#解析第一行(最多80个字符)读取(3,";#!/bin/bash\n睡眠30\n#ECHO\";Don';";.,80)=64#返回起始处(3,0,SEEK_SET)=0#将其转移到文件描述符255dup2(3,255)=255#读取64字节的块以获取命令(255,";#!/bin/bash\n睡眠30\n#ECHO\";Don';";.,64)=64#将光标放回命令WE'的末尾。Re即将运行#Offset 21 is the`#`lSeek(255,-43,Seek_Cur)=21#do the sleepwait4(-1,[{WIFEXITED(S)&;&;WEXITSTATUS(S)==0}],0,null)=2072#在wait4返回之前,文件从`30`编辑为`3`#读取64字节的块以获取下一个命令#对于此演示,我替换了危险命令。\n ECHO\n#34;Ti";.,64)=42#Bash决定在不重新读取的情况下同时执行这两个回显#很明显,此处有一些细微差别(1,#34;不要执行我\n#34;,17)=17write(1,#34;时间!\n#34;,11)=11#再读一次块,就会发现我们在EOFread(255I)=17write(1,#34;Time";s up!\n";,11)=11#再读一次块,会发现我们在EOFread(255。

因此要小心运行当前可能正在执行的bash脚本。它可能会执行无效的命令,或者做一些非常令人惊讶的事情。