shell
运行脚本
Linux 程序有三种方式运行:
- 使文件具有可执行权限,直接运行文件
- 直接调用命令解释器执行文件
- 使用
source
或.
执行文件
区别:
一: 第一种和第二种类似,区别只是 谁来寻找 命令解释器。第一种是通过文件顶部的 #!
来指定,第二种是直接指定。这两种方式,都会新fork
一个子进程来运行程序。第三种 则是在本进程中执行程序。
二: 因为第一种是通过顶部的 #!
内容来运行指定的命令解释器。因此程序顶部的语句必然会运行。而第二种和第三种因为只直接指定了运行该程序的命令解释器,因此不会运行程序顶部的语句。
#!/bin/rm
echo "hello world"
当使用第一种方式运行该程序时,会发现,该程序会被自己删除,且永远不会用打印语句出现。然而,第二种和第三种方式运行该程序时,该文件会一直存在,且成功打印语句。
变量
shell 变量有几个特点:
- 变量赋值时,
=
两边不能有空格,否则变量名会被认为为命令 - 双引号引起的变量是可以替换的。单引号引起的变量不能 替换。
- 局部变量必须用 关键字
local
声明,否则,即使该变量在函数内部被声明,依旧是全局变量。声明全局变量不需要加修饰符。默认就是全局变量。 $haha
是${haha}
的简写
5.如果变量值多余一个单词,那么需要用引号将其引起来。
#!/bin/bash
name="ann"
function test(){
fav="dota"
local hate="get up" # 定义局部变量
echo "in of function test ----> $name"
echo "in of function test ----> $fav"
echo "in of function test ----> $hate"
name="yang"
echo "change name in test $name"
}
test
echo "out of function test ----> $name"
echo "out of function test ----> $fav"
echo "out of function etst ----> $hate"
# ----------------------------------------------
in of function test ----> ann
in of function test ----> dota # 函数内部创建全局变量
in of function test ----> get up
change name in test yang # 函数内部修改 变量值
out of function test ----> yang # 波及到了函数外部的变量值
out of function test ----> dota # 函数外部该全局变量依旧存在
out of function etst ----> # 局部变量在函数外部不存在
echo
用于打印输出
export
用于配置环境变量
unset
用于删除变量
函数
shell 有两种方式来创建函数:
- function name(){}
- name(){}
shell 调用函数直接使用函数名即可。
nction first(){
echo "I'm first fun"
}
second(){
echo "I'm sechod fun"
}
function third(){
echo "x == $1"
echo "y == $2"
}
first
second
third 1 2
参数
通过传参,可以将外部的值,传递到 shell 脚本函数中。
shell 参数分 位置参数 全局参数 局部参数
位置参数
参数名称 | 参数意义 |
---|---|
0, 1,2,3,4 … | 位置参数,0表示脚本名(不一定是函数名);其余为位置参数。最大为9,如果超过9个了, 那么 $1 这类写法已经行了,得用 ${10} |
* | 一个字符串 显示多有向脚本传递的参数 |
@ | 从 参数 1 开始显示所有向脚本传递的参数。如果被双引号引起来了“$@” ,那么等同于逐次调用$1 $2 …. |
# | 参数数量,不含 参数0 (即脚本名) |
$ | 脚本运行的当前进程ID |
! | 后台运行的最后一个进程 ID |
? | 显示最后命令的 退出状态 0 表示正常 |
管道与重定向
标准输入
文件描述符 0
cat 0</tmp/b.txt
标准输出
文件描述符 1
cat /tmp/.b.txt > /dev/null 2>/dev/null
cat /tmp/b.txt 1>/dev/null 2>/dev/null
cat /tmp/b.txt &>/dev/null
&表示标准输出和标准错误
标准错误
文件描述符 2
使用样例
grep 'a' /tmp/zz.log 2>/dev/null
重定向
> (目标不存在就创建)
重定向追加
>> (目标不存在就创建)
管道
|
管道的作用是以 前一个命令的输出作为下一个命令的输入。
ls | grep *.log
该命令并不是说,将 ls
的结果, 放到 grep
的前面,即 grep
和 |
之间,而是说,将 ls
的结果 作为 grep
的 输入, grep
命令 这样的 grep 字符串 文件名
, 所以, 管道连接 ls
的结果之后,它的意思是,ls
的结果就是 文件名
。
文件描述符
内核(kernal)通过文件描述符(file descriptor)访问文件。 文件描述符是非负整数,打开已有文件,或创建新文件时,内核都会返回一个文件描述符。读写文件也需要使用文件描述符还指定特定的读写文件。
文件描述符表
位于用户空间,进程中的每个打开的文件,文件描述符表都有一个对应的条目(感觉类似于,一个文件,一个代号),每个进程都有自己的文件描述符表,自己对其维护。当进程调用文件描述符相关函数或命令时,会对其进行修改。
系统文件表
为系统中所有进程所共享。每个系统文件表的条目都包含文件的偏移量,访问模式(读、写、读写),以及指向它的文件描述符表的条目的数量。
每个进程的文件表在系统文件表中的区域都是不重合的。这种安排是为了使每个进程都有它自己对该文件的当前偏移量
内存索引节点表
对于系统中每个活动的文件(即被某个进程打开了),内存中都有索引节点表包含一个条目,几个系统文件表条目可能对应同一个内存索引节点表(即 不同进程打开同一个文件)
关系
每个进程维护自己的文件描述符表,文件描述符表中的每一项指向系统文件表,系统文件表指向内存索引节点表。
这样进程通过对文件描述符表的操作,,访问内存中索引节点表控制的文件。
文件描述符
文件描述符是由无符号整数表示的句柄,进程 用它来表示文件。文件描述符与包括相关信息(如文件打开模式,文件位置,文件初始类型)等文件对象关联,这些信息被称为上下文。
进程获取文件描述符的常用方法是本机子例程 open
和 create
文件。或通过 fork
从父进程中继承。
特殊文件
/dev/null
可以把 /dev/null
想想成一个黑洞,所有写入它里面的内容都会丢失。
/dev/zero
类似于 /dev/null
,其作用主要用来创建一个指定长度,并且初始化为空的文件,这种文件一般作为临时交换文件。
/dev/tty
表示当前终端,既可以从该文件获取内容(在终端写入),也可以将内容写入到该文件,从而在终端显示。