shell学习
1、什么是shell和shell脚本
Shell 本身是一个用 C 语言编写的程序,是一个命令行解释器,它的作用就是遵循一定的语法将输入的命令加以解释并传给系统,它是用户使用 Linux 的桥梁,是UNIX/Linux系统的用户与操作系统之间的一种接口。
这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Shell是操作系统的接口,是用户与操作系统交互的桥梁。
Shell既是一种命令语言,又是一种程序设计语言(shell脚本)。它虽然不是 Linux系统内核的一部分,但它调用了系统内核的大部分功能来执行程序、创建文档并以并行的方式协调各个程序的运行。
它既是终端上的用户与UNIX/Linux操作系统交互的命令解释程序(shell命令),又是一种高级的命令程序设计语言(shell脚本)。
作为命令程序设计语言,shell具有一般高级语言的许多特征,如变量定义、赋值、条件和循环语句等。用户可以利用SHELL的这些功能将多条命令组织成一个命令程序,以完成某种特定的任务。这个命令程序称为shell程序或shell过程。
参考:https://www.cnblogs.com/The-explosion/articles/12336061.html
2、关于shell的变量设置
1.变量与变量内容以一个等号“=”来链接,如下所示:
“myname=VBird”
2.等号两边不能直接接空白字符,如下所示为错误:
“myname = VBird”或“myname=VBird Tsai”
3.变量名称只能是英文字母与数字,但是开头字符不能是数字,如下为错误:
“2myname=VBird”
4.变量内容若有空白字符可使用双引号“"”或单引号“'”将变量内容结合起来,但
#双引号内的特殊字符如 $等,可以保有原本的特性,如下所示:
“var="lang is $LANG"”则“echo $var”可得“lang is zh_TW.UTF-8”
#单引号内的特殊字符则仅为一般字符(纯文本),如下所示:
“var='lang is $LANG'”则“echo $var”可得“lang is $LANG”
5.可用转义符“ \”将特殊符号(如 [Enter], $, \,空白字符, '等)变成一般字符,如:
“myname=VBird\ Tsai”
6.在一串指令的执行中,还需要借由其他额外的指令所提供的信息时,可以使用反单
引号“`指令`”或“$(指令)”。特别注意,那个 `是键盘上方的数字键 1左边那个按
键,而不是单引号!例如想要取得核心版本的设置:
“version=$(uname -r)”再“echo $version”可得“3.10.0-229.el7.x86_64”
注:在一串指令中,在` `之内的指令将会被先执行,而其执行出来的结果将做为外部的输入信息
7.若该变量为扩增变量内容时,则可用 "$变量名称"或 ${变量}累加内容,如下所
示:
“PATH="$PATH":/home/bin”或“PATH=${PATH}:/home/bin”或 "PATH=$PATH:/home/bin”
8.若该变量需要在其他子程序执行,则需要以 export来使变量变成环境变量:
“export PATH”实现"分享自己的变量设置给后来调用的文件或其他程序。"
9.通常大写字符为系统默认变量,自行设置变量可以使用小写字符,方便判断(纯
粹依照使用者兴趣与嗜好);
10.取消变量的方法为使用 unset:“unset变量名称”例如取消 myname的设置:
“unset myname”
代码示例
范例一:设置一变量 name,且内容为 VBird
[dmtsai@study ~]$ 12name=VBird
bash: 12name=VBird: command not found... <==屏幕会显示错误!因为不能以数字开头!
[dmtsai@study ~]$ name = VBird <==还是错误!因为有空白!
[dmtsai@study ~]$ name=VBird <==OK的啦!
范例二:承上题,若变量内容为 VBird's name呢,就是变量内容含有特殊符号时:
[dmtsai@study ~]$ name=VBird's name
#单引号与双引号必须要成对,在上面的设置中仅有一个单引号,因此当你按下 enter后,
#你还可以继续输入变量内容。这与我们所需要的功能不同,失败啦!
#记得,失败后要复原请按下 [ctrl]-c结束!
[dmtsai@study ~]$ name="VBird's name" <==OK的啦!
#指令是由左边向右找→,先遇到的引号先有用,因此如上所示,单引号变成一般字符!
[dmtsai@study ~]$ name='VBird's name' <==失败的啦!
#因为前两个单引号已成对,后面就多了一个不成对的单引号了!因此也就失败了!
[dmtsai@study ~]$ name=VBird\'s\ name <==OK的啦!
#利用反斜线(\)跳脱特殊字符,例如单引号与空白键,这也是 OK的啦!
范例三:我要在 PATH这个变量当中“累加”:/home/dmtsai/bin这个目录
[dmtsai@study ~]$ PATH=$PATH:/home/dmtsai/bin
[dmtsai@study ~]$ PATH="$PATH":/home/dmtsai/bin
[dmtsai@study ~]$ PATH=${PATH}:/home/dmtsai/bin
#上面这三种格式在 PATH里头的设置都是 OK的!但是下面的例子就不见得啰!
范例四:承范例三,我要将 name的内容多出 "yes"呢?
[dmtsai@study ~]$ name=$nameyes
#知道了吧?如果没有双引号,那么变量成了啥?name的内容是 $nameyes这个变量!
#呵呵!我们可没有设置过 nameyes这个变量呐!所以,应该是下面这样才对!
[dmtsai@study ~]$ name="$name"yes
[dmtsai@study ~]$ name=${name}yes <==以此例较佳!
范例五:如何让我刚刚设置的 name=VBird可以用在下个 shell的程序?
[dmtsai@study ~]$ name=VBird
[dmtsai@study ~]$ bash <==进入到所谓的子程序
[dmtsai@study ~]$ echo $name <==子程序:再次的 echo一下;
<==嘿嘿!并没有刚刚设置的内容喔!
[dmtsai@study ~]$ exit <==子程序:离开这个子程序
[dmtsai@study ~]$ export name
[dmtsai@study ~]$ bash <==进入到所谓的子程序
[dmtsai@study ~]$ echo $name <==子程序:在此执行!
VBird <==看吧!出现设置值了!
[dmtsai@study ~]$ unset name <==最后用unset命令取消变量值
3、shell中常见环境变量
用 env 或 export 观察环境变量与常见环境变量说明
范例一:列出目前的 shell环境下的所有环境变量与其内容。
[dmtsai@study ~]$ env
HOSTNAME=study.centos.vbird <==这部主机的主机名称
TERM=xterm <==这个终端机使用的环境是什么类型
SHELL=/bin/bash <==目前这个环境下,使用的 Shell是哪一个程序?
HISTSIZE=1000 <==“记录指令的笔数”在 CentOS默认可记录 1000笔
OLDPWD=/home/dmtsai <==上一个工作目录的所在
LC_ALL=en_US.utf8 <==由于语系的关系,鸟哥偷偷丢上来的一个设置
USER=dmtsai <==使用者的名称啊!
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:
or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:
*.tar=01... <==一些颜色显示
MAIL=/var/spool/mail/dmtsai <==这个使用者所取用的 mailbox位置
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
PWD=/home/dmtsai <==目前使用者所在的工作目录(利用 pwd取出!)
LANG=zh_TW.UTF-8 <==这个与语系有关,下面会再介绍!
HOME=/home/dmtsai <==这个使用者的主文件夹啊!
LOGNAME=dmtsai <==登陆者用来登陆的帐号名称
_=/usr/bin/env <==上一次使用的指令的最后一个参数(或指令本身)
3.1 用set查看全部变量
[dmtsai@study ~]$ set #查看所有变量(含环境变量和自订变量)
BASH=/bin/bash <== bash的主程序放置路径
BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.2.46(1)-release' <==这两行是 bash的版本
COLUMNS=90 <==在目前的终端机环境下,使用的字段有几个字符长度
HISTFILE=/home/dmtsai/.bash_history <==历史命令记录的放置文件,隐藏文件
HISTFILESIZE=1000 <==存起来(与上个变量有关)的文件之指令的最大纪录笔数。
HISTSIZE=1000 <==目前环境下,内存中记录的历史命令最大笔数。
IFS=$' \t\n' <==默认的分隔符号
LINES=20 <==目前的终端机下的最大行数
MACHTYPE=x86_64-redhat-linux-gnu <==安装的机器类型
OSTYPE=linux-gnu <==操作系统的类型!
PS1='[\u@\h \W]\$ ' <== PS1是命令提示字符,也就是我们常见的[root@www ~]
#或 [dmtsai ~]$的设置值啦!可以更动的!
PS2='> ' <==如果你使用转义符号(\)第二行以后的提示字符也
$ <==目前这个 shell所使用的 PID
? <==刚刚执行完指令的回传值。
3.2 PSI值
\d: #可显示出“星期月日”的日期格式,如:"Mon Feb 2"
\H: #完整的主机名称。举例来说,鸟哥的练习机为“study.centos.vbird”
\h: #仅取主机名称在第一个小数点之前的名字,如鸟哥主机则为“study”后面省略
\t: #显示时间,为 24小时格式的“HH:MM:SS”
\T: #显示时间,为 12小时格式的“HH:MM:SS”
\A: #显示时间,为 24小时格式的“HH:MM”
\@: #显示时间,为 12小时格式的“am/pm”样式
\u: #目前使用者的帐号名称,如“dmtsai”;
\v: #BASH的版本信息,如鸟哥的测试主机版本为 4.2.46(1)-release,仅取“4.2”显示
\w: #完整的工作目录名称,由根目录写起的目录名称。但主文件夹会以 ~取代;
\W: #利用 basename函数取得工作目录名称,所以仅会列出最后一个目录名。
\#: #下达的第几个指令。
\$: #提示字符,如果是 root时,提示字符为 #,否则就是 $啰~
CentOS默认的 PS1内容:"[\u@\h \W]\$ "
当然也可任意设置,如:
[zengcj@localhost ~]$ PS1='[\u@\h \w \A #\#]\$ '
[zengcj@localhost ~ 14:58 #48]$ ls
Docker Exercise genomic GWAS miniconda3 n.sh SMR software 下载 公共 图片 文档 桌面 模板 视频 音乐
[zengcj@localhost ~ 14:59 #49]$ PS1='[\u@\h \w \A ]\$ '
[zengcj@localhost ~ ]$ PS1='[xiangsui@\h \w ]\$ '
[xiangsui@localhost ~ ]$ PS1='[xiangsui@\h \w ]\$'
[xiangsui@localhost ~ ]$PS1='[xiangsui@^_^ \w ]\$' #一般最后引号前需多一个空格
[xiangsui@^_^ ~ ]$PS1='[xiangsui@^_^ \w ]\$ '
[xiangsui@^_^ ~ ]$ PS1="[xiangsui@^_^ \w ]\$ "
3.3 其他
$:(关于本 shell的 PID)
$字号本身也是个变量,代表的是“目前这个 Shell的线程代号”,亦即是所谓的 PID(Process ID)。更多的程序观念,我们会在第四篇的时候提及。想要知道我们的 shell的 PID,就可以用:“ echo $$”即可!出现的数字就是你的 PID号码。
?:(关于上个执行指令的回传值)
在 bash里面这个变量可重要的很!这个变量是:“上一个执行的指令所回传的值”,上面这句话的重点是“上一个指令”与“回传值”两个地方。当我们执行某些指令时,这些指令都会回传一个执行后的代码。一般来说,如果成功的执行该指令,则会回传一个 0值,如果执行过程发生错误,就会回传“错误代码”,可以根据回传错误代码的不同,判断发生的错误类型。
用以下程序判断错误代码含义
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
int i=0;
for(i=0;i<43;i++) //43及以后就木有了
printf("errno:%d:\t%s\n",i,strerror(i));
return 0;
}
参考:https://www.cnblogs.com/castor-xu/p/12026515.html
3.4 关于Linux支持的语系
-- 可以用locale -a查询,语系文件一般放在 /usr/lib/locale/ 目录,系统默认语系定义为/etc/locale.conf
[xiangsui@^_^ ~]$ locale <==后面不加任何选项与参数即可!
LANG=en_US <==主语言的环境
LC_CTYPE="en_US" <==字符(文字)辨识的编码
LC_NUMERIC="en_US" <==数字系统的显示讯息
LC_TIME="en_US" <==时间系统的显示数据
LC_COLLATE="en_US" <==字串的比较与排序等
LC_MONETARY="en_US" <==币值格式的显示等
LC_MESSAGES="en_US" <==讯息显示的内容,如功能表、错误讯息等
LC_ALL= <==整体语系的环境
基本上,你可以逐一设置每个与语系有关的变量数据,但事实上,如果其他的语系变量都未设置,且你有设置 LANG或者是 LC_ALL时,则其他的语系变量就会被这两个变量所取代!这也是为什么我们在 Linux当中,通常说明仅设置 LANG或 LC_ALL这两个变量而已,因为他是最主要的设置变量!你是在 MS Windows主机以远端连线服务器的软件连线到主机的话,其实命令行确实是可以看到中文的。此时反而你得要在 LC_ALL设置中文编码!
4、变量键盘读取、阵列与宣告: read, array, declare
-- read
[dmtsai@study ~]$ read [-pt] variable
选项与参数:
-p :后面可以接提示字符!
-t :后面可以接等待的“秒数!”这个比较有趣~不会一直等待使用者啦!
范例一:让使用者由键盘输入一内容,将该内容变成名为 atest的变量
[dmtsai@study ~]$ read atest
This is a test <==此时光标会等待你输入!请输入左侧文字看看
[dmtsai@study ~]$ echo ${atest}
This is a test <==你刚刚输入的数据已经变成一个变量内容!
范例二:提示使用者 30秒内输入自己的大名,将该输入字串作为名为 named的变量内容
[dmtsai@study ~]$ read -p "Please keyin your name: " -t 30 named
Please keyin your name: VBird Tsai <==注意看,会有提示字符喔!
[dmtsai@study ~]$ echo ${named}
VBird Tsai <==输入的数据又变成一个变量的内容了!
read之后不加任何参数,直接加上变量名称,那么下面就会主动出现一个空白行等待你的输入(如范例一)。如果加上 -t后面接秒数,例如上面的范例二,那么 30秒之内没有任何动作时,该指令就会自动略过了~如果是加上 -p,嘿嘿!在输入的光标前就会有比较多可以用的提示字符给我们参考!在指令的下达里面,比较美观啦! ^_^
-- declare / typeset
declare或 typeset是一样的功能,就是在“宣告变量的类型”。如果使用 declare后面并没有接任何参数,那么 bash就会主动的将所有的变量名称与内容通通列出来,就好像使用 set一样。
[dmtsai@study ~]$ declare [-aixr] variable
选项与参数:
-a :将后面名为 variable的变量定义成为阵列(array)类型
-i :将后面名为 variable的变量定义成为整数数字(integer)类型
-x :用法与 export一样,就是将后面的 variable变成环境变量;
-r :将变量设置成为 readonly类型,该变量不可被更改内容,也不能 unset
范例一:让变量 sum进行 100+300+50的加总结果
[dmtsai@study ~]$ sum=100+300+50
[dmtsai@study ~]$ echo ${sum}
100+300+50 <==咦!怎么没有帮我计算加总?因为这是文字体态的变量属性啊!
[dmtsai@study ~]$ declare -i sum=100+300+50
[dmtsai@study ~]$ echo ${sum}
450 <==瞭乎??
由于在默认的情况下面, bash对于变量有几个基本的定义:
- 变量类型默认为“字串”,所以若不指定变量类型,则 1+2为一个“字串”而不是“计算式”。所以上述第一个执行的结果才会出现那个情况的;
- bash环境中的数值运算,默认最多仅能到达整数形态,所以 1/3结果是 0;
- 如果需要非字串类型的变量,那就得要进行变量的宣告才行!
范例二:将 sum变成环境变量
[dmtsai@study ~]$ declare -x sum
[dmtsai@study ~]$ export | grep sum
declare -ix sum="450" <==果然出现了!包括有 i与 x的宣告!
范例三:让 sum变成只读属性,不可更动!
[dmtsai@study ~]$ declare -r sum
[dmtsai@study ~]$ sum=tesgting
-bash: sum: readonly variable <==老天爷~不能改这个变量了!
范例四:让 sum变成非环境变量的自订变量吧!
[dmtsai@study ~]$ declare +x sum <==将 -变成 +可以进行“取消”动作
[dmtsai@study ~]$ declare -p sum <== -p可以单独列出变量的类型
declare -ir sum="450" <==看吧!只剩下 i, r的类型,不具有 x啰!
-- 阵列(array)变量类型
在 bash中,阵列的设置方式是:var[index]=content
范例:设置var[1]~ var[3]的变量。
[dmtsai@study ~]$ var[1]="small min"
[dmtsai@study ~]$ var[2]="big min"
[dmtsai@study ~]$ var[3]="nice min"
[dmtsai@study ~]$ echo "${var[1]}, ${var[2]}, ${var[3]}"
small min, big min, nice min
5、与文件系统及程序的限制关系: ulimit
bash是可以“限制使用者的某些系统资源”的,包括可以打开的文件数量,可以使用的 CPU时间,可以使用的内存总量等等。主要依靠ulimit命令实现。
[dmtsai@study ~]$ ulimit [-SHacdfltu] [配额]
选项与参数:
-H :hard limit,严格的设置,必定不能超过这个设置的数值;
-S :soft limit,警告的设置,可以超过这个设置值,但是若超过则有警告讯息。
在设置上,通常 soft会比 hard小,举例来说,soft可设置为 80而 hard
设置为 100,那么你可以使用到 90(因为没有超过 100),但介于 80~100之间时,
系统会有警告讯息通知你!
-a :后面不接任何选项与参数,可列出所有的限制额度;
-c :当某些程序发生错误时,系统可能会将该程序在内存中的信息写成文件(除错用),
这种文件就被称为核心文件(core file)。此为限制每个核心文件的最大容量。
-f :此 shell可以创建的最大文件大小(一般可能设置为 2GB)单位为 KBytes
-d :程序可使用的最大断裂内存(segment)容量;
-l :可用于锁定(lock)的内存量
-t :可使用的最大 CPU时间(单位为秒)
-u :单一使用者可以使用的最大程序(process)数量。
范例一:列出你目前身份(假设为一般帐号)的所有限制数据数值
[dmtsai@study ~]$ ulimit -a
core file size (blocks, -c) 0 <==只要是 0就代表没限制
data seg size (kBytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited < ==可创建的单一文件的大小
pending signals (-i) 4903
max locked memory (kBytes, -l) 64
max memory size (kBytes, -m) unlimited
open files (-n) 1024 < ==同时可打开的文件数量
pipe size (512 Bytes, -p) 8
POSIX message queues (Bytes, -q) 819200
real-time priority (-r) 0
stack size (kBytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kBytes, -v) unlimited
file locks (-x) unlimited
范例二:限制使用者仅能创建 10MBytes以下的容量的文件
[dmtsai@study ~]$ ulimit -f 10240
[dmtsai@study ~]$ ulimit -a | grep 'file size'
core file size (blocks, -c) 0
file size (blocks, -f) 10240 <==最大量为10240Kbyes,相当10MBytes
[dmtsai@study ~]$ dd if=/dev/zero of=123 bs=1M count=20
File size limit exceeded(core dumped) <==尝试创建 20MB的文件,结果失败了!
[dmtsai@study ~]$ rm 123 <==赶快将这个文件删除啰!同时你得要登出再次的登陆才能解开 10M的限制
注:想要复原 ulimit的设置最简单的方法就是登出再登陆,否则就是得要重新以ulimit设置才行!不过,要注意的是,一般身份使用者如果以 ulimit -f设置了的文件大小,那么他“只能继续减小文件大小,不能增加文件大小!”