shell基础

一、编程语言分类

编程的语言的发展经历了 如下阶段:

1.1机器语言:

站在计算机的角度,说计算机能听懂的语言,那就是直接用二进制编程,直接操作硬件;
优点:执行效率最高
缺点:

  1. 二进制指令难以记忆,开发时极容易出错
  2. 开发程序的复杂度高:即便是完成一个简单的功能,需要用到的二进制指令的条数都会非常多

1.2 汇编语言

站在计算机的角度,简写的英文标识符取代二进制指令去编写程序,本质仍然是直接操作硬件;
优点:解决了机器语言的二进制指令难以记忆的问题,执行效率还是高
缺点:开发程序的复杂度依然很高:因为汇编语言就是用英文标签对应原来的二进制指令,好记归好记,开发 的复杂度却没有降低

ps:因为上述两类语言都是在直接与计算机硬件打交道,离计算机硬件比较近,所以又统称为低级语言

1.3 高级语言

站在人类的角度,说人话,即用人类的字符去编写程序,屏蔽了硬件操作
优点:开发复杂度低,即开发效率高
缺点:速度肯定是不如低级语言,一直到今天,对速度要求极高的场景还会用到低级语言,比如操作系统的调度程序

高级语言更贴近人类语言,因而造成了:它必须被翻译成计算机能读懂二进制后,才能被执行,按照翻译方式分为

  1. 编译型(需要编译器,相当于用谷歌翻译):如C,执行速度快,调试麻烦

  2. 解释型(需要解释器,相当于同声传译):如Python,执行速度慢,调试方便

二、Shell

2.1 shell语言

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。是一门解释型,弱类型,动态语言。
Shell 中文意思贝壳,寓意类似内核的壳。Shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务,简而言之就是只要能够操作应用程序的接口都能够称为SHELL。狭义的shell指的是命令行方面的软件,广义的SHELL则包括图形界面。
了解:几种shell

  1. sh(Bourne Shell)是一个早期的重要shell,1978年由史蒂夫·伯恩编写,并同Version 7 Unix一起发布。

  2. bash(Bourne-Again Shell)是一个为GNU计划编写的Unix shell。1987年由布莱恩·福克斯创造。主要目标是与POSIX标准保持一致,同时兼顾对sh的兼容,是各种Linux发行版标准配置的Shell,在Linux系统上/bin/sh往往是指向/bin/bash的符号链接。

  3. dash (Debian Almquist shell)一种 Unix shell。它比 Bash 小,只需要较少的磁盘空间,但是它的对话性功能也较少。它由 NetBSD版本的Almquist shell (ash)发展而来,于1997年由赫伯特·许(Herbert Xu)移植到Linux上,于2002年改名为 dash。
    在这里插入图片描述

sh 遵循POSIX规范:“当某行代码出错时,不继续往下解释”。bash 就算出错,也会继续向下执行。
sh 跟bash的区别,实际上是bash有没开启POSIX模式的区别。
简单说,sh是bash的一种特殊的模式,sh就是开启了POSIX标准的bash, /bin/sh 相当于 /bin/bash --posix。
在Linux系统上/bin/sh往往是指向/bin/bash的符号链接
ln -s /bin/bash /bin/sh

2.2 shell脚本

Shell 脚本(shell script)是一种为 shell 编写的脚本程序。常说的shell通常都是指 shell 脚本,但shell和shell script是两个不同的概念。通常说“shell编程”都是指 shell 脚本编程,不是指开发 shell 自身。

2.3 GNU bash

Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等,Shell Script大致都类同,即命令大都通用。当您学会一种Shell以后,其它的Shell会很快就上手,大多数的时候,一个Shell Script通常可以在很多种Shell上使用。

bash是大多数Linux系统以及Mac OS X v10.4默认的shell,bash具有极强的可移植性,它能运行于大多数Unix风格的操作系统之上,甚至被移植到了Microsoft Windows上的Cygwin系统中,以实现windows的POSIX虚拟接口。此外,它也被DJGPP项目移植到了MS-DOS上。bash的命令语法是Bourne shell命令语法的超集。数量庞大的Bourne shell脚本大多不经修改即可以在bash中执行,只有那些引用了Bourne特殊变量或使用了Bourne的内置命令的脚本才需要修改。

bash的命令语法很多来自Korn shell (ksh) 和 C shell (csh), 例如命令行编辑,命令历史,目录栈,$RANDOM 和 $PPID 变量,以及POSIX的命令置换语法: $(…)。GNU bash作为一个交互式的shell,按下TAB键即可自动补全已部分输入的程序名,文件名,变量名等等。

2.4 POSIX

POSIX(Portable Operating System Interface,可移植操作系统接口),是操作系统为应用程序提供的接口标准。
简单的说, POSIX(主要是解决了应用程序在各个操作系统上兼容性这样一个普遍存在的问题。只要一个应用程序的开发是为了在一个实现了POSIX模式的操作系统上运行,那么这个应用程序就可以在所用实现了POSIX模式的操作系统上运行

问题: 不同操作系统内核为同一功能提供的系统调用(函数)是不同的,例如创建进程,linux下是fork函数,windows下是createprocess函数,如果在Linux下写了一个程序用到了fork函数,要往windows上移植就得把源代码里面的fork通通改成createprocess,然后重新编译。

解决方法: 定义POSIX标准, linux和windows实现基于POSIX标准,提供同样的接口,例如定义创建进程的接口为posix_fork(示例名/非真实名字), 且linux和windows都把各自创建进程的调用封装成posix_fork,都声明在unistd.h里。 这样程序员编写应用时,只需包含unistd.h, 调用这个POSIX标准中定义的API接口: posix_fork函数,即可实现源代码级别的可移植。

2.5 shell

shell有两层意思:

  1. 一层指的是shell这门语言,是一种特定的语法风格,规范等
  2. 另外一层指的是专门用于解释执行shell这门语言语法的应用程序,即shell解释器,我们常用的是bash
    在这里插入图片描述

linux系统上自带多种shell解释器,无需安装

[root@manager ~]# chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

2.6 shell 与python对比

shell本身就是一门解释型、弱类型、动态语言,与python相对应,Python属于解释型、强类型、动态语言,我们平时登录成功一个用户后进入的就是bash解释器的交互式环境,我们敲的命令其实都属于shell这门语言的语法
在这里插入图片描述

2.6.1 shell语言

Shell 脚本的优势在于处理偏操作系统底层的业务,例如,Linux 内部的很多应用(有的是应用的一部分)都是使用 Shell 脚本开发的,因为有 1000 多个 Linux 系统命令为它作支撑,特别是 Linux 正则表达式以及三剑客 grep、awk、sed 等命令。

对于一些常见的系统脚本,使用 Shell 开发会更简单、更快速,例如,让软件一键自动化安装、优化,监控报警脚本,软件启动脚本,日志分析脚本等,虽然 Python 也能做到这些,但是考虑到掌握难度、开发效率、开发习惯等因素,它们可能就不如 Shell 脚本流行以及有优势了。对于一些常见的业务应用,使用 Shell 更符合 Linux 运维简单、易用、高效的三大原则。

2.6.2 python 语言

Python 是近几年非常流行的语言,它不但可以用于脚本程序开发,也可以实现 Web 程序开发(知乎、豆瓣、YouTube、Instagram 都是用 Python 开发),甚至还可以实现软件的开发(大名鼎鼎的 OpenStack、SaltStack 都是 Python 语言开发)、游戏开发、大数据开发、移动端开发。

现在越来越多的公司要求运维人员会 Python 自动化开发,Python 也成了运维人员必备的技能,每一个运维人员在熟悉了 Shell 之后,都应该再学习 Python 语言。

Python 语言的优势在于开发复杂的运维软件、Web 页面的管理工具和 Web 业务的开发(例如 CMDB 自动化运维平台、跳板机、批量管理软件 SaltStack、云计算OpenStack 软件)等。

三、第一个shell程序

3.1 编写shell程序的两种环境

我们可以在两个地方编写shell程序

  1. 交互式环境
    调试方便,无法永久保存代码

  2. 写到文件中
    我们采用解释型语言编写的代码文件通常都会被称之为脚本程序:可以永久保存代码

3.2 编写shell脚本程序

其实我们在交互式环境里敲的命令直接放到一个文本文件里,一个简单的shell程序就完成了。
shell解释器执行程序是解释执行,即打开文件读内容,因此文件的后缀名没有硬性限制,但通常定义为.sh结尾

[root@manager ~]# mkdir scripts
[root@manager ~]# 
[root@manager ~]# cd scripts/
#!/bin/bash

#第一个shell小程序
echo "hello world!"  

解释

  • 1、第一行表示我们选择使用的shell解释器是bash

    shell的第一行比较特殊,一般都会以#!开始来指定使用的shell解释的类型。
    在linux中,除了bash shell以外,还有很多版本的shell, 例如zsh、dash等等...
    不过bash shell还是我们使用最多的。
    
  • 2、第二行以#符号开始,表示本行是注释,注释是对代码的解释说明,注释的内容不会执行,对关键代码加注释是一种好的编程习惯

  • 3、第三行中的echo是linux中的输出命令,该行的意思很明显的就是输出hello world!

各种语言的hello world对比

#C++
#include <iostream>
 int main(void)
 {
  std::cout<<"Hello world";
 }

#C
#include <stdio.h>
int main(void)
{
printf("\nhello world!");
return 0;
}

#JAVA
public class HelloWorld{
  // 程序的入口
  public static void main(String args[]){
    // 向控制台输出信息
    System.out.println("Hello World!");
  }
}

#PHP
<?php  
             echo "hello world!";  
?>

#Ruby
puts "Hello world."

#GO
package main
import "fmt"
func main(){

    fmt.Printf("Hello World!\n God Bless You!");

}

# Python
print("Hello World!")

3.3 运行shell脚本程序

编写好shell脚本之后,运行它有几种方式
方式一:绝对路径,此时采用的是文件头指定的解释器来解释执行文件内代码
权限:
1、对沿途文件夹有x权限
2、对目标文件有x权限

方式二:相对路径,需要加./作为前缀,此时采用的仍是文件头指定的解释器来解释执行文件内代码
权限:
1、对沿途文件夹有x权限
2、对目标文件有r和x权限

方式三:解释器+文件路径(绝对路径或相对路径都可以),此时采用的是我们自己指定的解释器来解释执行文件内代码

注: 三种方式都会开启新的子shell

3.4 脚本编写规范

  • 脚本存放目录需要统一

  • shell脚本的结尾以.sh

  • 脚本开头要有解释器如#!/bin/bash 或者 #!/usr/bin/env bash

  • 脚本开头注意加时间、作者、联系邮箱、脚本作用等信息

    # Author xxx 2020-8-30  version 1  des:xxxxx
    
  • 关键代码应该用#号加上注释

3.4 代码编写习惯

1、成对的符号尽量一次性写出来,防止遗漏

例如大括号{},中括号[],小括号(),单引号’’,双引号””,反引号``等

2、括号的保留空格习惯

中括号[ ]两端需要留有空格,不然会报错。书写时即可留出空格然后书写内容。
如果不知道大括号{},中括号[],小括号(),到底哪种括号需要两端留空格,
可以在书写这些括号的时候两端都保留空格来进行书写,这样可以有效避免因空格导致的各种错误。

3、流程控制语句一次性书写完再添加内容

四、变量

什么是变量?

​ 量指的是记录事物的状态

​ 变指的是事物的状态是可以发生变化的

​ 变量本质就是一种数据存取的机制,变量的数据都是存放于内存中的

1、shell变量

在程序运行过程中,不断变化的标识符
先定义,后引用
$:取定义变量的值 
变量三大组成部分
变量名:用来访问变量值
赋值符号:把变量值的地址绑定给变量名
变量值:记录事务的状态,即存入内存中的数据

定义变量时候赋值号两边不能有空格
2、变量名

    1.见名知意,由数字字母下划线组成
    2.不能以数字开头
    3.不能与shell中关键字重复
    4.不能使用中文

3、变量值:

1. 整型
2. 浮点型
3. 字符串
name=bert
name="bert" # 两种都可以,建议使用

4、引号对变量的影响
双引号,弱引用;
单引号强引用,所见即所得,看到什么就打印什么,不会解析变量;
\ 取消转义


[root@localhost ~]# name="bert"
[root@localhost ~]# echo "hello $name"
hello bert
[root@localhost ~]# echo 'hello $name'
hello $name
[root@localhost ~]# echo "hello \$name"
hello $name


[root@localhost ~]# echo "$abc"

[root@localhost ~]# echo "\$abc"
$abc

[root@localhost ~]# echo '$abc'
$abc

5、反引号:去一条命令的结果

root@localhost ~]# today=`date +%F`
[root@localhost ~]# echo $today
2021-07-12

[root@localhost ~]# today=$(date +%H:%M:%S)
[root@localhost ~]# echo $today
19:34:40
[root@localhost ~]# tar -czvPf `date +%F`_bak.tar.gz /root
[root@localhost ~]# ls
2021-07-12_bak.tar.gz

[root@localhost ~]# x=1   # 定义变量
[root@localhost ~]# x 
-bash: x: command not found
[root@localhost ~]# echo $x   # 查看变量
1
[root@localhost ~]# echo ${x} # 可以这么打印
1
[root@localhost ~]# echo ${x}%
1%
[root@localhost ~]# echo ${x}G
1G

4.1变量值的三种来源

1. 直接赋值

# 1. 显式赋值:变量名=变量值
示例:
ip1=192.168.11.200
school="Peking University "
today1=`date +%F`
today2=$(date +%F)

# 2、应用示例
[root@localhost ~]# url="www.baidu.com"
[root@localhost ~]# echo $url
www.baidu.com
[root@localhost ~]# url="www.sina.com.cn"
[root@localhost ~]# echo $url
www.sina.com.cn

2. 从位置参数获取变量值

从调用脚本时传入的位置参数获取变量值:./test1.sh a1 a2 a3需要用到$n获取第n个位置参数值,超过10需要用${n},如下$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}

[root@manager scripts]# cat test1.sh 
echo $0
echo $1
echo $2
echo $3
echo $4
echo $5
echo $6
echo $7
echo $8  
echo $9
echo ${10}
echo ${11}
echo ${12}

[root@manager scripts]# sh test1.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14
test1.sh
1
2
3
4
5
6
7
8
9
10
11
12

3. 与用户交互获取值

read接收用户键盘输入值

read选项含义
-p打印信息
-t限定时间
-s不回显,用于密码
-n接收字符个数
read -p "请输入值" 变量名
[root@manager scripts]# read -p "请输入姓名:" name
请输入姓名:bertwu
[root@manager scripts]# 
[root@manager scripts]# 
[root@manager scripts]# echo $name
bertwu
[root@manager scripts]# echo ${name}
bertwu


read 变量名
read -p "提示信息:"  变量名
read -t 5 -p "提示信息:"  变量名  # -t指定秒数 5秒后自动消失
read -n 2 -p "提示信息:" 变量名  # -n读取的字符个数 只接收两个字符

[root@manager scripts]# read -p  "密码:"  -s passwd
密码:
[root@manager scripts]# echo $passwd
123456

4.变量输出

  1. echo
[root@manager scripts]# name=bertwu
[root@manager scripts]# age=18
[root@manager scripts]# echo "my name is $name,my age is $age"
my name is bertwu,my age is 18
[root@manager scripts]# echo "my name is ${name},my age is ${age}"
my name is bertwu,my age is 18


还可以输出带颜色(了解即可)
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[34m 黄色字 \033[0m"
echo -e "\033[41;33m 红底黄字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
  1. printf命令
[root@manager scripts]# printf "my name is %s,my age is %s\n" $name $age
my name is bertwu,my age is 18



%s
%d
%7.3f  打印浮点数,总宽度为7,小数位保留3,并且四舍五入
[root@manager scripts]# salary=15.5555
[root@manager scripts]# printf "salary is %5.3f\n" $salary
salary is 15.555

5.预定义变量

$*	所有的参数 传递给脚本或函数的所有参数,当被双引号 "" 包含时,所有的位置参数被看做一个字符串
$@ 	所有的参数 传递给脚本或函数的所有参数,当被双引号 "" 包含时,每个位置参数被看做独立的字符串
$# 	参数的个数
$$ 	当前进程的PID  # 此外,可以使用只读变量来获取父进程的PID:$PPID、获取执行脚本的用户ID:$UID
$?	上一个命令的返回值 0表示成功
$0 脚本的路径+名称
$1 $2 位置参数
#例1.脚本如下
[root@manager scripts]# cat test3.sh 
echo $*
echo $@
echo $#
echo $$

ping -c1 baidu.com &>/dev/null
echo $?

#查看执行结果
[root@manager scripts]# sh test3.sh a b c # 传递三个变量 a b c
a b c
a b c
3
50169
0

如果我们想从命令行中获取脚本调用者传入的参数值,用$n可以取到,但如果脚本调用者在命令行传入的参数个数不固定,那么此时就需要用$*$@来获取了。

$*$@获取的是所有位置参数,$0除外

[root@manager scripts]# cat test2.sh 
for i in $*
do 
	echo $i
done

echo "====================="

for j in $@
do 
	echo $j
done


[root@manager scripts]# sh test2.sh a b c
a
b
c
=====================
a
b
c

当脚本调用者传递参数如下形式

[root@manager scripts]# sh test2.sh 命令1 命令2 "命令3 参数"
命令1
命令2
命令3
参数
=====================
命令1
命令2
命令3
参数

针对for循环语句:for i in 元素,for循环会按照空格作为分隔符来一个个取元素,所以此时$*$@如果不加引号,第三个命令:“命令3 参数”,为被以空格为分隔符识别成两部分。
如果对 $*$@ 加上引号

[root@manager scripts]# cat test2.sh 
for i in "$*"
do 
	echo $i
done

echo "====================="

for j in "$@"
do 
	echo $j
done

再次执行:

[root@manager scripts]# sh test2.sh 命令1 命令2 "命令3 参数"
命令1 命令2 命令3 参数
=====================
命令1
命令2
命令3 参数

此时为$*就会把所有位置参数识别成一个整体,所以总结如下:

当脚本调用者的传参形式如下时
[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 参数"

需要使用"$@"来分别获取一个个完整的命令
for i in "$@"
do
    echo $i
done

其余情况$*与$@完全一致

6.示例

示例1 :通过位置变量创建 Linux 系统账户及密码,执行 var1.sh username password,控制最多传递两个参数。

[root@manager scripts]# cat test5.sh 
#!/bin/bash
if [ $# -ne 2 ];then
	echo "USAGE $0 [ User | Password ]"
	exit

fi

useradd $1
echo $2 | passwd --stdin $1

示例2: 通过位置变量创建 Linux 系统账户及密码,执行 test6.sh username password,控制最多传递两个
参数,且必须是root身份;

root@manager scripts]# cat test6.sh 
#!/bin/bash
if [ $UID -ne 0 ];then
	echo "$USER Permission Deny,Please User Root User"
	exit

fi

if [ $# -ne 2 ];then
		echo "USAGE $0 [ user| passwd ]"
		exit
fi


useradd $1
echo $2 | passwd --stdin $1

3.模拟登陆
使用 read 模拟 Linux 登陆页面
1.如果输入用户为root,密码为123,则输出欢迎登陆;
2.否则输出用户或密码错误
在这里插入图片描述

[root@manager scripts]# cat login.sh 
echo $(hostnamectl | grep Operating | awk -F ":" '{print $NF}')
echo "$(hostnamectl | grep Kernel | awk -F "[: ]+" '{print $2, $4}') on an $(hostnamectl | grep Arc | awk -F ": " '{print $2}')" 
read -p "$(hostname) login:" user
read -s -p  "Password:" passwd  
echo ""
if [ ${user} ==  "root" ] && [ ${passwd} -eq 123 ];then
	echo "登陆成功!"

else
	echo "登陆失败"

fi

[root@manager scripts]# sh login.sh 
CentOS Linux 7 (Core)
Kernel 3.10.0-1160.el7.x86_64 on an x86-64
manager login:root
Password:
登陆成功!
[root@manager scripts]#

4.系统备份
使用 read 编写一个备份脚本,需要用户传递2个参数,源和目标。
1.提示用户,你需要备份的文件在哪个路径下;
2.提示用户,你要备份到哪个目录;
3.你确定要备份吗? [ yes | no ]
4.如果输入yes 就进行备份的操作,如果输入no,
则取消备份

[root@test scripts]# cat backup.sh 
read -p "请输入源文件路径:" src
if [ ! -d $src ] &&  [ ! -f $src ];then
	echo "文件不存在"
	exit
fi
read -p "请输入要备份的路径:" dest
if [ ! -d $dest ];then
	mkdir $dest -p

fi


filename=$(echo  ${src##*/}) # 取文件或目录名称
cp -rpv $src $dest/${filename}_$(date +%F)

5.探测主机存活状态

[root@test scripts]# cat ping.sh 
read -p "请输入ip:" Ip
ping -c1 $Ip &>/dev/null

if [ $? -eq 0 ];then
	echo "host $Ip is ok"
else
	echo "not connection"
fi

6.修改主机名称

[root@test scripts]# cat change_hostname.sh 
Hostname=$(hostname)
echo "当前主机名是 ${Hostname}"

read -p "请输入要改的主机名:" host
read -p "你确定将$Hostname 变更为$host吗?[ yes|no ]" action


if  [ $action == "yes" ];then
	echo "正在修改"
	sleep 2
	hostnamectl set-hostname $host
	echo "修改成功,当前主机名称为$host"

fi

五、变量值操作

5.1、shell 变量删除(类似python中的字符串split后取指定索引的值)

简单来说,就是在不改变原有变量的情况下,对变量值进行删除。
比如:我们需要对某个变量的值进行整数比对,但变量的值是一个小数。怎么办?我们可以使用变量删除的方式,将小数位进行删除,然后在进行整数比对。(shell默认不支持zj)

5.1.1 变量删除的几种方式

变量说明
${变量#匹配规则}从头开始匹配,最短删除
${变量##匹配规则}从头开始匹配,最长删除
${变量%匹配规则}从尾开始匹配,最短删除
${变量%%匹配规则}从尾开始匹配,最长删除
[root@test scripts]# url=www.baidu.com.cn # 定义变量
[root@test scripts]# echo ${url#*.} # 从头开始匹配. 非贪婪匹配
baidu.com.cn
[root@test scripts]# echo ${url##*.} # 从头开始匹配. 贪婪匹配
cn

[root@test scripts]# echo ${url%.*} # 从后向前,非贪婪匹配
www.baidu.com
[root@test scripts]# echo ${url%%.*} # 从后向前,贪婪匹配
www

5.2 示例

1.提取内存百分比

方式1
[root@test scripts]# cat men.sh 
Free=$(free -m | grep Mem | awk '{ print $3}')
Total=$(free -m | grep Mem | awk '{ print $2}')
Per=$(echo "$Free*100/$Total" | bc )
echo ${Per}%

if [ $Per -gt 80 ];then
	echo "报警"
fi


[root@test scripts]# while true;do bash men.sh;sleep 1;clear;done  # 实时查看


方式2.实时查看
[root@test scripts]# cat men.sh 
Per=$(free -m | grep Mem | awk '{ print $3/$2}')
echo ${Per}%

[root@test scripts]# while true;do sh men.sh ;sleep 1;clear;done

2.为不同版本centos系统安装不同的base源

[root@manager scripts]# cat repo.sh 
sys_version=$(cat /etc/redhat-release | awk '{print$(NF-1)}') # 因为centos6并不支持hostnamctl命令
if [ ${sys_version%%.*} -eq 7 ];then
	wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo &>/dev/null
elif [ $(sys_version%%.*) -eq 6 ];then
	wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo
fi
echo "CentOS ${sys_version} Epel Installed  OK"

5.2 变量值长度 ${#变量名}, 类似python中 len(string)

[root@manager scripts]# string="hello linux" 
[root@manager scripts]# 
[root@manager scripts]# echo ${#string}
11
# 企业面试题:已知变量msg='hello world!',请统计出变量中包含的字符数量
# 方法一:
[root@manager scripts]# echo ${#msg}
12
# 方法二:
[root@manager scripts]# echo $msg | wc -L
12
# 方法三:
[root@manager scripts]# echo $msg|awk '{print length}'
12

# 方法四:
[root@manager scripts]# expr length "$msg" #length是一个函数,注意因为msg的值有空格,所以$msg必须用引号包含
12

5.3 变量大小写转换 (类似 python中 upper 、title、lower)

  ${parameter^pattern}
  ${parameter^^pattern}   
  ${parameter,pattern}
  ${parameter,,pattern}^):把变量中的第一个字符换成大写
(^^):把变量中的所有小写字母,全部替换为大写

(,):把变量中的第一个字符换成小写
(,,):把变量中的所有大写字母,全部替换为小写。
[root@manager scripts]# string="hello world"
[root@manager scripts]# echo ${string^} # 将第一个字符转换成大写
Hello world
[root@manager scripts]# echo ${string^^} # 将所有字符转换成大写
HELLO WORLD
[root@manager scripts]# 
[root@manager scripts]# str="HELLO WORLD" # 将第一个字符 转换成小写
[root@manager scripts]# echo ${str,}
hELLO WORLD
[root@manager scripts]# echo ${str,,} # 将所有字符转换成小写
hello world

例,当用户输入q 或Q 则退出程序

   read -p "请输入以上功能编号,按q或Q退出 [1|2|3|4|q|Q]:" Choice
    if [ ${Choice^} == "Q" ];then
    	break
    fi

5.4 变量切片 相当于python中列表或字符串切片,但是没有python中强大

${paramter:offset:length}

[root@manager scripts]# msg="hello world"
[root@manager scripts]# echo ${msg:3} # 三号索引开始,一直到最后
lo world
[root@manager scripts]# echo ${msg:3:4} #从从三号索引开始,往后取四个字节
lo w
[root@manager scripts]# echo ${msg::3} # 从零号索引开始取三个字节
hel

5.5 let

# (1) 变量的值
[root@manager scripts ~]# j=1
[root@manager scripts ~]# let ++j # 或j++
[root@manager scripts ~]# echo $j
2
[root@manager scripts ~]# 

# (2) 表达式的值
[root@manager scripts ~]# unset i
[root@manager scripts ~]# unset j
[root@manager scripts ~]# 
[root@manager scripts ~]# i=1
[root@manager scripts ~]# j=1
[root@manager scripts ~]# 
[root@manager scripts ~]# let x=i++  # 先把i赋值给x,然后再 i++
[root@manager scripts ~]# echo $i
2
[root@manager scripts ~]# echo $x
1

[root@manager scripts ~]# let y=++j  # 先++j,然后再把j的结果赋值给y
[root@manager scripts ~]# echo $j
2
[root@manager scripts ~]# echo $y
2

5.6 变量替代

${x-临时变量信息}, ${x:-临时变量信息}, ${x:=新的变量信息}, ${x:?没有设置变量提示信息}, ${x:+有设置变量提示信息}

1、${parameter-word}: 当调取变量没有定义过, 就返回word字符串信息
[root@manager scripts]# unset name 
[root@manager scripts]# echo $name


[root@manager scripts]# echo ${name:"bertwu"} # 没有定义过变量name,则使用-后的值
bertwu
[root@manager scripts]# gender= 
[root@manager scripts]# echo ${gender"male"}   # 定义过变量了,则使用变量的原值,哪怕变量的值为空值


[root@manager scripts]# age=18
[root@manager scripts]# echo ${age:19}  # 定义过变量了,则使用变量的原值
18

2、${parameter:-word}: 当调取变量信息值为空时或未定义变量, 就返回word字符串信息
[root@manager scripts]# unset x
[root@manager scripts]# unset y
[root@manager scripts]# unset z
[root@manager scripts]# 
[root@manager scripts]# 
[root@manager scripts]# echo ${x:-aaa} # 没有定义过变量x,则使用-后的值
aaa
[root@manager scripts]# y=
[root@manager scripts]# echo ${y:-aaa} # 定义过变量y,但变量y的值为空值,则使用-后的值
aaa


[root@manager scripts]# z=ccc
[root@manager scripts]# echo ${z:-aaa} # 定义过变量了,并且变量有一个非空的原值,则使用变量的原值
ccc


3{parameter:=word}:当调取变量信息值为空时或未定义,则设置指定字符串为新的变量值
[root@manager scripts]# unset x
[root@manager scripts]# echo ${x:=123}
123
[root@manager scripts]# echo $x
123

4、${parameter:?word}:当调取变量信息值为空时或未定义,指定为赋值的错误提示信息
[root@manager scripts]# unset x
[root@manager scripts]# echo ${x:?该变量未定义}
-bash: x: 该变量未定义


5、${parameter:+word}:当调取变量信息值为空时或未定义,不做任何处理,否则word字符串将替代变量值
[root@manager scripts]# unset x
[root@manager scripts]# 
[root@manager scripts]# 
[root@manager scripts]# echo ${x:+sxx}
[root@manager scripts]# x=123
[root@manager scripts]# echo $x
123
[root@manager scripts]# echo ${x:+sxx}
sxx

5.7 shell变量替换(类似python中的replace)

在不改变原有变量值的情况下,改变其输出值

变量说明
${变量/旧字符串/新字符串}替换变量内的旧字符串为新字符串,只替换第一个
${变量//旧字符串/新字符串}替换变量内的旧字符串为新字符串,全部替换
例1、将 $PATH 中的/bin/替换为/BIN
[root@manager scripts]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@manager scripts]# 

[root@manager scripts]# path=$PATH
[root@manager scripts]# echo ${path/bin/BIN} # 替换第一个
/usr/local/sBIN:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@manager scripts]# echo ${path//bin/BIN} # 替换所有
/usr/local/sBIN:/usr/local/BIN:/usr/sBIN:/usr/BIN:/root/BIN

例2、

需求:变量 string="Bigdata process is Hadoop, Hadoop is open source project" 执行脚本后,
打印输出 string 变量,并给出用户以下选项:
1)、打印 string 长度
2)、删除字符串中所有的 Hadoop
3)、替换第一个Hadoop为Linux
4)、替换全部Hadoop为Linux
用户输入数字1|2|3|4,可以执行对应项的功能,
输入q|Q则退出交互模式
[root@manager scripts]# cat string.sh 
[root@manager scripts]# cat string.sh 
#!/bin/bash
string="Bigdata process is Hadoop, Hadoop is open source project"
array=(1 2 3 4 "q" "Q")
    echo "
1)、打印 string 长度
2)、删除字符串中所有的 Hadoop
3)、替换第一个Hadoop为Linux
4)、替换全部Hadoop为Linux"
    
while true
do
    read -p "请输入以上功能编号,按q或Q退出 [1|2|3|4|q|Q]:" Choice

    if [[ ! "${array[*]}" =~ ${Choice} ]];then
	echo "输入不正确,请重新输入"
	continue
    fi

    if [ ${Choice^} == "Q" ];then
    	break
    fi
    
    if [ $Choice -eq 1 ];then
    	echo "长度为${#string}"
    elif [ $Choice -eq 2 ];then
    	echo ${string//Hadoop/}
    elif [ $Choice -eq 3 ];then
    	echo ${string/Hadoop/Linux}
    elif [ $Choice -eq 4 ];then
    	echo ${string//Hadoop/Linux}
    fi
done


六、变量运算 元字符

元字符指的是能够被shell解释的特殊字符,每个特殊字符都有其特殊含义,这些字符一方面可用于变量值的运算、我们可以称之为运算符,另外一方面可以和shell命令配合使用来达到更高级的效果

变量运算就是变量的加减乘除
通常整数运算有 expr、$(())、$[]等方式小数运算有bc、awk方式。

操作符含义
a+b
a-b
a*b
a/b
a%b取余
  1. bc是比较常用的linux计算工具了,而且支持浮点运算
[root@localhost ~]# res=`echo 1+1 | bc`
[root@localhost ~]# echo $res
2

[root@localhost ~]# res=`echo 10 % 3 | bc`
[root@localhost ~]# echo $res
1

[root@localhost ~]# res=`echo 1.2+1.3|bc`
[root@localhost ~]# echo $res
2.5

[root@localhost ~]# res=`echo 5.0+3.0|bc`
[root@localhost ~]# echo $res
8.0

[root@localhost ~]# res=`echo "scale=2;5.0/3.0"|bc`
[root@localhost ~]# echo $res
1.66

[root@localhost ~]# res=`echo "scale=2;5.0/6.0"|bc`
[root@localhost ~]# echo $res
.83

  1. expr不支持浮点数计算。而且要注意数字与运算符中的空格
[root@localhost ~]# res=`expr 5 / 3`  # 不支持浮点计算
[root@localhost ~]# echo $res
1

[root@localhost ~]# res=`expr 1+1`  # 注意:要有空格
[root@localhost ~]# echo $res
1+1
[root@localhost ~]# res=`expr 1 + 1`
[root@localhost ~]# echo $res
2

如果是乘法,如需要转义\*
[root@localhost ~]# expr 3 * 10
expr: syntax error
[root@localhost ~]# expr 3 \* 10
30
[root@manager scripts]# 

  1. $(()) 同expr,不支持浮点数运算
# 例如:
[root@localhost ~]# echo $((1+1))
2
[root@localhost ~]# echo $((1.0+2.0))  # 不支持浮点运算符
-bash: 1.0+2.0: 语法错误: 无效的算术运算符 (错误符号是 ".0+2.0")
           
# 注意:
echo $(($num1+$num2))  # 也可以简写为 echo $((num1+num2)) 
echo $(((5-3)*2))  # 可以嵌套括号
  1. $[]同expr以及$(()),不支持浮点运算​
[root@manager scripts]# num1=1
[root@manager scripts]# num2=2
[root@manager scripts]# echo $[num1+num2]
3
[root@manager scripts]# echo $[$num1+$num2] # 等同于 echo $[num1+num2]
3
[root@manager scripts]# echo $[1.3+3.1] 
-bash: 1.3+3.1: 语法错误: 无效的算术运算符 (错误符号是 ".3+3.1"
  1. let 不支持浮点数运算,而且不支持直接输出,只能赋值
[root@localhost ~]# let res=1+1
[root@localhost ~]# echo $res
2
[root@localhost ~]# 
[root@localhost ~]# let res=50/5
[root@localhost ~]# echo $res
10
[root@localhost ~]# let c=1.3*3
-bash: let: c=1.3*3: 语法错误: 无效的算术运算符 (错误符号是 ".3*3"
  1. 强调:整数与非整数之间运算会报错
[root@manager scripts]# expr 1 + a
expr: non-integer argument
[root@manager scripts]# expr 1 + 2
3
[root@manager scripts]# expr 1 + 1.1
expr: non-integer argument

练习
1、编写小脚本, 可以实现2位数加减乘除运算


[root@localhost shell]# cat a.sh 
#!/bin/bash

a=$1
b=$2

[ $# -ne 2 ] && {
    echo "请输入两位数字信息"
    exit
}

echo "a-b=$((a-b))"
echo "a+b=$((a+b))"
echo "a*b=$((a*b))"
echo "a/b=$((a/b))"

2、 判断输入参数是否是整数信息脚本


#!/bin/bash

a=$1
expr $a + 0 &>/dev/null  # 只有整型加0才不会报错,浮点数及字符与0详解都会报错
[ $? -eq 0 ] && echo "输入的是整数信息" || echo "输入的是非整数信息"

3、如何利用脚本计算1+2+3+4…10总和数值

方法一:
seq -s "+" 10|bc  # -s表示用+分割序列,默认为 \n

方法二:
echo {1..10}|tr " " "+"|bc
 
方法三:
awk 'BEGIN{for (i=1; i<=10; i++) a=a+i; print a}'

七、 常量

相对于变量,常量就是不可以被改变的量,即只能读不能改,所以又称之为只读变量

[root@manager scripts]# x=123
[root@manager scripts]# readonly x
[root@manager scripts]# x=456
-bash: x: readonly variable
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值