当前位置 : 首页 » 文章分类 :  开发  »  Linux-Shell脚本

Linux-Shell脚本

Shell 笔记


Shell脚本概述

#! 解释器声明,是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell
例如 #!/bin/sh,shell 脚本通常以此开头,用来告诉系统 /bin/sh 是用来执行该文件的程序。

执行Shell脚本的两种方法

作为可执行程序

开头声明”#!/bin/sh”,将脚本保存为test.sh,chmod +x test.sh 使脚本具有执行权限,./test.sh执行脚本。
注意,一定要写成./test.sh,而不是test.sh,运行其它二进制的程序也一样,直接写test.sh,linux系统会去PATH里寻找有没有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找。

作为解释器参数

这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:
/bin/sh test.sh
/bin/php test.php
这种方式运行的脚本,不需要在第一行指定解释器信息。
如果在test.sh开头声明”#!/bin/sh”,则./test.sh 等价于 /bin/sh test.sh
其实,若不指定文件是什么格式,内核一律当作shell脚本,所以即使开头不写”#!/bin/sh”,加执行权限后也可以./tesh.sh执行成功


多个命令一起执行的方法

在命令行可以一次执行多个命令,有以下几种:

; 多个命令顺序执行结果不影响

1 每个命令之间用 ; 隔开
说明:各命令的执行结果,不会影响其它命令的执行。换句话说,各个命令都会执行,
但不保证每个命令都执行成功。
cd /home/PyTest/src; python suning.py

&& 前命令成功才执行后续命令

2 每个命令之间用 && 隔开
说明:若前面的命令执行成功,才会去执行后面的命令。这样可以保证所有的命令执行完毕后,执行过程都是成功的。
cd /home/PyTest/src && python suning.py

|| 前命令成功则不执行后续命令

3、 每个命令之间用 || 隔开
说明:|| 是或的意思,只有前面的命令执行失败后才去执行下一条命令,直到执行成功
cd /home/masikkk/dir || echo "dir not exist"
dir 如果存在,后面的 echo 就不会执行。否则打印 dir not exist


Shell提示符$和#的区别

对于普通用户,Bash shell 默认的提示符是美元符号 $
对于超级用户(root 用户),Bash Shell 默认的提示符是井号#
该符号表示 Shell 等待输入命令。

不同的 Linux 发行版使用的提示符格式不同。例如在 CentOS 中,默认的提示符格式为:
[user@localhost ~]$
这种格式包含了以下三个方面的信息:
启动 Shell 的用户名,也即 user
本地主机名称,也即 localhost;
当前目录,波浪号~是主目录的简写表示法。


shell初始化过程

shell的初始化过程是这样的:

  1. bash 检查文件/etc/profile 是否存在, 如果存在,bash 就读取该文件,否则,跳过

  2. bash 检查家目录下的文件.bash_profile 是否存在。 如果存在,bash 就读取該文件,否则,跳过

  3. bash 检查主目录下的.bash_login 是否存在。 如果存在,bash 就读取该文件,否则,跳过

  4. bash 检查主目录下的文件.profile 是否存在 如果存在, bash 就读取该文件,否则,跳过。

这些步骤都执行完后,就出现提示符了, 非root用户默认提示符是 $, root用户默认提示符为#.


换行与注释

行尾添加 \ 进行换行
行首添加 # 进行注释


重定向

重定向命令列表

命令 说明
command > file 将标准输出重定向到 file,与 1>相同
command < file 将标准输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。
n > file 将文件描述符为 n 的文件重定向到 file。
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m 将输出文件 m 和 n 合并。
n <& m 将输入文件 m 和 n 合并。
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。

输出重定向

command1 > file1
上面这个命令执行command1然后将输出的内容存入file1。
执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
重定向由shell处理,command1命令并不知道自己的输出有变化。
注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。

>是重定向标准输出到文件,如果文件不存在,就创建文件;如果文件存在,就将其清空;
一般我们备份清理日志文件的时候,就是这种方法:先备份日志,再用>,将日志文件清空(文件大小变成0字节);
shell遇到”>”操作符,会判断右边文件是否存在,如果存在就先删除,并且创建新文件。不存在直接创建。 无论左边命令执行是否成功。右边文件都会变为空。
>>,这个是将输出内容追加到目标文件中。如果文件不存在,就创建文件;如果文件存在,则将新的内容追加到那个文件的末尾,该文件中的原有内容不受影响。

利用>/dev/null丢弃不需要的输出

输入重定向

command1 < file1
这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

示例
统计 users 文件的行数
wc -l users
结果为:
2 users
也可以将输入重定向到 users 文件:
wc -l < users
结果为:
2
注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
还有个例子:
more /vim/vimrc:输出有百分比,因为读的文件,知道文件长度
more < /vim/vimrc:输出无百分比,从重定向后的stdin读,不知道百分比

<< 多行输入重定向

<< 是在 Linux Shell 中的一种特殊的重定向方式,它的基本的形式如下

cmd << delimiter
  Here Document Content
delimiter

它的作用就是将两个 delimiter 之间的内容(Here Document Content 部分) 传递给cmd 作为输入参数。

1 比如在终端中输入 cat << eof ,系统会提示继续进行输入,输入多行信息再输入 eof,中间输入的信息将会显示在屏幕上。

$ cat << eof
heredoc> 123
heredoc> abd
heredoc> eof
123
abd

2 比如命令行输入邮件时,也可以用 << 输入多行内容到 mail 命令

#!/bin/sh
mail xxx@aaa.com << EOT
HOST xxx
DATE `date`
NOTE xxxx
EOT

3 将一个文件的内容输出到另一个文件中
cat fileA > fileB
或者
cat << EOF > fileB 执行后提示用户输入内容,多行输入结束后再输入 EOF,用户的输入内容被保存到了 fileB 中。

4 向文件 output.txt 里输入内容

# cat << EOF >output.txt
> 123123123
> 3452354345
> asdfasdfs
> EOF

追加

# cat << EOF >>output.txt
> 123123123
> 3452354345
> asdfasdfs
> EOF

同时输入输出重定向
command1 < infile > outfile
同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。
例如
cat >catfile <test.sh
cat <test.sh >catfile
cat 从test.sh 获得输入数据,然后输出给文件catfile

stdin(0),stdout(1),stderr(2)

每个进程默认打开三个文件:stdin(0), stdout(1), stderr(2),0都指向终端输入,1,2指向终端输出。继承自同一进程组的shell进程,shell进程继承自开启时的getty进程。
每个进程默认(无参数时)从stdin输入,默认向stdout输出
终端操作模式为行缓冲模式时,需按下回车将内容输入进程。
从某终端登录后,执行的shell是此终端对应的会话组中的shell进程,所以不会显示到其他终端的输出中。

/dev/stdin -> /proc/self/fd/0
/dev/stdout -> /proc/self/fd/1
/dev/stderr -> /proc/self/fd/2

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。重定向方式:<
  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。重定向方式:> 或 1>
  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。重定向方式:2>

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

> 为标准输出重定向,与 1> 相同
& 代表标准输出和标准错误输出,&> 表示同时重定向标准输出和标准错误
2>&1:将标准错误2重定向到标准输出1中的,此处1前面的&就是为了让bash将1解释成标准输出而不是文件1。试想2>1代表什么,2与>结合代表错误重定向,而1则代表错误重定向到一个文件1,而不代表标准输出;换成2>&1&与1结合就代表标准输出了,就变成错误重定向到标准输出
&[n]代表是已经存在的文件描述符,&1 代表输出,&2代表错误输出,
&-代表关闭与它绑定的描述符,>&-表示将标准输出关闭,<&-表示关闭标准输入(键盘),n>&-表示将n号输出关闭,n<&-表示将n号输入关闭

例如
ls -al 1>list.txt 2>list.err
将ls执行结果,正确的输出到 list.txt,错误的数据输出到 list.err

ls -al 1>list.txt 2>&1
将ls执行结果,不论正确或错误均输出到 list.txt 当中

ls -al 1>list.txt 2>&-
将错误输出信息关闭掉

ls -al >/dev/null 2>&1
将标准输出重定向到/dev/null,将标准错误转发到标准输出,所以也到/dev/null了

ls -al &>/dev/null
将所有标准输出与错误输出 输入到/dev/null

执行命令或 shell 脚本并将输出重定向到文件

ls xxx >out.txt 2>&1 实际上可换成 ls xxx 1>out.txt 2>&1 重定向符号 > 默认是1, 错误和输出都传到out.txt了。
为何 2>&1 要写在后面?
command > file 2>&1
首先是command > file将标准输出重定向到file中,2>&1 是标准错误拷贝了标准输出的行为,也就是同样被重定向到file中,最终结果就是标准输出和错误都被重定向到file中。
command 2>&1 >file
2>&1 标准错误拷贝了标准输出的行为,但此时标准输出还是在终端。>file 后输出才被重定向到file,但标准错误仍然保持在终端。


Shell变量

shell变量全都是字符串

shell脚本与环境变量

Linux是一个多用户的操作系统。每个用户登录系统后,都会有一个专用的运行环境。

用户登录到Linux系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行 shell脚本程序。
运行shell脚本程序时,系统将创建一个子shell。此时,系统中将有两个shell,一个是登录时系统启动的shell,另一个是系统为运行脚本程序创建的shell。 当一个脚本程序运行完毕,它的脚本shell将终止,可以返回到执行该脚本之前的shell。
执行一个脚本 时,会先开启一个子shell环境(不知道执行其它程序是不是这样),然后将父shell中的所有系统环境变量复制过来,这个脚本中的语句就在子 shell中执行。(也就是说父shell的环境变量在子shell中可以调用,但反过来就不行,如果在子shell中定义了环境变量,只对该shell 或者它的子shell有效,当该子shell结束时,也可以理解为脚本执行完时,变量消失。

从这种意义上来 说,用户可以有许多 shell,每个shell都是由某个shell(称为父shell)派生的。

在子 shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内 的一个局部变量,其他的shell不能引用它。
要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。 export命令将使系统在创建每一个新的shell时定义这个变量的一个拷贝。这个过程称之为变量输出。

1、执行脚本时是在一个子shell环境运行的,脚本执行完后该子shell自动退出;
2、一个shell中的系统环境变量(用export定义的变量)才会被复制到子 shell中;
3、一个shell中的系统环境变量只对该shell或者它的子shell有效,该shell结束时变量消失 (并不能返回到父shell中)。
4、不用export定义的变量只对该shell有效,对子shell也是无效的。

env,set,export区别

set: 显示或设置shell变量, 包括用户环境变量和shell局部变量
env: 显示或设置环境变量, 只涉及当前用户的环境变量, 所以 env看到的变量是set看到的变量的子集, 或者说env看到的变量个数小于等于set看到的变量个数
shell变量(set) = shell局部变量 + env环境变量
export:显示当前导出成用户环境变量的shell变量,或将shell局部变量导出为用户环境变量

举例:

# aaa=1234567  #在当前shell定义变量
# echo $aaa
1234567
# env |grep aaa #环境变量中没有
# set |grep aaa #shell变量中有
aaa=1234567
# export |grep aaa #shell导出环境变量中也没有
# export aaa #导出为环境变量
# env |grep aaa #环境变量中有
aaa=1234567
# export| grep aaa #shell导出环境变量中有
declare -x aaa="1234567"

shell环境变量以及set,env,export的区别
https://blog.csdn.net/longxibendi/article/details/6125075

shell变量类别

我们通常所说的 shell 变量,是由用户的环境变量和shell局部变量共同组成的,这些变量保证了shell的正常运行
shell变量(set) = shell局部变量 + env环境变量

shell变量包括两种变量

shell局部变量

局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
通过赋值语句定义好的变量,可以通过如下方法定义shell变量

A1="1234"
delcare A2="2345"

用户环境变量

所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
通过export语法导出的shell私有变量,可以通过如下方法导出用户环境变量

A1="1234"
export A1  #先定义再导出
export A3="34"

环境变量
export:把普通变量变为环境变量,将变量从普通变量表拷贝到环境变量表中
unset xx,删除xx环境变量的定义
普通变量,环境变量的区别:子进程能否看得到。exec命令时,环境变量表会保留在新进程的内存中,但普通变量已被丢弃。
c语言中:getenv()获取环境变量,setenv()设置环境变量
shell所有变量默认都是全局变量。在函数中定义的变量也是全局的,函数外也能用。
关键字local用于定义局部变量。


定义变量

val="valtest"
注意,变量名和等号之间不能有空格,如果写成val = "valtest"会报错val: command not found,因为linux中命令和参数间以空格分割,解释器会将val看做一个命令,后面的看做val的两个参数,PATH中找不到val命令,所以报错。

已定义的变量,可以被重新定义,如:

your_name="tom"
echo $your_name
your_name="alibaba"
echo $your_name

这样写是合法的,但注意,第二次赋值的时候不能写$your_name=”alibaba”,使用变量的时候才加美元符

删除变量

使用 unset 命令可以删除变量。语法:
unset variable_name
变量被删除后不能再次使用。unset 命令不能删除只读变量。


使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

your_name="matt"
echo $your_name
echo ${your_name}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,建议加上。


Shell字符串

字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

单引号和双引号

单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

双引号的优点:
双引号里可以有变量
双引号里可以出现转义字符;

例如

val=lalala
str1="string test $val"
str2="string test "$val""
str3="string test \"val\""
echo $str1
echo $str2
echo $str3

结果:
string test lalala
string test lalala
string test “val”

获取字符串长度

$ str="adsa"
$ echo ${#str}
4

字符串拼接

#!/bin/bash
name="Shell"
str="Test"
str1=$name$str #中间不能有空格
str2="$name $str" #如果被双引号包围,那么中间可以有空格
str3=$name": "$str #中间可以出现别的字符串
str4="$name: $str" #这样写也可以
str5="${name}Script: ${str}" #这个时候需要给变量名加上大括号,否则无法区分出变量
echo $str1
echo $str2
echo $str3
echo $str4
echo $str5

运行结果:
ShellTest
Shell Test
Shell: Test
Shell: Test
ShellScript: Test


删除左边字符

假设有变量 var=http://www.aaa.com/123.htm

#号截取,删除左边字符,保留右边字符
${var#*char}, var 表示要截取的字符变量, char 是指定的字符(或者子字符串),是通配符的一种,表示任意长度的字符串。char连起来使用的意思是:从左边开始直到遇见 char

echo ${var#*//}, // 表示从左边开始删除第一个 // 号及左边的所有字 符
其中 var 是变量名即要处理的字符串,# 号是运算符,
// 表示从左边开始删除第一个 // 号及左边的所有字符
即删除 http://
结果是 :www.aaa.com/123.htm

url="http://c.biancheng.net/index.html"
echo ${url#*:} # 结果为 //c.biancheng.net/index.html

##号截取,删除左边字符,保留右边字符
echo ${var##*/}, ##*/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符
即删除 http://www.aaa.com/
结果是 123.htm

${var%}删除右边字符

假设有变量 var=http://www.aaa.com/123.htm
%号截取,删除右边字符,保留左边字符
echo ${var%/*}, %/* 表示从右边开始,删除第一个 / 号及右边的字符
%/* 表示从右边开始,删除第一个 / 号及右边的字符
结果是:http://www.aaa.com

%% 号截取,删除右边字符,保留左边字符
echo ${var%%/*}, %%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符
%%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符
结果是:http:


${var:start:len} 截取字符串

1、指定从左边第几个字符开始以及子串中字符的个数
${var:start:len} 从左边第 start+1 个字符开始的连续 len 个字符
例如:

$ a=welcome
$ echo $a
welcome
$ echo ${a:1:2}
el

2、从左边第几个字符开始一直到结束
${var:n} 从左边第 n+1 个字符到结束
例如

$ a=welcome
$ echo ${a:3}
come

3、从右边第几个字符开始以及字符的个数
${var:0-start:len}, 从右边起第 start 个字符开始的连续 len 个字符

$ a=welcome
$ echo ${a:0-4:2}
co

4、从右边第几个字符开始一直到结束
${var:0-start}, 从右边起第 start 个字符开始一直到结束

$ a=welcome
$ echo ${a:0-4}
come

字符串替换

${变量/查找值/替换值}
一个/表示替换第一个
两个//表示替换所有
当查找出中出现了一些需要转义的需要加上\:”/“需要转移成”/“,”#”需要转移成”#“

var=analy#analy.properties
echo ${var/#/=}
输出是:analy=analy.properties


字符串分割

字符串按,分隔

#!/bin/bash
string="hello,shell,haha"
array=(${string//,/ })
for var in ${array[@]}
do
echo $var
done

其中的 ${string//,/ } 是字符串替换命令,也就是将所有 , 替换为空格


特殊变量

  • !!,连续两个英文叹号,表示执行上一条指令
  • $n,n为数字,$n为执行脚本的第一个参数,相当于main函数的argv[n]。例如,$0就是argv[0],即脚本程序自身的名称
  • $#,传递到脚本的参数个数,不包括脚本本身
  • $*,以一个单字符串显示所有向脚本传递的参数,不包括$0
  • $@,与$*类似,从$1开始的所有参数,但每个都作为独立的字符串

$*$@的区别
相同点:都是引用所有参数。
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则"$*"等价于 “1 2 3”(单个字符串),而"$@"等价于 “1” “2” “3”(三个字符串)。

  • $?:当前shell进程中,上一个命令的返回值,如果上一个命令成功执行则$?的值为0,否则为其他非零值,常用做if语句条件
  • $$:当前shell进程的pid
  • $!:后台运行的最后一个进程的pid
  • $-:显示Shell使用的当前选项,与set命令功能相同。
  • $_:之前命令的最后一个参数

可以修改自己的argv[0]来伪装成别的程序,在程序中用execl调用自己,调用时argv[0]设为其他命令。所以ps看时可能不准。
如何查看某pid xxx真正的命令?
/proc/xxx/下有个exe符号链接,指向此pid真正的命令。
readlink exe,读取符号链接


expr

expr命令可以实现数值运算、数值或字符串比较、字符串匹配、字符串提取、字符串长度计算等功能。它还具有几个特殊功能,判断变量或参数是否为整数、是否为空、是否为0等。

SHELL脚本–expr命令全解
https://www.cnblogs.com/f-ck-need-u/p/7231832.html

算数表达式

expr支持普通的算术操作,算术表达式优先级低于字符串表达式,高于逻辑关系表达式。

+ - 加减运算。两端参数会转换为整数,如果转换失败则报错。

* / % 乘,除,取模运算。两端参数会转换为整数,如果转换失败则报错。

将shell中变量’foo’的值增加1:
foo=$(expr $foo + 1)

算术乘法符号 * 因为是shell的元字符,所以要转义,可以使用引号包围,或者使用反斜线。

$ expr $a * $b
expr: syntax error

$ expr $a '*' $b
12

$ expr $a \* $b
12

$ expr $b / $a    # 除法只能取整数
1

$ expr $b % $a
1

任意操作符两端都需要有空格,否则:

$ expr 4+$a
4+3

$ expr 4 +$a
expr: syntax error

由于expr在进行算术运算时,首先会将操作符两边的参数转换为整数,任意一端转换失败都将会报错,所以可以用来判断参数或变量是否为整数。

$ expr $a + $c
expr: non-integer argument

$ if [ $? != 0 ];then echo '$a or $c is non-integer';fi
$a or $c is non-integer

逻辑表达式

‘expr’支持普通的逻辑连接和逻辑关系。它的优先级最低。

|
如果第一个参数非空且非0,则返回第一个参数的值,否则返回第二个参数的值,但要求第二个参数的值也是非空或非0,否则返回0。如果第一个参数是非空或非0时,不会计算第二个参数。

经过测试,以上手册内容是错误的。正确的应该是:如果第一个参数非0,则返回第一个参数的值,否则返回第二个参数。但如果任意一个参数为空,则报错。除非空字符串使用引号包围,此时将和0的处理方式一样。

&
如果两个参数都非空且非0,则返回第一个参数,否则返回0。如果第一个参为0或为空,则不会计算第二个参数。

经过测试,以上手册内容是错误的。正确的应该是:如果两个参数都非0,则返回第一个参数,否则返回0。但任意一个参数为空,则报错。除非空字符串使用引号包围,此时将和0的处理方式一样。

< <= = == != >= >
比较两端的参数,如果为true,则返回1,否则返回0。”==”是”=”的同义词。”expr”首先尝试将两端参数转换为整数,并做算术比较,如果转换失败,则按字符集排序规则做字符比较。

括号’()’可以改变优先级,但使用时需要使用反斜线对括号进行转义。

其中”<”和”>”是正则表达式正的锚定元字符,且”<”会被shell解析为重定向符号,所以需要转义或用引号包围。

$ a=3

$ expr $a = 1
0

$ expr $a = 3
1

$ expr $a \* 3 = 9
1

$ expr abc \> ab
1

$ expr akc \> ackd
1

逻辑连接符号”&”和”|”用法示例。这两个符号都需要转义,或使用引号包围。

$ expr $abc '|' 1
expr: syntax error

$ expr "$abc" '|' 1
1

$ expr "$abc" '&' 1
0

$ expr $abc '&' 1
expr: syntax error

$ expr 0 '&' abc
0

$ expr abc '&' 0
0

$ expr abc '|' 0
abc

$ expr 0 '|' abc
abc

$ expr abc '&' cde
abc

$ expr abc '|' cde
abc

expr length STRING 长度

返回STRING的字符长度。
该表达式是返回string的长度,其中string不允许为空,否则将报错,所以可以用来判断变量是否为空。

$ expr length abcde
5

$ expr length 111
3

$ expr length $xxx
expr: syntax error

$ if [ $? -ne 0 ];then echo '$xxx is null';fi
$xxx is null

expr substr 字符串截取

substr STRING POSITION LENGTH
返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。如果POSITION或LENGTH为负数,0或非数值,则返回空字符串。

$ expr substr abcde 2 3
bcd

$ expr substr abcde 2 4
bcde

$ expr substr abcde 2 5
bcde

$ expr substr abcde 2 0

$ expr substr abcde 2 -1

expr index STRING CHARSET 子串匹配

CHARSET中任意单个字符在STRING中最前面的字符位置。如果在STRING中完全不存在CHARSET中的字符,则返回0。
expr index string chars 该表达式是从string中搜索chars中某个字符的位置,这个字符是string中最靠前的字符。
例如:

$ expr index abcde dec
3
$ expr index abcde xdc
3

该命令将对字符串”dec”逐字符分解,首先分解得到第一个字符d,从abcde中搜索到d的位置为4,再分解得到第二个字符e,该字符在abcde中的位置为5,最后得到的字符是c,该字符在abcde中的位置为3。其中3是最靠前的字符,所以命令返回的结果为3。

如果chars中的所有字符都不存在于string中,则返回0。

$ expr index abcde dec
$ expr index abcde 1
0

$ expr index abcde 1x
0

expr STRING : REGEX 模式匹配

expr STRING : REGEX
expr match STRING REGEX 等价于 expr STRING : REGEX

执行模式匹配。两端参数会转换为字符格式,且第二个参数被视为正则表达式(GNU基本正则),它默认会隐含前缀”^”。随后将第一个参数和正则模式做匹配。
如果匹配成功,且REGEX使用了’(‘和’)‘,则此表达式返回匹配到的,如果未使用’(‘和’)‘,则返回匹配的字符数。
如果匹配失败,如果REGEX中使用了’(‘和’)‘,则此表达式返回空字符串,否则返回为0。
只有第一个’(…)‘会引用返回的值;其余的’(…)‘只在正则表达式分组时有意义。
在正则表达式中,’+‘,’?‘和’|‘分表代表匹配一个或多个,0个或1个以及两端任选其一的意思。

“string : REGEX”字符串匹配要输出匹配到的字符串结果,需要使用”(“和”)“,否则返回的将是匹配到的字符串数量。

expr aaa : 'a\+'
解释:因为REGEX部分没有使用(),所以返回匹配的字符数
=> 3

expr abc : 'a\(.\)c'
解释:因为REGEX部分使用了(),所以返回匹配的字符
=> b

注意,由于REGEX中隐含了”^”,所以使得匹配时都是从string首字符开始的。
expr abcde : 'cd.*' 结果为 0
之所以为0,是因为真正的正则表达式是”^cd.*”,而abcde不是c开头而是a开头的,所以无法匹配到任何结果。因此,任何字符串匹配时,都应该从首字符开始。

$ expr abcde : 'ab\(.*\)'
cde

$ expr abcde : 'ab\(.\)'
c

$ expr abcde : 'ab.*'
5

$ expr abcde : 'ab.'
3

$ expr abcde : '.*cd*'
4

Shell运算符(条件表达式)

常用判断命令:test[
[命令位于user/bin/中,是test命令的别名
命令格式:
[ expressiontest expression,expression后必须加]

[root@localhost ~]# which [
/usr/bin/[
[root@localhost ~]# which test
/usr/bin/test

man test命令查看如何写表达式

注意:使用方括号[时,必须在条件两边加上空格


单方括号与双方括号

[ ] 两个符号左右都要有空格分隔
内部操作符与操作变量之间要有空格:如 [ “a” = “b” ]
字符串比较中,> < 需要写成> < 进行转义
[ ] 中字符串或者 ${} 变量尽量使用”” 双引号扩住,避免值未定义引用而出错的好办法
[ ] 中可以使用 –a –o 进行逻辑运算
[ ] 是bash 内置命令:[ is a shell builtin

[[ ]] 两个符号左右都要有空格分隔
内部操作符与操作变量之间要有空格:如 [[ “a” = “b” ]]
字符串比较中,可以直接使用 > < 无需转义
[[ ]] 中字符串或者${}变量尽量如未使用”” 双引号扩住的话,会进行模式和元字符匹配
[[] ] 内部可以使用 && || 进行逻辑运算
[[ ]] 是bash keyword:[[ is a shell keyword

双方括号的优势:
[[是 bash 程序语言的关键字。并不是一个命令,[[ ]] 结构比[ ]结构更加通用。在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割,但是会发生参数扩展和命令替换。

支持字符串的模式匹配,使用=~操作符时甚至支持shell的正则表达式。字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。[[ ]] 中匹配字符串或通配符,不需要引号。

使用[[ … ]]条件判断结构,而不是[… ],能够防止脚本中的许多逻辑错误。比如,&&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。

bash把双中括号中的表达式看作一个单独的元素,并返回一个退出状态码。


关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。

布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
││ 逻辑的 OR [[ $a -lt 100 ││ $b -gt 100 ]] 返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为0,不为0返回 true。 [ -n $a ] 返回 true。
str 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

注意:字符串允许使用赋值号做等号,字符串的 等于”比较,为了与POSIX一致,在[]中使用=,(尽管==也可以可以用的),注意在=前后各有一个空格,如果没有空格就是赋值的关系,不是比较的关系


文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。
变量 file 表示文件”/var/www/runoob/test.sh”,它的大小为100字节,具有 rwx 权限

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),
如果是,则返回 true。
[ -f $file ] 返回 true。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

参考

Shell 教程 | 菜鸟教程
http://www.runoob.com/linux/linux-shell.html

shell编程——if语句 if -z -n -f -eq -ne -lt
http://www.dutycode.com/post-46.html


上一篇 CentOS-6.8安装笔记

下一篇 首次组装电脑(3)系统安装

阅读
评论
8,918
阅读预计34分钟
创建日期 2016-08-01
修改日期 2020-08-26
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论