声明: 本篇博客的学习途径主要为以下网站和课堂讲解,发博客目的仅为学习使用,在该博客的基础上做了一定程序的简略和修改。
参考博客 :
原文链接:http://c.biancheng.net/shell/
Shell基础
Shell基础
什么是Shell?
【总结】
Shell 既是一种脚本编程语言,也是一个连接内核和用户的软件。
Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。
- Shell 是如何连接用户和内核的?
我们运行一个命令,大部分情况下 Shell 都会去调用内核暴露出来的接口,这就是在使用内核,只是这个过程被 Shell 隐藏了起来,它自己在背后默默进行,我们看不到而已。
- Shell 还能连接其它程序?
在 Shell 中输入的命令,- 有一部分是 Shell 本身自带的,这叫做内置命令;
- 有一部分是其它的应用程序(一个程序就是一个命令),这叫做外部命令。
Shell 可以调用其他的程序,每个程序就是一个命令,这使得 Shell 命令的数量可以无限扩展,如文本或字符串检索、文件的查找或创建、大规模软件的自动部署、更改系统设置、监控服务器性能、发送报警邮件、抓取网页内容、压缩文件等。
Shell 还可以让多个外部程序发生连接,在它们之间很方便地传递数据,也就是把一个程序的输出结果传递给另一个程序作为输入
- Shell 也支持编程
Shell 主要用来开发一些实用的、自动化的小工具,而不是用来开发具有复杂业务逻辑的中大型软件,例如检测计算机的硬件参数、搭建 Web 运行环境、日志分析等,Shell 都非常合适。
- Shell 是一种脚本语言
Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。
bash Shell
bash是最常用的Shell
bash shell 是 Linux 的默认 shell
Shell 是一个程序,一般都是放在/bin或者/usr/bin目录下,
当前 Linux 系统可用的 Shell 都记录在/etc/shells文件中。
/etc/shells是一个纯文本文件,你可以在图形界面下打开它,也可以使用 cat 命令查看它。
$ cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
内置命令和外部命令
内置命令
最常用的命令才有理由成为内置命令,比如 cd、kill、echo 等;
[root@localhost ~]# type cd
cd is a Shell builtin
[root@localhost ~]# type ifconfig
ifconfig is /sbin/ifconfig
由此可见,cd 是一个 Shell 内建命令,而 ifconfig 是一个外部文件,它的位置是/sbin/ifconfig。
外部命令
【问题】一个外部的应用程序究竟是如何变成一个 Shell 命令的呢?
- 应用程序就是一个文件,只不过这个文件是可以执行的,用户在 Shell 中输入一个外部命令后,只是将可执行文件的名字告诉了 Shell,但是并没有告诉 Shell 去哪里寻找这个文件。
【问题】Shell是如何找到这个文件的?
- Shell 在启动文件中增加了一个叫做 PATH 的环境变量,该变量就保存了 Shell 对外部命令的查找路径,如果在这些路径下找不到同名的文件,Shell 也不会再去其它路径下查找了,它就直接报错。
我们使用 echo 命令输出 PATH 变量的值,看看它保存了哪些检索路径:
[mozhiyan@localhost ~]$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/home/mozhiyan/.local/bin:/home/mozhiyan/bin
不同的路径之间以:分隔。
由此 我们可以编写自己的Shell文件 或者 仿写bash命令
【举例】
编译名字为 getsum 的应用程序,并放在 ~ / bin目录(~ 表示用户主目录)下,然后在 Shell 中输入下面的命令,就可以计算 1+2+3 … +99+100 的值。
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int start = 0;
int end = 0;
int sum = 0;
int opt;
char *optstring = ":s:e:";
while((opt = getopt(argc, argv, optstring))!= -1){
switch(opt){
case 's': start = atoi(optarg); break;
case 'e': end = atoi(optarg); break;
case ':': puts("Missing parameter"); exit(1);
}
}
if(start<0 || end<=start){
puts("Parameter error"); exit(2);
}
for(int i=start; i<=end; i++){
sum+=i;
}
printf("%d\n", sum);
return 0;
}
-s选项表示起始数字,-e选项表示终止数字。
[mozhiyan@localhost ~]$ getsum -s 1 -e 100
5050
除了可以新增加Shell命令到/bin目录下,也可以改变PATH,让Bash搜索的时候搜索该目录
具体操作:
http://c.biancheng.net/view/vip_3233.html
Shell命令选项和参数的本质:函数传参
一个 Shell 内置命令就是一个内部的函数,一个外部命令就是一个应用程序。
内置命令后面附带的所有数据(所有选项和参数)最终都以参数的形式传递给了函数
外部命令后面附带的所有数据(所有选项和参数)最终都以参数的形式传递给了应用程序。
不管是内置命令还是外部命令,它后面附带的数据最终都以参数的形式传递给了函数
getsum -s 1 -e 100要传递的四个参数分别是 -s、1、-e、100
减号-也会一起传递过去,在函数内部,减号-可以用来区分该参数是否是命令的选项。
自己编写的外部命令getsum源码
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int start = 0;
int end = 0;
int sum = 0;
int opt;
char *optstring = ":s:e:";
//分析接收到的参数
while((opt = getopt(argc, argv, optstring))!= -1){
switch(opt){
case 's': start = atoi(optarg); break;
case 'e': end = atoi(optarg); break;
case ':': puts("Missing parameter"); exit(1);
}
}
//检测参数是否有效
if(start<0 || end<=start){
puts("Parameter error"); exit(2);
}
//打印接收到的参数
printf("Received parameters: ");
for(int i=0; i<argc; i++){
printf("%s ", argv[i]);
}
printf("\n");
//计算累加的和
for(int i=start; i<=end; i++){
sum+=i;
}
printf("sum=%d\n", sum);
return 0;
}
Shell命令提示符
[mozhiyan@localhost ~]$
- []是提示符的分隔符号,没有特殊含义。
- mozhiyan表示当前登录的用户,我现在使用的是 mozhiyan 用户登录。
- @是分隔符号,没有特殊含义。
- localhost表示当前系统的简写主机名(完整主机名是 localhost.localdomain)。
- ~代表用户当前所在的目录为主目录(home 目录)。如果用户当前位于主目录下的 bin 目录中,那么这里显示的就是bin。
- $是命令提示符。Linux 用这个符号标识登录的用户权限等级:如果是超级用户(root 用户),提示符就是#;如果是普通用户,提示符就是$。
第二层命令提示符
有些命令不能在一行内输入完成,需要换行,这个时候就会看到第二层命令提示符。
第二层命令提示符默认为>
- echo 命令用来输出一个字符串。字符串是一组由" "包围起来的字符序列,echo 将第一个"作为字符串的开端,将第二个"作为字符串的结尾。
[mozhiyan@localhost ~]$ echo "Shell教程"
Shell教程
[mozhiyan@localhost ~]$ echo "
> http://
> c.biancheng.net
> "
http://
c.biancheng.net
第二个 echo 命令需要多行才能输入完成,提示符>用来告诉用户命令还没输入完成,请继续输入。
Shell脚本
【举例说明】
打开文本编辑器,新建一个文本文件,并命名为 test.sh。
扩展名sh代表 shell,扩展名并不影响脚本执行,见名知意就好
#!/bin/bash
echo "Hello World !" #这是一条语句
- 第 1 行的#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell;后面的/bin/bash就是指明了解释器的具体位置。
- 第 2 行的 echo 命令用于向标准输出文件 输出文本
【举例说明】
#!/bin/bash
# Copyright (c) http://c.biancheng.net/shell/
echo "What is your name?"
read PERSON
echo "Hello, $PERSON"
- 第 4 行中 read表示从终端读取用户输入的数据,并赋值给 PERSON 变量
read 命令用来从标准输入文件(Standard Input,stdin,一般就是指键盘)读取用户输入的数据。
- 第 5 行中,echo表示输出变量 PERSON 的内容。注意在变量名前边要加上$,这样才能表示是值value,否则变量名会作为字符串的一部分处理。
执行Shell脚本(多种方法)
【总结】
如果需要在新进程中运行 Shell 脚本,可以使用bash test.sh这种写法;
如果在当前进程中运行 Shell 脚本,可以使用source test.sh这种写法。
在新进程中运行 Shell 脚本
在新进程中运行 Shell 脚本有多种方法。
- 方法一 : 将 Shell 脚本作为程序运行
Shell 脚本也是一种解释执行的程序,可以在终端直接调用(需要使用 chmod 命令给 Shell 脚本加上执行权限)
[mozhiyan@localhost ~]$ cd demo #切换到 test.sh 所在的目录
[mozhiyan@localhost demo]$ chmod +x ./test.sh #给脚本添加执行权限
[mozhiyan@localhost demo]$ ./test.sh #执行脚本文件
Hello World ! #运行结果
- 方法二 : 将 Shell 脚本作为参数传递给 Bash 解释器
直接运行 Bash 解释器,将脚本文件的名字作为参数传递给 Bash
(通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没用。)
[mozhiyan@localhost ~]$ cd demo #切换到 test.sh 所在的目录
[mozhiyan@localhost demo]$ /bin/bash test.sh #使用Bash的绝对路径
Hello World ! #运行结果
⭐更加简洁的写法是运行 bash 命令。
bash 是一个外部命令,Shell 会在 /bin 目录中找到对应的应用程序,也即 /bin/bash
[mozhiyan@localhost ~]$ cd demo
[mozhiyan@localhost demo]$ bash test.sh
Hello World !
检测是否开启了新进程
Linux 中的每一个进程都有一个唯一的 ID,称为 PID,$$是 Shell 中的特殊变量
使用$$变量就可以获取当前进程的 PID。
#!/bin/bash
echo $$ #输出当前进程PID
使用以上两种方式来运行 check.sh,观察到PID不一样,所以判定为两个进程
[mozhiyan@localhost demo]$ echo $$
2861 #当前进程的PID
[mozhiyan@localhost demo]$ chmod +x ./check.sh
[mozhiyan@localhost demo]$ ./check.sh
4597 #新进程的PID
[mozhiyan@localhost demo]$ echo $$
2861 #当前进程的PID
[mozhiyan@localhost demo]$ /bin/bash check.sh
4584 #新进程的PID
在当前进程中运行 Shell 脚本
source 命令
source 是 Shell 内置命令的一种,它会读取脚本文件中的代码,并依次执行所有语句。
你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
source 命令的用法为:
source filename
简写为 (注意点号.和文件名中间有一个空格。)
. filename
【举例】使用 source 运行上节的 test.sh:
这四种写法均可
[mozhiyan@localhost ~]$ cd demo #切换到test.sh所在的目录
[mozhiyan@localhost demo]$ source ./test.sh #使用source
Hello World !
[mozhiyan@localhost demo]$ source test.sh #使用source
Hello World !
[mozhiyan@localhost demo]$ . ./test.sh #使用点号
Hello World !
[mozhiyan@localhost demo]$ . test.sh #使用点号
Hello World !
【对比】:使用 source 命令不用给脚本增加执行权限,并且写不写./都行。
检测是否在当前 Shell 进程中
仍然借助$$变量来输出进程的 PID
[mozhiyan@localhost ~]$ cd demo
[mozhiyan@localhost demo]$ echo $$
5169 #当前进程PID
[mozhiyan@localhost demo]$ source ./check.sh
5169 #Shell脚本所在进程PID
[mozhiyan@localhost demo]$ echo $$
5169 #当前进程PID
[mozhiyan@localhost demo]$ . ./check.sh
5169 #Shell脚本所在进程PID
进程的 PID 都是一样的,当然是同一个进程了。