一个 shell 脚本是一系列命令。shell 会读取脚本文件,并依次执行这些命令。
通常情况下,这里没有什么意外;但初学者常见的错误是认为有些命令会接管 shell,并开始执行脚本文件中的后续命令,而不是当前正在运行此脚本的 shell。但实际上并不是这样的。
基本上,脚本的工作方式与交互式命令完全相同,但需要正确理解它们的工作方式。在交互模式下,shell 会读取命令(从标准输入),运行该命令(使用标准输入的输入),当完成时,它会再次读取另一个命令(从标准输入)。
现在,在执行脚本时,标准输入仍然是终端(除非您使用重定向),但是命令是从脚本文件中读取的,而不是从标准输入中读取的。(反过来将非常麻烦——任何read
都会消耗脚本的下一行,cat
会读取剩余的整个脚本,而且无法与其交互!)脚本文件仅包含用于执行它的 shell 实例的命令(尽管您当然仍然可以使用 here document 等将输入嵌入为命令参数)。
换句话说,这些“误解”的命令(su
、ssh
、sh
、sudo
、bash
等),在单独运行(没有参数)时,会启动一个交互式 shell,在交互会话中,这显然是可以的;但在从脚本中运行时,往往不是想要的结果。
所有这些命令都有接受命令的其他方式,而不是在交互式终端会话中。通常,每个命令支持一种将命令作为选项或参数传递的方式:
su root -c 'who am i'
ssh user@remote uname -a
sh -c 'who am i; echo success'
许多这些命令也可以接受标准输入上的命令:
许多这些命令也可以从标准输入接收命令:
printf 'uname -a; who am i; uptime' | su
printf 'uname -a; who am i; uptime' | ssh user@remote
printf 'uname -a; who am i; uptime' | sh
同时方便地允许您使用此类文件:
ssh user@remote <<'____HERE'
uname -a
who am i
uptime
____HERE
sh <<'____HERE'
uname -a
who am i
uptime
____HERE
对于只接受单个命令参数的命令,该命令可以是具有多个命令的sh
或bash
命令:
sudo sh -c 'uname -a; who am i; uptime'
作为旁注,通常不需要显式使用
exit
命令,因为当执行你传递给它的脚本(命令序列)后,命令会自动终止。
shell脚本
呢?这样,你只需要运行sudo sh yourshellscript.sh
即可。 - Jose Serodiossh
场景下是行不通的,例如(或者你必须先将脚本scp
到远程主机),并且会使得一些简单的情况变得非常复杂(然后你必须管理两个脚本文件,并确保调用脚本知道另一个脚本文件的路径)。当然,它也不能轻松地扩展到某些命令是动态的情况下(例如,取决于调用脚本中执行的计算)。 - tripleee