linux笔记1shell程序设计

由三条简单命令组成的shell程序(文件名为ex1)
$ cat  ex1

date

pwd

cd ..


shell脚本的建立:利用编辑器录入和编辑,如vi

通常用户不能直接执行由文本编辑器建立的shell脚本
因为直接编辑生成的脚本文件没有“执行”权限

执行shell脚本的三种方式:


(1)输入定向到shell脚本:
用输入重定向方式让shell从给定文件中读入命令行,并进行相应处理
$ bash < 脚本名

(2)以脚本名作为bash参数

其一般形式是:  
$ bash  脚本名  [参数]
$bash ex2  /user/meng /usr/zhang
优点:
        
可以在脚本名后面带有参数
从而将参数值传递给程序中的命令,
 
使得一个shell脚本可以处理多种情况
 可以用来进行程序调试;

(3)将shell脚本的权限设置为可执行,然后在提示符下直接执行它
要用chmod命令将其设置为有“执行”权限

$ chmod  a+x  ex2
                   
$ PATH=$PATH:.
                   
$ ex2


说明:

第二行将当前工作目录(以“.”表示)添加到命令搜索路径中;

该脚本所在的目录应被包含在命令搜索路径(PATH)中;

其中$PATH表示引用变量的值;

“:”是在PATH变量中,不同路径之间的分隔符;

2  命令历史
bash提供了命令历史的功能
系统为每个用户维护一个命令历史文件
其默认目录是“~/.bash_history”;
其中“~”表示注册用户的主目录

作用
   
便于调用、修改和快捷执行命令
把全部或部分先前命令作为新命令,予以快捷执行
语法格式是:history  [option] [arg…] 


常用的选项有:
  
-a  在历史文件中添加“新”历史命令行。
  
-n  从历史文件中读取尚未读入的历史命令行,添加到当前历史清单中。
  
-r  读取历史文件的内容,并把它作为当前的历史命令。
  
-w  把当前的历史写到历史文件中,覆盖原有内容。
  
-c  删除历史清单中所有的项。 

如果不带任何参数,则history命令会显示历史命令的清单 

如果history 后给出一个正整数(如50),
   就只显示历史表中的最后50行命令  

如果history后给出一个文件名,就把它作为历史文件名


执行历史命令
执行历史命令是命令替换之一,它以字符“!”开头、后随1个或多个字符,用来定义用户所需的某种类型的历史命令,
$ date

      一 6月 26 21:35:18 CST 2006

$ pwd

      /home/mengqc

$ ls

      desktop ex1 ex3 exam15 myfile m1

$ cat m1

      echo hello!

$ history

      1 date

      2 pwd

      3 ls

      4 cat m1

      5 history

$ !2

      pwd

      /home/mengqc

$ !c

     cat m1

     echo hello!

$ !?w?

     pwd

     /home/mengqc

用环境变量配置历史命令环境
用户可以通过重新为环境变量赋值,改变存放历史命令的文件
$ HISTFILE="/home/mengqc/.myhistory"
还可以重新设定能够保留的命令个数
 $ HISTSIZE=600

别    名

优点:可以简化输入,方便用户,减少出错率
一般语法格式为: alias [name[=value]]…

如果没有指定参数,将在标准输出(屏幕)上显示别名清单
定义别名时,往往用单引号将它代表的内容括起来,从而防止shell对其中的内容产生歧义,如:空格和特殊字符
例:$ alias ll=‘ls -l’

    $ my=/home/mengqc 
        (定义变量并赋值)
       
    $ ll $my
$ alias
     alias ..=‘cd..’
     alias cp=‘cp -i’

假定/home/mengqc目录下有m1,ln,m2,ttt四个文件
$ alias ll=ls -l
   bash:alias: ‘-l’ not found
       
$ pwd

   /home/mengqc
       
$ ll

  m1 ln m2 ttt

在此情况下,执行ll别名命令时,并不是执行ls –l,而是ls



取消别名
如果想取消先前定义的别名,可使用如下命令:
unalias name…
可以一次将所有的别名都从别名表中删除,使用如下命令:
        
unalias -a

shell特殊字符 
通配符
*(星号),它匹配任意字符的0次或多次出现
?(问号),它匹配任意一个字符


引号

1.双引号
由双引号括起来的字符(除$、倒引号(`)和反斜线(\)外)均作为普通字符对待。


2.单引号 

由单引号括起来的字符都作为普通字符出现

3.倒引号` 位于键盘的左上角
倒引号括起来的字符串被shell解释为命令行,先执行该命令行,并以它的标准输出结果取代整个倒引号部分
倒引号还可以嵌套使用。
注意,嵌套使用时内层的倒引号必须用反斜线(\)将其转义。
$cat ex3

      echo “current directory is `pwd`”

      echo “home directory is $HOME”

      echo “file*.?”

      echo “directory ‘$HOME’ ”


$ ex3
      current directory is /home/zhang/prog
      home directory is /home/zhang
      file*.?
      directory ‘/home/zhang’

倒引号还可以嵌套使用。但应注意,嵌套使用时内层的倒引号必须用反斜线(\)将其转义。 


$ usrnum=`echo The number of users is \` who | wc -l\``

$ echo $usernum

此外,转义字符(即反斜线\)能把特殊字符变成普通字符:
$ echo “Filename is No\$\*”
Filename is No$*


输入/输出重定向符

执行一个shell命令时,通常自动打开三个标准文件,即

1.标准输入文件(stdin),通常对于键盘;
  
2.标准输出文件(stdout);
  
3.标准出错输出文件(stderr),这两个文件都对应屏幕

在shell中,这三个文件都可以用重定向符进行重新定位

1.输入重定向符“<” 

作用:把命令的标准输入定向到指定的文件



一般形式是:命令 < 文件名
经常需要执行的shell命令可以放进一个文件,并且让shell从该文件读取这些命令

$cat cmds

        echo “your working directory is `pwd`”

        echo “your name is `logname`”

        echo “the time is `date`”

        who

$ bash <cmds

Shell命令解释程序将从文件cmds中读取命令,并加以执行

2.输出重定向符“>”

作用:把命令的标准输出重新定向到指定的文件

一般形式是:命令 > 文件名
$ who > abc 
$ cat abc

who命令的输出重定向到abc文件中,
屏幕上看不到执行who命令的结果,
可以查看abc文件的内容即可看到。

$cat exp1

        echo “the time is `date`”

        echo “working directory is `pwd`”

        echo “it has `ls –l | wc -l` files”
 
$ exp1 > tmp1
 
$ cat tmp1

执行脚本文件后,才能把重定向的目标文件显示出来,正是exp1文件的执行结果;
如果不同的输出都重定向到同一文件,那么只有最后一次执行的输出保留在文件中,原有内容被覆盖。

输入和输出重新定向可以连在一起使用。例如:
$ wc  -l  < infile > outfile

功能是:
  
命令wc从文件infile中输入信息,
  
按“行”统计后的结果送到另一个文件outfile中,不在屏幕上显示。

3.输出附加定向符“>>”

把命令的标准输出附加到指定文件的后面,原有内容不变

一般形式是:命令>>文件名 

$ ps -l >> psfile
ps命令的输出附加到psfile文件的结尾处

与文件描述字有关的重定向

Linux系统中每个打开的文件,都由系统赋予的一个文件描述字,是个小整数。



系统为每个进程自动打开三个标准文件
标准输入、标准输出、错误输出,
 
其文件描述字分别为0,1和2。

标准错误输出也可重定向到一个文件中,其一般形式是:
命令  2> 文件名

命令  2>> 文件名
$gcc m1.c 2> errfile

注释:以#开头的正文行表示注释
如果shell脚本中第一行是以“#!”开头,则后面所跟的字符串就是所使用的shell的绝对路径名。

对于C shell脚本,第一行通常是: #!/bin/csh

对于bash脚本,第一行通常是: #!/bin/bash


这一行说明,该脚本是用哪一种shell编写的,从而调用相应的解释程序予以执行。

2.管道线
管道线是由竖杠“|”隔开的若干个命令组成的序列,
每个命令执行时都有一个独立的进程,
  前一个命令的输出是下一个命令的输入;

管道线中有一类命令也成为“过滤器”,即

首先读取输入,然后将以某种简单方式进行变换,相当于过滤,再将处理结果输出,
  
如:grep,sort,wc等命令就是过滤器;
一个管道线中可以包括多条命令:
ls -l  $HOME | wc –l
    
ls  | grep  m*.c | wc –l


3.后台命令 


前台方式:命令提示符之后输入,立即执行
后台方式:
可能有些程序的执行需要花费较长时间,如:调用C编译器对C程序进行编译,如果在此同时想做别的事情,则要输入
 $ gcc  m1.c&

在一条命令后面输入“&”,告诉shell该命令后台运行,
   而shell马上显示提示符状态,可以输入新命令。

命令执行操作符
多条命令可以在一行中出现,它们可以顺序执行,也可能在相邻的命令间存在逻辑关系,即:逻辑“与”和“或”

1.顺序执行
在执行时,以分号隔开的各条命令从左到右依次执行
   pwd ;  who | wc  -l ;  cd  /usr/bin
等价于每条命令独占一行 

逻辑与 &&

一般格式:命令1 && 命令2


其功能是:
先执行命令1,如果执行成功,才执行命令2;
 否则,若命令1执行不成功,则不执行命令2。 

$cp ex1 ex10 && rm ex1

3.逻辑或||
一般格式为:命令1 || 命令2

其功能是:
先执行命令1,如果执行不成功,则执行命令2;
          
否则,若命令1执行成功,则不执行命令2。
$cat abc || pwd

如果不能将文件abc的内容列出来
则显示当前的工作目录的绝对路径。



成组命令

Shell中有两种方式可以将若干命令组合在一起,
使其在逻辑上,被视为一条命令。

组合命令方式有两种:花括号{}和圆括号()
1.{ }形式

以花括号括起来的全部命令可视为语法上的一条命令,出现在管道符的一边。
执行顺序:根据命令出现的先后顺序,由左向右执行;

在管道线中,成组命令把各个命令的执行结果汇集到一起,形成一个输出流,作为管道线中下一个命令的输入

$ {  echo  “User  Report  for  ` date ` ”; who ; } | pr


pr命令可以用来将文本转换成适合打印的文件,一个基本用途就是将较大的文件分割成多个页面,并为每个页面添加标题。 

使用花括号时在格式上应注意:
  
左括号 “{ ”后面应有一个空格;
  
右括号“}”之前应有一个分号( ;)

2.( )形式
$ (echo  "Current  directory  is  ` pwd ` "
 cd  /home/mengqc ;  ls -l ;
 cp  m1  em1 && rm  m1; cat  em1) | pr



使用圆括号时在格式上:
 
左括号 “( ”后面不需空格;

右括号“)”之前也不需加一个分号

二者存在重要区别:

用花括号括起来的成组命令只是在本shell内执行,不产生新的进程;

而用圆括号括起来的成组命令要建立新的子进程,
因此不会改变父shell的变量值和工作目录等。

$ a=“current value”; export a

$ echo $a

      current value

$ (a=“new value-1”; echo $a)

      new value-1

$ echo $a

      current value

()会产生新的进程

$ a=“current value”; export a

$ echo $a

      current value

$ { a=“new value-2”; echo $a; }

      new value-2

$ echo $a

      new value-2

$ pwd

      /home/mengqc

$ (cd /bin; pwd)

     /bin

$ pwd

     /home/mengqc

6.shell变量
Shell有两类变量:环境变量和临时变量。

环境变量

  永久性的变量,其值不会随shell脚本执行结束而消失;

临时变量

   在shell程序内部定义,使用范围仅限于定义它的程序,出了本程序之外不能再使用,当程序执行完毕,它的值也就不存在了。

用户定义的变量:

1.用户定义的变量是最普通的shell变量,变量名是以字母或下线符打头的字母、数字和下线符序列,且大小写字母意义不同
 变量赋值
    定义变量并赋值的一般形式是:     变量名=字符串

例如: myfile=/usr/meng/m1.c
3.引用变量值: 在变量名前面加上一个符号“$”
如果在赋给变量的值中要含有空格、制表符或换行符,
  那么,就应该用双引号把这个字符串括起来。

    $ names="Zhangsan  Lisi  Wangwu"

如果变量值须出现在长字符串的开头或者中间,为了使变量名与其后的字符区分开,
避免shell把它与其它字符混在一起视为一个新变量,则应该用花括号将该变量名括起来。

例如,
     
 $ dir=/usr/meng 
     
 $ cat  ${dir}qc/m1.c



命令替换:将一个命令的执行结果赋值给变量
有两种形式的命令替换:
一种是使用倒引号引用命令,其一般形式是:
             
`命令`,如:$ dir=`pwd`

另一种形式是:
$(用分号隔开的命令)
    
 如:
$ dir=$(pwd)
            
$ echo  $(pwd ; cd  /home/mengqc ; ls)

数组

bash只提供一维数组,并且没有限定数组的大小。

类似C语言,数组元素的下标由0开始编号。

对数组元素赋值的一般形式是:
 数组名[下标]=值

$city[0]=Beijing
           
$city[1]=Shanghai
           
$city[2]=Wuhan

1.可以用declare命令显式声明一个数组,一般形式是:
             
declare  -a  数组名

读取数组元素值的一般格式是:
             
${数组名[下标]}    
例如:$ echo ${city[0]}

定义一个数组并为其赋初值的一般形式是:
数组名=(值1  值2  …  值n)
其中,各个值之间以空格分开。

例如:
$ A=(This is an example of shell script)
 
$ echo ${A[0]} ${A[2]} ${A[3]} ${A[6]}

2.若没有给出数组元素的下标,则数组名表示下标为0的数组元素


使用*或@当作下标,则会以数组中所有元素取代[*]或[@]
           
$echo ${A[*]} 
             
This is an example of shell script


变量引用
表达式$name表示变量name的值,若变量未定义,则用空值替换。
表达式${name}将被变量name的值替换。用花括号括起name,目的在于把变量名与后面的字符分隔开,避免出现混淆。替换后花括号被取消。

${name[n]}表示数组变量name中第n个元素的值
表达式${name[*]}和${name[@]}都表示数组name中所有非空元素的值,每个元素的值用空格分开。
表达式${#@}和${#*}
   它们的值分别是由$@和$*返回的参数的个数。
表达式${#name[i]}
   该表达式的值是数组name第i个元素值的长度(字符个数)。
表达式${#name[*]}和${#name[@]}
   它们的值都是数组name中已经设置的元素的个数。

4  输入/输出命令
可以利用read命令从键盘上读取数据,然后赋给指定的变量。
read命令的一般格式是:
      read  变量1 [ 变量2 …]

变量个数与给定数据个数相同,则依次对应赋值

变量个数少于数据个数,则从左至右对应赋值,但最后一个变量被赋予剩余的所有数据。

变量个数多于给定数据个数,则依次对应赋值,而没有数据与之对应的变量取空串
例:
read name
       
read a b c

echo命令
     
显示其后的变量值或者直接显示它后面的字符串 

位置参数
位置参数及其引用
   
位置变量的名称很特别,分别是0,1,2,… 

与命令行上具体位置上的实参相对应

如果位置变量包括两个或多个数字,则要用{}
引用它们的方式依次是$0, $1, $2, …, $9, ${10}等
其中,$0始终表示命令名或shell脚本名

位置变量不能通过一般赋值的方式直接赋值
通过命令行上对应位置的实参传值

命令行实参与脚本中位置变量的对应关系如下所示:
    
exam1  m1   m2  m3  m4
   
$0    $1     $2   $3   $4

用set命令为位置参数赋值
例:set m1.c m2.c m3.c

把字符串m1.c赋值给$1;m2.c赋值给$2;m3.c赋值给$3

但是$0不能用set命令赋值,它的值总是命令名

$cat ex7

      #!/bin/bash

      # test

         set  m1.c  m2.c

         cat $1 $2 $3 | wc –l

      #end

$ ex7

      9
 
移动位置参数

每执行一次shift命令,就把命令行上的实参向左移一位,
即相当于位置参数向右移动一个位置

    命令行:   ex7   A    B   C   D    E    F

原位置参数:   $0   $1   $2  $3   $4   $5   $6
    
    移位后:   $0  	 $1  $2   $3   $4    $5

注意:shift命令不能将$0移走,所以经shift右移位置参数后,
$0的值不会发生变化。
shift命令可以带有一个整数作为参数

set命令用来设定位置参数的值:

环境变量
用户注册过程中,系统需要做的一件事情就是建立用户环境;
Linux环境,也称为shell环境,由许多变量以及这些变量的值组成;
Shell环境包括:使用的shell类型、主目录所在位置、正在使用的终端类型等等

许多变量是在注册过程中定义的,一些为只读类型,一些为非只读类型,可以随意增加或修改。

1.常用的环境变量
HOME:用户主目录的全路径名 

LOGNAME:即你的注册名,由Linux自动设置
          
MAIL:你的系统信箱的路径
         
PATH:shell从中查找命令的目录列表。可以设置它,

       如:PATH=$PATH:$HOME/bin
           
PS1:shell的主提示符。 
  
   bash默认的主提示符一般为“\s-\v\$ ”。  其中,\s表示shell的 名称;\v表示bash的版本号。
    可以随意设置PS1的值,如: 
           PS1="Enter Command> “
   
           则:主提示符改成:”Enter Command>”
         
PWD:你当前工作目录的路径
      
SHELL:你当前使用的shell
        
TERM:你的终端类型

使用环境变量:
可以用echo命令查看任何一个环境变量的值,也可以在命令中将环境变量的值作为参数。
如果要使用环境变量时,必须在变量名之前加上一个“$”符号,不能直接使用变量名。

 例:
$echo $SHELL
 
   /bin/bash
                  
$cd $HOME  

参数置换变量:
参数置换变量是另一种为变量赋值的方式,其一般形式是:

               变量2=$ {变量1 op 字符串}

其中,op表示操作符,它可以是下列操作符之一:

      :-          : =          : +          : ?
1、变量2=$(变量1:-字符串)
作用:
如果变量1的值为空,则变量2的值等于给定的字符串;
      
否则变量2的值等于变量1的值,变量1的值保持不变。

算 术 运 算:

bash中执行整数算术运算的命令是let,其语法格式为:

       let arg …


其中arg是单独的算术表达式

例如:
let “j=i*6+2”
等价于: ((j=i*6+2))


当let命令计算表达式的值时,
  
若最后结果不为0,则let命令返回值为0,表示“真”;
  
否则返回值为1,表示“假”

当表达式中有shell的特殊字符时,必须用双引号将其括起来。
  
例如:let “val=a|b”,
  
如果不括起来,则shell会把命令行里的|看成管道符。

只有使用 $((算术表达式))的形式才能返回表达式的值

$ echo “((12*9))”
     ((12*9))

$ echo “$((12*9))”
     108



 if语句


if语句用于条件控制结构中,其一般格式为:
          
if  测试条件

     then  命令1

     else  命令2

fi


通常if语句的测试条件部分是利用test命令实现的
if  test  -f  "$1"

then  echo  "$1  is  an  ordinary  file . "

else  echo  "$1  is  not  an  ordinary  file . "

fi


test命令各选项的含义:
-e            该档名是否存在
-f            该档名是否为普通文件(file)
-d            该文件名是否为目录(directory)
-b            该文件名是否为一个block device装置
-c            该文件名是否为一个character device装置
-S            该档名是否为一个Socket档案
-p            该档名是否为一个FIFO(pipe)档案
-L            该档名是否为一个连结档

条件判断部分也可以用一般命令执行成功与否来判断,所以if语句更一般的表达形式是
if  命令表1

then  命令表2

else  命令表3

fi

if语句中else部分可以缺省
if  test  -f  "$1"
    
then  echo  "$1  is  an  ordinary  file . "
 
fi

if 语句的else部分还可以是else—if结构,则用关键字“elif”代替“else  if”。例如,


当测试条件由一条或多条命令组成时,以最后一条命令是否执行成功为准,来判断then和else


条件测试:

条件测试有三种常用形式:
  
 一种是用test 命令,如上所示;
   
另一种是用一对方括号将测试条件括起来。
   这两种形式是完全等价的。
  
 例如,测试位置参数$1是否是已存在的普通文件,可写为:
 test  -f  "$1“      
    

也完全可写成:[ -f  "$1" ]


第三种形式是: [[条件表达式]]

case语句:
case语句允许进行多重条件选择
case    字符串  in
      
模式字符串1)  命令

                      …
 
                     命令;;

                      …
      
模式字符串n)  命令

                      …

                      命令;;
esac


$ cat caseexp1

    echo “please choose a number from 1, 2,3”

    echo “1: copy a file”
                     
    echo “2: delete a file”
                     
    echo “3: quite”
                   
    read number
                  
    case $number in
         
    1) cp myfile newfile;;
    
    2) rm myfile;;
 
    3) echo “Goodbye”;;
 
    esac


while语句:
shell中有三种用于循环的语句,它们是:while语句、for语句和until语句。

while语句的一般形式是
while  测试条件
do
 
        命令表
 
done

测试条件部分除使用test命令或等价的方括号外,还可以是一组命令。
根据其最后一个命令的退出值决定是否进入循环体执行。

$ cat whileexp1

   while [$1]

   do 
 
       if [ -f $1 ]
 
       then echo “display:$1”
 
       cat $1
       
       else echo “$1 is not a filename”

       fi
 
       shift
    
   done

until语句

until语句的一般形式是:
until   测试条件
do

      命令表

done

它与while语句很相似,只是测试条件不同:
当测试条件为假时,才进入循环体,直至测试条件为真时终止循环。

for语句:
for语句是最常用的建立循环体的语句,使用方式主要有两种:
值表方式、算术表达式方式。

格式一:
      
for  变量  in  值表
do

        命令表
done

for day in Monday Wednesday Friday
do
 
   echo $day

done

格式二:
for  变量  in  文件正则表达式
     do
        命令表
 
     done


变量的值依次取当前目录下与正则表达式匹配的文件名,并进入循环,执行命令表。
for file in m*.c

    do

           cat $file | pr
 
    done


格式三:值表可以全部是位置参数,此时格式如下
 for  i  in  $*        等价于     for  变量

      do                               do

          命令表                          命令表

      done                             done

For语句使用算术表达式方式:
其一般格式是:
    
for ((e1;e2;e3))
    
do

        命令表
    
done



其中:e1, e2, e3是算术表
先按算术运算规则计算表达式e1;达式
接着计算e2,如果e2值不为0,则执行命令表中的命令,并且计算e3;

然后重复②,直至e2为0,退出循环
for ((i=1;i<=j;i++) )

打印给定行数的星号,第一行一个星号,第二行两个星号
for ((i=1;i<=$1;i++))
do
    for ((j=1;j<=i;j++))
    do
 
         echo –n “*”

    done
 
    echo “”
done


break命令和continue命令

break命令使程序从循环体中退出来。
其语法格式是:break  [ n ]


其中n表示要跳出几层循环,默认值为1.

continue命令

 跳过循环体中在它之后的语句,回到本层循环的开头,进行下一次循环。
其语法格式是:continue  [ n ]


n表示从包含continue的最内层循环体向外跳到第几层循环。默认值为1.


$ cat exp5

  for i in 1 2 3 4 5 6

  do
  
      if [ “$i” –eq 3 ]
 
      then continue
 
      else echo “$i”
      fi
                      
   done

exit命令
exit命令的功能是立即退出正在执行的shell脚本,并设定退出值
exit  [ n ]

n是设定的退出值,即退出状态。

select语句
select 语句通常用于菜单的设计,它自动完成接收用户输入的整个过程,
包括显示一组菜单项以及读入用户的选择

select 语句的语法形式为:
    
select identifier[in  word…]
     
do
       命令表
     
done



如果in word…这一部分被省略,那么参数identifier就以位置参数($1, $2, …)作为给定的值


函数
在shell脚本中可以定义并使用函数。其定义格式为:
      
[function]函数名( )
{ 
   命令表
 
}


函数应先定义,后使用。调用函数时,直接利用函数名,如showfile,不必带圆括号

shell脚本调试:
通常采用自底向上的方法,
即:先搞清楚要脚本做什么,然后将过程的连续阶段分解为独立的步骤,
最后利用shell提示符,交互式地检查和调试每个独立的步骤。

 解决办法是设置PATH:PATH=$PATH:. 

一个有用的技巧是在程序中经常使用echo或print命令,以显示脚本的执行过程,进行跟踪判断。


$# 是个特殊变量,表示命令行上参数的个数,不包括脚本名或命令名

  

原文地址:https://www.cnblogs.com/wust221/p/shell.html