超级详细的 shell编程知识讲解 —— 第一部分(全文3万多字,看完之后不想成为高手也难!)

文章目录

    • 写在开头的话
  • shell编程入门
    • 00. 前述
    • 01. 基础知识
      • 1.1 编写一个简单的`sh`程序
      • 1.2 命令历史
      • 1.3 关于Linux执行命令时的`hash`概念
      • 1.4 bash常用快捷键
    • 02. 输入输出重定向
    • 03. 多命令顺序执行
    • 04. 管道符
    • 05. bash的变量和运算符
    • 06. Shell运算符
      • 数值运算的方法
      • Shell 常用运算符
      • 变量的测试与替换
    • 07. 环境变量配置文件
    • 07. 例题
      • 7.1 一行代码实现从1到10000的累加
      • 7.2 对传入的任意个位置参数进行累加
        • 演示
      • 7.3 shell脚本实现加减乘除
    • 写在最后的话:


这里是一段防爬虫文本,请读者忽略。
本文原创首发于CSDN,作者IDYS
博客首页:https://blog.csdn.net/weixin_41633902/
本文链接:https://blog.csdn.net/weixin_41633902/article/details/108446460
未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!


写在开头的话

  • 请记住:实践是掌握知识的最快方法
  • 如果你只是怀着看看的态度去快速浏览文章,而不去认认真真的把文章里面讲的任何一个知识点去实践一遍,那么你永远也掌握不了它
  • 生命不息,折腾不止!

shell编程入门

00. 前述

  • 查看Linux支持的shell类型
[root@dayuanshuai ~] cat /etc/shells 
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
  • echo 命令

    • -n:输出内容不换行
    • -e:开启反斜杠转义字符
  • 关闭TAB补全时的提示音

[root@dayuanshuai ~] vim /etc/inputrc 
# do not bell on tab-completion
# set bell-style none前面的\#号即可关闭TAB补全时的提示音
# set bell-style none

set meta-flag on
set input-meta on
set convert-meta off
set output-meta on

# Completed names which are symbolic links to
# directories have a slash appended.
  • echo可以输出带有颜色的字符
[root@dayuanshuai ~] echo -e "\e[1;33m abcd \e[0m"
 abcd 

\e[1是标准格式,代表颜色输出开始,\e[0m代表颜色输出结束,31m=红色,30m=黑色,32m=绿色,33m=黄色,34m=蓝色,35m=洋红,36m=青色,37m=白色

[root@dayuanshuai ~] echo -e "\e[1;45m idys \e[0m" 
 idys 

这条命令会给 idys 加入一个洋红色的背景。echo 可以使用的背景颜色如下:40m=黑色,41m=红色, 42m=绿色,43m=黄色,44m=蓝色,45m=洋红,46m=青色,47m=白色。


01. 基础知识

1.1 编写一个简单的sh程序

[dayuanshuai@dayuanshuai idys_learn_sh] vim ys_01_a_simple.sh
echo 
#!/bin/bash
# author: idys
# date:2020-5-18
# description: print a char
echo '-------------------- program start ---------------------------'
echo 'hello!,i am idys!'
echo '-------------------- program start ---------------------------'

# 检查是否有语法错误
[dayuanshuai@dayuanshuai idys_learn_sh] bash -n ys_01_a_simple.sh

执行程序
# 给程序添加执行权限
[dayuanshuai@dayuanshuai idys_learn_sh] chmod u+x ./ys_01_a_simple.sh 
[dayuanshuai@dayuanshuai idys_learn_sh] ./ys_01_a_simple.sh 
-------------------- program start ---------------------------
hello!,i am idys!
-------------------- program start ---------------------------
  • 执行脚本的方式
    • 赋予执行权限,直接运行
      • 当然运行时可以使用绝对路径,也可以使用相对路径运行
      • ./ys_01_a_simple.sh:相对路径
      • ~/idys_learn_sh/ys_01_a_simple.sh:绝对路径执行
    • 通过 Bash 调用执行脚本
      • 这种方法不需要赋予脚本执行权限
      • bash ys_01_a_simple.sh
[dayuanshuai@dayuanshuai idys_learn_sh] bash ys_01_a_simple.sh 
-------------------- program start ---------------------------
hello!,i am idys!
-------------------- program start ---------------------------

1.2 命令历史

  • history:历史命令查看

    • -c: 清空内存中的历史命令
    • -w:把缓存中的历史命令写入历史命令保存文件。如果不手工指定历史命令保存文件,则放入默认历史命令保存文件~/.bash_history中,这种方式相当于将内存中的历史命令直接覆盖保存至~/.bash_history文件中,如果你清空缓存中的命令再来执行history -w命令,那么~/.bash_history文件中以前保存信息都会清除
    • -a: 追加缓存中执行的历史命令到历史文件~/bash_history
    • -s:伪造历史命令,比如使用history -s mkdir mysql那么,会向历史命令中添加mkdir mysql这条命令,但是不会执行mkdir mysql命令
    • -r:读取历史文件中的所有命令到内存中的历史列表
    • -d #:删除缓存中记录的第多少条命令
  • 关于命令历史的几个环境变量

    • HISTSIZE:定义内存中历史命令保存条数
    • HISTFILESIZE:定义命令历史文件中保存命令的条数
    • HISTFILE:定义命令历史文件保存的位置
[dayuanshuai@dayuanshuai idys_learn_sh] set | grep HIST
HISTCONTROL=ignoredups
HISTFILE=/home/dayuanshuai/.bash_history
HISTFILESIZE=1000
HISTSIZE=1000
  • 历史命令的调用
    • 如果想要使用原先的历史命令有这样几种方法
    • 使用上、下箭头调用以前的历史命令
    • 使用!n执行第 n 条历史命令
    • 使用!!执行上一条命令
    • 使用!STRING执行最后一条以该字串开头的命令
    • 使用!$上一条命令的最后一个参数
  • 关于符号.
    • . /.bashrc这个前面的. 相当于source
    • 文件名开头的点.bashrc相当于隐藏文件
    • ./mydir:这个斜杠/前面的.相当于当前目录下
  • 关于Linux命令执行时的顺序:
  1. 第一顺位执行用绝对路径或相对路径执行的命令
  2. 第二顺位执行别名
  3. 第三顺位执行 bash 的内部命令
  4. 第四顺位执行按照$PATH 环境变量定义的目录查找顺序,执行找到的第一个命令
  • 为了让这个别名永久生效,可以把别名写入环境变量配置文件~/.bashrc。命令如下:
vi /root/.bashrc

1.3 关于Linux执行命令时的hash概念

  • linux系统下会有一个hash表,当你刚开机时这个hash表为空,每当你执行过一条命令时,hash表会记录下这条命令的路径,就相当于缓存一样。第一次执行命令shell解释器默认的会从PATH路径下寻找该命令的路径,当你第二次使用该命令时,shell解释器首先会查看hash表,没有该命令才会去PATH路径下寻找。hash表的作用:大大提高命令的调用效率。

同时需要注意的是:如果hash表中记录了该命令的映射时,那么再次执行该命令时,linux会直接从hash表中取出映射对应的命令所在位置,而不是去$PATH环境变量中逐个查找命令文件。如果我们把一个已经执行过的命令从环境变量的一个目录位置移到另外一个位置,虽然都是在环境变量中,但是当我们再次执行命令时,Linux就会报错,因为它已经在hash表中建立映射路径,当那个路径中没有这个命令文件时,它就会直接报错,而不是到环境变量中再查找执行命令。

# 首先执行cat命令
[root@dayuanshuai ~] cat /etc/profile
# /etc/profile

# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/
..... 后面的省略

# 查看hash表,发现hash表中有cat对于的命令执行路径
[root@dayuanshuai ~] hash -l
builtin hash -p /usr/bin/pstree pstree
builtin hash -p /usr/bin/head head
builtin hash -p /bin/rm rm
builtin hash -p /bin/cat cat
builtin hash -p /bin/sh sh
builtin hash -p /bin/cp cp
builtin hash -p /usr/bin/man man
builtin hash -p /bin/ls ls
builtin hash -p /bin/su su


# 打印PATH环境变量的值
[root@dayuanshuai ~] echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

# 将cat命令文件从/bin移动到/usr/bin下,注意/bin和/usr/bin都在环境变量中
[root@dayuanshuai ~] mv /bin/cat /usr/bin/

# 此时再次执行cat就会报错
[root@dayuanshuai ~] cat /etc/profile
-bash: /bin/cat: 没有那个文件或目录

# 清空hash表再次执行cat就会成功
[root@dayuanshuai ~] hash -r

# 可以看到cat执行成功
[root@dayuanshuai ~] cat /etc/issue
CentOS release 6.10 (Final)
Kernel \r on an \m
  • 关于hash的几个命令参数
    • -l 显示hash表内容
    • -r 清除hash表
    • -d cat 删除表中某一条(删除cat)
    • -t cat 查看openssl命令路径(hash表中没有的话,可以调用which命令)
    • -p /bin/cat mycathash表中添加一条,执行mycat即执行cat命令(起别名)

1.4 bash常用快捷键

快捷键 作用
ctrl+A 把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移 动到命令行开头时使用
ctrl+E 把光标移动到命令行结尾
ctrl+C 强制终止当前的命令
ctrl+L 清屏,相当于 clear 命令
ctrl+U 删除或剪切光标之前的命令
ctrl+K 删除或剪切光标之后的内容
ctrl+Y 粘贴 ctrl+U 或 ctrl+K 剪切的内容
ctrl+R 在历史命令中搜索,按下 ctrl+R 之后,就会出现搜索界面,只要输入搜索内容,就会从历史命令中搜索
ctrl+D 退出当前终端
ctrl+Z 暂停,并放入后台
ctrl+S 暂停屏幕输出
ctrl+Q 恢复屏幕输出

02. 输入输出重定向

  1. Bash的标准输入与输出
设备 设备文件名 文件描述符 类型
键盘 /dev/stdin 0 标准输入
显示器 /dev/stdout 1 标准输出
显示器 /dev/stdout 2 标准错误输出
  1. 输出重定向
  • 标准输出重定向
    • 命令 > 文件
    • 命令 >> 文件
  • 标准错误输出重定向
    • 错误命令 2> 文件
    • 错误命令 2>> 文件
  • 正确输出和错误输出同时保存
    • 命令 > 文件 2>&1
    • 命令 >> 文件 2>&1
    • 命令 &> 文件
    • 命令 &> 文件
    • 命令 &>> 文件
    • 命令 1>> 文件 2>>文件2
  1. 输入重定向
  • wc [OPTIONS] [FILENAME]
    • -c 统计字节数
    • -w 统计单词数
    • -l统计行数
# 统计两个EOF之间的行数,单词数,字节数(用EOF做分隔)
[dayuanshuai@dayuanshuai idys_learn_sh] wc << EOF
> who am i
> your name
> is 
> and
> EOF
 4  7 27

03. 多命令顺序执行

多命令执行符 格式 作用
; 命令1;命令2 多个命令顺序执行,命令之间没有任何逻辑联系
&& 命令1 && 命令2 当命令1 正确执行($?=0),则命令2 才会执行
当命令 1 执行不正确($? ≠ 0),则命令 2 不会执行
|| 命令1 || 命令2 当命令 1 执行不正确($? ≠ 0),则命令 2 才会执行
当命令 1 正确执行($?=0),则命令 2 不会执行
  • 示例
#命令正确执行显示yes,错误执行显示no
[dayuanshuai@dayuanshuai idys_learn_sh] whoami1 &>/dev/null && echo yes || echo no
no
[dayuanshuai@dayuanshuai idys_learn_sh] whoami &>/dev/null && echo yes || echo no 
yes

04. 管道符

  1. 行提取命令grep
  • 参数选项
选项 含义
-A # 列出符合条件的行,并列出后续的n行
-B # 列出符合条件的行,并列出前面的n行
-c 统计找到的符合条件的字符串的次数
-i 忽略大小写
-n 输出行号
-v 反向查找
–color=auto 搜索出的关键词用颜色显示
  • 示例
# 查找不含有 /bin/bash的用户,其实就是查找伪用户
[dayuanshuai@dayuanshuai idys_learn_sh] grep --color -v "/bin/bash" /etc/passwd

  1. find与grep
  • find 命令是在系统当中搜索符合条件的文件名,如果需要模糊查询,使用通配符(通配符我们下 一小节进行介绍)进行匹配,搜索时文件名是完全匹配。
# 查找/etc/ 下以.conf结尾,大于5k,的普通文件
[dayuanshuai@dayuanshuai idys_learn_sh] find /etc/ -type f -size +5k -name "*.conf" -exec ls -lh {
     } \; &>./2.txt | grep -v "Permission denied" ./2.txt 
-rw-r--r--. 1 root root 5.8K Mar 22  2017 /etc/modprobe.d/dist.conf
-rw-r--r--. 1 root root 5.3K May 27  2014 /etc/fonts/fonts.conf
-rw-r--r--. 1 root root 7.6K May 27  2014 /etc/fonts/conf.avail/65-nonlatin.conf
-rw-r--r--. 1 root root 9.7K May 27  2014 /etc/fonts/conf.avail/65-fonts-persian.conf
-rw-r--r--. 1 root root 83K Nov 15  2017 /etc/lvm/lvm.conf
-rw-r--r--. 1 root root 7.2K May 11  2016 /etc/dbus-1/system.d/ConsoleKit.conf
-rw-r--r--. 1 root root 8.0K Aug 31  2019 /etc/kdump.conf

find 命令是可以通过-regex 选项识别正则表达式规则的,也就是说 find 命令可以按照正则表达式规则匹配,而正则表达式是模糊匹配。

  • grep 命令是在文件当中搜索符合条件的字符串,如果需要模糊查询,使用正则表达式进行匹配, 搜索时字符串是包含匹配。
# 查看服务器建立的tcp连接
[dayuanshuai@dayuanshuai idys_learn_sh] netstat -ant | grep -i establish
Active Internet connections (servers and established)
tcp        0     96 192.168.200.141:22          192.168.200.1:10138         ESTABLISHED 
  • 通配符
通配符 作用
? 匹配一个任意字符
* 匹配 0 个或任意多个任意字符,也就是可以匹配任何内容
[] 匹配中括号中任意一个字符。例如:[abc]代表一定匹配一个字符,或者是 a,或者是 b,或者是 c。
[-] 匹配中括号中任意一个字符,-代表一个范围。例如:[a-z]代表匹配一 个小写字母。
[^] 逻辑非,表示匹配不是中括号内的一个字符。例如:[^0-9]代表匹配一 个不是数字的字符。
  • bash中其他特殊符号
符号 作用
’ ’ 单引号。在单引号中所有的特殊符号,如$和"`"(反引号)都没有特殊含义。
“” 双引号。在双引号中特殊符号都没有特殊含义,但是“$”、“`”和“\” 是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。
`` 反引号。反引号括起来的内容是系统命令,在 Bash 中会先执行它。和$() 作用一样,不过推荐使用$(),因为反引号非常容易看错。
$() 和反引号作用一样,用来引用系统命令。
{} 用于一串命令执行时,{}中的命令会在当前 Shell 中执行。也可以用于变量变形与替换。
() 用于一串命令执行时,()中的命令会在子 Shell 中运行
[] 用于变量的测试。
# 在 Shell 脚本中,#开头的行代表注释。
$ 用于调用变量的值,如需要调用变量 name 的值时,需要用$name的方式得到变量的值。
\ 转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如\$将输出“$”符号,而不当做是变量引用。
  • 关于(){}大区别
# 给变量myname赋值idys
[root@dayuanshuai ~] myname=idys
# 给该shell的子shell赋值dayuanshuai,然后再打印输出
[root@dayuanshuai ~] ( myname=dayuanshuai;echo $myname )
dayuanshuai

# 这时你再打印myname的值,你会发现myname的值仍然为idys
[root@dayuanshuai ~] echo $myname
idys

# 通过大括号圈起来,将 dayuanshuai赋值给myname
[root@dayuanshuai ~] {
      myname=dayuanshuai;echo $myname;}
dayuanshuai

# 这次再次打印 myname 你会发现myname的值已经变为dayuanshuai了
[root@dayuanshuai ~] echo $myname
dayuanshuai

()最后一个命令可以不用分号“;”,而{}中最后一个命令必须用“ ;”


05. bash的变量和运算符

  1. 变量的定义
  • 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是“2age” 则是错误的。
  • 在 Bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为数值型。
  • 变量用等号连接值,等号左右两侧不能有空格
  • 变量的值如果有空格,需要使用单引号或双引号包括。如:“test=“hello world!””。其中双引号括起来的内容“$”、“\”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
  • 在变量的值中,可以使用“\”转义符
  • 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含"$变量名"或用${变量名}包含变量名
[root@dayuanshuai ~] words="who am"
[root@dayuanshuai ~] echo $words
who am
[root@dayuanshuai ~] words="$words i"
[root@dayuanshuai ~] echo $words
who am i
[root@dayuanshuai ~] words=${words}hhh
[root@dayuanshuai ~] echo $words
who am ihhh

变量值的叠加可以使用两种格式:“$变量名”${变量名}

  • 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令。如:
[root@dayuanshuai ~] linux_release=`cat /etc/centos-release`
[root@dayuanshuai ~] echo $linux_release 
CentOS release 6.10 (Final)
[root@dayuanshuai ~] linux_release_2=$( cat /etc/issue )
[root@dayuanshuai ~] echo $linux_release_2 
CentOS release 6.10 (Final) Kernel \r on an \m

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

  1. set命令
选项 作用
-u 如果设定此选项,调用未声明变量时会报错(默认无任何提示)
-x 如果设定此选项,在命令执行之前,会把命令先输出一次
set不加任何选项 直接使用set命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
# 因为myage变量没有定义,所以打印myage没有任何输出
[root@dayuanshuai ~] echo $myage

# 使用set -u 选项,当调用没有声明的变量时会报错
[root@dayuanshuai ~] set -u
[root@dayuanshuai ~] echo $myage
-bash: myage: unbound variable

# 取消set的u选项
[root@dayuanshuai ~] set +u

# 设置 x 选项
[root@dayuanshuai ~] set -x

# 此时执行ll命令时会把命令输出一遍
[root@dayuanshuai ~] ll
+ ls --color=auto -l --color=auto
total 24
-rw-------. 1 root root   943 Aug 31  2019 anaconda-ks.cfg
-rw-r--r--. 1 root root 13195 Aug 31  2019 install.log
-rw-r--r--. 1 root root  3482 Aug 31  2019 install.log.syslog
  • env:查看环境变量(不包括自定义变量)
[root@dayuanshuai ~] env
HOSTNAME=dayuanshuai
SELINUX_ROLE_REQUESTED=
TERM=linux
  • unset删除环境变量
[root@dayuanshuai ~] myname=idys
[root@dayuanshuai ~] echo $myname
idys
[root@dayuanshuai ~] unset myname
[root@dayuanshuai ~] echo $myname


  1. 环境变量
  • 环境变量设置
    • 使用export即可声明变量为环境变量
  • 环境变量与用户自定义变量的区别:
    • 环境变量可以在子shell中生效
    • 用户自定义变量只能在当前shell中生效
# 定义环境变量
[root@dayuanshuai ~] export myname=idys
# 定义用户自定义变量
[root@dayuanshuai ~] age=18

# 在当前shell中打印age和myname的值
[root@dayuanshuai ~] echo $age
18
[root@dayuanshuai ~] echo $myname
idys

# 在子shell中再次打印age和myname的值,发现age的值消失了
[root@dayuanshuai ~] bash
[root@dayuanshuai ~] echo $age

[root@dayuanshuai ~] echo $myname
idys
  • env命令可以查询到所有的环境变量,可是还有一些变量虽然不是环境变量,却是和 Bash 操作接口相关的变量,这些变量也对我们的 Bash 操作终端起到了重要的作用。这些变量就只能用 set 命令来查看了
[root@dayuanshuai ~] set 
BASH=/bin/bash        # 所使用的bash shell类型
BASH_VERSION='4.1.2(2)-release'  #bash版本
COLORS=/etc/DIR_COLORS  #颜色记录文件
  • PATH 变量:系统查找命令的路径
  • PS1 变量:命令提示符设置,PS1 可以支持以下这些选项:
选项 含义
\d 显示日期,格式为“星期 月 日”
\H 显示完整的主机名。如默认主机名“localhost.localdomain”
\h 显示简写主机名。如默认主机名“localhost”
\t 显示 24 小时制时间,格式为“HH:MM:SS”
\A 显示 24 小时制时间,格式为“HH:MM”
\T 显示 12 小时制时间,格式为“HH:MM:SS”
\@ 格式为“HH:MM am/pm”
\u 显示当前用户名
\v 显示 Bash 的版本信息
\w 显示当前所在目录的完整名称
\W 显示当前所在目录的最后一个目录
\# 执行的第几个命令
\$ 提示符。如果是 root 用户会显示提示符为“#”,如果是普通用户会显示提示符为"$"

查看PS1的值

[root@IDYS /]# echo $PS1
[\u@\h \W]\$

修改PS1的值

[root@IDYS /]# PS1='[\u@\H \w]\$ '

环境变量配置文件有

  • /etc/bashrc
  • ~/.bashrc
  • /etc/profile
  • /etc/profile.d/*.sh
  • ~/.bashrc_profile

修改Linux系统默认使用的语言

[root@IDYS /usr/local/src]# ls -l /root
总用量 20
-rw-------. 1 root root  921 4月  28 17:32 anaconda-ks.cfg
-rw-r--r--. 1 root root 9430 4月  28 17:32 install.log
-rw-r--r--. 1 root root 3161 4月  28 17:31 install.log.syslog
[root@IDYS /usr/local/src]# echo $LANG  #查看当前系统使用的默认语言
zh_CN.utf8
[root@IDYS /usr/local/src]# LANG='en_US.utf8'  # 修改系统默认语言
[root@IDYS /usr/local/src]# ls -l /root  # 现在再次查看显示效果
total 20
-rw-------. 1 root root  921 Apr 28 17:32 anaconda-ks.cfg
-rw-r--r--. 1 root root 9430 Apr 28 17:32 install.log
-rw-r--r--. 1 root root 3161 Apr 28 17:31 install.log.syslog

  1. 位置参数变量
位置参数变量 作用
$n n 为数字,$0 代表命令本身,$1 - $9 代表第一到第九个参数,十以上的参数需要用大括号包含,如${10}.
$* 这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体
$@ 这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
$# 这个变量代表命令行中所有参数的个数

  • $():括系统命令
  • $(()):用于执行变量加减,复制

  • 如何区分$@$*的区别,以下我通过代码的演示区别
#!/bin/bash
# encoding=utf-8

function comtwo()
{
     
echo "**********************使用\"\$*\"*************************"
for i in "$*"
do
    echo "$i"
done
echo ""
echo "**********************使用修改后的\"\$*\"*****************"
for i in "$*"
do
    echo "$i" | tr " " "\n"
done
echo ""
echo "**********************使用\"\$@\"*************************"
for i in "$@"
do
    echo "$i"
done
}
echo ""
comtwo $@
  • 演示情况,结果
[dayuanshuai@IDYS sh_day01]$ bash comtwo.sh 11 22 33 44 55 66

**********************使用"$*"*************************
11 22 33 44 55 66

**********************使用修改后的"$*"*****************
11
22
33
44
55
66

**********************使用"$@"*************************
11
22
33
44
55
66

  1. 预定义变量
预定义变量 作 用
$? 最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
$$ 当前进程的进程号(PID)
$! 后台运行的最后一个进程的进程号(PID)
  • 演示
[dayuanshuai@IDYS sh_day01]$ ls ./
comtwo.sh  count.sh  countv2.sh  testnet.sh
[dayuanshuai@IDYS sh_day01]$ echo $?
0
[dayuanshuai@IDYS sh_day01]$ ls asdas
ls: cannot access asdas: No such file or directory
[dayuanshuai@IDYS sh_day01]$ echo $?
2
[dayuanshuai@IDYS sh_day01]$ echo $$
5712
[dayuanshuai@IDYS sh_day01]$ top &
[1] 5906
[dayuanshuai@IDYS sh_day01]$ echo $!
5906

[1]+  Stopped                 top
[dayuanshuai@IDYS sh_day01]$ ps 
   PID TTY          TIME CMD
  5712 pts/1    00:00:00 bash
  5906 pts/1    00:00:00 top
  5907 pts/1    00:00:00 ps

  1. 接受键盘输入
  • read 命令

  • read [选项] [变量名]

  • [选项]

    • -p “提示信息”: 在等待 read 输入时,输出提示信息
    • -t 秒数: read 命令会一直等待用户输入,使用此选项可以指定等待时间
    • -n 字符数: read 命令只接受指定的字符数,就会执行
    • -s: 隐藏输入的数据,适用于机密信息的输入
  • 变量名:

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

06. Shell运算符

数值运算的方法

如果我需要进行数值运算,可以采用以下三种方法中的任意一种:

  • 使用 declare 声明变量类型

既然所有变量的默认类型是字符串型,那么只要我们把变量声明为整数型不就可以运算了吗?使用declare命令就可以实现声明变量的类型。命令如下:

  • declare [+/-] [选项] 变量名
  • 选项:
选项 含义
- 给变量设定类型属性
+ 取消变量的类型属性
-a 将变量声明为数组型
-i 将变量声明为整数型(integer)
-r 讲变量声明为只读变量。注意,一旦设置为只读变量,
既不能修改变量的值, 也不能删除变量,甚至不能通过+r 取消只读属性
-x 将变量声明为环境变量
-p 显示指定变量的被声明的类型

  • 数值运算
    • 那我们只要把变量声明为整数型
[root@IDYS ~] num1=11
[root@IDYS ~] num2=22
[root@IDYS ~] declare -i sum=num1+num2
[root@IDYS ~] echo $sum
33

  • 定义数组
[dayuanshuai@IDYS sh_day01]$ system[0]=unix
[dayuanshuai@IDYS sh_day01]$ system[1]=linux
[dayuanshuai@IDYS sh_day01]$ system[2]=Windows
# 定义一个数组,第一个元素为unix,第二个元素Linux,第三个元素Windows
[dayuanshuai@IDYS sh_day01]$ echo ${system}
unix
[dayuanshuai@IDYS sh_day01]$ echo ${system[0]}
unix
[dayuanshuai@IDYS sh_day01]$ echo ${system[2]}
Windows
[dayuanshuai@IDYS sh_day01]$ echo system[0]
system[0]
  • 环境变量

我们其实也可以使用 declare 命令把变量声明为环境变量,和 export 命令的作用是一样的:

[dayuanshuai@IDYS sh_day01]$ declare -p test  #查看test变量的被声明类型
-bash: declare: test: not found
[dayuanshuai@IDYS sh_day01]$ declare -x test="123"  #将test 声明为环境变量
[dayuanshuai@IDYS sh_day01]$ declare -p test      
declare -x test="123"
[dayuanshuai@IDYS sh_day01]$ declare +x test="123" #取消test为环境变量
[dayuanshuai@IDYS sh_day01]$ declare -p test      
declare -- test="123"
[dayuanshuai@IDYS sh_day01]$ unset test  #取消变量声明
[dayuanshuai@IDYS sh_day01]$ declare -p test
-bash: declare: test: not found
[dayuanshuai@IDYS sh_day01]$ export test="456"  #将test设为环境变量
[dayuanshuai@IDYS sh_day01]$ declare -p test
declare -x test="456"
  • 只读属性
[dayuanshuai@IDYS sh_day01]$ declare -ar test="0" #将test设置为只读
-bash: test: readonly variable
[dayuanshuai@IDYS sh_day01]$ test=23
-bash: test: readonly variable
[dayuanshuai@IDYS sh_day01]$ echo $test

[dayuanshuai@IDYS sh_day01]$ declare -p test
declare -ar test='()'
[dayuanshuai@IDYS sh_day01]$ declare +r test
-bash: declare: test: readonly variable
[dayuanshuai@IDYS sh_day01]$ unset test     
-bash: unset: test: cannot unset: readonly variable

重新登录或重启,这个变量就会消失了。

  • 查询变量属性和取消变量属性
[dayuanshuai@IDYS sh_day01]$ declare -p
***************
declare -x TERM="linux"
declare -ir UID="500"
declare -x USER="dayuanshuai"
declare -- _="test"
declare -- colors="/etc/DIR_COLORS"
declare -- num="0"
declare -- sum="0"
declare -a system='([0]="unix" [1]="linux" [2]="Windows")'
declare -ar test='()'
[dayuanshuai@IDYS sh_day01]$ declare -p test
declare -ar test='()'
[dayuanshuai@IDYS sh_day01]$ declare -p system
declare -a system='([0]="unix" [1]="linux" [2]="Windows")'

  • 使用 exprlet 数值运算工具
[dayuanshuai@IDYS ~]$ num1=11
[dayuanshuai@IDYS ~]$ num2=22
[dayuanshuai@IDYS ~]$ dd=$(expr $num1 + $num2) #dd 的值是 num1 和 num2 的和。注意“+”号左右两侧必须有空格
[dayuanshuai@IDYS ~]$ echo $dd
33

使用 expr 命令进行运算时,要注意+号左右两侧必须有空格,否则运算不执行。 至于 let 命令和 expr 命令基本类似,都是 Linux 中的运算命令,命令格式如下


[dayuanshuai@IDYS ~]$ num1=11
[dayuanshuai@IDYS ~]$ num2=22
[dayuanshuai@IDYS ~]$ let sum=$num1+$num2  
[dayuanshuai@IDYS ~]$ echo $sum
33
[dayuanshuai@IDYS ~]$ n=20
[dayuanshuai@IDYS ~]$ let n+=1
[dayuanshuai@IDYS ~]$ echo $n
21

expr 命令和 let 命令大家可以按照习惯使用,不过 let 命令对格式要求要比 expr 命令宽松,所 以推荐使用 let 命令进行数值运算。


  • 使用$((运算式))$[运算式]方式运算 其实这是一种方式$(())$[]这两种括号按照个人习惯使用即可。命令如下
[dayuanshuai@IDYS ~]$ num1=11
[dayuanshuai@IDYS ~]$ num2=22
[dayuanshuai@IDYS ~]$ sum1=$(( $num1+$num2 ))
[dayuanshuai@IDYS ~]$ echo $sum1
33
[dayuanshuai@IDYS ~]$ sum2=$[ $num1+$num2 ]
[dayuanshuai@IDYS ~]$ echo $sum2
33

推荐使用$((运算式))


Shell 常用运算符

优先级 运算符 说明
13 -, + 单目负、单目正
12 !, ~ 逻辑非、按位取反或补码
11 * , / , % 乘、除、取模
10 +, - 加、减
9 << , >> 按位左移、按位右移
8 < =, > =, < , > 小于或等于、大于或等于、小于、大于
7 == , != 等于、不等于
6 & 按位与
5 ^ 按位异或
4 | 按位或
3 && 逻辑与
2 || 逻辑或
1 =,+=,-=,*=,/=,%=,&=, ^=,=, <<=, >>= 赋值、运算且赋值

运算符优先级表明在每个表达式或子表达式中哪一个运算对象首先被求值,数值越大优先级越高,具有较高优先级级别的运算符先于较低级别的运算符进行求值运算。

  • 加减乘除
[dayuanshuai@IDYS ~]$ aa=$(( (11+3)*3/2 ))  
[dayuanshuai@IDYS ~]$ echo $aa
21
  • 取模
[dayuanshuai@IDYS ~]$ bb=$(( 17 % 3 ))
[dayuanshuai@IDYS ~]$ echo $bb
2
  • 逻辑与
[dayuanshuai@IDYS ~]$ cc=$(( 11 && 1 ))
[dayuanshuai@IDYS ~]$ echo $cc
1

变量的测试与替换

  • 表格
变量置换方式 变量 y没有设置 变量 y 为空值 变量 y 设置值
x=${y-新值} x=新值 x 为空 x=$y
x=${y:-新值} x=新值 x=新值 x=$y
x=${y+新值} x 为空 x=新值 x=新值
x=${y:+新值} x 为空 x 为空 x=新值
x=${y=新值} x=新值
y=新值
x 为空
y 值不变
x=$y
y 值不变
x=${y:=新值} x=新值
y=新值
x=新值
y=新值
x=$y
y 值不变
x=${y?新值} 新值输出到标准错误 输出(就是屏幕) x 为空 x=$y
x=${y:?新值} 新值输出到标准错误 输出 新值输出到标准错误输出 x=$y
  • 如果大括号内没有,则变量 y 是为空,还是没有设置,处理方法是不同的;如果大括号内有,则变量y 不论是为空,还是没有设置,处理方法是一样的。

  • 如果大括号内是-+,则在改变变量 x 值的时候,变量 y 是不改变的;如果大括号内是 =,则在改变变量 x 值的同时,变量 y 的值也会改变。

  • 如果大括号内是,则当变量 y 不存在或为空时,会把新值当成报错输出到屏幕上


  • 演示
[dayuanshuai@IDYS ~]$ x=${y-3}
[dayuanshuai@IDYS ~]$ echo $y

[dayuanshuai@IDYS ~]$ echo $x
3
[dayuanshuai@IDYS ~]$ y="50"
[dayuanshuai@IDYS ~]$ x=${y-3}
[dayuanshuai@IDYS ~]$ echo $x
50
[dayuanshuai@IDYS ~]$ echo $y
50

# 换另外一种形式  := 测试
[dayuanshuai@IDYS ~]$ x=${y:=10}
[dayuanshuai@IDYS ~]$ echo $x
10
[dayuanshuai@IDYS ~]$ echo $y
10
[dayuanshuai@IDYS ~]$ y=200
[dayuanshuai@IDYS ~]$ x=${y:=300}
[dayuanshuai@IDYS ~]$ echo $x
200
[dayuanshuai@IDYS ~]$ echo $y
200

# 使用  ? 继续测试
[dayuanshuai@IDYS ~]$ x=${y?4}
-bash: y: 4
[dayuanshuai@IDYS ~]$ echo $y

[dayuanshuai@IDYS ~]$ echo $x

[dayuanshuai@IDYS ~]$ y="who"
[dayuanshuai@IDYS ~]$ x=${y?4}
[dayuanshuai@IDYS ~]$ echo $x
who
[dayuanshuai@IDYS ~]$ echo $y
who

07. 环境变量配置文件

  1. source 命令
[dayuanshuai@IDYS ~]$ source 配置文件
[dayuanshuai@IDYS ~]$ . 配置文件

  1. 环境变量配置文件
  • 登录时生效的环境变量配置文件 在 Linux 系统登录时主要生效的环境变量配置文件有以下五个:
    • /etc/profile
    • /etc/profile.d/*.sh
    • ~/.bash_profile
    • ~/.bashrc
    • /etc/bashrc
  • 环境变量配置文件调用过程

超级详细的 shell编程知识讲解 —— 第一部分(全文3万多字,看完之后不想成为高手也难!)_第1张图片


  • 在用户登录过程先调用/etc/profile文件

  • 在这个环境变量配置文件中会定义这些默认环境变量:

    • USER 变量:根据登录的用户,给这个变量赋值(就是让 USER 变量的值是当前用户)
    • LOGNAME 变量:根据 USER 变量的值,给这个变量赋值。
    • MAIL 变量:根据登录的用户,定义用户的邮箱为/var/spool/mail/用户名。
    • PATH 变量:根据登录用户的 UID 是否为 0,判断 PATH 变量是否包含/sbin/usr/sbin/usr/local/sbin 这三个系统命令目录。
    • HOSTNAME 变量:更加主机名,给这个变量赋值。
    • HISTSIZE 变量:定义历史命令的保存条数。
    • umask:定义 umask 默认权限。注意/etc/profile 文件中的 umask 权限是在“有用户登录过程(也就是输入了用户名和密码)”时才会生效。
    • 调用/etc/profile.d/*.sh 文件,也就是调用/etc/profile.d/目录下所有以.sh 结尾的 文件。
    • /etc/profile 文件调用/etc/profile.d/*.sh 文件 这个目录中所有以.sh 结尾的文件都会被/etc/profile 文件调用,这里最常用的就是 lang.sh 文件,而这个文件又会调用/etc/sysconfig/i18n 文件。/etc/sysconfig/i18n 这个文件眼熟吗?就是我们前面讲过的默认语系配置文件啊
    • /etc/profile 文件调用~/.bash_profile 文件 ~/.bash_profile 文件就没有那么复杂了,这个文件主要实现了两个功能:
      • 调用了~/.bashrc 文件。
      • PATH 变量后面加入了:$HOME/bin这个目录。那也就是说,如果我们在自己的家 目录中建立 bin 目录,然后把自己的脚本放入~/bin目录,就可以直接执行脚本, 而不用通过目录执行了。
    • ~/.bash_profile 文件调用~/.bashrc 文件在~/.bashrc文件中主要实现了:
      • 定义默认别名,所以我们应该把自己定义的别名也放入这个文件。
      • 调用/etc/bashrc
  • ~/.bashrc 调用了/etc/bashrc 文件

  • /etc/bashrc 文件中主要定义了这些内容:

    • PS1 变量:也就是用户的提示符,如果我们想要永久修改提示符,就要在这个文件中修改,
    • umask:定义 umask 默认权限。这个文件中定义的 umask 是针对没有登录过程(也就是不需要输入用户名和密码时,比如从一个终端切换到另一个终端,或进入子 Shell)时生效的。如果是“有用户登录过程”,则是/etc/profile 文件中的 umask生效。
    • PATH 变量:会给 PATH变量追加值,当然也是在“没有登录过程”时才生效。
    • 调用/etc/profile.d/*.sh 文件,这也是在“没有用户登录过程”是才调用。在“有用户登录过程”时,/etc/profile.d/*.sh 文件已经被/etc/profile 文件调用过了。
  • 这样这五个环境变量配置文件会被依次调用,那么如果是我们自己定义的环境变量应该放入哪个 文件呢?如果你的修改是打算对所有用户生效的,那么可以放入/etc/profile 环境变量配置文件;如果你的修改只是给自己使用的,那么可以放入~/.bash_profile~/.bashrc 这两个配置文件中的任一个。 可是如果我们误删除了这些环境变量,比如删除了/etc/bashrc 文件,或删除了~/.bashrc文件, 那么这些文件中配置就会失效(~/.bashrc 文件会调用/etc/bashrc 文件)。那么我们的提示符就会 变成:

-bash-4.1#
  • 注销时生效的环境变量配置文件
    • 在用户退出登录时,只会调用一个环境变量配置文件,就是~/.bash_logout。这个文件默认没有写入任何内容,可是如果我们希望再退出登录时执行一些操作,比如清除历史命令,备份某些数据, 就可以把命令写入这个文件。
  • 其他配置文件
    • 还有一些环节变量配置文件,最常见的就是~/bash_history 文件,也就是历史命令保存文件。

  1. Shell登录信息
  • /etc/issue
  • 我们在登录tty1-tty6 这六个本地终端时,会有几行的欢迎界面。这些欢迎信息是保存在哪里的? 可以修改吗?当然可以修改,这些欢迎信息是保存在/etc/issue 文件中,我们查看下这个文件:
[dayuanshuai@IDYS ~] cat /etc/issue
CentOS release 6.10 (Final)
Kernel \r on an \m

  • 可以支持的转义符我们可以通过 man agetty 命令查询,在表中我们列出常见的转义符作用:
转义符 作 用
\d 显示当前系统日期
\s 显示操作系统名称
\l 显示登录的终端号,这个比较常用
\m 显示硬件体系结构,如 i386、i686 等
\n 显示主机名
\o 显示域名
\r 显示内核版本
\t 显示当前系统时间
\u 显示当前登录用户的序列号

  • /etc/issue.net
    • /etc/issue是在本地终端登录是显示欢迎信息的,如果是远程登录(如 ssh 远程登录,或 telnet远程登录)需要显示欢迎信息,则需要配置/etc/issue.net 这个文件了。使用这个文件时由两点需要注意:
    • 首先,在/etc/issue文件中支持的转义符,在/etc/issue.net 文件中不能使用。  其次,ssh 远程登录是否显示/etc/issue.net 文件中的欢迎信息,是由 ssh 的配置文件决定 的。 如果我们需要 ssh 远程登录可以查看/etc/issue.net的欢迎信息,那么首先需要修改 ssh 的配置

  • 文件/etc/ssh/sshd_config,加入如下内容:
[root@IDYS ~] cat /etc/ssh/sshd_config 

# no default banner path
# Banner none
Banner /etc/issue.net

这样在 ssh 远程登录时,也可以显示欢迎信息,只是不再可以识别\d\l等信息了


  • /etc/motd
    • /etc/motd文件中也是显示欢迎信息的,这个文件和/etc/issue/etc/issue.net文件的区别是: /etc/issue/etc/issue.net 是在用户登录之前显示欢迎信息,而/etc/motd 是在用户输入用户名和 密码,正确登录之后显示欢迎信息。在/etc/motd 文件中的欢迎信息,不论是本地登录,还是远程登录都可以显示。

  • 定义Bash快捷键
[root@IDYS ~] stty -a       
speed 38400 baud; rows 24; columns 77; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0
ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

  • 那么这些快捷键可以更改吗?可以啊,只要执行:
[root@IDYS ~] stty 关键字 快捷键

07. 例题


7.1 一行代码实现从1到10000的累加

  • 代码如下
sum=0;seq 1 100 | awk '{sum+=$1}END{print sum}'
  • 输出结果
[dayuanshuai@IDYS sh_day01]$ sum=0;seq 1 10000 | awk '{sum+=$1}END{print sum}'   
50005000

7.2 对传入的任意个位置参数进行累加

  1. 定义一个菜单项,用于提示用户输入,实现交互
  2. 如果传入的位置参数含有非法字符,那么则提示用户输入不规范,并且退出程序
  3. 对传入的任意个位置参数进行累加

演示

  • 代码内容
#!/bin/bash

# 首先判断输入的参数里面是否含有字符串
function mycount(){
     
i=1
while [ "$i" -le "$#" ]
do
# 现在 通过${!i}去实现第一个参数,第二个参数,第n个参数的效果
if echo ${!i} | grep -q '[^1-9]'
then
  echo "your input contain str,please input again."
  exit 1
fi
let i++  # 让i递增
done

i=1

while [ "$i" -le "$#" ]
do
let sum=$sum+${!i}
let i=$i+1
done
echo $sum
}
function menu(){
     

cat << EOF
`echo "*****************this is a counting program start***************************"`
`echo "1.对你输入的参数实行累加"`
`echo "2.不做任何操作,直接退出"`
`echo "*****************this is a counting program end*****************************"`
EOF
read -p "请输入上述编号" num
case $num in
1)
mycount $@
;;
2)
echo "正常退出程序";;
*)
echo "你输入的$num为非法输入,异常退出"
esac
}
menu $@
  • 代码演示
[dayuanshuai@IDYS sh_day01]$ ./countv2.sh 12 13 14
*****************this is a counting program start***************************
1.对你输入的参数实行累加
2.不做任何操作,直接退出
*****************this is a counting program end*****************************
请输入上述编号1
39
[dayuanshuai@IDYS sh_day01]$ ./countv2.sh 12 13 14
*****************this is a counting program start***************************
1.对你输入的参数实行累加
2.不做任何操作,直接退出
*****************this is a counting program end*****************************
请输入上述编号2
正常退出程序
[dayuanshuai@IDYS sh_day01]$ ./countv2.sh 12 13 asa
*****************this is a counting program start***************************
1.对你输入的参数实行累加
2.不做任何操作,直接退出
*****************this is a counting program end*****************************
请输入上述编号1
your input contain str,please input again.

7.3 shell脚本实现加减乘除

  • 代码演示
#!/bin/bash

if [ $# -gt 3 ];
then
  echo "your input over " 
  exit -1
fi

case $2 in
"*")
echo $(( $1 * $3 ))
;;
"/")
echo $(( $1 / $3))
;;
"+")
echo $(( $1 + $3))
;;
"-")
echo $(( $1 - $3 ))
esac

  • 运行结果
[dayuanshuai@IDYS sh_day03]$ ./four_type_count.sh 22 "*" 4 
88
[dayuanshuai@IDYS sh_day03]$ ./four_type_count.sh 22 "/" 4 
5
[dayuanshuai@IDYS sh_day03]$ ./four_type_count.sh 22 "+" 4 
26
[dayuanshuai@IDYS sh_day03]$ ./four_type_count.sh 22 "-" 4 
18

  • 代码演示二:改进版本
#!/bin/bash

function fourOperator(){
     

read -t 40 -p "请输入第一个数字" num1
read -t 40 -p "请输入第二个数字" num2
read -t 40 -p "请输入操作符"  operator
if echo $num1 | grep -q '[^0-9]' || echo $num2 | grep -q '[^0-9]'
then
  echo "你输入的不是一个数字,程序错误退出"
  exit -1
fi
num1=`echo $num1 | sed "s/^0*//g"`
num2=`echo $num2 | sed "s/^0*//g"`
case $operator in
"+" | "-" | "*" | "/")
echo "运算结果为 $(( $num1 $operator $num2))"
;;
*)
echo "您的运算符不合法"
exit -1
esac
}

function menu(){
     

cat << EOF
`echo "*********************** 加减乘除运算程序开始 **********************"`
`echo "1. 输入1 进入加减乘除运算程序"`
`echo "2. 输入2 退出程序!"`
EOF
read -t 40 -p "请输入你所要执行程序对于的编号" num
case $num in 
1)
fourOperator
;;
2)
echo "退出程序"
;;
*)
echo "你输入的数字不合法"
exit -1
esac
}

menu
  • 运算结果
[dayuanshuai@IDYS sh_day03]$ ./four_type_countv2.sh 
*********************** 加减乘除运算程序开始 **********************
1. 输入1 进入加减乘除运算程序
2. 输入2 退出程序!
请输入你所要执行程序对于的编号1
请输入第一个数字12
请输入第二个数字45
请输入操作符+
运算结果为 57
[dayuanshuai@IDYS sh_day03]$ ./four_type_countv2.sh 
*********************** 加减乘除运算程序开始 **********************
1. 输入1 进入加减乘除运算程序
2. 输入2 退出程序!
请输入你所要执行程序对于的编号1
请输入第一个数字006
请输入第二个数字045
请输入操作符*
运算结果为 270


写在最后的话:

  • 无论每个知识点的难易程度如何,我都会尽力将它描绘得足够细致
  • 欢迎关注我的CSDN博客,IDYS’BLOG
  • 持续更新内容
    linux基础 | 数据通信(路由交换,WLAN) | Python基础 | 云计算
  • 如果你有什么疑问,或者是难题。欢迎评论或者私信我。你若留言,我必回复!
  • 虽然我现在还很渺小,但我会做好每一篇内容。谢谢关注!

你可能感兴趣的:(shell编程,shell,linux,编程)