[shell编程] 入门基础总结 (一) (一种豁然开朗的感觉)

Shell Shell脚本 终端

Shell

  • Shell其实是一种应用程序, 用来操控计算机的内核.
  • Shell分为图形化Shell和命令行Shell, 我们经常用的就是嵌套在终端里的命令行Shell.
  • Shell也是一个命令解释器, 用来解释Shell脚本语言
  • Shell只是一层壳, 将内核包裹在内部. 在外层接收用户命令, 通过解析命令告诉 内核 要执行哪些系统调用以及相关API操作.

Shell脚本(Shell Script)

  • Shell脚本语言和我们学习过的c, python, java一样都是一种编程语言, 也拥有自己的语法
  • Shell脚本是由Shell脚本语言编写而成的程序
  • 不同的Shell脚本语言比如bash ,zsh等等, 可以看做不同版本的java语言, 当然也需要对应不同版本的解释器
  • Shell脚本第一行要指定用哪一个版本的解释器解释该脚本, 格式#!/bin/bash
  • 我们常说的Shell编程其实是Shell脚本语言编程
  • Shell脚本语言的语句结束标志
    • 一行中使用分号;分隔语句
    • 多行中,使用换行符分隔, 每一行代表一个语句

终端

  • 早期的终端是一种硬件, 用来和计算机交换信息.
  • 用户通过终端输入命令, 终端将命令传递给计算机, 计算机接受命令进行处理后将结果返还给终端, 终端将结果显示给用户.
  • 后来计算机硬件一体化程度越来越高,输入输出设备完全没必要单独用一个硬件终端和计算机进行交换信息,取而代之的是使用软件终端和计算机进行信息交互, 称为终端模拟器, 也就是我们如今常见的终端.
  • 终端的功能其实只有是三个: 接收输入, 显示输出, 一个GUI界面

变量

变量的分类

按照生命周期分类

  • 永久变量: 不会随着shell进程的关闭而消失的变量, 永久存在. 需要在文件中设置
  • 临时变量: 仅在当前的shell进程中生效, 子进程或者其余进程中均不能访问, 当前shell关闭时, 该变量失效. 用户在命令行或脚本中自定义的变量

具体说明

说明前的预备知识
  • 运行一个shell脚本有三种方式:
    • source filename 在当前shell进程中执行, 不需要权限
    • bash/sh filename 打开一个subshell进程执行, 不需要权限
    • ./filename 打开一个subshell进程执行, 需要可执行权限
      • 使用ll命令查看文件权限, chmod命令修改权限
        • 权限分为三个类别owner, group, others, 每个类别分别由三个字符代表可读(r), 可写(w), 可执行(x)
          在这里插入图片描述
        • 更改权限的三种方法
          • 在某个类别增加或减少某个权限
            • chmod u+x / u-x case 在owner类别增加/减少可执行权限
            • chmod g+w / g-w case 在group类别增加/减少可写权限
            • chmod o+r / o-r case 在others类别增加/减少可读权限
          • 直接给所有类别同时增加
            • chmod +x / +r / +w case 给所有类别同时增加可执行/可读/可写权限
          • 使用二进制的方法, 每一个类别的三个字符都相当于三个二进制位, 7表示rwx, 6表示rw-, 5表示r-x以此类推
            • chmod 777 case 将所有类别都增加可读可写可执行权限, 最后成为rwxrwxrwx
            • chmod 642 case owner类别为rw-, group类别为r–, others类别为-w-
        • 在使用./filename的方法执行时, 需要先执行命令chmod u+x case给当前用户增加可执行权限

  • 关于subshell子进程
  1. 打开两个bash程序, 在其中一个使用 ps -ef | grep bash 命令可以看到, 当前有两个bash进程
    在这里插入图片描述
  2. 在其中一个bash程序中使用./case命令执行脚本case, (case 脚本中有read命令, 所以会等待键盘输入)
    在这里插入图片描述
  3. 此时在另一个bash程序中再次运行ps -ef | grep bash 命令, 可以发现多了一个bash子进程 (pid为81838, 这个其实是pid为59864进程的子进程, 从下图中的pid中可以看出这种关系, 也可以使用pstree -p更直观的查看)
    在这里插入图片描述在这里插入图片描述
  4. 当从键盘输入并使得case脚本运行完毕退出后, 在另一个bash程序中再次运行ps -ef | grep bash, 发现之前的那一个pid为81838的子进程也被杀死了
    在这里插入图片描述
    在这里插入图片描述
  5. 同理, bash filename执行脚本也是同样的情况, 所以说这两种方式执行脚本都是开启了一个subshell子进程来执行
  6. 使用上述同样的验证方法, 当使用source case的方法执行case脚本时, 会发现并没有产生subshell, 而是在当前的bash进程中执行该脚本

- 关于Shell脚本

当你打开一个shell程序的时候, 有两种方式执行命令, 一种是直接在命令行中键入, 另一种是写脚本. 脚本其实就可以看作是一组命令, 执行脚本, 其实就是在命令行中一次执行了一组命令.

两者的区别在于, 命令行中执行命令, 肯定是在当前的shell程序中执行; 而脚本执行, 可能是在当前的shell程序中执行(source filename), 也可能是新开了一个subshell, 在另一个shell程序中执行.



永久变量和临时变量
  • 永久变量一般在/etc/profile中或者在家目录的.bash_profile中设置, 二者的区别会在之后的环境变量中说明.

    在/etc/profile中设置一个永久变量, 最后一行添加export TESTCASE=‘testcse’, 并且使用source /etc/profile 使该文件生效.
    然后在case脚本文件的开头添加一行 echo $TESTCASE
    你会发现无论使用三种方法中的哪一种执行该脚本(无论是在subshell中执行, 还是当前shell中执行), 都会输出‘testcase’
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    即使你退出当前shell, 重新登录, 仍然可以使用这个变量echo $TESTCASE

上述例子说明了, 永久变量是会一直存在的, 不会因为shell进程的关闭而消失

  • 临时变量只能在当前创建它的shell进程中使用, 比如在命令行中创建一变量, 在命令行中输入TEMP=’temp‘; 并且在case脚本中开头再添加一行echo $TEMP

    在这里插入图片描述在这里插入图片描述
    当使用./casebash case两种方法执行脚本的时候, 只会显示我们之前设置的永久变量$TESTCASE的值testcase, 并没有显示临时变量$TEST的值
    当使用source case 执行脚本时, 会显示临时变量$TEMP的值temp
    在这里插入图片描述
    在这里插入图片描述
    退出当前shell, 重新登录, 在命令行输入echo $TEMP , 会发现为空, 说明之前设置的临时变量已经失效
    在这里插入图片描述

上述例子说明了, 临时变量只能在当前shell程序中生效, 在别的shell程序中无法访问, 一旦当前shell程序关闭, 改变量也被清除.

按照作用域分类

  • 系统环境变量: 对该系统中所有用户都生效. 在/etc/profile 中配置
  • 用户环境变量: 只对特定的用户生效. 在家目录的.bash_profile中配置

具体说明

  1. “环境” 可以理解为作用域的意思
  2. 环境变量都属于永久变量
  3. 这两种环境变量都是在文件中配置, 所以都是永久变量, 即使当前shell程序关闭, 仍然存在, 再次登录的时候仍然可以使用
  4. 用户环境变量如果和系统环境变量重名, 用户环境变量会覆盖系统环境变量
  5. 不同用户之间的用户环境变量不互相访问
  6. 每次修改/etc/profile和.bash_profile 时, 需要使用上述执行脚本的三种方法执行这两个文件使其立即生效, 或者退出后重新登录使文件生效.
  7. 使用env命令查看所有环境变量
  8. 使用set命令查看本地变量: 当前用户在当前shell进程中可以使用的所有变量, 包含env
  9. 一些常用的环境变量
    • PATH: 存储命令的搜索路径. 每执行一个命令时, 会在该路径中搜索, 如果搜索不到会报错.
    • UID: 存储当前用户的ID
    • PWD: 存储当前目录的路径
    • HOME: 当前用户的家目录(主工作目录)
    • PS1:命令基本提示符,对于root用户是#,对于普通用户是$
    • PS2:附属提示符,默认是“>“
    • SHELL: 当前使用的shell类型
    • HOStNAME: 主机名

变量的使用

注意事项:

  1. 变量的使用需要在变量前加$. echo $PATH
  2. 变量的赋值语句中, 赋值符号两边没有空格. TEMP='temp'
  3. 可以使用${}确定变量名范围, echo ${TEMP}AND 如果直接写echo $TEMPAND 就会混淆
  4. 一般情况下变量赋值不需要加引号, 可以直接写TEMP=temp, 但是如果等号右边赋值的字符串中含有空格, 则需要增加引号TEMP='good bye'因为shell是以默认以空格为分隔符
  5. 单引号和双引号的区别
    单引号: 原封不动的输出引号内的内容. T='${TEMP}232'会输出 ${TEMP}232
    双引号: 会将引号内的变量转换成相应的值.T="${TEMP}232" 会输出good bye232
  6. unset VAR删除已经设置的变量VAR, 如果删除的是永久变量, 当前shell进程将不再能使用该变量, 但是重新登录后仍然可以使用

位置变量和特殊变量

位置变量

执行一个脚本命令时, 比如./temp.sh a b c, 其中./temp称为命令名, 使用位置便利那个$0引用; a, b, c称为位置参数, 使用位置变量$1, $2, $3引用

定义: shell执行用户命令时, 会把命令的第一个字段称为命令名, 后面的字段称为参数 ( 字段使用空格分隔 )

示例如下
在这里插入图片描述
在这里插入图片描述

特殊变量

shell程序自带的不可修改的变量

  1. $# : 传递给脚本或者函数的参数个数
  2. $* : 传递给脚本或者参数的所有参数
  3. $? : 上个命令的退出状态,或函数的返回值
  4. $! : 上一个后台进程的PID
    • 在命令行中键入ping www.baidu.com > ping.txt &, 启动一个后台服务
    • 然后在命令行键入jobs -l查看后台进程PID
    • 最后键入echo $!查看上一个后台进程的PID, 发现两者一样
      在这里插入图片描述
  5. $$: 当前 Shell 进程的 ID; 对于 Shell 脚本,就是这些脚本所在的进程 ID.
    • 打开两个bash, 在其中一个bash中输入ps -ef | grep bash
      在这里插入图片描述
    • 在两个bash中分别输入echo$$, 可以知道两个bash分别对应的PID, 也就是当前Shell进程的ID
      在这里插入图片描述在这里插入图片描述
    • 在case脚本中开头增加一行echo $$, 然后执行case脚本./case, 在另一个bash中键入ps -ef | grep bash, 会发现echo $$在脚本中显示的是该脚本的PID在这里插入图片描述在这里插入图片描述

read expr test命令

read命令

含义

从键盘读取输入, 赋值给变量

扫描二维码关注公众号,回复: 10585508 查看本文章

格式

read 变量1 变量2 …

示例

在这里插入图片描述
注意: 键盘输入以空格为分隔符, 回车是结束标志, 未被输入赋值的变量默认为空
在这里插入图片描述

expr

含义

计算四则运算的表达式

格式

expr expression

注意

  1. expr 只能用来计算整数
  2. 算数表达式中运算符两边要有空格, 与赋值符号两边没有空格做区分
  3. *号因为在shell中有特殊含义, 因此在表示时需要转义\*
  4. \除 在shell中是整除, 即运算结果的小数部分会直接舍弃

示例

在这里插入图片描述

test

含义

用来测试字符串, 整数, 文件的一些属性

格式

测试整数

表达式 含义 简写
test int1 -eq int2 判断int1是否等于int2
[equal]
[ int1 -eq int2 ]
test int1 -gt int2 判断int1是否大于int2
[greater than]
[ int1 -gt int2 ]
test int1 -ge int2 判断int1是否大于等于int2
[greater equal]
[ int1 -ge int2 ]
test int1 -lt int2 判断int1是否小于int2
[less than]
[ int1 -lt int2 ]
test int1 -le int2 判断int1是否小于等于int2
[less equal]
[ int1 -le int2 ]
test int1 -ne int2 判断int1是否不等于int2
[not equal]
[ int1 -ne int2 ]

测试字符串

规定: 下面的str1和str2都是字符串字面量, 也可以使用字符串变量

表达式 含义 简写 备注
test str1 = str2 判断str1是否等于str2 [ str1 = str2 ] 等于为真
不等于为假
test str1 != str2 判断str1是否不等于str2 [ str1 != str2 ] 不等于为真
等于为假
test str1 判断str1是否存在 [ str1 ] 存在即非空为真
不存在即空为假
test !str1 判断str1是否不存在 [ !str1 ] 不存在即空为真
存在即不可为假
test -n str1 判断str1长度是否不为0
[nonzero]
[ -n str1 ] 非0为真
0为假
test -z str1 判断str1长度是否为0
[zero]
[ -z str1 ] 0为真
非0为假

测试文件

Linux中一切皆文件, 目录也是一种特殊的文件, 只不过它存放的是别的文件

表达式 含义 简写
test -d file 判断文件file是否为目录类型
[directory]
[ -d file ]
test -f file 判断文件file是否为文件类型
[file]
[ -f file ]
test -b file 判断文件file是否为块类型
[block]
[ -f file ]
test -c file 判断文件file是否为字符类型
[char]
[ -f file ]
test -x file 判断文件file是否可执行
[excute]
[ -x file ]
test -r file 判断文件file是否可读
[read]
[ -r file ]
test -w file 判断文件file是否可写
[write]
[ -w file ]
test -e file 判断文件file是否存在
[exist]
[ -e file ]
test -s file 判断文件file是否有内容
[size]
[ -s file ]

逻辑运算符

  • -a : 逻辑与, 仅当两个条件都成立时, 结果为真
    • [ exp1 -a exp2 ] (and)
  • -o :逻辑或, 两个条件有一个成立时, 结果为真
    • [ exp1 -o exp2 ] (or)

注意

  1. 简写模式前后都有空格, [ … ]

if条件语句

格式

只有if

if 条件
then
	命令块
fi

或者

if 条件 ; then
	命令块
fi

if … else …

if 条件
then
	命令块
else
	命令块
fi

或者

if 条件 ; then
	命令块1
else
	命令块2
fi

if … elif … elif … … else

if 条件1
then
	命令块1
elif 条件2
	命令块2
elif 条件3
	命令块3
...
else
	命令块n
fi

或者

if 条件1 ; then
	命令块1
elif 条件2 ; then
	命令块2
elif 条件3 ; then
	命令块3
...
else
	命令块n
fi

示例

只有if

在这里插入图片描述

if … else …

在这里插入图片描述
在这里插入图片描述

if … elif … elif … … else

在这里插入图片描述
注意: 这里不能以使用.来执行, 可能是文件名带了_的缘故
在这里插入图片描述

学习

  1. 阿里云大学Shell编程入门到精通
  2. 终端与Shell的区别
发布了4 篇原创文章 · 获赞 3 · 访问量 840

猜你喜欢

转载自blog.csdn.net/weixin_40996518/article/details/105369431