2 Linux Shell

最简单 Shell 程序解析及运行 #

#!/bin/sh 是指此脚本使用/bin/sh 来解释执行,#!是特殊的表示符,其后面跟的是此解释此脚本的 shell 的路径。

Shell 程序第一行规范建议

  • 建议写法
    • #! /bin/sh
  • 强烈不建议写法—— #!型注释
    • #! 其它注释内容
  • 两种运行方式
    • ./
    • sh

Shell 程序调试方式 #

命令 选项 功能说明
sh -n 程序名 纯粹性 解释分析 解释但不执行命令(用于检查程序语法错误)
sh -x 程序名 显示替换结果程序并执行 在变量替换之后、执行命令之前,显示程序的每一有效行(非空且非注释行)
sh -v 程序名 显示原始程序并执行 在执行之前,按输入的原样显示输出程序中所有各行(包括空行、注释行)
set -x 打开(命令)回显 跟踪程序的执行(主要用于 Shell 程序中)
set +x 关闭(命令)回显 关闭跟踪功能(主要用于 Shell 程序中)

局部变量应用编程 #

Shell 变量赋值与引用

  • Shell 变量赋值
    • <变量名>=<初始值>
    • 譬如:fileName=“zgsFile.txt”
    • 注意等号两边不能有空格!!!
  • Shell 变量引用
    • $<变量名>
    • "$fileName"、“$fileName”、‘$fileName’ 或任何引号外 $fileName 有效
    • '$fileName' 无效($ 亦按普通字符处理)
    • $fileName 比较特殊(按命令串处理)
  • 清理变量 unset varName
空格在linux中时作为一个很典型的分隔符,比如string1=this is astring,这样执行就会报错。为了避免这个问题,因此就产生了单引号和双引号。他们的区别在于,单引号将剥夺其中的所有字符的特殊含义,而双引号中的'$'(参数替换)和'`'(命令替换)是例外。所以,两者基本上没有什么区别,除非在内容中遇到了参数替换符$和命令替换符`。

Shell 变量分类

  • 局部变量(本地变量)
    • 自己定义的变量
  • 环境变量(类似于全局变量)
    • 可以自己定义并全局化
    • 譬如 $PATH
  • 预定义变量(特殊变量)
    • $+【*#$?!】
  • 位置变量(类似于 C 程序的命令行参数)
    • $+【数字】

环境变量及 export 命令 #

查看系统环境变量取值 -env 命令

BASH脚本基础:环境变量PS2介绍_bash ps2-CSDN博客

$ echo $HOME
 home/zhaigaoshou2018
$ echo $LANG
  en_US.UTF-8
$ echo $TERM
  xterm-256color
$ echo $PS2
  >
$ echo $PATH
opt/ros/kinetic/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Shell 程序的环境变量

  • 定义方式类似于局部变量,但在作为环境变量使用前必须通过 export 命令进行导出
  • 环境变量生命周期和作用域涵盖对应所有各级子孙进程
  • 系统环境变量生命周期和作用域涵盖所有用户及各子孙进程,最好在.profile 文件中定义

export 命令解析

  • 是变量(名)而非仅仅变量取值的传递

位置变量及特殊变量应用编程 #

Shell 程序的位置变量

  • 类似于 C 程序的命令行参数,用来对命令行中各对应位置的参数进行描述
    • $0 对应 Shell 命令或 Shell 程序名本身
    • $1、$2、…、$9 分别对应传入的第 1 个参数、第 2 参数、…、第 9 个参数
  • 当实际参数大于 9 个时,需要用 shift 命令来移动位置参数
    • 每执行一次 shift n 命令,最前面的 n 个传入参数将被移去,于是第 n+1 个参数成为 $1,以此类推
    • 缺省情况下,n 等于 1

Shell 命令中的特殊变量

$*
命令行中的传入参数序列
$#
命令行中的传入参数的个数
$?
前一个命令返回的结果(状态)值,0为正常
$$
当前Shell进程的进程标识符PID值
$!
最近访问的后台进程的进程标识符PID值

条件判断控制结构应用编程 #

if-else 基本代码结构

  • command-list1 或 command-list2 等两处命令行部分均可嵌套使用 if 条件判断控制结构
  • else 分支可无
if [ "$varZGS" = valueZGS ] 
then 
 command-list1
else
 command-list2
fi
  • command-list1 或 command-list2 等三处命令行部分均可嵌套使用 if 条件判断控制结构,且 then 命令行与 fi 之间的 elif-then 命令行可有连续多个
if [ "$varZGS1" = valueZGS1 ] 
then 
 command-list1
elif [ "$varZGS2" = valueZGS2 ]
then 
 command-list2
else
 command-list3
fi

分支判断控制结构应用编程 #

case-Shell 例程

case "$cmdID" in
 d|D)  date ;;
 w|W)  who ;;
 s|S)  echo "Hello! `who`" ;;
 c|C)  echo "Hello! China!" ;;
 *)    echo "$cmdID is not a valid choice!"
       echo "Run the program and try again!"
       exit 1 ;;
esac
exit 0

case 代码基本结构

case "$varZGS" in 
 pattern1) command-list1 ;;
 pattern2) command-list2 ;;
 pattern3) command-list3 ;;
 pattern4) command-list4 ;;
 ……
 *) command-list ;;
esac

循环控制结构应用编程

while-Shell 例程

#! /bin/sh

Sum=0
i=0
while [ $i != "100" ]
do
  i=`expr $i + 1`
  Sum=`expr $Sum + $i`
done
echo "Sum-from-1-to-$i = $Sum"

while 循环结构编程要旨

  • 基本代码结构
while [ "$varZGS" != valueZGS ] 
do 
 command-list
done
  • while 循环执行过程
    • 首先判断循环条件是否成立
    • 若不成立,则转③结束循环;否则,进入循环体执行有关命令,然后转①
    • 循环结束

until 循环结构编程要旨

  • 基本代码结构
until [ $varZGS = valueZGS ] 
do 
 command-list
done
  • until 循环执行过程
    • 首先判断循环条件是否成立
    • 若成立,则转③结束循环;否则,进入循环体执行有关命令,然后转①
    • 循环结束

for shell 例程

#! /bin/sh

Sum=0
for zI in 1 2 3 4 5 6
do
  Sum=`expr $Sum + $zI`
done

echo "SumOf[1 2 3 4 5 6] = $Sum"

for 循环结构 1 编程要旨

  • 基本代码结构
for varZGS 
do 
 command-list
done
  • for 循环执行过程
    • 首先判断 Shell 程序还有无传入参数
    • 若无,则转③结束循环;否则,将最前面的传入参数赋值给循环变量,进入循环体执行有关命令,然后传入参数执行左移操作,转①
    • 循环结束

for 循环结构 2 编程要旨

  • 基本代码结构
for zI in zList
do 
 command-list
done
  • for 循环执行过程
    • 首先判断循环变量取值列表还有无数值
    • 若无,则转③结束循环;否则,将最前面的数值赋值给循环变量,进入循环体执行有关命令,然后有关取值列表执行左移操作,转①
    • 循环结束

for 循环结构 3 编程要旨

  • 基本代码结构
for varZGS in `cmd`
do 
 command-list
done

break 与 continue 编程要旨

逆序显示命令行参数的 shell 例程

#! /bin/sh
# A continue shell-script

count=$#
cmd='echo'
while true
do
 cmd="$cmd \$$count"
 count=`expr $count - 1`
 [ $count -gt 0 ] && continue
 eval $cmd
 exit 0
done
#! /bin/sh
# A break shell-script

count=$#
cmd='echo'
while true
do
 cmd="$cmd \$$count"
 count=`expr $count - 1`
 [ $count -eq 0 ] && break
done
eval $cmd

拷贝若干文件的 shell 例程

Fetching Title#lq1u

#! /bin/sh
# A shell script for copying files to directory  
eval dir=\$$#
if [ -d $dir ]
then
 echo $dir
 cmd=cp
 while [ $1 ]
 do
  if [ -f $1 ]
  then
  {
   cmd="$cmd $1"
   echo $cmd
  }
  else
   echo "$1 is not a file"
  fi
  shift
 done
 cmd="$cmd $dir"
 echo $cmd
 $cmd
fi

显示若干文件内容的 Shell 例程

#! /bin/sh
# A shell script for displaying contents of specified files

echo "Error messgages:" > errFile
exitStatus=0
if [ $# = 0 ]
then
{
 echo "At least one argument is needed!" >> errFile
 echo "Usage: shApp-CatFilesBv file1 [file2 … fileN]" >> errFile
 exitStatus=1
}
fi
while [ $1 ]
do
 if [ -f "$1" ] && [ -r "$1" ]
 then
 {
  i=1
  while read LINE
  do
   echo "$i#: " $LINE
   i=`expr $i + 1`
  done < "$1"
 }
 else
  echo "$1 is a not ordinary file or it can't be read!!!" >> errFile
  exitStatus=2
 fi
 shift
done

监控系统用户的 shell 例程

#! /bin/sh
# A shell script for monitoring log-user

who | sort > prev
while true
do
 sleep 10
 who | sort > curr
 echo "logged out:"
 comm -23 prev curr   #显示只在`prev`中而不在`curr`中的行,即已注销的用户
 echo "================="
 echo "logged in:"
 comm -13 prev curr   #显示只在`curr`中而不在`prev`中的行,即新登录的用户
 echo "================="
 mv curr prev
done

函数应用编程 #

  • 函数定义
function_name()
{
 <命令行序列>
 return [n]
}
  • 调用方式
function_name [<参数序列>]

Shell 文件中函数调用要旨

  • 命令行中定义的函数
    • 存在于 B-Shell 内存空间
  • 文件中定义的 Shell 函数使用方式
    • source <ShellFunFileName>
    • 若想使有关函数永久保留在 Shell 空间,则需将语句 “.~/<ShellFunFileName>” 写入文件.profile 和.bashrc 中,从而在启动 Shell 时读取这些函数文件加载到 Shell 空间中 【/etc/profile.d/*.sh】
  • 查看(Shell 空间中的)函数与导出函数
    • set 命令、export <函数名>【使子进程可用】
#! /bin/sh
# A shell script demonstrating functions for Jia/Jian/Cheng/Chu
Add()
{
 echo -e "add: $1 + $2 = \c"
 echo "$1 + $2" | bc
}
Sub()
{
 echo -e "sub: $1 - $2 = \c"
 echo "$1 - $2" | bc
}
Mult()
{
 echo -e "mult: $1 * $2 = \c"
 echo "$1 * $2" | bc
}
Div()
{
 echo -e "div: $1 / $2 = \c"
 echo "$1 / $2" | bc
}
Mod()
{
 echo -e "mod: $1 % $2 = \c"
 echo "$1 % $2" | bc
}
  • 函数必须先定义,后使用
  • 函数在当前环境中运行,可共享其主调程序中的变量,还允许以给位置参量赋值的方式向函数传递参数
  • 可使用 local 功能在函数内部创建局部变量
  • return 命令返回主调程序;exit 命令结束整个程序
  • 可用 export -f 把函数导出到子 Shell 中
  • 函数可以递归,且递归调用次数没有限制