网站变移动网站,校园引流推广方法,网站建设seo 视频,暴雪第二部分#xff1a;Shell编程#xff08;一#xff09;
这一章我们正式进入 Shell 脚本编程#xff0c;重点讲解变量、字符串、数组、数学计算、选择结构、循环结构和函数。 Shell 的编程思想虽然和 C、Java、Python、C# 等其它编程语言类似#xff0c;但是在语法细节方…第二部分Shell编程一
这一章我们正式进入 Shell 脚本编程重点讲解变量、字符串、数组、数学计算、选择结构、循环结构和函数。 Shell 的编程思想虽然和 C、Java、Python、C# 等其它编程语言类似但是在语法细节方面差异还是比较大的有编程经验的程序员可能会觉得非常别扭要慢慢适应。
一、Shell变量Shell变量的定义、赋值和删除
变量是任何一种编程语言都必不可少的组成部分变量用来存放各种数据。脚本语言在定义变量时通常不需要指明类型直接赋值就可以Shell 变量也遵循这个规则。在 Bash shell 中每一个变量的值都是字符串无论你给变量赋值时有没有使用引号值都会以字符串的形式存储。 这意味着Bash shell 在默认情况下不会区分变量类型即使你将整数和小数赋值给变量它们也会被视为字符串这一点和大部分的编程语言不同。例如在C语言或者C 中变量分为整数、小数、字符串、布尔等多种类型。 当然如果有必要你也可以使用Shell declare关键字显式定义变量的类型但在一般情况下没有这个需求Shell 开发者在编写代码时自行注意值的类型即可。
1、定义变量
Shell 支持以下三种定义变量的方式 variablevalue variablevalue variablevalue variable 是变量名value 是赋给变量的值。如果 value 不包含任何空白符例如空格、Tab 缩进等那么可以不使用引号如果 value 包含了空白符那么就必须使用引号包围起来。使用单引号和使用双引号也是有区别的稍后我们会详细说明。注意赋值号的周围不能有空格这可能和你熟悉的大部分编程语言都不一样。 Shell 变量的命名规范和大部分编程语言都一样
变量名由数字、字母、下划线组成必须以字母或者下划线开头不能使用 Shell 里的关键字通过 help 命令可以查看保留关键字。
变量定义举例 urlhttp://c.biancheng.net/shell/ echo $url nameC语言中文网 echo $name author严长生 echo $author 2、使用变量
使用一个定义过的变量只要在变量名前面加美元符号$即可如 author严长生 echo $author echo ${author} 变量名外面的花括号{ }是可选的加不加都行加花括号是为了帮助解释器识别变量的边界比如下面这种情况 skillJava echo I am good at ${skill}Script 如果不给 skill 变量加花括号写成echo I am good at $skillScript解释器就会把 $skillScript 当成一个变量其值为空代码执行结果就不是我们期望的样子了。推荐给所有变量加上花括号{ }这是个良好的编程习惯。
3、修改变量的值
已定义的变量可以被重新赋值如 urlhttp://c.biancheng.net echo ${url} urlhttp://c.biancheng.net/shell/ echo ${url} 第二次对变量赋值时不能在变量名前加$只有在使用变量时才能加$。
4、单引号和双引号的区别
前面我们还留下一个疑问定义变量时变量的值可以由单引号 包围也可以由双引号 包围它们到底有什么区别呢不妨以下面的代码为例来说明 #!/bin/bash urlhttp://c.biancheng.net website1C语言中文网${url} website2C语言中文网${url} echo $website1 echo $website2 运行结果 C语言中文网${url} C语言中文网http://c.biancheng.net 以单引号 包围变量的值时单引号里面是什么就输出什么即使内容中有变量和命令命令需要反引起来也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况即不希望解析变量、命令等的场景。 以双引号 包围变量的值时输出时会先解析里面的变量和命令而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。我的建议如果变量的内容是数字那么可以不加引号如果真的需要原样输出就加单引号其他没有特别要求的字符串等最好都加上双引号定义变量时加双引号是最常见的使用场景。
5、将命令的结果赋值给变量
Shell 也支持将命令的执行结果赋值给变量常见的有以下两种方式 variablecommand variable$(command) 第一种方式把命令用反引号 位于 Esc 键的下方包围起来反引号和单引号非常相似容易产生混淆所以不推荐使用这种方式第二种方式把命令用$()包围起来区分更加明显所以推荐使用这种方式。 例如我在 demo 目录中创建了一个名为 log.txt 的文本文件用来记录我的日常工作。下面的代码中使用 cat 命令将 log.txt 的内容读取出来并赋值给一个变量然后使用 echo 命令输出。 [mozhiyanlocalhost ~]$ cd demo [mozhiyanlocalhost demo]$ log$(cat log.txt) [mozhiyanlocalhost demo]$ echo $log 严长生正在编写Shell教程教程地址http://c.biancheng.net/shell/ [mozhiyanlocalhost demo]$ logcat log.txt [mozhiyanlocalhost demo]$ echo $log 严长生正在编写Shell教程教程地址http://c.biancheng.net/shell/ 6、只读变量 readonly 命令
使用 readonly 命令可以将变量定义为只读变量只读变量的值不能被改变。 下面的例子尝试更改只读变量结果报错 #!/bin/bash myUrlhttp://c.biancheng.net/shell/ readonly myUrl myUrlhttp://c.biancheng.net/shell/ 运行脚本结果如下 bash: myUrl: This variable is read only. 7、删除变量 unset 命令
使用 unset 命令可以删除变量。语法 unset variable_name 变量被删除后不能再次使用unset 命令不能删除只读变量。
举个例子 #!/bin/sh myUrlhttp://c.biancheng.net/shell/ unset myUrl echo $myUrl 上面的脚本没有任何输出。
二、Shell变量的作用域全局变量、环境变量和局部变
Shell 变量的作用域Scope就是 Shell 变量的有效范围可以使用的范围。
在不同的作用域中同名的变量不会相互干涉就好像 A 班有个叫小明的同学B 班也有个叫小明的同学虽然他们都叫小明对应于变量名但是由于所在的班级对应于作用域不同所以不会造成混乱。但是如果同一个班级中有两个叫小明的同学就必须用类似于“大小明”、“小小明”这样的命名来区分他们。 Shell 变量的作用域可以分为三种
有的变量只能在函数内部使用这叫做局部变量local variable有的变量可以在当前 Shell 进程中使用这叫做全局变量global variable而有的变量还可以在子进程中使用这叫做环境变量environment variable。
1、Shell 局部变量 函数内部使用
Shell 也支持自定义函数但是 Shell 函数和 C、Java、C# 等其他编程语言函数的一个不同点就是在 Shell 函数中定义的变量默认也是全局变量它和在函数外部定义变量拥有一样的效果。请看下面的代码 #!/bin/bash #定义函数 function func(){ a99 } #调用函数 func #输出函数内部的变量 echo $a 输出结果 99 a 是在函数内部定义的但是在函数外部也可以得到它的值证明它的作用域是全局的而不是仅限于函数内部。要想变量的作用域仅限于函数内部可以在定义时加上local命令此时该变量就成了局部变量。请看下面的代码 #!/bin/bash #定义函数 function func(){ local a99 } #调用函数 func #输出函数内部的变量 echo $a 输出结果为空表明变量 a 在函数外部无效是一个局部变量。 Shell 变量的这个特性和 JavaScript 中的变量是类似的。在 JavaScript 函数内部定义的变量默认也是全局变量只有加上var关键字它才会变成局部变量。
2、Shell 全局变量 当前 Shell 进程中使用
所谓全局变量就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域彼此之间互不影响。在 Shell 中定义的变量默认就是全局变量。 想要实际演示全局变量在不同 Shell 进程中的互不相关性可在图形界面下同时打开两个 Shell或使用两个终端远程连接到服务器SSH。 首先打开一个 Shell 窗口定义一个变量 a 并赋值为 99然后打印这时在同一个 Shell 窗口中是可正确打印变量 a 的值的。然后再打开一个新的 Shell 窗口同样打印变量 a 的值但结果却为空如图 1 所示。 图1打开两个 Shell 窗口 这说明全局变量 a 仅仅在定义它的第一个 Shell 进程中有效对新的 Shell 进程没有影响。这很好理解就像小王家和小徐家都有一部电视机变量名相同但是同一时刻小王家和小徐家的电视中播放的节目可以是不同的变量值不同。 需要强调的是全局变量的作用范围是当前的 Shell 进程而不是当前的 Shell 脚本文件它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程打开多个 Shell 窗口就创建了多个 Shell 进程每个 Shell 进程都是独立的拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件此时全局变量在这些脚本文件中都有效。 例如现在有两个 Shell 脚本文件分别是 a.sh 和 b.sh。a.sh 的代码如下 #!/bin/bash echo $a b200 b.sh 的代码如下 #!/bin/bash echo $b 打开一个 Shell 窗口输入以下命令 [c.biancheng.net]$ a99 [c.biancheng.net]$ . ./a.sh 99 [c.biancheng.net]$ . ./b.sh 200 这三条命令都是在一个进程中执行的从输出结果可以发现在 Shell 窗口中以命令行的形式定义的变量 a在 a.sh 中有效在 a.sh 中定义的变量 b在 b.sh 中也有效变量 b 的作用范围已经超越了 a.sh。 注意必须在当前进程中运行 Shell 脚本不能在新进程中运行 Shell 脚本不了解的读者请转到《Shell脚本Linux Shell脚本学习指南第一部分Shell基础二十一、执行Shell脚本多种方法》。
3、Shell 环境变量 在子进程中使用
全局变量只在当前 Shell 进程中有效对其它 Shell 进程和子进程都无效。如果使用export命令将全局变量导出那么它就在所有的子进程中也有效了这称为“环境变量”。环境变量被创建时所处的 Shell 进程称为父进程如果在父进程中再创建一个新的进程来执行 Shell 命令那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时它会继承父进程的环境变量为自己所用所以说环境变量可从父进程传给子进程。不难理解环境变量还可以传递给孙进程。注意两个没有父子关系的 Shell 进程是不能传递环境变量的并且环境变量只能向下传递而不能向上传递即“传子不传父”。创建 Shell 子进程最简单的方式是运行 bash 命令如图 2 所示。 图2进入 Shell 子进程 通过exit命令可以一层一层地退出 Shell。 下面演示一下环境变量的使用 [c.biancheng.net]$ a22 #定义一个全局变量 [c.biancheng.net]$ echo $a #在当前Shell中输出a成功 22 [c.biancheng.net]$ bash #进入Shell子进程 [c.biancheng.net]$ echo $a #在子进程中输出a失败 [c.biancheng.net]$ exit #退出Shell子进程返回上一级Shell exit [c.biancheng.net]$ export a #将a导出为环境变量 [c.biancheng.net]$ bash #重新进入Shell子进程 [c.biancheng.net]$ echo $a #在子进程中再次输出a成功 22 [c.biancheng.net]$ exit #退出Shell子进程 exit [c.biancheng.net]$ exit #退出父进程结束整个Shell会话 可以发现默认情况下a 在 Shell 子进程中是无效的使用 export 将 a 导出为环境变量后在子进程中就可以使用了。export a这种形式是在定义变量 a 以后再将它导出为环境变量如果想在定义的同时导出为环境变量可以写作export a22。 我们一直强调的是环境变量在 Shell 子进程中有效并没有说它在所有的 Shell 进程中都有效如果你通过终端创建了一个新的 Shell 窗口那它就不是当前 Shell 的子进程环境变量对这个新的 Shell 进程仍然是无效的。请看下图 第一个窗口中的环境变量 a 在第二个窗口中就无效。 环境变量也是临时的
通过 export 导出的环境变量只对当前 Shell 进程以及所有的子进程有效如果最顶层的父进程被关闭了那么环境变量也就随之消失了其它的进程也就无法使用了所以说环境变量也是临时的。 有读者可能会问如果我想让一个变量在所有 Shell 进程中都有效不管它们之间是否存在父子关系该怎么办呢 只有将变量写入 Shell 配置文件中才能达到这个目的Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作如果将变量放在配置文件中那么每次启动进程都会定义这个变量。不知道如何修改配置文件的读者请猛击《Shell脚本Linux Shell脚本学习指南第一部分Shell基础二十三、Shell配置文件的加载和十四、如何编写自己的Shell配置文件执行Shell脚本多种方法》。
三、Shell命令替换将命令的输出结果赋值给变量
Shell 命令替换是指将命令的输出结果赋值给某个变量。比如在某个目录中输入 ls 命令可查看当前目录中所有的文件但如何将输出内容存入某个变量中呢这就需要使用命令替换了这也是 Shell 编程中使用非常频繁的功能。 Shell 中有两种方式可以完成命令替换一种是反引号 一种是$()使用方法如下 variablecommands variable$(commands) 其中variable 是变量名commands 是要执行的命令。commands 可以只有一个命令也可以有多个命令多个命令之间以分号;分隔。 例如date 命令用来获得当前的系统时间使用命令替换可以将它的结果赋值给一个变量。 #!/bin/bash begin_timedate #开始时间使用替换 sleep 20s #休眠20秒 finish_time$(date) #结束时间使用$()替换 echo Begin time: $begin_time echo Finish time: $finish_time 运行脚本20 秒后可以看到输出结果 Begin time: 2019年 04月 19日 星期五 09:59:58 CST Finish time: 2019年 04月 19日 星期五 10:00:18 CST 使用 data 命令的%s格式控制符可以得到当前的 UNIX 时间戳这样就可以直接计算脚本的运行时间了。UNIX 时间戳是指从 1970 年 1 月 1 日 00:00:00 到目前为止的秒数不了解的读者请猛击这里。 #!/bin/bash begin_timedate %s #开始时间使用替换 sleep 20s #休眠20秒 finish_time$(date %s) #结束时间使用$()替换 run_time$((finish_time - begin_time)) #时间差 echo begin time: $begin_time echo finish time: $finish_time echo run time: ${run_time}s 运行脚本20 秒后可以看到输出结果 begin time: 1555639864 finish time: 1555639884 run time: 20s 第 6 行代码中的(( ))是 Shell 数学计算命令。和C、C#、Java 等编程语言不同在 Shell 中进行数据计算不那么方便必须使用专门的数学计算命令(( ))就是其中之一。 注意如果被替换的命令的输出内容包括多行也即有换行符或者含有多个连续的空白符那么在输出变量时应该将变量用双引号包围否则系统会使用默认的空白符来填充这会导致换行无效以及连续的空白符被压缩成一个。请看下面的代码 #!/bin/bash LSLls -l echo $LSL #不使用双引号包围 echo -------------------------- #输出分隔符 echo $LSL #使用引号包围 运行结果 total 8 drwxr-xr-x. 2 root root 21 7月 1 2016 abc -rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh -rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh~ -------------------------- total 8 drwxr-xr-x. 2 root root 21 7月 1 2016 abc -rw-rw-r--. 1 mozhiyan mozhiyan 147 10月 31 10:29 demo.sh -rw-rw-r--. 1 mozhiyan mozhiyan 35 10月 31 10:20 demo.sh~ 所以为了防止出现格式混乱的情况我建议在输出变量时加上双引号。
再谈反引号和 $()
原则上讲上面提到的两种变量替换的形式是等价的可以随意使用但是反引号毕竟看起来像单引号有时候会对查看代码造成困扰而使用 $() 就相对清晰能有效避免这种混乱。而且有些情况必须使用 $()$() 支持嵌套反引号不行。 下面的例子演示了使用计算 ls 命令列出的第一个文件的行数这里使用了两层嵌套。 [c.biancheng.net]$ Fir_File_Lines$(wc -l $(ls | sed -n 1p)) [c.biancheng.net]$ echo $Fir_File_Lines 36 anaconda-ks.cfg 要注意的是$() 仅在 Bash Shell 中有效而反引号可在多种 Shell 中使用。所以这两种命令替换的方式各有特点究竟选用哪种方式全看个人需求。
四、Shell位置参数命令行参数
我们先来说一下 Shell 位置参数是怎么回事。 运行 Shell 脚本文件时我们可以给它传递一些参数这些参数在脚本文件内部可以使用$n的形式来接收例如$1 表示第一个参数$2 表示第二个参数依次类推。 同样在调用函数时也可以传递参数。Shell 函数参数的传递和其它编程语言不同没有所谓的形参和实参在定义函数时也不用指明参数的名字和数目。换句话说定义 Shell 函数时不能带参数但是在调用函数时却可以传递参数这些传递进来的参数在函数内部就也使用$n的形式接收例如$1 表示第一个参数$2 表示第二个参数依次类推。这种通过$n的形式来接收的参数在 Shell 中称为位置参数。 在讲解《一、Shell变量Shell变量的定义、赋值和删除》一节时我们提到变量的名字必须以字母或者下划线开头不能以数字开头但是位置参数却偏偏是数字这和变量的命名规则是相悖的所以我们将它们视为“特殊变量”。 除了 $nShell 中还有 $#、$*、$、$?、$$ 几个特殊参数我们将在下节讲解。
1、给脚本文件传递位置参数
请编写下面的代码并命名为 test.sh #!/bin/bash echo Language: $1 echo URL: $2 运行 test.sh并附带参数 [mozhiyanlocalhost ~]$ cd demo [mozhiyanlocalhost demo]$ . ./test.sh Shell http://c.biancheng.net/shell/ Language: Shell URL: http://c.biancheng.net/shell/ 其中Shell是第一个位置参数http://c.biancheng.net/shell/是第二个位置参数两者之间以空格分隔。
2、给函数传递位置参数
请编写下面的代码并命名为 test.sh #!/bin/bash #定义函数 function func(){ echo Language: $1 echo URL: $2 } #调用函数 func C http://c.biancheng.net/cplus/ 运行 test.sh [mozhiyanlocalhost ~]$ cd demo [mozhiyanlocalhost demo]$ . ./test.sh Language: C URL: http://c.biancheng.net/cplus/ 1注意事项
如果参数个数太多达到或者超过了 10 个那么就得用${n}的形式来接收了例如 ${10}、${23}。{ }的作用是为了帮助解释器识别参数的边界这跟使用变量时加{ }是一样的效果。
2下节展望
在 Shell 中传递位置参数时除了能单独取得某个具体的参数还能取得所有参数的列表以及参数的个数等信息下节我们将会详细讲解。
五、Shell特殊变量Shell $#、$*、$、$?、$$
上节我们讲到了 $n它是特殊变量的一种用来接收位置参数。本节我们继续讲解剩下的几个特殊变量它们分别是$#、$*、$、$?、$$。 Shell 特殊变量及其含义 变量含义$0当前脚本的文件名。$nn≥1传递给脚本或函数的参数。n 是一个数字表示第几个参数。例如第一个参数是 $1第二个参数是 $2。$#传递给脚本或函数的参数个数。$*传递给脚本或函数的所有参数。$传递给脚本或函数的所有参数。当被双引号 包含时$ 与 $* 稍有不同我们将在《六、Shell $*和$之间的区别》一节中详细讲解。$?上个命令的退出状态或函数的返回值我们将在《七、Shell $?》一节中详细讲解。$$当前 Shell 进程 ID。对于 Shell 脚本就是这些脚本所在的进程 ID。
下面我们通过两个例子来演示。
1、给脚本文件传递参数
编写下面的代码并保存为 test.sh #!/bin/bash echo Process ID: $$ echo File Name: $0 echo First Parameter : $1 echo Second Parameter : $2 echo All parameters 1: $ echo All parameters 2: $* echo Total: $# 运行 test.sh并附带参数 [mozhiyanlocalhost demo]$ . ./test.sh Shell Linux Process ID: 5943 File Name: bash First Parameter : Shell Second Parameter : Linux All parameters 1: Shell Linux All parameters 2: Shell Linux Total: 2 2、给函数传递参数
编写下面的代码并保存为 test.sh #!/bin/bash# 定义函数 function func(){ echo Language: $1 echo URL: $2 echo First Parameter : $1 echo Second Parameter : $2 echo All parameters 1: $ echo All parameters 2: $* echo Total: $# } #调用函数 func Java http://c.biancheng.net/java/ 运行结果为 Language: Java URL: http://c.biancheng.net/java/ First Parameter : Java Second Parameter : http://c.biancheng.net/java/ All parameters 1: Java http://c.biancheng.net/java/ All parameters 2: Java http://c.biancheng.net/java/ Total: 2 六、Shell $*和$之间的区别
$* 和 $ 都表示传递给函数或脚本的所有参数我们已在《五、Shell特殊变量》一节中进行了演示本节重点说一下它们之间的区别。 当 $* 和 $ 不被双引号 包围时它们之间没有任何区别都是将接收到的每个参数看做一份数据彼此之间以空格来分隔。 但是当它们被双引号 包含时就会有区别了
$*会将所有的参数从整体上看做一份数据而不是把每个参数都看做一份数据。$仍然将每个参数都看作一份数据彼此之间是独立的。
比如传递了 5 个参数那么对于$*来说这 5 个参数会合并到一起形成一份数据它们之间是无法分割的而对于$来说这 5 个参数是相互独立的它们是 5 份数据。 如果使用 echo 直接输出$*和$做对比是看不出区别的但如果使用 for 循环来逐个输出数据立即就能看出区别来。
编写下面的代码并保存为 test.sh #!/bin/bash echo print each param from \\$*\ for var in $* do echo $var done echo print each param from \\$\ for var in $ do echo $var done 运行 test.sh并附带参数 [mozhiyanlocalhost demo]$ . ./test.sh a b c d print each param from $* a b c d print each param from $ a b c d 从运行结果可以发现对于$*只循环了 1 次因为它只有 1 分数据对于$循环了 5 次因为它有 5 份数据。
七、Shell $?获取函数返回值或者上一个命令的退出状态
$? 是一个特殊变量用来获取上一个命令的退出状态或者上一个函数的返回值。 所谓退出状态就是上一个命令执行后的返回结果。退出状态是一个数字一般情况下大部分命令执行成功会返回 0失败返回 1这和C语言的 main() 函数是类似的。 不过也有一些命令返回其他值表示不同类型的错误。
1、$? 获取上一个命令的退出状态
编写下面的代码并保存为 test.sh #!/bin/bash if [ $1 100 ] then exit 0 #参数正确退出状态为0 else exit 1 #参数错误退出状态1 fi exit表示退出当前 Shell 进程我们必须在新进程中运行 test.sh否则当前 Shell 会话终端窗口会被关闭我们就无法取得它的退出状态了。 例如运行 test.sh 时传递参数 100 [mozhiyanlocalhost ~]$ cd demo [mozhiyanlocalhost demo]$ bash ./test.sh 100 #作为一个新进程运行 [mozhiyanlocalhost demo]$ echo $? 0 再如运行 test.sh 时传递参数 89 [mozhiyanlocalhost demo]$ bash ./test.sh 89 #作为一个新进程运行 [mozhiyanlocalhost demo]$ echo $? 1 2、$? 获取函数的返回值
编写下面的代码并保存为 test.sh #!/bin/bash #得到两个数相加的和 function add(){ return expr $1 $2 } add 23 50 #调用函数 echo $? #获取函数返回值 运行结果 73 有C、C#、Java等编程经验的读者请注意严格来说Shell 函数中的 return 关键字用来表示函数的退出状态而不是函数的返回值Shell 不像其它编程语言没有专门处理返回值的关键字。 以上处理方案在其它编程语言中没有任何问题但是在 Shell 中是非常错误的Shell 函数的返回值和其它编程语言大有不同后面将展开讨论。
八、Shell字符串详解
字符串String就是一系列字符的组合。字符串是 Shell 编程中最常用的数据类型之一除了数字和字符串也没有其他类型了。 字符串可以由单引号 包围也可以由双引号 包围也可以不用引号。它们之间是有区别的稍后我们会详解。 字符串举例 str1c.biancheng.net str2shell script str3C语言中文网 下面我们说一下三种形式的区别 1) 由单引号 包围的字符串
任何字符都会原样输出在其中使用变量是无效的。字符串中不能出现单引号即使对单引号进行转义也不行。
2) 由双引号 包围的字符串
如果其中包含了某个变量那么该变量会被解析得到该变量的值而不是原样输出。字符串中可以出现双引号只要它被转义了就行。
3) 不被引号包围的字符串
不被引号包围的字符串中出现变量时也会被解析这一点和双引号 包围的字符串一样。字符串中不能出现空格否则空格后边的字符串会作为其他变量或者命令解析。
我们通过代码来演示一下三种形式的区别 #!/bin/bash n74 str1c.biancheng.net$n str2shell \script\ $n str3C语言中文网 $n echo $str1 echo $str2 echo $str3 运行结果 c.biancheng.net74 shell script 74 C语言中文网 $n str1 中包含了$n它被解析为变量 n 的引用。$n后边有空格紧随空格的是 str2Shell 将 str2 解释为一个新的变量名而不是作为字符串 str1 的一部分。 str2 中包含了引号但是被转义了由反斜杠\开头的表示转义字符。str2 中也包含了$n它也被解析为变量 n 的引用。 str3 中也包含了$n但是仅仅是作为普通字符并没有解析为变量 n 的引用。
获取字符串长度
在 Shell 中获取字符串长度很简单具体方法如下 ${#string_name} string_name 表示字符串名字。 下面是具体的演示 #!/bin/bash strhttp://c.biancheng.net/shell/ echo ${#str} 运行结果 29 九、Shell字符串拼接连接、合并
在脚本语言中字符串的拼接也称字符串连接或者字符串合并往往都非常简单例如
在 PHP中使用.即可连接两个字符串在JavaScript 中使用即可将两个字符串合并为一个。
然而在 Shell 中你不需要使用任何运算符将两个字符串并排放在一起就能实现拼接非常简单粗暴。请看下面的例子 #!/bin/bash nameShell urlhttp://c.biancheng.net/shell/ str1$name$url #中间不能有空格 str2$name $url #如果被双引号包围那么中间可以有空格 str3$name: $url #中间可以出现别的字符串 str4$name: $url #这样写也可以 str5${name}Script: ${url}index.html #这个时候需要给变量名加上大括号 echo $str1 echo $str2 echo $str3 echo $str4 echo $str5 运行结果 Shellhttp://c.biancheng.net/shell/ Shell http://c.biancheng.net/shell/ Shell: http://c.biancheng.net/shell/ Shell: http://c.biancheng.net/shell/ ShellScript: http://c.biancheng.net/shell/index.html 对于第 7 行代码$name 和 $url 之间之所以不能出现空格是因为当字符串不被任何一种引号包围时遇到空格就认为字符串结束了空格后边的内容会作为其他变量或者命令解析。 对于第 10 行代码加{ }是为了帮助解释器识别变量的边界。 Shell 这种拼接字符串的方式和Python 非常类似Python 既支持用拼接字符串也支持将两个字符串放在一起《五Python字符串常用方法详解_别致的SmallSix的博客-CSDN博客》了解详情。
十、Shell字符串截取非常详细
Shell 截取字符串通常有两种方式从指定位置开始截取和从指定字符子字符串开始截取。
1、从指定位置开始截取
这种方式需要两个参数除了指定起始位置还需要截取长度才能最终确定要截取的字符串。 既然需要指定起始位置那么就涉及到计数方向的问题到底是从字符串左边开始计数还是从字符串右边开始计数。答案是 Shell 同时支持两种计数方式。
1从字符串左边开始计数
如果想从字符串的左边开始计数那么截取字符串的具体格式如下 ${string: start :length} 其中string 是要截取的字符串start 是起始位置从左边开始从 0 开始计数length 是要截取的长度省略的话表示直到字符串的末尾。 例如 urlc.biancheng.net echo ${url: 2: 9} 结果为biancheng。 再如 urlc.biancheng.net echo ${url: 2} #省略 length截取到字符串末尾 结果为biancheng.net。
2从右边开始计数
如果想从字符串的右边开始计数那么截取字符串的具体格式如下 ${string: 0-start :length} 同第 1) 种格式相比第 2) 种格式仅仅多了0-这是固定的写法专门用来表示从字符串右边开始计数。 这里需要强调两点
从左边开始计数时起始数字是 0这符合程序员思维从右边开始计数时起始数字是 1这符合常人思维。计数方向不同起始数字也不同。不管从哪边开始计数截取方向都是从左到右。
例如 urlc.biancheng.net echo ${url: 0-13: 9} 结果为biancheng。从右边数b是第 13 个字符。 再如 urlc.biancheng.net echo ${url: 0-13} #省略 length直接截取到字符串末尾 结果为biancheng.net。
2、从指定字符子字符串开始截取
这种截取方式无法指定字符串长度只能从指定字符子字符串截取到字符串末尾。Shell 可以截取指定字符子字符串右边的所有字符也可以截取左边的所有字符。
1 使用 # 号截取右边字符
使用#号可以截取指定字符或者子字符串右边的所有字符具体格式如下 ${string#*chars} 其中string 表示要截取的字符chars 是指定的字符或者子字符串*是通配符的一种表示任意长度的字符串。*chars连起来使用的意思是忽略左边的所有字符直到遇见 charschars 不会被截取。 请看下面的例子 urlhttp://c.biancheng.net/index.html echo ${url#*:} 结果为//c.biancheng.net/index.html。 以下写法也可以得到同样的结果 echo ${url#*p:} echo ${url#*ttp:} 如果不需要忽略 chars 左边的字符那么也可以不写*例如 urlhttp://c.biancheng.net/index.html echo ${url#http://} 结果为c.biancheng.net/index.html。 注意以上写法遇到第一个匹配的字符子字符串就结束了。例如 urlhttp://c.biancheng.net/index.html echo ${url#*/} 结果为/c.biancheng.net/index.html。url 字符串中有三个/输出结果表明Shell 遇到第一个/就匹配结束了。 如果希望直到最后一个指定字符子字符串再匹配结束那么可以使用##具体格式为 ${string##*chars} 请看下面的例子 #!/bin/bash urlhttp://c.biancheng.net/index.html echo ${url#*/} #结果为 /c.biancheng.net/index.html echo ${url##*/} #结果为 index.html str---aaaa echo ${str#*aa} #结果为 aa echo ${str##*aa} #结果为 2 使用 % 截取左边字符
使用%号可以截取指定字符或者子字符串左边的所有字符具体格式如下 ${string%chars*} 请注意*的位置因为要截取 chars 左边的字符而忽略 chars 右边的字符所以*应该位于 chars 的右侧。其他方面%和#的用法相同这里不再赘述仅举例说明 #!/bin/bash urlhttp://c.biancheng.net/index.html echo ${url%/*} #结果为 http://c.biancheng.net echo ${url%%/*} #结果为 http: str---aaaa echo ${str%aa*} #结果为 ---aa echo ${str%%aa*} #结果为 --- 3、汇总
最后我们对以上 8 种格式做一个汇总请看下表
格式说明${string: start :length}从 string 字符串的左边第 start 个字符开始向右截取 length 个字符。${string: start}从 string 字符串的左边第 start 个字符开始截取直到最后。${string: 0-start :length}从 string 字符串的右边第 start 个字符开始向右截取 length 个字符。${string: 0-start}从 string 字符串的右边第 start 个字符开始截取直到最后。${string#*chars}从 string 字符串第一次出现 *chars 的位置开始截取 *chars 右边的所有字符。${string##*chars}从 string 字符串最后一次出现 *chars 的位置开始截取 *chars 右边的所有字符。${string%*chars}从 string 字符串第一次出现 *chars 的位置开始截取 *chars 左边的所有字符。${string%%*chars}从 string 字符串最后一次出现 *chars 的位置开始截取 *chars 左边的所有字符。