Shell脚本 - 入门

Shell基础

编程语言

  • 编程语言分类(发展史):低级语言、高级语言、面向对象语言(在IDL教材中有一种说法是,IDL属于第四类编程语言,也就是交互式数据语言,是VC、VB、JAVA、FORTRAN、MATLAB、OPENGL等语言的集成)

    • 低级语言时代:机器语言、汇编语言(面向机器的程序设计语言)
    • 高级语言时代:开始接近自然语言和数学语言:Fortran 、ALGOL 、BASIC、Pascal 、C语言/C++
    • 面向对象时代:封装性、继承性、多态性:Java、Python、PHP
  • 类外现代编程语言还可以分为:编译性语言、解释性语言

    • 编译性:只须编译一次:源代码 —>机器语言:C/C++、C#、go、Pascal/Object Pascal(Delphi)
    • 解释性:逐行翻译解释运行:源代码—>中间代码—>机器语言:PHP、Python、shell

    解释性语言写的代码 -脚本

    Java属于编译-解释性语言,因为其同时具备编译性和解释性两种特性

    java文件先编译成与平台无关的.class的字节码文件,然后.class的字节码文件既可以在Windows平台上的java虚拟机(JVM)上进行解释运行,也可以在Linux平台上的JVM上解释运行;而JVM的翻译过程时解释性的,JVM从.class的字节码文件中读出一条指令,翻译一条指令,然后执行一条指令,这个过程就称为java的解释执行

    源自CSDN博主@你的代码有灵魂么?

编写脚本(shell script)

创建脚本

  1. 创建shell脚本一般以.sh结尾:

    cd /root
    vim hello.sh
  2. 脚本第一行固定写入

    #!/bin/bash
  3. 编写要执行的shell 命令

    echo "hello world!"
  4. 编写完成,vim方式退出保存

执行脚本

bash /root/hello.sh
# 或者加执行权限,使用路径调用
chmod +x /root/hello.sh
/root/hello/sh
./hello/sh

echo命令

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出

输出后面指定的字符串、并换行

语法: echo 【选项】 【输出内容】

选项:

-n 取消输出内容后面的换行符
-e 支持反斜线控制字符的转换“激活转义符识别”

[root@localhost ~]# vim hello.sh

#! /bin/bash
echo hello world!        # 双引号可以省略
echo "hello world!"
transname=999
echo $transname
echo -n  请输入你的名字:
read transname
echo "$transname is your name. "
~

[root@localhost ~]# bash hello.sh 
hello world!
hello world!
999
请输入你的名字:reeskysui   
reeskysui is your name.

[root@localhost ~]# vim hello.sh

  1 #! /bin/bash
  2 
  3 echo -e  不加引号不能识别控制字符"\n"上一行加了这一行没加 \n 换行符
  
[root@localhost ~]# bash hello.sh 
不加引号不能识别控制字符
上一行加了这一行没加 n 换行符
控制字符作用
\\、\"输出“ \ ”,“ " ”本身
\a输出系统警告音
\b退格,向左删除一个字符
\f换页符,换行但是换行后的新行的开头位置连接着上一行的行尾
\n换行符
\t制表符
\e输出颜色,需要搭配

\e的使用方式:

格式:

​ “\e[颜色代码m”代表颜色输出开始,\e[0m代表颜色输出结束;

其中,\e是转义起始符,定义一个转义序列;[表示开始定义颜色,后加颜色色代码;m是转义终止符; #可以进行的设置,多个设置之间用分号“ ;”分割开,同时生效:

字体颜色30 31 323334353637
背景颜色4041424344454647
特殊效果1(字体高亮)4(下划线)5(闪烁)7(反显)8(消隐)

bash的基本功能

历史命令

#历史命令的查看
history [选项]
-c 清空历史命令
-w 把缓存中的历史命令保存到历史命令文件(默认在~/.bash_history)

#当我们正常退出(logout、exit、ctrl+d)操作系统时,系统会自动将缓存中的历史命令保存到文件里
     正常退出(exit、ctrl+D):history    -  >.bash_history  自动写入,-w
     登录:.bash_history ->history
#历史命令的调用
* 使用上、下光标键调用
* 使用“!!”,重复执行上一条命令
* 使用“!n”,重复执行第n条历史命令
* 使用“!字符”,重复执行最近一条以此字符开头的命令

使用tab将命令或文件名补全

Tab键补全:

  • 可以自动补全文件名、目录名、命令、命令选项(ls --help)、服务名等;
  • 能提高用户的输入速度,也能提高准确性;
  • 自动补全之前,输入的字符越多补齐的范围越小,就越精确;
  • 使用的软件包:bash-completion;

[!NOTE]

补齐功能会在目录结尾处自动补上/符号,有时会导致系统识别出现问题,从而报错,如:xfsdump

命名别名的管理

# 查看系统中已经生效的别名,alias
 alias
# 设置新的别名,alias 别名=’原命令 [选项]’
 alias vi='vim'
 alias ls='cd'   #不能随意定义
 
# 命令执行的优先级:
绝地路径  > 别名  > 系统命令(内置cd、外部ls- PATH) 
=>执行顺序:
NO.1 用绝对路径或相对的方式执行命令
NO.2 别名命令
NO.3 bash内置命令,如cd等,可用type区分
NO.4 根据环境变量(PATH)定义的目录顺序找到的第一个命令
# 退出当前终端| 删除别名,unalias 别名
unalias ls

----
# 用命令alias设置的别名是临时的,退出当前终端则不再生效
# 永久设置别名,设置在配置文件中,生效后自动加载,使用alias命令可查看
# 系统中部分配置文件用souce或.命令调用可生效
单个用户生效,写入:  ~/.bashrc
所有用户生效,写入: /etc/bashrc

# 删除永久设置的别名
在设置的配置文件中删除  -》  source或. 配置文件  -》  unalias 别名

bash常用快捷键

快捷键作用
ctrl+a把光标移动到行首
ctrl+e把光标移动到行尾
ctrl+c强制终止当前命令
ctrl+l清屏,相当于clear命令
ctrl+u删除或剪切光标之前的内容
ctrl+k删除或剪切光标之后的内容
ctrl+y粘贴CTRL+u或CTRL+k剪切的内容
ctrl+r在历史命令中搜索
ctrl+d退出当前终端
ctrl+z暂停并放入后台
ctrl+s暂停屏幕输出
ctrl+q恢复屏幕输出
# 查看所有已存在的快捷键
$ stty -a
# 修改快捷键,stty  关键词  快捷键
# 定义ctrl+p快捷键为强制终止,^字符只要手工输入即可
$ stty intr ^p
$ stty -a
注:尽量不要对快捷键做出修改,毕竟服务器不是一个人在维护,你的习惯不是别人的习惯

输入输出重定向

bash的标准输入输出

$ ll /dev/st*
 lrwxrwxrwx 1 root root 15 12月  3 16:07 /dev/stderr -> /proc/self/fd/2
 lrwxrwxrwx 1 root root 15 12月  3 16:07 /dev/stdin -> /proc/self/fd/0
 lrwxrwxrwx 1 root root 15 12月  3 16:07 /dev/stdout -> /proc/self/fd/1
设备设备名文件描述符类型
键盘/dev/stdin0标准输入
显示器/dev/stdout1标准输出
显示器/dev/stderr2标准错误输出

bash的输出重定向

#标准输出重定向,即把命令的正确输出输出到指定文件或设备中
格式1:命令 > 文件     #覆盖方式 
格式2:命令 >> 文件    #追加方式
#标准错误输出重定向,即把命令的错误输出输出到指定文件或设备中
格式1:命令 2> 文件     
#覆盖方式 
格式2:命令 2>> 文件    #追加方式
#正确输出和错误输出同时重定向
#输出到同一个文件中
格式1:命令 > 文件 2>&1    或:  命令 &> 文件        #覆盖方式 
格式2:命令 >> 文件 2>&1   或:  命令 &>> 文件        #追加方式
#输出到不同一个文件中
格式1:命令 > 文件1  2> 文件2     #覆盖方式 
格式2:命令 >> 文件1  2>> 文件2   #追加方式
------
# 使用EFO非交互式地往文件中写多行内容
# 将内容写入或重定向到文件
echo "echo nihaoya~" > test.txt
cat test.sh
#写简单的多行
echo -e "123 \n 456" > test.txt
cat test.txt
#复杂的语句、再多行..
echo 'echo -e "\e[31m kkk \e[0m"' > test.txt
cat test.sh
#写多行,用EOF语法
<<EOF
> echo -e "\e[31m nihaoya~ \e[0m"
> echo "keyima"
> EOF
#EOF一般会配合cat实现多行文本输出:
#理解:cat的操作对象是文件,但是此处cat的操作对象不是文件,而是用户输入
cat <<efo
> echo -e "\e[31m nihaoya~ \e[0m"
> from test
> efo
#EOF配合cat也能将多行文本重定向到文件中(追加或覆盖都行):
cat << EOF > test.sh
> 123
> 456
> 789
> EOF
或:
cat > test.sh <<EOF
> abc
> def
> ghk
> EOF
#自定义结束符:
#EOF是END Of File的缩写,表示自定义终止符;即EOF不是固定的,可以随意设置别名
cat > t1.txt <<efo
> abc
> 123
> efo
或:
cat > t2.txt <<bbb
> abc
> 123
> bbb

bash输入重定向

tee 命令

作用:会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存在给定文件中;
语法:tee [选项][文件]
选项:
    -a 内容追加到给定的文件,默认是覆盖
    --help 查看帮助信息
    --version 查看版本信息

# 将键盘输入内容存在文件中:
tee t1.txt
    how are you?
    fine,thank you
cat t1.txt
# 再次写入,内容被覆盖
tee t1.txt
    not good!
cat t1.txt
 
# 将文件系统使用的信息追加到文件(追加可以使用来记录日志)
df -h | tee -a disk.log

wc命名

作用:统计指定文件中的行数、单词数、字数,并将统计结果显示输出
语法:wc 选项 文件名
选项: 
    -c 统计字数(字符)
    -w 统计单词数(字符串,由空白、空格或换行符分隔的字符串)
    -l 统计行数
    
$ wc anaconda-ks.cfg
72  179 1947 anaconda-ks.cfg
$ wc -c anaconda-ks.cfg
1947 anaconda-ks.cfg
$ wc -w anaconda-ks.cfg
179 anaconda-ks.cfg
$ wc -l anaconda-ks.cfg
72 anaconda-ks.cfg

多命令的执行顺序

格式作用
命令1 ; 命令2多个命令顺序执行,命令间没有逻辑关系
命令1 && 命令2逻辑与 当命令1正确执行时才会执行命令2
命令1 || 命令2逻辑或 当命令1执行不正确时才会执行命令2
$ cd /lianxi ; tail -2 /etc/passwd-bash: cd: /lianxi: 没有那个文件或目录
zhangsan:x:1001:1001::/home/zhangsan:/bin/bash
zhangsan1:x:1002:1002::/home/zhangsan1:/sbin/nologin
$ cd /lianxi && tail -2 /etc/passwd-bash: cd: /lianxi: 没有那个文件或目录
$ cd /lianxi || tail -2 /etc/passwd
zhangsan:x:1001:1001::/home/zhangsan:/bin/bash
zhangsan1:x:1002:1002::/home/zhangsan1:/sbin/nologin

通配符

通配符作用
匹配任意一个字符
*匹配0个或任意多个字符,可以匹配任何内容
[]匹配中括号内任意一个字符
[-]匹配括号内任意一个字符,-代表一个范围
[^]逻辑非,表示匹配不是中括号内的一个字符
#准备测试数据
$ cd
$ mkdir tong
$ cd tong
$ touch abc ABC abcd 0123 0abc
$ ls abc*
abc  abcd
$ ls abc?
abcd
$ ls ?abc
0abc
$ chmod +x *abc* 
$ ls [A-Z]    
ls: 无法访问[A-Z]: 没有那个文件或目录
$ ls [A-Z]? 
ls: 无法访问[A-Z]: 没有那个文件或目录
$ ls [A-Z]??
 ABC
$ ls [a-zA-Z]??
 abc  ABC
$ find . -name "[a-z0-9]*"  
$ ls [^0-9]*  
abc  ABC  abcd
$ mkdir t1
$ mv [^a-z][a-z]* t1
$ rm -rf *.txt

grep命令

作用:过滤一个文件中的指定字符串,是行提取命令(只要包含关键词的行都被显示)
格式:grep  选项  ”关键词“  文件名
选项:
    -A 数字 #列出符合条件的行,并连续列出后续n行
    -B 数字 #列出符合条件的行,并连续列出前面n行
    -c #统计符合条件的字符串行数
    -i #忽略大小写
    -n #输出行号
    -v #反向查找(取反)
    -o #只列出关键字
    --color=auto #搜索出的关键词高亮显示
    
$ grep "jimmy" /etc/passwd
jimmy:x:1000:1000:jimmy:/home/jimmy:/bin/bash
$ grep -A 2 "jimmy" /etc/passwd
jimmy:x:1000:1000:jimmy:/home/jimmy:/bin/bash
zhangsan:x:1001:1001::/home/zhangsan:/bin/bash
zhangsan1:x:1002:1002::/home/zhangsan1:/sbin/nologin
$ grep -cA 2 "jimmy" /etc/passwd
1
$ grep -n "jimmy" /etc/passwd
44:jimmy:x:1000:1000:jimmy:/home/jimmy:/bin/bash
#find 和 grep 的区别:
find 是在系统中找符合条件的文件名,默认是完全匹配,需要使用通配符进行模糊匹配;
grep 是在文件中查找符合条件的字符串,是包含(模糊)匹配,如果需要精确查询,需要使用正则表达式,但
此处暂不介绍,高级篇中讲解

bash中的其他特殊字符

符号作用
#在shell脚本中,#开头的行代表注释
\转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符
$用于调用变量的值
''单引号,在单引号中所有的符号都是普通字符,即所有的特殊字符都没有特殊含义
""双引号,在双引号中除了特殊符号($、\、``)外,所有符号都是普通字符
``反引号,反引号括起来的内容是系统命令,在bash中会先执行它
$()和反引号作用一样,用来引用系统命令,使用较多、不易与其他符号混淆
()用于一串命令执行时,()中的命令会在子shell中运行
{}用于一串命令执行时,{}中的命令会在当前shell中执行;也可以用于变量变形与替换等
[]用于变量测试,同test命令

单引号和双引号

=>单引号当中的符号都没有特殊含义;
双引号里绝大多数符号也没有特殊含义,但这3个符号保留含义: $(调用变量的值) 和 `(反引号,引用命
令) 和 \(转义符) 。
#使用$调用环境变量PATH
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
$ echo '$PATH'
$PATH
$ echo "$PATH"
 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
 #使用反斜杠取消字符的特殊含义
 $ echo '\$PATH'
 \$PATH
$ echo "\$PATH"
$PATH
#使用反引号调用系统命令date
$ echo `date`
 2023年 10月 07日 星期六 14:47:41 CST
$ echo 'date'
 date
$ echo "date"
 date
$ echo '`date`'
 `date`
$ echo "`date`"
 2023年 10月 07日 星期六 14:47:41 CST

反引号和$()

$ echo `date`
2023年 10月 07日 星期六 14:47:41 CST
$ echo $(date)
2023年 10月 07日 星期六 14:47:41 CST
$ echo $(ls /root)

中括号

#对表达式做比较
name=lisi
[ "$name" == "lisi" ] && echo "dui" || echo "cuo"
或:
"$name" == "lisi" && echo "dui" || echo "cuo"
#注:[ 表达式 ],中括号的表达式所有更有一个空格;
#注:表达式目前未具体涉及,了解此语法格式即可;

大括号、小括号

#shell解释器是可以手动开启的,可以开启多个shell、也可以开启子shell;
#最明显的感觉就是当你新开启一个shell后,你刚才执行的历史命令调用不出来;
#使用bash命令可以打开一个新的bash,查看pstree结果中的sshd部分;
$ pstree
#打开一个新的bash
$ bash
#发现在原有的基础上又开启了个新的bash
$ pstree

#结合父shell和子shell我们来解释花括号和小括号的区别:
相同:
()和{}都是把一串命令放在括号里,并且命令之间用;号隔开
不同:
()执行一串命令时,需要重新开一个子shell进行执行
{}执行一串命令时,在当前shell执行
()在命令最后可以不加分号
{}在命令最后不用分号不能正确执行
()里的各命令和括号之间可以没有空格
{}的第一个命令和左括号之间必须要有一个空格
#实例证明:
() 中执行命令时,新开启shell 进行
$ name=zhangsan
$ (name=lisi;echo $name)
lisi
$ echo $name
zhangsan
{ } 执行命令时,就在本shell中进行
$ name=zhangsan
$ { name=lisi;echo $name;}
lisi
$ echo $name
lisi

Shell的变量

变量的定义和调用

变量即可变化的量,为了能描述变量,一般用某个特定字符串来代表,称其为变量名

声明变量: 变量名=值

调用变量:echo $变量名

#定义变量:
$ bomei = beibei    ❌
$ bomei= beibei     ❌
$ bomei=beibei        ✅

$ 2ha=beibei        ❌
$ xiao2ha=beibei    ❌
$ xiao~ha=beibei    ❌
$ xiao$ha=beibei    ❌
$ xiao_ha=beibei    ✅
$ dog=alasijia xueqiaoquan      ❌
$ dog="alasijia xueqiaoquan"    ✅
$ dog='alasijia xueqiaoquan'    ✅
#回顾:单引号和双引号的区别:
$ name="dongyuhui"
$ echo $name      
$ echo "$nanme"   --dongyuhui
$ echo '$name'    -- $name
$ echo "\$name"   -- $name
$ echo `date`
$ echo "`date`"   -- 2023年 09月 15日 星期五 00:13:07 CST
$ echo '`date`'   -- `date`
🎈1,定义变量时,=左右两边不能有空格
🎈2,变量名可以由字母、数字和下划线组成,但是不能以数字开头
🎈3,变量值中若有空格则需要用单引号或双引号引起来
注:单引号中任何字符都是普通字符,不具备任何特殊含义;而双引号“ ”内的 $、\(转义字符)、``(反
引号) 仍然具有特殊含义
----------------------------
🎈4,可以把一个现有变量的值作为变量值赋给变量
$ lujing="$PATH"
$ echo $lujing
🎈5,可以把一个命令的结果作为变量值赋给变量
$ shijian=`date`   
或:shijian=$(date)
$ echo $shijian
🎈6,调用变量值时,为了保证不与后面的字符(字母、数字、下划线)被误判成新的变量名,需要加花括号进行分隔
$ shijian="$(date)"
$ echo "now time is $shijian ma?"
now time is 2023年 09月 15日 星期五 00:19:48 CST ma?
$ echo "nowtimeis$shijianma?"
nowtimeis?
$ echo "nowtimeis${shijian}ma?"
nowtimeis2023年 09月 15日 星期五 00:19:48 CSTma?
$ echo $shijian     
$ echo $shijian+    
$ echo $shijian_   --加
$ echo $shijian2   --加
$ echo $shijianer  --加
$ echo $shijian人
------------------------
🎈7,变量是有类型的,默认是字符串类型
🎈8,变量是可以叠加的
$ a=10;b=20
$ echo $a+$b   -- 10+20
$ d1="ni hao ma,"; d2="tongxuemen"; d3="laoshi"
或:d1="ni hao ma," d2="tongxuemen"  d3="laoshi"
$ echo $d1$d3
ni hao ma,laoshi
$ echo $d1$d2
ni hao ma,tongxuemen
$ echo ${d1}dog
ni hao ma,dog
或:
$ echo "$d1"dog
ni hao ma,dog

变量的分类

  • 自定义变量:由用户自由定义变量名和变量的值,这种变量是最常见的变量;
  • 环境变量:保存的值是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的;
  • 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的;
  • 位置参数变量:用来向脚本中传递参数或数据的,变量名不能自定义,变量作用是固定的

自定义变量

#变量的定义,变量名=变量值
gangwei="yunwei"
#变量调用,echo $变量名
echo $gangwei
#变量查看,set [选项]
🎈0,set :查看所有的变量
shijian=$(date)
set | grep shijian
🎈1,set -u  :调用没有声明的变量时会报错
[root@localhost ~]# echo $abc
[root@localhost ~]#
[root@localhost ~]# set -u
[root@localhost ~]#
[root@localhost ~]# echo $abc-bash: abc: unbound variable-bash: abc: 未绑定变量
[root@localhost ~]#
[root@localhost ~]# set +u
🎈2,set -x  :命令执行前,会先把命令输出一次
[root@localhost ~]# set -x
[root@localhost ~]# echo "nihao"
+ echo nihao
nihao
[root@localhost ~]# set +x
#调试脚本时,可以在脚本中写入set -x,每次运行脚本(bash 脚本名)都会先打印
#也可以有问题时打印一次,bash -x 脚本名
[root@localhost ~]# cat for1.sh 
#!/bin/bash
set -x
for i in 1 2 3 4
do
echo $i
done
#变量的删除,uset 变量名
unset gangwei

环境变量

#环境变量的定义,export 变量名=值
#环境变量名一般都大写,但一般很少自定义环境变量,都是使用系统设置好的
export SIZE=1000
或:
SIZE=1000
export SIZE
#环境变量的查看,env
#set命令查看所有变量,env命令只能查看环境变量
set | grep SIZE=
env | grep SIZE=
#环境变量的删除,unset 变量名
unset SIZE
--------------------------------
#系统中的环境变量
🎈1,PATH变量:系统命令存放路径
[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
#将一个系统外部命令使用命令名就可以执行
方法一:可以考虑把自己写的脚本放入PATH对应的目录内
or
方法二:修改PATH变量的值,将我们存放脚本的目录叠加到PATH变量中
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
$ PATH="$PATH:/sh"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/sh
#永久设置PATH变量:
单用户生效:~/.bash_profile
所有用户生效:/etc/profile
#相关的配置文件:
/etc/profile  => ~/.bash_profile  => ~/.bashrc  => /etc/bashrc
#修改/etc路径下的配置文件将会应用到整个系统,属于系统级的配置
#修改用户目录下的.bashrc则只是限制在用户应用上,属于用户级设置------
🎈2,PS1变量:命令提示符设置
#PS1的默认格式:
[root@localhost ~]# echo $PS1
[\u@\h \W]\$
可用选项: 
\u:显示当前用户名
\h:显示简写主机名。如默认主机名“localhost”
\W:显示当前所在目录的最后一个目录
\$:提示符。如果是root提示符为“#”,如果是普通用户提示符为“$”
\H:显示完整的主机名。如默认主机名“localhost.localdomain”
\d:显示日期,格式为“星期月日”Ø
\t:显示24小时制时间,格式为“HH:MM:SS”
\T:显示12小时制时间,格式为“HH:MM:SS”
\A:显示24小时制时间,格式为“HH:MM”
\@:显示12小时制时间,格式为“HH:MM am/pm”
\v:显示Bash的版本信息 
\w:显示当前所在目录的完整名称
\#:执行的第几个命令
#自定义PS1变量值时需要使用单引号,否则不生效
$ PS1='[\u@\H \w]'
[root@localhost.localdomain ~]
$ mkdir test
$ cd test
[root@localhost.localdomain ~/test]
$ PS1='[\e[35;1m\u\e[0m@\A \w]\$ '
[root@16:31 ~/test]# 
#永久设置PS1变量:/etc/bashrc------
🎈3,LANG变量:系统语系变量
$ echo $LANG
zh_CN.UTF-8
#Linux安装时,选择的是中文安装,所以默认的主语系变量是“zh_CN.UTF-8”
$ localectl status
System Locale: LANG=zh_CN.UTF-8
VC Keymap: cn
X11 Layout: cn
#查看Linux中支持的所有语系:
$ localectl list-locales
$ localectl list-locales | tail -5
zh_TW.euctw
zh_TW.utf8
zu_ZA
zu_ZA.iso88591
zu_ZA.utf8
$ localectl list-locales | wc -l
787
#永久修改操作系统语言环境
$ cat /etc/locale.conf
LANG="zh_CN.UTF-8"
#该命令直接修改配置文件,退出终端后生效
$ localectl set-locale LANG=en_US  

预定义变量

预定义变量作用
$?最后一次执行命令的返回状态,返回值为0-255,0为成功,非0为失败
$$当前进程的进程号PID
$!后台运行的最后一个进程的进程号PID
#$?:命令执行成功:返回值为0;命令执行失败:返回值为非0
$ ls
$ echo $?
0
$ lls-bash: lls: 未找到命令
$ echo $?
127
$ grep "lisi" /etc/passwd
$ echo $?
1
--------------------
#$$:当前进程的PID(即显示当前登录shell的进程号)
$ echo $$
19033
$ ps aux | grep 19033 | grep -v grep
root      
19033  0.0  0.0 116096  2948 pts/1    
#关闭一个终端
要关闭的终端: echo $$
另一个终端: kill -9  要关闭终端的终端号 
--------------------
#$!:最后一个放入后台执行的命令的进程
$ dd if=/dev/zero of=/dev/null &
[1] 19126
$ echo $!
19126
$ ps aux | grep 19126 | grep -v grep
root      
Ss   
02:33   
0:00 -bash
19126  100  0.0 107996   620 pts/1    R    02:57   0:53 dd 
if=/dev/zero of=/dev/null
&:将该符号前的命令放入后台执行,一般针对那些可持续执行的命令,瞬间执行的没必要 
ctrl+z是将命令放在后台但状态是暂停,也不会用这个变量看到

位置参数变量

位置参数变量:根据调用脚本时传入参数值的位置,进行变量赋值和取值

位置参数变量作用
$nn为数字,$0代表命令本身,$1-$9代表第一到第九个参数,十以上的参数要用大 括号包含例 ${10}
$*代表命令行中所有的参数,不包含$0,会把所有的参数看成一个整体,不可遍历
$@代表命令行中所有参数,不包含$0,会把每个参数看成单独的元素,可遍历
$#这个变量代表命令行中所有参数的个数,不包含命令本身
#编写脚本,输出各个参数
$ vim cs1.sh
#!/bin/bash
echo "输出\$n的结果:"
echo $0
echo $1
echo $2
echo $3
echo "输出\$#的结果:"
echo $#
echo "输出\$*的结果:"
echo $*
echo "输出\$@的结果:"
echo $@
#给脚本加执行权限
$ chmod +x cs1.sh
#调用脚本,传入几个参数值
$ ./cs1.sh 1 a b ff
输出$n的结果:
./cs1.sh
1
a
b
输出$#的结果:
4
输出$*的结果:
1 a b ff
输出$@的结果:
1 a b ff
------------------------------------------
#区分$*和$@,使用for循环
$ vim for1.sh
#!/bin/bash
echo "ni"
echo "hao"
echo "ya"
echo "nihaoya~"
echo "------------------"
for i in ni hao ya nihaoya~
do
echo $i
done--------------------------
$ vim cs2.sh
#!/bin/bash
echo "共有参数:"$#
echo "for循环\$@接收的参数:"
for i in "$@"
do
echo "@:"$i
done
echo "for循环\$*接收的参数:"
for j in "$*"
do
echo "*:"$j
done
=>
$*显示所有参数(作为整体接收并传递到脚本内)
$@显示所有参数(分开接收,依次传递到脚本内)
$#显示参数个数
read 接受键盘输入,完成交互式操作
语法:read [选项]  [变量名] 
选项:
    -p “提示信息”:在read等待时输入的信息 
    -t 秒数:read等待的秒数 
    -s 隐藏输入信息
    -n 字符数:read最多能接收的字符数(达标即执行) 
    
#测试read接收脚本传值
$ vim cs3.sh
#!/bin/bash
read -p "Please enter your name:" name
read -t 10 -p "Please enter your age(10 seconds):" age
echo
read -n 1 -p "Please enter your gender(m/w):" gender
echo
read -s -p "Please enter your phone number:" pn
echo
#可以用echo不换行等价read -p
echo -n "Please enter your home address:"
read addr
echo -e "\e[33;1m******$name Personal Information *******\e[0m"
echo "Name:$name"
echo "Age:$age"
echo "Gender:$gender"
echo "Phone Number:$pn"
echo "Home Addres:$addr"
--------------------------------------------------
#可以同时接受多个参数,分别放在多个变量里
$ vim cs4.sh
#!/bin/bash
echo "Please enter your Name, Age, Gender, Phone Number, Home Address"
#接收变量的参数个数,可以尝试修改其个数,再次运行看效果
read name age gender ph addr
echo -e "Name:$name\nAge:$age\nGender:$gender\nPhone Number:$ph\nHome Adderss:$addr"

[!CAUTION]

注意:因为某些特殊选项会出现不自动换行的bug,请使用echo 来设置换行。
read变量名定义:

  • 变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY
  • 如果只提供了一个变量名,则整个输入全部赋予该变量
  • 如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有值

shell的常用运算符

声明指定类型的变量

数值运算需要使用declare声明变量类型

类型分类:整数型、数组型、环境变量、只读变量

命令:declare [+|-] [选项] 变量
    -    给变量设置类型
    +    取消变量类型
    --    将变量声明成字符串类型
    -i    将变量声明成整数型
    -a    将变量声明成数组型
    -x    将变量声明成环境变量
    -r    将变量声明成只读
    -p    显示指定变量的类型和内容
    
#查看变量类型
name=lisi
declare -p name
#整数型
#shell中的变量默认类型是字符串,两个字符串变量相加效果是字符串拼接;
declare -i a=100
declare -i b=200
或:
declare -i a=100 b=200
declare -i sum=$a+$b
echo $sum
=>类型自动识别、转换
#查看变量的类型
a2=100
declare -p a2
b2=200
declare -p b2
declare -i sum2=$a2+$b2
echo $sum2
int=int+int        运算
int=str+str        str都为整数则为运算;为非整数则=0
str=int+int        拼接
str=str+str        拼接
----------------------------
#数组型
#数组是多个相同类型的元素组成的,是有序的,可以通过下标进行操作;
#数组的定义及赋值,数组下标是从0开始的,范围是[0,数组长度-1]
declare -a tianqis
tianqis=(qing "duo yun" yu)
tianqis[0]='qing'
tianqis[1]='duo yun'
tianqis[2]='yu'
=>类型自动识别、转换
nums[0]=1
nums[1]=2
delcare -p nums
#取消或删除变量:
unset tianqis
#读数组里的值,格式:${数组[下标]}
echo $tianqis       --默认取数组中第一个值
echo ${tianqis[0]}  --取数组中第一个值
echo ${tianqis[1]}  --取数组中第二个值
echo ${tianqis[3]}  --取超过数组长度的值,为空字符串
#获取数组所有的值,*将所有值作为一个整体
echo ${tianqis[*]}
#获取数组所有的值,@可以将所有值分开
echo ${tianqis[@]}
#获取数组长度
echo ${#abc[@]}
或:
echo ${#abc[*]}
#获取元素长度
echo ${#abc[1]}
#获取所有值时,*和@的区别,同位置参数变量
[root@localhost ~]# cat for1.sh 
#!/bin/bash
tianqis=(a b c d)
echo ${tianqis[2]}
for i in "${tianqis[@]}"
do
echo "@:"$i
done
for i in "${tianqis[*]}"
do
echo "*:"$i
done
------------------------
#环境变量
#用declare -x命令把变量声明为环境变量,和export命令的作用是一样的
declare -x HA=haha
env | grep HA
unset HA
#与普通变量不同,环境变量也被称为全局变量,可以传到子shell中,但传子不传父
declare -i name=keji
declare -x AGE=3
bash
echo $name  -- 看不到值
echo $AGE
----------------------
#只读属性
#只要对变量设置了只读属性,这个变量只能进行调用,不能进行修改和删除,甚至不能进行取消只读选项
declare -r salary=10w
echo $salary
#以下都不能操作,报错:---bash: salary: 只读变量
salary=12w  
declare +r salary
unset salary   #退出终端可就没有了
------
#其他参数使用:
declare -rx T1="123"   #选项可以多个一起用
declare +x KU            
#+取消一些属性,不再是环境变量,但还存在

shell常用的运算符

优先级运算符功能描述
1+ -正 负
2()小括号
3# / %乘 除 取余
4+ -加 减
5+= -=自增 自减

[!NOTE]

% n 取余的结果会在 [0,n-1] 范围内

环境变量$RANDOM 可以用于生成一个[0, 32676]随机数

所以生成指定范围内的随机数可以用二者进行组合写成一个函数

#!/bin/bash

function rand(){
 min=$1
 max=$(($2-$min+1))
 num=$(($RANDOM+1000000000)) #增加一个10位的数再求余
 echo $(($num%$max+$min))
}

rnd=$(rand 400000 500000)
echo $rnd
exit 0
🎈1 使用expr进行数值运算
#注意在运算符两边要有空格,否则不会正常运算
x=1000
y=2000
expr $x + $y
3000
🎈2 使用let进行数值计算
#运算符左右不需要空格,但是需要声明一个新变量用于接收计算结果
x=1000
y=-2000
let he=$x+$y
echo $he-1000
#let还能进行自增减,或者指定增加量或减少量
#增加
n=10
let n++
echo $n
11
let n=$n+1
echo $n
12
let n+=1
echo $n
13
let n+=5
echo $n
18
#减
m=100
let m--
let m-=1
let m-=10
🎈3 使用$(())或$[]进行数值运算,使用$(())较多
unset x;unset y
x=100
y=200
echo $(($x+$y))
300
echo $[$x+$y]
300

------------ 
#测试运算符的顺序
echo $((16/5))
3
echo $((-16/5))-3
echo $((-16/-5))
3
echo $((16/-5))-3
#a%b,取余结果的符号和a一致
echo $((16%5))
1
echo $((-16%5))-1
echo $((-16%-5))-1
#对一个数n取余,余数是有范围的,即[0-(n-1)]
#指定范围的随机数,默认范围是[0,32767]
$ echo $RANDOM
#在0~10之间产生一个随机数
$ echo $(($RANDOM%11))
#在1~100之间产生一个随机数
$ echo $(($RANDOM%100+1))
#表格中的优先级是乘除优先于加减,但是若加减被()调用,则括号内的优先级高于乘除
n1=$(((11+22)*33/44))
echo $n1
24
#逻辑与(一般单独使用)
echo $((0&&0))
0
echo $((0&&1))
0
echo $((1&&0))
0
echo $((1&&1))
1

----------
#shell中处理的数值都是整数类型,若处理小数用bc
yum -y install bc
echo "2.5+3.5"|bc
6.0
echo "2.3*3"|bc
6.9

问题反馈 reeskysui@qq.com

Shell脚本 - 入门
https://reeskysui.xyz/index.php/archives/7/
本文作者 明关
发布时间 2025-04-08
许可协议 CC BY-NC-SA 4.0
发表新评论