3小时精通shell脚本

2022-10-26 17:31:11 浏览数 (1)

3小时精通shell脚本
  • 1 shell简介
    • 1.1 shell是什么
    • 1.2 环境变量
  • 2.shell编程
    • 2.1 文档命令
    • 2.2 管道服务
    • 2.3 输入输出
    • 2.4 shell脚本
      • 2.4.1 hello,world
      • 2.4.2 定义变量
      • 2.4.3 数组
      • 2.4.4 运算符
      • 2.4.5 进制转换
      • 2.4.6 条件判断
      • 2.4.7 循环语句
      • 2.4.8 用户交互
      • 2.4.9 文本批量处理
      • 2.4.10 调试

1 shell简介

1.1 shell是什么

linux操作系统包含内核kernel,人机交互程序与应用,其中shell就属于人机交互程序。

当我们在linux终端输入一个命令时,shell会把他先转换为内核可以理解的命令,有内核执行并将执行结果返回到终端。shell在这里扮演的角色就是翻译。

同时shell也是一种编程语言。shell有很多版本,目前linux默认装载的是Bash Shell

查看当前系统应用的shell

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ echo $SHELL
/bin/bash

临时切换shell

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ tcsh
wangzhou-Latitude-5410:~> 

查看当前系统支持哪些版本shell。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/bin/tcsh
/usr/bin/tcsh

1.2 环境变量

前面提到shell相当于一个翻译官,那么shell是怎么找到要执行的命令(执行命令其实是执行脚本程序)的应用的位置呢?答案是通过环境变量。使用如下命令查看当前用户环境变量(不同用户有不同的环境变量)。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ echo $PATH
/home/wangzhou/Applications/node-v16.4.1-linux-x64/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/sbin/platform-tools/

增加环境变量。 法1:export命令。这种方法是临时修改,只对当前窗口有效,重新打开一个新窗口就失效了。注意一定要加$PATH,这表示追加环境变量,否则你的其它环境变量就没有了。

代码语言:javascript复制
 export PATH=$PATH:/HOME/ct

法2:可以通过修改文件来实现,这种方式是非临时修改。根目录下使用ls -a可以看到.bashrc(个人环境变量),.bash_profile(个人用户环境变量),这两个文件都可以实现相同的功能,只不过前者是每打开一个新的窗口都会被加载一次,而后者在切换用户时加载一次。在/etc/profile/etc/bashrc两个文件中修改环境变量则是系统级别,所有用户都会生效。

2.shell编程

2.1 文档命令

shell命令有19种,只要是linux的命令都可以被称shell命令。如此多的命令我们肯定记不住,我们着重讲解文档命令,这样不懂的命令使用文档命令就可以看到命令的详细介绍。

我们最常用的文档命令就是man.下面应用他查下ls的用法。

代码语言:javascript复制
man ls

效果如下。

2.2 管道服务

符号|表示管道。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ ls -a
.         anriodagent    .bashrc  Documents   .gradle       .local    .mysql_history      .npmrc             platform-tools   runoob      .ssh_bak                   .viminfo    图片
..        Applications   .cache   Downloads   IdeaProject   logs      .nbprofiler         .oracle_jre_usage  .profile         snap        .sudo_as_admin_successful  .visualvm   桌面
adb       .arthas        .config  .gitconfig  IdeaProjects  .m2       nodedev             .pam_environment   Public           soft        Templates                  .vscode
a.jpg     .bash_history  Desktop  .gnupg      .java         .mozilla  .node_repl_history  Pictures           .python_history  springdemo  .thunderbird               .wget-hsts
.android  .bash_logout   dev      .gphoto     learngit      Music     .npm                .pki               .rpmdb           .ssh        Videos                     下载
wangzhou@wangzhou-Latitude-5410:~$ ls -a | grep .bash
.bash_history
.bash_logout
.bashrc

上面ls -a | grep .bash会先执行ls -a,然后把执行结果传递给管道,管道再传给grep .bash执行过滤。可以多次使用管道。

2.3 输入输出

(1) <> <,>表示输入、输出符号。它主要有两个作用:重定向和输入输出。先理解下重定向。

linux所有的文件、目录都有一个文件描述符作为唯一标识。我们可以使用文件描述符来访问linux操作系统的所有文件、目录。后面我们也会体会到这种机制的好处。

首先将之前ls -a查询到的内容保存在文件1.txt中,然后使用exec 3< 1.txt将文件1.txt标识为3,类型是标准输入。现在执行grep .bash <&3,效果如下。

可以看到,标准输入指的是将3所指代的内容输入到目标文件/命令,而标准输出则是从目标文件/命令输出到标识3所指代文件,输入输出是相对于目标而言的。

注意:0(STDIN),1(STDOUT),2(STDERR)是系统自带的标识,请不要使用。

其实上面重定向已经包含了输入输出的作用了,下面单独使用他的输入、输出功能,将ls -a的内容从1.txt输出。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ ls -a > 1.txt
wangzhou@wangzhou-Latitude-5410:~$ cat 1.txt 

(2)>><< >><<<>作用类似,不过是追加模式,使用方式如下。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ cat << END
> 123
> 234
> END
123
234

(3) read 使用read可以通过键盘按行定义变量。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ read a
/Desktop
wangzhou@wangzhou-Latitude-5410:~$ ls -a `echo $a`
.  ..

2.4 shell脚本

2.4.1 hello,world

为实现某个任务,将多个shell命令组合成可执行文件就是shell脚本。shell脚本用文档编辑器就可以编写,给予可执行权限就可以执行。

新建a.sh

代码语言:javascript复制
#!/bin/bash
# This is a test scrpit.
# time:2021-11-19
# autor:z
# motification time:
# motifycation author:
echo "hello,world!"
exit 0

#表示注解,其作用是解释脚本功能,记录脚本的创建、修改时间、作者等。

#!/bin/bash指定了该shell脚本的解析器,建议文件后缀与解析器的指定匹配,比如.sh就对应bash解析器。

指定执行权限。 x参数表示给所有用户赋予执行权限,u x则表示给当前用户赋予权限。

代码语言:javascript复制
chmod  x a.sh

执行结果如下。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ ./a.sh
hello,world!

另外,将a.sh的路径加到环境变量中,就可以像其它普通的shell命令一样执行他了。

2.4.2 定义变量

使用set命令可以查看已经被linux系统占用的变量(相当于java的保留字),由于个数太多,我们筛选了少部分看即可。

代码语言:javascript复制
set | grep -e HOME -e BASH

在b.sh中随便打个系统变量看看。

代码语言:javascript复制
echo "hello,world!"
echo "$0":$0
exit 0

执行结果如下。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ ./a.sh
hello,world!
$0:./a.sh

其它常用的shell系统变量参考下表。

自己定义变量很简单,有字母、数字、下划线组成。局部变量和全局变量定义示范如下。

代码语言:javascript复制
#!/bin/bash
var=10
function getvar(){
local var1=1
echo "var1:$var1"
echo "var:$var"
}
getvar

执行结果如下。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ ./b.sh 
var1:1
var:10
2.4.3 数组

参考下列代码,值的注意的是,shell中数组长度可变。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ ARRAY=(1 2 4)
wangzhou@wangzhou-Latitude-5410:~$ echo $ARRAY
1
wangzhou@wangzhou-Latitude-5410:~$ echo ${ARRAY[0]}
1
wangzhou@wangzhou-Latitude-5410:~$ echo ${ARRAY[@]}
1 2 4
wangzhou@wangzhou-Latitude-5410:~$ echo ${ARRAY[@]:1}
2 4
wangzhou@wangzhou-Latitude-5410:~$ ARRAY[3]=7
wangzhou@wangzhou-Latitude-5410:~$ echo ${ARRAY[@]:1}
2 4 7
2.4.4 运算符

如下表。

我们使用let来进行运算。下面示例如下。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ let var=11%2
wangzhou@wangzhou-Latitude-5410:~$ echo $var
1
2.4.5 进制转换

八进制以0开头,0x十六进制。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ let var=011
wangzhou@wangzhou-Latitude-5410:~$ echo $var
9

可以自定义进制,比如二进制。

代码语言:javascript复制
wangzhou@wangzhou-Latitude-5410:~$ let var=2#111
wangzhou@wangzhou-Latitude-5410:~$ echo $var
7
2.4.6 条件判断

下面代码在实际shell编程中常用于脚本连接:下一个shell脚本常常依赖于上一个脚本,我们先依次检查每一个脚本,避免前面脚本的错误影响后面脚本的结果。

代码语言:javascript复制
vim d.sh
#!/bin/bash
ls $1
if[$? -eq 0];then
  echo "The last exit code is 0"
else
  echo "The last exit code is not 0"
  exit 1
 fi

执行 ./d.sh a.sh则输出"The last exit code is 0",执行./d.sh e.sh则输出“The last exit code not 0",这是因为e.sh都还没有创建呢。

条件判断的格式还有扩展写法。

代码语言:javascript复制
if [ command ];then
     符合该条件执行的语句
elif [ command ];then
     符合该条件执行的语句
else
     符合该条件执行的语句
fi

常用的一些类型的判断有默认方法,这些方法常常结合上面的if语句使用,十分方便。

代码语言:javascript复制
[ -a FILE ] 如果 FILE 存在则为真。
[ -d FILE ] 如果 FILE 存在且是一个目录则返回为真。
[ -e FILE ] 如果 指定的文件或目录存在时返回为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则返回为真。
[ -r FILE ] 如果 FILE 存在且是可读的则返回为真。
[ -w FILE ] 如果 FILE 存在且是可写的则返回为真。(一个目录为了它的内容被访问必然是可执行的)
[ -x FILE ] 如果 FILE 存在且是可执行的则返回为真。
[ -z STRING ] 如果STRING的长度为零则返回为真,即空是真
[ -n STRING ] 如果STRING的长度非零则返回为真,即非空是真
[ STRING1 ]  如果字符串不为空则返回为真,与-n类似
[ STRING1 == STRING2 ] 如果两个字符串相同则返回为真
[ STRING1 != STRING2 ] 如果字符串不相同则返回为真
[ STRING1 < STRING2 ] 如果 “STRING1”字典排序在“STRING2”前面则返回为真。
[ STRING1 > STRING2 ] 如果 “STRING1”字典排序在“STRING2”后面则返回为真。
[ INT1 -eq INT2 ] INT1和INT2两数相等返回为真 ,=
[ INT1 -ne INT2 ] INT1和INT2两数不等返回为真 ,<>
[ INT1 -gt INT2 ] INT1大于INT2返回为真 ,>
[ INT1 -ge INT2 ] INT1大于等于INT2返回为真,>=
[ INT1 -lt INT2 ] INT1小于INT2返回为真 ,<
[ INT1 -le INT2 ] INT1小于等于INT2返回为真,<=
[ ! EXPR ] 逻辑非,如果 EXPR 是false则返回为真。
[ EXPR1 -a EXPR2 ] 逻辑与,如果 EXPR1 and EXPR2 全真则返回为真。
[ EXPR1 -o EXPR2 ] 逻辑或,如果 EXPR1 或者 EXPR2 为真则返回为真。
[ ] || [ ] 用OR来合并两个条件
[ ] && [ ] 用AND来合并两个条件
2.4.7 循环语句

for循环。

代码语言:javascript复制
for 变量 in 串行
do
   执行命令
done

while循环:当条件满足时执行。

代码语言:javascript复制
while 条件测试
do
   执行命令
done

util循环:直到满足条件停止。

代码语言:javascript复制
until 条件测试
do
  执行命令
done

使用break关键字可以跳出循环,而且还可以跳出嵌套循环,下面给出了示例代码,特别注意空格与示例一致,不加空格的后果您感兴趣可以尝试。

代码语言:javascript复制
$ cat test1.sh
#!/bin/bash
i=1
while true
do
        if [ $i -gt 3 ] #没有空格则报错
        then
                break
        fi
        echo "i="$i
        sleep 1
        i=`expr $i   1` # 有空格是数字运算,没有空格是字符串拼接
done

执行结果如下。

代码语言:javascript复制
24724@LAPTOP-OCSC7S98 MINGW64 ~
$ ./test1.sh
i=1
i=2
i=3

下面示例break 2跳出双层循环。

代码语言:javascript复制
#!/bin/bash
i=1
j=1
while true
do
        while true
        do
                if [ $i -gt 3 ]
                then
                break 2
        fi
        echo "i="$i
        echo "j="$j
        sleep 1
        i=`expr $i   1`
        done
j=`expr $i   $j`
echo "j="$j
done

执行结果如下,说明在跳出内存循环时也跳出了第二层外层循环。

代码语言:javascript复制
24724@LAPTOP-OCSC7S98 MINGW64 ~
$ ./test1.sh
i=1
j=1
i=2
j=1
i=3
j=1

关键字continue可以跳出当层循环,这里不再赘述。

2.4.8 用户交互

最简单的交互。使用read实现用户交互,让用户输入密码。

代码语言:javascript复制
echo -e "请输入密码: c"
pwdstr=''
while : ;
do
    read -n 1 -s -p "" pw
    if [ $pw ]; then
        pwdstr=${pwdstr}$pw
        echo -e "*c" 
    else
        echo
        break
    fi
done
 
echo "password is :$pwdstr"

执行结果如下。

代码语言:javascript复制
$ ./test2.sh
password is :echo -e 请输入密码: c
******
password is :123455

第二种方式是通过菜单选择来实现交互。

代码语言:javascript复制
function menu ()
#!/bin/bash
select i in a b c d
do
	case $i in 
		a)
			ls
			;;
		b)
			pwd
			;;
		c)
			w
			;;
		d)
			uname -a
			;;
		*) 
			echo "Please input 1-4"
			;;
	esac
done 

效果如下。

代码语言:javascript复制
$ ./test2.sh
1) a
2) b
3) c
4) d
#? a
Please input 1-4
#? 1
 「开始」菜单                Contacts    git.ignore             'My Documents'     NTUSER.DAT{53b39e88-18c4-11ea-a811-000d3aa4692b}.TM.blf                                        Pictures       SendTo
'3D Objects'                 Cookies     IntelGraphicsProfiles   NetHood           NTUSER.DAT{53b39e88-18c4-11ea-a811-000d3aa4692b}.TMContainer00000000000000000001.regtrans-ms   PrintHood      Templates
 AppData                     Desktop     Links                   NTUSER.DAT        NTUSER.DAT{53b39e88-18c4-11ea-a811-000d3aa4692b}.TMContainer00000000000000000002.regtrans-ms   Recent         test1.sh
'Application Data'           Documents  'Local Settings'         ntuser.dat.LOG1   ntuser.ini                                                                                    'Saved Games'   test2.sh
 BullseyeCoverageError.txt   Downloads   Music                   ntuser.dat.LOG2   OneDrive                                                                                       Searches       Videos
#? 2
/c/Users/24724
2.4.9 文本批量处理

grep过滤,sed批量行处理,awk批量列处理。号称文本处理三剑客,特别常用。这里仅仅抛砖引玉,读者需要使用他们进行文本处理时可以参考博客:https://www.cnblogs.com/along21/p/10366886.html.

2.4.10 调试
  • 在命令行提供参数:$sh -x script.sh-
  • .脚本开头提供参数:#!/bin/sh -x
  • 在脚本中用set命令启用or禁用参数:其中set -x表示启用,set x表示禁用。

0 人点赞