Linux学习笔记七【SHELL篇】

初识Shell——bash

  Linux中的shell有点类似Windows下的cmd.exe或者.bat。管理整个计算机硬件的其实是操作系统的内核(Kernel),用户无法与内核直接交互,所以通过shell来跟内核通信。即shell介于操作系统(Kernel)和用户之间,它是用户与内核的翻译官,是一个命令解释器。

  常见的shell种类有:Bsh、Csh、Ksh、Bash、Zsh

Bsh和Csh出现的较早,Ksh继承了它两的功能,Bash继承了Bsh和Ksh的升级版,而且是Linux系统中默认的shell,Zsh则兼具了各种shell的程序有点,交互式操作效率更高,但仍不及bash应用广泛。

  Shell环境的切换:

  登录Shell:指用户每次登录系统后自动加载的Shell程序,大多数Linux系统采用 /bin/bash 作为默认登录Shell;/etc/shells 文件记录了系统支持的有效登录Shell

 
[root@localhost ~]# cat /etc/shells   ==>使用chsh -l命令也可查看
/bin/sh
/bin/bash
/sbin/nologin
/bin/tcsh
/bin/csh
/bin/ksh 
[root@localhost ~]#

  默认的shell是bash,那么如何切换shell环境呢?分为两种,一种是临时切换,即当此终端关闭时,下次启动仍然是bash,方法很简单,直接在终端输入shell名称;第二种则是更改用户登录Shell,需要修改/etc/passwd文件中用户记录的最后一个字段,或者像前面讲到的使用修改用户的命令:usermod  -s  shell程序路径   用户名,也可以使用chsh命令,下次登录有效。

  查看当前shell可以通过/etc/passwd来查看,要查看系统默认使用的shell,可以使用echo $SHELL。

  bash的常用功能:

1)Tab键自动补齐 这个功能可以让您少打很多字,并且确定输入是正确的。tab键接在一串命令的第一个字后面,则为命令补全;若接在一串命令的第二个字后面,则为文件补全;直接在bash提示符后面按两下tab键,则能显示所有可执行的命令。 2)命令编辑快捷键 如之前提到的ctrl+D,ctrl+L,ctrl+U等 3)命令历史  保存用户曾经执行过的命令操作;存放位置是:~/.bash_history隐藏文件;查看历史命令只需使用history命令就行了。  调用历史命令:   !n:执行历史记录中的第n条命令   !str:执行历史记录中以“str”开头的命令  设置记录历史命令的条数:修改HISTSIZSE参数即可(在/etc/profile文件里),默认为1000条。 (备注:正确使用历史命令可以有效提高命令行输入的效率,例如:如果之前不久执行过“service network restart”命令,则重新修改了网卡的配置文件以后,通常只需要执行“!ser”操作即可调用该历史命令,以重启network服务。合理控制历史命令的条数可以提高安全性,root用户的命令历史一般建议设置为少于100条) 4)命令别名  为使用较高的复杂命令行设置间断的调用名称,存放位置是:~/.bashrc隐藏文件。查看命令别名只需用alias命令即可。 (备注:手动使用alias命令设置的别名只在当前Shell环境中有效,如alias rm='rm -i';若需每次登录Shell环境时设置的别名都有效,需要修改宿主目录中的“.bashrc”文件,添加相应别名设置) 5)标准输入输出 交互式硬件设备:  标准输入:从该设备接收用户输入的数据 标准输出:通过该设备向用户输出数据 标准错误:通过该设备报告执行出错信息

类型

设备文件

文件描述编号

默认设备

标准输入

/dev/stdin

0

键盘

标准输出

/dev/stdout

1

显示器

标准错误输出

/dev/stderr

2

显示器

(备注:/dev/stdin、/dev/stdout、/dev/stderr这三个文件是一个符号链接,输入输出与重定向关系紧密)

6)重定向操作 改变标准输入、标准输出、标准错误的方向:

类型

操作符

用途

重定向标准输入

<

将命令中接收输入的途径由默认的键盘更改为指定的文件

重定向标准输出

>

将命令的执行结果输出到指定的文件中,而不是直接显示在屏幕上

>>

将命令执行的结果追加输出到指定文件

重定向标准错误

2>

清空指定文件的内容,并将标准错误信息保存到该文件中

2>>

将标准错误信息追加输出到指定的文件中

重定向标准输出和

标准错误

&>

将标准输出、标准错误的内容全部保存到指定的文件中,而不是直接显示在屏幕上

7)管道操作

  管道操作符号是“|”,连接左右两个命令,将左侧的命令输出的结果,作为右侧命令的输入(处理对象)。管道是脚本语言的特色,它可以讲多个功能连接起来形成一个大的功能,这点与面向对象有很大的不同。

  例如我现在要查看历史记录命令的第4行:则可先将前4条命令显示出来,在显示前4条命令的最后一条:history | head -4  |  tail  -1   这样即取出了第四条命令。

Shell变量

Shell变量和其他语言的变量含义大同小异,就是可以存放不同的内容,它也为灵活管理Linux系统提供特定参数。一般有两层意思:变量名:使用固定的名称,由系统预设或用户定义;变量值:能够根据用户设置、系统环境变化而变化。

Shell变量的种类:

用户自定义变量:由用户自己定义、修改和使用 环境变量:由系统维护,用于设置用户的Shell工作环境,只有极少数的变量用户可以修改 预定义变量:Bash预定义的特殊变量,不能直接修改 位置变量:通过命令行给程序传递执行参数,如ls -l  /etc/httpd,其中后面两个参数可视为位置参数。

变量的赋值与引用:

定义新的变量名要以英文字母或下划线开头,区分大小写,格式为:变量名=变量值

查看变量的值:echo  $变量名

[root@localhost ~]# DAY=Sunday 
[root@localhost ~]# echo $DAY   ==>通过$符号引用指定名称的变量值
Sunday 
[root@localhost ~]#

(注意:在查看变量时,如果变量名容易和后边的字符串连在一起导致混淆,则应该使用大括号将变量名括起来,使用形式为:${变量名}  ,例如:  若已知变量Var的值为Benet,则执行“echo $Var3.0”命令后将显示结果“.0”而不是“Benet3.0”,因为在该命令中,会将“Var3”当成变量名(默认未定义此变量)。若希望正确显示“Benet3.0”的输出结果,则需要执行“echo ${Var}3.0”)

[root@localhost ~]# Var =Benet
[root@localhost ~]# echo $Var
Benet
[root@localhost ~]# echo $Var3.0
.0
[root@localhost ~]# echo ${Var}3.0
Benet3.0
[root@localhost ~]# 

   从键盘输入内容为变量赋值,格式为:read  [-p  "输入信息"]  变量名

[root@localhost ~]#read var
haha    ==>这里等待输入
[root@localhost ~]#echo $var
haha
[root@localhost ~]#

    在给变量赋值时,使用不同的引号操作的区别:

1)使用双引号时,允许在双引号的范围内使用“$”符号引用其他变量的值(变量引用) 2)使用单引号时,将不允许在单引号的范围内引用其他变量的值,“$”符号或者其他任何符号将作为普通字符看待  
[root@localhost ~]#name=linuxidc
[root@localhost ~]#echo $name
linuxidc
[root@localhost ~]#myname="$name is me"  ==>这里使用的是双引号
[root@localhost ~]#echo $myname
linuxidc is me   ==>将$name这个变量执行出来了
[root@localhost ~]#myname='$name is me'   ==>这里使用的是单引号
[root@localhost ~]#echo $myname
$name is me   ==>原样显示出来了,$name没有执行
[root@localhost ~]#
  3)使用反撇号时,允许将执行特定命令的输出结果赋值给变量(命令替换),反撇号中的内容要求是可执行的命令。需要嵌套使用时,可以将反撇号改为 $( ... ) 的形式      这么说吧,在一串命令中,在`之内的命令会被先执行,然后将其执行出来的结果作为外部的输入信息。  
[linuxidc@localhost ~]$ locate crontab
/etc/anacrontab
/etc/crontab
/usr/bin/crontab
/usr/share/man/man1/crontab.1.gz
.....
[linuxidc@localhost ~]$ ls -l `locate crontab`    ==>先执行locate crontab,再执行ls -l
-rw-r--r-- 1 root root    298 2006-12-18 /etc/anacrontab
-rw-r--r-- 1 root root    255 2006-07-15 /etc/crontab
-rwsr-sr-x 1 root root 315416 2008-07-15 /usr/bin/crontab
-rw-r--r-- 1 root root   1846 2008-07-15 /usr/share/man/man1/crontab.1.gz
.....
[linuxidc@localhost ~]$ 
 

 设置变量的作用范围,格式:export  变量名

 一般情况下,父进程的自定义变量是无法在子进程中使用的,但是通过export将变量变成环境变量后,就能够在子进程下使用了。

 
[root@localhost ~]#name=linuxidc
[root@localhost ~]#echo $name
linuxidc
[root@localhost ~]# export name ==>输出为全局变量
[root@localhost ~]# zsh
[root@localhost]~# echo $name
linuxidc
[root@localhost]~# exit
[root@localhost root]# unset  name==>消除变量内容
[root@localhost root]# echo $name

[root@localhost ~]#

变量还有一个好处,当您经常访问一个目录时,而且这个目录比较长,可以将它付给一个简单的变量,这样进入这个目录时候只需cd  变量就行了。如work="~/temp/2013/test/",则进入这个目录只需cd  work。

数值变量的运算:

之前讲到过可以使用命令bc来打开一个计算器,其实Bash程序并不适合进行强大的数学运算,例如小数或指数运算的,一般只能进行简单的整数运算,若不使用bc,则也可以使用下列格式来进行简单数值计算:

格式:expr  变量1  运算符  变量2  [..运算符  变量n...]

expr命令常用的运算符有:+、-、\*(注意要有\)、/、%

[linuxidc@localhost ~]$ expr  10 * 2
expr :语法错误
[linuxidc@localhost ~]$ expr 10 \* 2
20
[linuxidc@localhost ~]$expr 10 + 2
12
[linuxidc@localhost ~]$

环境变量

   环境变量配置文件:全局配置文件:/etec/profile

          用户配置文件:~/.bash_profile

 查看环境变量:env  

    set    (set命令可以查看所有的Shell变量,其中包括环境变量)

 常见的环境变量:

$USER、$LOGNAME变量表示当前用户的登录名称 $UID变量表示当前用户的UID号 $SHELL变量表示用户使用的登录Shell $HOME变量表示用户的宿主目录 $PWD变量表示用户当前的工作目录 $PS1和$PS2变量分别代表了当前用户的主提示符(命令行提示符)和辅助提示符(例如,执行at命令后的“>”)

 位置变量:

    表示为$n,n为1~9之间的数字,$n的作用就是为脚本文件传递执行参数。

    在使用ls命令的时候,ls命令程序如何知道用户是否输入了“-l”、“-a”选项?如何知道用户要查看的是哪个目录?位置变量正是用来解决这个问题的,它可以将用户在命令行输入的参数复制给特定的变量,然后交给程序去处理 (例如当执行“service network restart”命令行时,第1个位置参数用“$1”表示,对应的值为“network”,第2个位置参数用“$2”表示,对应的值为“restart”;当用户输入的位置参数超过9个时,位置变量 $9 将自动包含最后部分的所有字符串(即使有空格分隔))
[linuxidc@localhost ~]ls -a -l -l  ==>a就是第1个位置变量,依次类推,其实这个命令就是ls -al

 预定义变量:

[root@localhost ~]# bash 
[root@localhost ~]# echo  $0  $$  ==>查看当前所执行进程的名称、PID号
-bash 32484
[root@localhost ~]# exxit   ==>执行一条错误的命令
bash: exxit: command not found
[root@localhost ~]# echo $?   
127    ==>返回非0值,表示上一条命令异常
[root@localhost ~]# exit
exit
[root@localhost ~]# echo $? 
0    ==>返回0,说明上一条命令正常

   灵活使用这些变量,将大大增强Shell脚本程序的功能,$0作为预定义变量,表示当前执行的程序名,需要与$1~$9的位置变量区分开。

  引入shell脚本 

     shell脚本:

用途:完成特定的、较复杂的系统管理任务 格式:集中保存多条Linux命令,普通文本文件 执行方式:按照预设的顺序依次解释执行 (Shell脚本类似于Windows系统中.bat批处理文件,只需要有相应的命令解释器即可,不需要编译   Shell脚本是为了完成一定的管理任务才创建的,因此脚本文件中的各条命令并不是杂乱无章随便放置的,需要用户进行组织和设计 )    建立包含执行语句的脚本文件:
[root@localhost ~]# vi repboot.sh
#!/bin/bash  ==>声明使用的shell环境,根据执行的方式不同可加可不加
# To show usage of /boot directory and mode of kernel file.
echo "Useage of /boot: "
du -sh /boot
echo "The mode of kernel file:"
ls -lh /boot/vmlinuz-*
[root@localhost ~]# chmod a+x repboot.sh 
Shell脚本文件的扩展名并无严格的约束,不一定非得是“.sh”结尾,只是便于识别   运行Shell脚本程序: 直接执行具有“x”权限的脚本文件     例如:./repboot.sh 使用指定的解释器程序执行脚本内容     例如:bash  repboot.sh、sh  repboot.sh 通过source命令(或 . )读取脚本内容执行     例如:souce  repboot.sh  或  .  hello.sh (说明: 在调试阶段可以使用Shell程序直接调用脚本文件,不要求脚本具有可执行权限,格式是:bash 脚本名 为脚本文件设置了可执行属性后,在Shell命令行中可以直接通过脚本文件的路径执行脚本程序 使用Bash的内部命令“.”脚本文件执行时,将不会开启新的Shell环境。使用这种方式时,脚本文件作为“.”命令的参数,因此同样不要求具备可执行权限。)

编写Shell脚本的简单语法

  提到编程,条件、分支、循环等语法肯定少不了。shell脚本编程亦是如此,下面只是结合最近学习的一些简单语法做的笔记,如有错误,欢迎指正。

  if条件语句

  条件测试操作

    test命令:

      用途:测试特定的表达式是否成立,当条件成立时,命令执行后的返回值为0,否则为其它值

      格式:test  条件表达式   或者

         [  条件表达式  ](注意:方括号[]和条件表达式语句之间至少需要一个空格)

  常见的测试类型:测试文件状态;字符串比较;整数值比较;逻辑测试

测试文件状态:[  操作符   文件或目录  ]

常用的测试操作符 -d:测试是否为目录(Directory) -e:测试目录或文件是否存在(Exist) -f:测试是否为文件(File) -r:测试当前用户是否有权限读取(Read) -w:测试当前用户是否有权限写入(Write) -x:测试当前用户是否可执行(Excute)该文件 -L:测试是否为符号连接(Link)文件
如何判断当前所在的工作目录是否为 /usr/src?
[ $PWD = “/usr/src” ] && echo “YES” || echo “NO”
[root@localhost ~]# [  -d /etc/vsftpd  ]
[root@localhost ~]# echo $?
0   ==>返回值为0,表示上一步测试的条件成立
[root@localhost ~]# [  -d /etc/hosts  ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [ -e /media/cdrom ] && echo "YES"   ==>如果测试的条件成立则输出“YES”
YES 
[root@localhost ~]# [ -e /media/cdrom/Server ] && echo "YES“
[root@localhost ~]#

    上述&&是“与”的意思,“… && echo YES”表示如果没有输出,则表示前面执行的测试条件不成立或命令出错。(备注:l测试文件是否可写(-w)时,不要以root用户(特权用户)的身份执行测试,否则可能会无法准确判断)

整数值比较:[  整数1  操作符  整数2  ]

常用的测试操作符 -eq:等于(Equal) -ne:不等于(Not Equal) -gt:大于(Greater Than) -lt:小于(Lesser Than) -le:小于或等于(Lesser or Equal) -ge:大于或等于(Greater or Equal)  
[root@localhost ~]# who | wc -l
5
[root@localhost ~]# [ `who | wc -l` -le 10 ] && echo "YES"  ==>如果登录用户数小于或等于10则输出YES
YES 
[root@localhost ~]# df -hT | grep "/boot" | awk '{print $6}'
12% 
[root@localhost ~]# BootUsage=`df -hT | grep "/boot" | awk '{print $6}' | cut -d "%" -f 1`
解释: df -hT                查看所有分区的磁盘空间使用情况 grep “/boot”          过滤出关于/boot分的数据行 awk ‘{print $6}’      以空格为分隔符,只取第六个字段数据 cut -d “%” -f1        以%为分隔符,只取第一个字段数据
[root@localhost ~]# echo $BootUsage
12
[root@localhost ~]# [ $BootUsage -gt 95 ] && echo "YES"  ==>如果/boot分区的磁盘使用率超过95%则输出YES

条件测试操作:[  字符串1 =  字符串2  ]

常用的测试操作符 =:字符串内容相同 !=:字符串内容不同,! 号表示相反的意思 -z:字符串内容为空
[root@localhost ~]# read -p "Location:" FilePath
Location:/etc/inittab
[root@localhost ~]# [ $FilePath = "/etc/inittab" ] && echo "YES" ==>如果键入路径与指定的目录一致则输出YES
YES 
[root@localhost ~]# [ $LANG != "en.US" ] && echo $LANG  ==>如果当前的语言环境不是en_US,则输出LANG变量的值
zh_CN.UTF-8 

(字符串比较可以用于检查用户输入,例如在提供交互式操作时,判断用户输入的选择项是否与指定的变量内容相匹配)

逻辑测试:[  表达式1  ]  操作符  [  表达式2  ]

     常用的测试操作符 -a或&&:逻辑与,“而且”的意思(前后两个表达式都成立时整个测试结果才为真,否则为假) -o或||:逻辑或,“或者”的意思(操作符两边至少一个为真时,结果为真,否则结果为假) !:逻辑否( 当指定的条件不成立时,返回结果为真)
[root@localhost ~]# echo $USER
root
[root@localhost ~]# [ $USER != "teacher" ]  &&  echo "Not teacher" ==>如果发现用户不是teacher,则提示“Not teacher”
Not teacher
[root@localhost ~]# [ $USER = "teacher" ]  ||  echo "Not teacher"
Not teacher  
逻辑测试指的是:同时使用两个(或多个)条件表达式时,判断它们之间的关系

  if语句结构——当“条件成立”时执行相应的操作

      单分支:

if  条件测试命令 

    then   命令序列

fi                                      

  if语句简单应用示例

#!/bin/bash
temp=2
if [  $temp -eq 2  ]
then 
    echo 'temp is 2'
fi

   保存为test文件,并将它设置为可执行(+x)

[jzhou@localhost ~]# chmod 775 test
[jzhou@localhost ~]# bash test
temp is 2

     (备注:lthen可以写到与if一行,但要用分号隔开,例如: if  [  $RATE  -gt  80  ]  ;  then)

  单分支:

if  条件测试命令 

    then  命令序列1 

    else  命令序列2

fi

  判断mysqld是否在运行,若已运行则输出提示信息,否则重新启动mysqld服务
#!/bin/bash
service mysqld status &> /dev/null 
if  [  $?  -eq  0  ]   ==>判断上句是否执行成功
    then
        echo  "mysqld service is running."
    else
        /etc/init.d/mysqld  restart
fi
   
提示用户输入一个整数,如何判断该值是否小于100?
read  “Input an integer:” NUM ; 
           if  [ $NUM  -lt  100 ] ; then
               echo “小于100”
           else
              echo  “大于或等于100”
           fi
 

  多分支: 

if  条件测试命令1  ;  then

    命令序列1

elif  条件测试命令2  ;  then

    命令序列2

elif  ...

else

    命令序列n

fi

  for循环语句——根据变量的不同取值,重复执行一组命令操作

   for语句的结构

for  变量名  in  取值列表

do

    命令序列

done

   for语句简单应用示例

 
依次输出3条文字信息,包括一天中的“Morning”、“Noon”、“Evening”字串
[root@localhost ~]# vi showday.sh
#!/bin/bash
for TM in "Morning" "Noon" "Evening"
do
    echo "The $TM of the day."
done 
 
[root@localhost ~]# sh showday.sh
The Morning of the day.
The Noon of the day.
The Evening of the day 
 
 示例2:获得用户的满足条件的文件数
#!/bin/bash DIR="/opt" LMT=100 ValidUsers=`grep "/bin/bash" /etc/passwd | cut -d ":" -f 1` ==>获得使用bash作为登录shell的用户名列表 for UserName in $ValidUsers do Num=`find $DIR -user $UserName | wc -l` if [ $Num -gt $LMT ] ; then echo "$UserName have $Num files." fi done
 
[root@localhost ~]# sh chkfileown.sh
root have 6737 files.
teacher have 344 files. 

  while循环语句——重复测试指定的条件,只要条件成立则反复执行对应的命令操作

  while语句的结构

 while  命令或表达式 

 do 

      命令列表

 done

  while语句简单应用示例

批量添加20个系统用户帐号, 用户名依次为“stu1”、“stu2”、……、“stu20”
#!/bin/bash
i=1
while  [  $i  -le  20  ]
do
    useradd stu$i
    echo "123456" | passwd --stdin stu$i &> /dev/null
    i=`expr $i + 1`
done 
   
批量删除上述添加的20个系统用户帐号
#!/bin/bash
i=1
while  [  $i  -le  20  ]
do
    userdel -r stu$i
    i=`expr $i + 1`
done 

  分支控制语句

  case语句——根据变量的不同取值,分别执行不同的命令操作

case  变量值  in

    模式1)

          命令序列1

          ;;

    模式2)

          命令序列2

          ;;

  ……

    * )

          默认执行的命令序列

esac

(当遇到双分号“;;”后跳转至esac表示结束分支。如果一直找不到相匹配的值,则执行最后一个模式“*)”后的默认命令序列,直到遇到esac后结束分支)

#!/bin/bash 
case   $1   in
    start)
        echo  "Start MySQL service."
        ;;
    stop)
        echo  "Stop MySQL service."
        ;;
    *)
        echo  "Usage:$0  start|stop"
        ;;
esac

(/etc/init.d/ 目录下的各类脚本中,大量使用了case分支语句结构)

#!/bin/bash
read  -p  "Press some key, then press Return:“  KEY
case  "$KEY“  in
  [a-z]|[A-Z])
      echo "It's a letter."
      ;;
  [0-9])
      echo "It's a digit."
      ;;
  *)
      echo "It's function keys、Spacebar or other keys. "
esac

(匹配模式中可以使用方括号表示一个连续的范围,例如“[0-9]”;使用竖杠符号“|”表示或,例如“A|B”(A或者B))

  unitl语句——根据条件执行重复操作

until  条件测试命令

do

      命令序列

done

(•until 循环的结构与while命令类似,“until通过检测其后接命令的返回值“$?”来判断是否退出循环

   •until:直到”测试条件“成立时终止循环,而while是:当”测试条件“成立时进行循环

   •即:until在测试条件为假(非0)时执行循环,条件为真时(0)退出循环,正好与while循环相反)

 

  shift语句——用于迁移位置变量,将 $1~$9 依次向左传递(不常用)

例如,若当前脚本程序获得的位置变量如下:  $1=file1、$2=file2、$3=file3、$4=file4 则执行一次shift命令后,各位置变量为:  $1=file2、$2=file3、$3=file4 再次执行shift命令后,各位置变量为:  $1=file3、$2=file4
通过命令行参数传递多个整数值,并计算总和
[root@localhost ~]# vi showday.sh
#!/bin/bash
Result=0
while  [  $#  -gt  0  ]  ==> $# 为预定义变量,表示位置参数的个数
do
    Result=`expr $Result + $1`
    shift
done
echo "The sum is : $Result"
 
[root@localhost ~]# ./sumer.sh  12  34  56
The sum is : 102 

  循环控制语句

    break:在for、while、until等循环语句和case中用于跳出当前的循环体,执行循环体后的语句;并不退出程序。执行break命令后将跳到done语句之后。

 

    continue:在for、while、until等循环语句中,用于跳过循环体内余下的语句,重新判断条件以便执行下一次循环

  shell函数应用

  定义新的函数:

   

  调用已定义的函数: 函数名

  向函数内传递参数:函数名   参数1   参数2 ......

#!/bin/bash
adder() {
    echo `expr $1 + $2`
}
adder 12 34
adder 56 789
[root@localhost ~]# sh adderfun.sh
46
845 

相关文章

用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把2...
#!/bin/bashcommand1&command2&wait从Shell脚本并行...
1.先查出MAMP下面集成的PHP版本cd/Applications/MAMP/bin/ph...
1、先输入locale-a,查看一下现在已安装的语言2、若不存在如...
BashPerlTclsyntaxdiff1.进制数表示Languagebinaryoctalhexa...
正常安装了k8s后,使用kubect工具后接的命令不能直接tab补全...