shell程序

一、shell程序简介

在Windows中,命令行界面只是对图形化界面的一个补充,但是作为Linux灵感来源的UNIX系统最初根本没有图形化界面,所有的任务都是通过命令来完成的,许多强大的功能都可以从shell中轻松实现。使用shell,可以快速、简单的完成编程。shell非常适合于编写一些执行相对简单的小工具,因为它们更强调易于配置、维护和可移植性,而不是很看重执行的效率,使用shell还可以对进程进行组织,使命令按照预定顺序在前一阶段命令成功完成的前提下顺序执行。通过shell可以执行命令、调用Linux工具,还可以自己编写程序。shell执行shell程序,这些程序通常被称为脚本,它们是运行时解释执行的。这使得调试工作比较容易进行,因为你可以逐行地执行指令,而且节省了重新编译的时间。然而,这也使得shell不适合用来完成时间紧迫型和处理器忙碌型的任务。
shell是一个作为用户与Linux系统间接口的程序,它允许用户像操作系统输入需要执行的命令。这点和Windows的命令提示符类似,但linux的shell更强大。在Linux中也可以安装多个shell,用户可以挑选一种自己喜欢的shell来使用。(如bash和csh),标准shell是GNU工具集中的bash。

查看bash的版本号:
bin/bash --version

GNU bash, version 4.3.8(1)-release (i686-pc-linux-gnu)
Copyright © 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html

从其他shell中切换到标准shell——bash:
/bin/bash

下载bash
www.gnu.org

sheel的总结:

shell名称 相关历史
sh(Bourne) 源于UNIX早期版本的最初的shell。
csh、tcsh、zsh C shell及其变体,最初是由Bill Joy在Berkeley UNIX上创建的。它可能是继bash和Korn shell之后第三个最流行的shell。
ksh、pdksh korn shell 和它的公共域兄弟pdksh(public domain korn shell)由David Korn编写,它是许多商业版本UNIX的默认shell。
bash 来自GNU项目的bash或Bourne Again Shell是Linux的主要shell。它的优点是可以免费获取源代码,bash和Korn shell有很多相似之处 。

二、管道和重定向

1.重定向输出:
ls -1 lsoutput.txt
ls -1 lsoutput.txt命令是把ls命令的输出保存到文件lsoutput.txt中。

标准文件描述符 说明
0 数字0代表标准输入
1 数字1代表标准输出
2 数字2代表标准错误输出

默认情况下,如果该文件(lsoutput.txt)已经存在,它的内容将被覆盖。
阻止文件覆盖:
set -o noclobber

允许文件覆盖:
set +o noclobber

使用>>操作符将输出内容附加到一个文件中:
ps >> lsoutput.txt
ps >> lsoutput.txt命令会将ps命令的输出内容附加到指定文件lsoutput.txt的尾部。

将标准输出和标准错误输出分别重定向到不同的文件中:
kill -HUP 1234 >killout.txt 2>killerr.txt
kill -HUP 1234 >killout.txt 2>killerr.txt命令是将标准输出重定向到killout.txt,将标准错误输出重定向到killerr.txt中。

将标准输出和标准错误输出都重定向到一个文件中:
kill -1 1234 >killouterr.txt 2>&1
kill -1 1234 >killouterr.txt 2>&1命令是将标准输出重定向到文件killouterr.txt,然后将标准错误输出重定向到与标准输出相同的地方。

因为可以通过返回码来了解kill命令的执行结果,所以通常不需要保存标准输出或标准错误输出的内容。可以通过Linux的通用“回收站”/dev/null来有效地丢弃所有的输出信息。
丢弃所有输出信息:
kill -1 1234 >/dev/null 2>&1

2.重定向输入
more < killout.txt
在Linux下这样做意义不大,因为Linux的more命令可以接受文件名作为参数,这与Windows命令行中对应的命令不同。

3.管道
可以通过管道操作符(“|”)来连接进程。Linux与MS-DOS不同,在Linux下通过管道连接的进程可以同时运行,并且随着数据流在它们之间的传递可以自动地进行协调。
使用sort命令对ps命令的输出进行排序。
ps > psout.txt
sort psout.txt > pssort.out
不使用管道,需要分两步来完成这个任务,第一步先将ps命令的输出内容重定向到psout.txt文件中,第二步使用sort命名将psout.txt中的内容进行排序并重定向到pssort.out文件中。

使用管道来链接进程:
ps | sort > pssort.out
使用ps | sort > pssort.out命令可以使ps和sort同时进行,将ps命令的输出内容排序后重定向到文件pssort.out中。

分页显示输出结果还可以再连接第三个进程more:
ps | sort | more pssort.out
允许连接的进程数目是没有限制的。

查看系统中运行的所有进程的名字,但不包括shell本身:
ps -xo comm | sort | uniq | grep -v sh | more
使用ps -xo comm | sort | uniq | grep -v sh | more命令,首先是按字母顺序排序ps命令的输出内容,再用uniq命令去除名字相同的进程,然后用grep -v sh命令删除名为sh的进程,最终将结果分页显示在屏幕上。

与使用一系列单独的命令并且每个命令都带有自己的临时文件相比,使用管道会更精巧。
注意:如果你有一系列的命令需要执行,相应的输出文件是在这一组命令被创建的同时立刻被或写入的,所以绝不要在命令流中重复使用相同的文件名。
cat mydata.txt | sort | uniq > mydata.txt
使用cat mydata.txt | sort | uniq > mydata.txt命令会得到一个空的文件,因为在读取mydata.txt之前就已经覆盖了这个文件的内容。

三、shell脚本程序

编写shell脚本程序有两种方式:
1.输入一系列命令让shell交互地执行它们;
2.把这些命名保存到一个文件中,然后将该文件作为一个程序来调用。
直接输入shell脚本是一种测试短小代码段的简单而快捷的方式。适用于学习shell脚本和测试。
交互式程序
假设你想要从大量C语言源文件中查找包含字符串POSIX的文件。与其使用grep命令在每个文件中搜索字符串,然后再分别列出包含该字符串的文件,不如用下面的交互式脚本来执行整个操作:
**for file in ***
do
if grep -l POSIX $file
then
**more f i l e f i d o n e s h e l l s h e l l file** **fi** **done** 当shell期待进一步的输入时,正常的shell提示符( )将改变为“>”提示符。你可以一直输入下去,由shell来判断何时输入完毕并立刻执行脚本程序。
上述命令中,grep命令输出它找到的包含POSIX字符的文件,然后more命令将文件的内容显示在屏幕上。最后,返回shell提示符。
注意:你用shell变量来处理每个文件,以使该脚本自文档化。你也可以将变量起名为i,但是变量名file更容易理解。

通配符
shell提供了通配符扩展(通常称为globbing)。
可以用通配符“*”来匹配一个字符串;
可以用通配符“?”来匹配单个字符;
[set]允许匹配方括号中任何一个单个字符;[^set]对方括号中的内容取反,即匹配任何没有出现在给出的字符集中的字符;
花括号“{}”(只能用在部分shell中,其中包括bash)允许你将任意的字符串组放在一个集合中,以供shell进行扩展。
列出Downloads和Documents目录下的文件:
ls Do{wnloads,cuments}

创建脚本
如果每次想要执行一系列命令式,都要经过这么一个冗长的输入过程,将会非常令人烦恼。我们可以将这些命令保存到一个文件中,即我们常说的shell脚本,这样就可以在需要的时候随时执行它们了。
1.创建一个包含命令的文件,文件名为first,内容如下:

#!/bin/sh
#first
#This file looks through all the files in the current
#directory for the string POSIX,and then prints the names of 
#those files to the standard output.

for file in *
do
    if grep -q POSIX $file
    then    
        echo $file
    fi
done
  
exit 0

程序中的注释以#开始,按照惯例,我们通常把#放在第一列。第一行的#!/bin/sh是一种特形式的注释,#!字符告诉系统同一行上紧跟在它后面的那个参数是用来执行本文件的程序,这个例子中的/bin/sh是默认的shell程序。
注意:注释中使用的是绝对路径。考虑到向后兼容性,这个路径按惯例最好不超过32字节。
脚本程序本质上被看作是shell的标准输入,所以它可以包含任何能够通过你的PATH环境变量引用到的Linux命令。
exit命令的作用是确保脚本程序能够返回一个有意义的退出码。当程序以交互方式运行时,我们很少需要检查它的退出码,但如果你打算从另一个脚本程序里调用这个脚本程序并查看它是否执行成功,那么返回一个适当的退出码就很重要了。在shell程序设计里,0表示成功。因为这个脚本程序并不能检测到任何错误,所以它总是返回一个表示成功的退出码。
注意:这个脚本没有使用任何的文件扩展名或后缀。一般情况下,Linux和UNIX很少利用文件扩展名来决定文件的类型。你可以为脚本使用.sh或者其他扩展名,但shell并不关心这一点。检查这些文件是否是脚本程序最好方法是使用file命令

检查文件是否为脚本程序:
file first或file /bin/bash

2.把脚本设置为可执行
运行脚本文件的方法有两种。
比较简单的方法是调用shell,并把脚本文件名当成一个参数,例如:
调用shell把脚本设置为可执行:
/bin/sh first

方法二:
chmod +x first
./first
先使用chmod命令来改变文件first的模式,使得这个文件(first)可以被所有用户执行,然后用./first命令告诉shell运行当前路径下的first脚本程序。
注意:
1.chmod +x first这并不是使用chmod命令将一个文件设置为可执行的唯一方式,可用man chmod命令查看它的八进制参数和其他选项用法。
2.直接使用first会报错,因为shell环境变量PATH并没有设置为当前目录下查找要执行的命令。要解决这个问题,一种办法是在命令行上直接输入命令:
PATH=$PATH:.
或者编辑你的.bash_profile文件,将刚才这条命令添加到文件的末尾。
3.不应该用这种方法来修改超级用户(root用户)的PATH变量。最好的办法是在其前面都加上一个./
在确信你的脚本程序能够正确执行后,你可以把它从当前目录移植到一个更合适的地方去。如果这个命令只供你本人使用,你可以在自己的home目录中创建一个bin目录,并且将该目录添加到你自己的PATH变量中。如果你想让其他人也能执行这个脚本程序,你可以将/usr/local/bin或其他系统目录作为添加新程序的适当位置。
为了防止其他用户修改脚本程序,哪怕只是意外地修改,你也应该去掉脚本程序的写权限。系统管理员用来设置文件属主和访问权限的一系列命令如下:
设置文件属主和访问权限:
cp first /usr/local/bin
chown root /usr/local/bin/first
chgrp root /usr/local/bin/first
chmod 755 /usr/local/bin/first
也可以使用如下chmod命令:
chmod u=rwx,go=rx /usr/local/bin/first

注意:
在Linux系统中,如果你拥有包含某个文件的目录的写权限,就可以删除这个文件。为安全起见,应该确保只有超级用户才能对你想保证文件安全的目录执行写操作。因为目录只是另一种类型的文件,所以拥有对一个目录文件写权限的用户可以添加和删除目录文件中的名称。

三、shell的语法

1.变量
在shell中,使用变量之前并不需要事先为它们做出声明。默认情况下,所有变量都被看作字符串并以字符串来存储,即使它们被赋值为数值时也是如此。shell和一些工具程序会在需要时把数值型字符串转换为对应的数值以对它们进行操作。同时,Linux是一个区分大小写的系统。
在shell中,可以通过在变量名前面加一个
美元符号(英文下shift+4)来访问它的内容。当你为变量赋值时,你只需要使用变量名,该变量会根据需要被自动创建。一种检查变量内容的简单方式就是在变量名前面加一个美元符号(英文下shift+4),再用echo命令将它的内容输出到终端上。
给变量赋值和输出到终端的命令:
salutation=Hello
echo $salutation
上述命令是将Hello赋值给salutation,然后把salutation的值输出到终端。
注意:如果字符串里包含引号就必须用引号把他们括起来,此外,等号两边不能有空格。
使用read命令将输入的值赋值给变量:
read salutation
然后输入你要输入的值,回车。

引号的使用
实例:
1.创建一个名为quotation的文件,内容如下:

#!/bin/sh
   
myvar="Hi there"
   
echo $myvar
echo "$myvar"
echo '$myvar'
echo \$myvar
   
echo Enter some text
read myvar
  
echo '$myvar' now equals $myvar
exit 0

2.使用chmod命令改变文件的模式
chmod +x quotation
3.执行脚本程序
./quotation
4.输出结果
Hi there
Hi there
$myvar
$myvar
Enter some text
Hello World
$myvar now equals Hello World
5.分析
双引号并不影响变量的替换;
使用单引号和斜线不进行变量的替换;
使用read命令读入一个字符串。

2.环境变量
当一个shell脚本程序开始执行时,一些变量会根据环境设置中的值进行初始化。这些变量通常用大写字母做名字,以便把他们和用户在脚本程序里定义的变量区分开来,后者按惯例都用小写字母做名字。
一些主要的变量:

环境变量 说明
$HOME 当前用户的home目录。
$PATH 以冒号分隔的用来搜索命令的目录列表。
$PS1 命令提示符,通常是美元符号,但在bash中,你可以使用一些更复杂的值。它给出用户名、机器名和当前目录名,当然也包括一个美元符号提示符。
$PS2 二级提示符,用来提示后续的输入,通常是>字符。
$IFS 输入域分隔符,当shell读取输入时,它给出用来分隔单词的一组字符,他们通常是空格、制表符和换行符。
$0 shell脚本的名字
$# 传递给脚本的参数个数
$$ shell脚本的进程号,脚本程序通常会用它来生成一个唯一的临时文件。

如果想通过执行env command 命令来查看程序在不同环境下是如何工作的,可以查阅env命令的使用手册。

3.参数变量
如果脚本程序在调用时带有参数,一些额外的变量就会被创建。即使没有传递任何参数,环境变量$#也依然存在,只不过它的值是0罢了。

参数变量 说明
$1,… 脚本程序的参数
$ * 在一个变量中列出所有的参数,各个参数之间用环境变量IFS中的第一个字符分隔开。如果IFS被修改了,那么(美元符号+*)将命令行分隔为参数的方式就将随之改变。
$0 它是(美元符号+*)的一种精巧的变体,它不使用IFS 环境变量,所以即使IFS为空,参数也不会挤在一起。

所以一般来说,如果你想访问脚本程序的参数,一般使用$@。
除了使用echo命令查看变量 的内容外,你还可以使用read命令来读取它们。

条件
所有程序设计语言的基础是对条件进行测试判断,并根据测试结果采取不同行动的能力。
一个shell脚本能够对任何可以从命令行调用的命令的退出码进行测试,其中也包括你自己编写的脚本程序。
test或[命令
在实际工作中,大多数脚本程序都会广泛使用shell的布尔判断命令[或test。在一些系统上,这两个命令的作用是一样的,知识为了增强可读性,当使用[命令时,我们还使用符号]来结尾。
检查执行的是哪一个test命令:
which test
注意:
你必须在[符号和被检查的条件之间流出空格。
test命令可以使用的条件类型可以分为3类:字符串比较、算数比较和与文件有关的条件测试。
字符串比较:

字符串比较 结果
string1 = string2 如果两个字符串相同则结果为真。
string1 != string2 如果两个字符串不同则结果为真。
-n string 如果字符串不为空则结果为真。
-z string 如果字符串为null则结果为空。

算数比较:

算数比较 结果
expression1 -eq expression2 如果两个表达式相等则结果为真。
expression1 -ne expression2 如果两个表达式不等则结果为真。
expression1 -gt expression2 如果expression1大于expression2则结果为真。
expression1 -ge expression2 如果expression1大于等于expression2则结果为真。
expression1 -lt expression2 如果expression1小于expression2则结果为真。
expression1 -le expression2 如果expression1小于等于expression2则结果为真。
! expression 如果表达式为假则结果为真,反之亦然。

文件条件测试:

文件条件测试 结果
-d file 如果文件是一个目录则结果为真
-e file 如果文件存在则结果为真,要注意的是,历史上-e选项不可移植,所以通常使用的是-f选项
- f file 如果文件是一个普通文件则结果为真
- g file 如果文件的set-group-id位被设置则结果为真
- r file 如果文件可读则结果为真
- s file 如果文件的大小不为0则结果为真
- u file 如果文件的set-user-id位被设置则结果为真
- w file 如果文件可写则结果为真
- x file 如果文件可执行则结果为真

注意:
set-group-id和set-user-id(也叫做set-gid和set-uid)位,set-uid位授予了程序其拥有者的访问权限而不是其使用者的访问权限,而set-gid位授予了程序其所在组的访问权限。这两个特殊位是通过chmod命令的选项s和g设置的。set-gid和set-uid标志对shell脚本程序不起作用。他们只对可执行的二进制文件有用。
实例:
1.使用vi创建了一个名为test的文件,内容如下:

#!/bin/sh
   
if [ -f /bin/bash ]
then
   echo "file /bin/bash exists"
fi
   
if [ -d /bin/bash ]
then
   echo "/bin/bash is a directory"
else
   echo "/bin/bash is not a directory"
fi

2.使用chmod命令改变文件test的模式
chmod +x test
3.执行脚本程序test
./test
4.输出结果
file /bin/bash exists
/bin/bash is not a directory
5.分析
[ -f /bin/bash ] 命令是一个布尔判断命令,该命令也可以写成test -f /bin/bash,其中-f选项的意思是一旦该文件是一个普通文件,则结果为真,使用echo命令输出“file /bin/bash exists”字符串到终端。
if [ -d /bin/bash ]命令中选项-d的意思是一旦该文件是一个目录文件则结果为真,显然这不是一个目录文件,所以输出结果为else中的“/bin/bash is not a directory”字符串。

控制结构

1.if语句

if condition
then
	statements
else
	statements
fi

2. elif语句
实例:
1.创建一个名为test的文件,内容如下:

#!/bin/sh
   
echo "Is it morning? Please answer yes or no"
read timeofday
   
if [ "$timeofday" = yes ]
then
    echo "Good morning"
elif [ "$timeofday" = no ] ; then
    echo "Good afternoon"
else
    echo "Sorry, $timeofday not recognized.Enter yes or no"
exit 1
fi
  
exit 0

2.使用chmod命令改变文件test的模式
chmod +x test
3.执行脚本程序test
./test
4.输出结果
Is it morning? Please answer yes or no
yes
Good morning
5.分析
echo "Is it morning? Please answer yes or no"命令为在终端输出引号中的字符串;
read timeofday命令为将输入的的值赋值给变量timeofday;
if [ “$timeofday” = yes ]命令为判断输入的值是否等于yes,如果等于则输出“Good morning”字符串。如果两次测试的结果都不成功,就打印一条错误信息并以1为退出码结束脚本程序,调用者可以在调用程序中利用这个退出码来检查脚本程序是否执行成功。
注意:
1.在对变量timeofday进行测试的时候,必须给变量加上引号。
2.如果想让echo命令去掉每一行后面的换行符,可移植性最好的办法是使用printf命令而不是echo命令。有的shell用echo -e 来完成这一任务,但并不是所有的系统都支持该命令。bash使用echo-n命令来去除换行符。如将echo “Is it morning? Please answer yes or no”
改为echo -n "Is it morning? Please answer yes or no: "

3.for语句

for variable in values
do
	statements
done

实例:
1.用vi创建一个名为test的文件,内容如下:

#!/bin/sh
   
for foo in bar fud 43
do
    echo $foo
done
exit 0

2.使用chmod命令改变文件test的模式
chmod +x test
3.执行脚本程序test
./test
4.输出结果
bar
fud
43
5.分析
for foo in bar fud 43命令为创建了一个foo变量,然后在for循环里每次给它赋一个不同的值。

4.while语句

while condition do
	statements
done

实例:
1.用vi创建一个名为test的文件,内容如下:

#!/bin/sh
   
echo "Enter password"
read trythis
   
while [ "$trythis" != "secret" ]; do
    echo "Sorry,try again"
    read trythis
done
exit 0

2.使用chmod命令改变文件test的模式
chmod +x test
3.执行脚本程序test
./test
4.输出结果
Enter password
password
Sorry,try again
secret
5.分析
while [ “$trythis” != “secret” ] 当变量trythis的值不等于secret时,执行循环体,直到变量trythis等于secret,退出循环体。
do和done之间的语句将反复执行,直到条件不再为真,while循环通常用于需要重复执行一个命令序列,但事先又不知道这个命令序列应该执行的次数。

5.until语句

until condition
do
    statements
done

until语句与while循环类似,只是把条件测试反过来了。即循环将反复执行直到条件为真。一般来说,如果需要循环至少一次,那么就使用while循环;如果可能根本都不需要执行循环,就使用until循环。
实例:
1.用vi创建一个名为test的文件,内容如下:

#!/bin/bash
   
until who | grep "$1" > /dev/null
do
   sleep 60
done
   
# now ring the bell and announce the expected user.
   
echo -e '\a'
echo "**** $1 has just logged in ****"
  
exit 0

2.使用chmod命令改变文件test的模式
chmod +x test
3.执行脚本程序test
./test
4.输出结果
**** has just logged in ****
5.分析

6.case语句

case variable in
    pattern [ | pattern] ...) statements;;
    pattern [ | pattern] ...) statements;;
...
esac

实例1
1.用vi创建一个名为test的文件,内容如下:

#!/bin/sh
  
echo "Is it morning? Please answer yes or no"
read timeofday
  
case "$timeofday" in
      yes)   echo "Good Morning";;
      no )   echo "Good Afternoon";;
      y  )   echo "Good Morning";;
      n  )   echo "Good Afternoon";;
      *  )   echo "Sorry,answer not recognized";;
esac
  
exit 0

2.使用chmod命令改变文件test的模式
chmod +x test
3.执行脚本程序test
./test
4.输出结果
Is it morning? Please answer yes or no
y
Good Morning
5.分析
当case语句被执行时,它会把变量timeofday的内容与各字符串依次进行比较,一旦某个字符串与输出匹配成功,case命令就会执行紧随右括号后面的代码,然后就结束。
6.注意
esac前面的双分号(;;)是可选的。

猜你喜欢

转载自blog.csdn.net/luoting2017/article/details/82107762
今日推荐