· 2027 words · 11 min
脚本(script) 原指拍摄电影或者戏剧时,演员依据其对白演出的文件,也称之为剧本。 在编程的世界里,有时需要重复执行某些指令,这些指令或简或繁,然而反复做重复的事情是令人烦恼的。 有句话是这样说的,人类所有的发明推进都是因为人类的懒惰,因为人们懒得走路,我们发明了自行车,因为懒得骑自行车,所以我们发明了汽车, 因为汽车太慢,所以我们发明了高铁、飞机等,种种都是因为人们追求 higher、faster、stronger。 脚本亦是如此,因为程序员懒得自己敲某些重复的代码,因此我们把这些重复的常用到的指令放在一起,当我们需要的时候,直接运行这些指令即可。 参照 “脚本” 的原意,让计算机按照我们写好的指令去执行或者 “演出” 的文件,我们称之脚本文件,生成脚本文件的语言我们称之为脚本语言(Scripting language)。
一个脚本通常是解释运行而非编译,脚本语言通常都有简单、易学、易用的特性,目的就是希望能让程序员快速完成程序的编写工作。 而宏语言则可视为脚本语言的分支,两者也有实质上的相同之处。虽然许多脚本语言都超越了计算机简单任务自动化的领域,不仅是大量重复的简单指令, 有些时候我们可以使用脚本编写更为复杂精巧的程序,但仍然还是被称为脚本。几乎所有计算机系统的各个层次都有一种脚本语言。 包括操作系统层,如计算机游戏,网络应用程序,字处理文档,网络软件等。在许多方面,高级编程语言和脚本语言之间互相交叉,二者之间没有明确的界限。
常见的脚本语言有多种,比如 C Shell、Python、PHP、VBScript、Ruby 等,还有前端的核心之一 JavaScript。
找到一个空的目录,在该目录下打开终端键入 touch myshell.sh
创建我们的第一个脚本文件,以 .sh
结尾的作用是让计算机方便的认出该文件“可能”是一个脚本文件,
会以默认的执行程序去运行该文件而已,否则后缀没有任何意义,举个例子,你可以用记事本去打开后缀为.html
的文件进行编辑,可见后缀名没有对你的操作有丝毫影响。
创建成功后键入 vim myshell.sh
编辑新建的脚本文件。键入如下命令:
mkdir ./demo ./demo/css ./demo/js
touch ./demo/index.html ./demo/css/style.css ./demo/js/main.js
完成以上步骤之后退出编辑,在终端键入 ls -l
发现 demo.sh
文件前面的一串字符为 -rw-r--r--
,末尾的字符为 -
说明该文件不具备执行权限。
在终端键入 chmod +x demo.sh
为该文件添加执行权限,chmod
意思为 change mode,+x
意思为添加执行权限
(读取权限缩写为 r
,写入权限的缩写为 w
,执行权限的缩写为 x
)。执行完成再运行 ls -l
发现 demo.sh
前面的字符变为 -rwxr-xr-x
,
说明该文件已具有执行权限。
找到计算机上的 .bashrc
文件,如果不存在该文件新建即可。编辑 .bashrc
文件,在末尾换行键入 export PATH="<file_path>$PATH"
,
该指令将 demo.sh
文件的绝对路劲添加至环境变量 $PATH
列表之中,其中 <file_path>
为 demo.sh
文件的绝对路径
(添加至环境变量 $PATH
之后即可在终端中直接执行命令 demo.sh
,bash 会在 PATH
列表中查找该“指令(文件)”的地址,
如果存在则执行该文件,不存在则报错,可在终端键入 which demo.sh
查看该“指令(文件)”的位置。完成以上步骤之后,
执行 source ~/.bashrc
,重新载入 .bashrc
。至此,脚本文件的创建已初步完成,在终端键入 demo.sh
后会在当前目录下创建一个名为 demo
的目录,
该目录下有名为 css
与 js
的目录以及名为 index.html
的文件,子目录下分别有名为 style.css
与 main.js
的文件。
进一步优化,可以实现目录名称自定义,bash 脚本给我们提供了参数列表,其中 $0
表示命令行本身,$1
表示第一个参数,$2
表示第二个参数,以此类推。
接下来优化我们的脚本,修改 demo
文件内容如下:
mkdir ./$1 ./$1/css ./$1/js
touch ./$1/index.html ./$1/css/style.css ./$1/js/main.js
编辑完成保存并退出,在终端键入 demo test
即可在当前目录下创建名为 test
的目录,该目录下有名为 css
与 js
的目录以及名为 index.html
的文件,
子目录下分别有名为 style.css
与 main.js
的文件。运行 demo
命令时后面不带参数则会报错。
文件重名会怎样呢?会不会覆盖掉已经存在的目录呢?如果会覆盖的话,那么之前目录下的文件会不会丢失?我们需要在该脚本新建目录之前进行判断:
// pseudo code
if (is_dir_exist) {
// directory already exist
// return
} else {
// mkdir $1
// return
}
继续修改我们的脚本文件 vim demo
,因为我们的脚本是 shell 脚本,因此需要遵循 shell 语法:
if [ -d $1 ]; then
echo "$1 already existed!"
exit
else
mkdir ./$1 ./$1/css ./$1/js
touch ./$1/index.html ./$1/css/style.css ./$1/js/main.js
echo "$1 create successful!"
exit
fi
编辑完成保存并退出。至此,我们的 demo
脚本文件已经成型了,如果需要同时往文件中写入字符,那么在脚本文件中使用 echo
指令即可。
使用 Node.js
编写脚本必然需要遵循 Node.js
的语法规则,新建名为 script
的文件,写入以下内容:
var fs = require('fs');
var dirName = process.argv[2];
fs.exists(dirName, function(exists) {
if (exists) {
console.log(dirName + ' already existed!');
return;
} else {
console.log(dirName + ' is creating...');
fs.mkdirSync(dirName);
process.chdir('./' + dirName);
fs.mkdirSync('css');
fs.mkdirSync('js');
fs.writeFileSync('./index.html', "<h1>Hello, I'm Captain!</h1>");
fs.writeFileSync('./css/style.css', 'h1{ color: red; }');
fs.writeFileSync('./js/main.js', "alert('Surprise!');");
console.log(dirName + ' create successful!');
}
});
以上内容涉及 Node.js
知识,其中第一行指令调用 file system
接口,然后将第三个参数传递给dirName
变量,
之后调用 fs.exists()
方法,该方法可以判断一个文件是否存在,判断流程与之前思路一致。编辑完成后保存并退出,
在终端键入 node script node-script
即可在当前目录下创建名为 node-script
的目录,其中内容与运行脚本文件 demo
后所生成目录中的内容一致。
Shebang
的名字来自于 SHArp
和 bang
,或 Hash bang
的缩写,Shebang
是指代 #!
两个符号的典型 Unix 名称。
Unix 术语中,#
号通常称为 sharp
、hash
或 mesh
,而 !
号则常常称为 bang
。
也有看法认为,Shebang
名字中的 sh
来自于默认 shell Bourne shell
的名称 sh
。
#!
出现在文件的第一行的前两个字符,在文件中存在 Shebang
的情况下,类 Unix 操作系统的程序载入器会分析 Shebang
后的内容,
将这些内容作为解释器指令,并调用该指令,并将载有 Shebang
的文件路径作为该解释器的参数。例如在 script
脚本的前面添加下面的 shebang
:
#!/usr/bin/env node
那么解释器就会分析 #!
后面的路径并按照指定的解释程序 node
去执行文件。如果该文件没有执行权限的话,那么会报错 permission denied
,
如果指定的解释程序不存在,那么会报错 No such file or directory
。#!
之后的解释程序,需要写其绝对路径,
它是不会自动到 PATH
列表中寻找解释器(或者执行文件)的。
当然,如果你使用 sh script node-script
这样显式的指明解释器来执行脚本,那么 #!
这一行将会被忽略掉,解释器当然是用命令行中显式指定的 sh
去执行文件。
完。