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

Linux-Shell脚本

[TOC]


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执行成功


重定向

重定向命令列表

命令 说明
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读,不知道百分比

<<:原地输入
原地输入直到EOT为止

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

同时输入输出重定向
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
&[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变量

shell变量全都是字符串

shell脚本与环境变量

用户登录到Linux系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行 shell脚本程序。运行shell脚本程序时,系统将创建一个子shell。此时,系统中将有两个shell,一个是登录时系统启动的shell,另一 个是系统为运行脚本程序创建的shell。当一个脚本程序运行完毕,它的脚本shell将终止,可以返回到执行该脚本之前的shell。从这种意义上来 说,用户可以有许多 shell,每个shell都是由某个shell(称为父shell)派生的。
在子 shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内 的一个局部变量,其他的shell不能引用它,要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。 export命令将使系统在创建每一个新的shell时定义这个变量的一个拷贝。这个过程称之为变量输出。

执行一个脚本 时,会先开启一个子shell环境(不知道执行其它程序是不是这样),然后将父shell中的所有系统环境变量复制过来,这个脚本中的语句就在子 shell中执行。(也就是说父shell的环境变量在子shell中可以调用,但反过来就不行,如果在子shell中定义了环境变量,只对该shell 或者它的子shell有效,当该子shell结束时,也可以理解为脚本执行完时,变量消失。

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

env,set,export区别

  • set:显示(设置)shell变量,包括shell私有变量以及用户变量(环境变量),不同类的shell有不同的私有变量 bash,ksh,csh每种shell私有变量都不一样
  • env:显示(设置)用户变量(当前用户的环境变量)
  • export:显示当前导出成用户变量(环境变量)的shell变量,将shell变量导出为用户变量(环境变量)。

举例:

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

shell变量类别

运行shell时,会同时存在三种变量:

  • 局部变量,局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
  • 环境变量,所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
  • shell变量,shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

环境变量
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”

获取字符串长度

string="abcd"
echo ${#string} #输出 4

特殊变量

  • !!,连续两个英文叹号,表示执行上一条指令

  • $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,读取符号链接


Shell运算符(条件表达式)

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

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

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

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

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 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


上一篇 Hexo博客(09)提交搜索引擎收录

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

阅读
4,713
阅读预计18分钟
创建日期 2016-08-01
修改日期 2017-07-23
类别
百度推荐