shell编程初步

简介: shell编程初步

0. shell的作用

Shell就是命令行工具的胶水,没有任何语言能像Shell一样方便地将一大堆命令行工具组合起来。原则上来说,Shell做什么都可以,但显然它最适合的是自动化,因为只需要将你原来手动敲的命令都复制到一个文件里面就行了。

Shell跟标准的编程语言区别很大,它基本上是一个面向字符串的编程语言,组合用好awk/sed/grep,偶尔配合eval,有时候会发挥奇效。

1. shell基本语法

1.1 变量

1.1.1 变量概念及使用

按照惯例,Shell变量通常由字母加下划线开头,由任意长度的字母、数字、下划线组成。有两种类型的Shell变量:

1.环境变量

环境变量可以从父进程传给子进程,因此Shell进程的环境变量可以从当前Shell进程传给fork出来的子进程。用printenv命令可以显示当前Shell进程的环境变量。

2.本地变量

只存在于当前Shell进程。

3. 变量赋值

环境变量是任何进程都有的概念,而本地变量是Shell特有的概念。在Shell中,环境变量和本地变量的定义和用法相似。在Shell中定义或赋值一个变量:

VAR_NAME=value

注意等号两边都不能有空格,否则会被Shell解释成命令和命令行参数。

一个变量定义后仅存在于当前Shell进程,它是本地变量,用export命令可以把本地变量导出为环境变量

4. 用unset删除变量

unset VAR_NAME

5. 引用变量

$VAR_NAME

1.1.2 特殊变量

$#:查看变量参数的个数
$0:查看脚本的名字
$!:查看shell后台的pid
$@:查看传递脚本所有参数的列表
$*:查看所有参数的列表,单字符串形式显示
$$:脚本本身进程的ID
$?:上一条命令的结果,显示0则成功,不是0则失败

1.1.3 内部环境变量

$PATH 
SHELL             当前使用的shell
UID             当前的用户环境 {0|其它数字}={root|其它用户}
HOME              当前使用的用户目录
PWD             当前的目录 
HISTFILE        历史命令路径
PS1             #[\u@\h \W]\$    用户@主机名\目录\$

1.2 单引号和双引号

被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引号的处理方式不同

1.3 条件测试

shell中提供两种条件判断的方法。

1. test指令

overboom@overboom:~/Work $ var=2
overboom@overboom:~/Work $ test $var -gt 1
overboom@overboom:~/Work $ echo $?
0
overboom@overboom:~/Work $ 
overboom@overboom:~/Work $ 
overboom@overboom:~/Work $ test $var -eq 3
overboom@overboom:~/Work $ echo $?
1
overboom@overboom:~/Work $ test $var -eq 2
overboom@overboom:~/Work $ echo $?
0

$? 代表上一个命令执行后的退出状态

2. [ condition ] 注意condition前后要有空格。非空返回0,0为 true,否则为 false 。

overboom@overboom:~/Work$ var=2
overboom@overboom:~/Work$ [ $var -gt 3 ] 
overboom@overboom:~/Work$ echo $?
1
overboom@overboom:~/Work$ [ $var -eq 2 ]
overboom@overboom:~/Work$ echo $?
0
overboom@overboom:~/Work$ 

常见测试指令如下:

[ -d DIR ] 如果DIR存在并且是一个目录则为真

[ -f FILE ] 如果FILE存在且是一个普通文件则为真

[ -z STRING ] 如果STRING的长度为零则为真

[ -n STRING ] 如果STRING的长度非零则为真

[ STRING1 = STRING2 ] 如果两个字符串相同则为真

[ STRING1 != STRING2 ] 如果字符串不相同则为真

[ ARG1 OP ARG2 ] ARG1和ARG2应该是整数或者取值为整数的变量,OP是-eq(等于)-ne(不等于)-lt(小于)-le(小于等于)-gt(大于)-ge(大于等于)之中的一个

3. 条件测试与逻辑运算符

和C语言类似,测试条件之间还可以做与、或、非逻辑运算:

[ ! EXPR ] EXPR可以是上表中的任意一种测试条件,!表示“逻辑反(非)”

[ EXPR1 -a EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-a表示“逻辑与”

[ EXPR1 -o EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-o表示“逻辑或”

1.4 分支结构

1.4.1 if/then/elif/else/fi

if语法如下:

多分支:
if [ 条件 ];then
  statement1
  .....
elif  [ 条件2 ];then
  statement2
  ....
else
  statement3
  ....
fi

和C语言类似,在Shell中用if、then、elif、else、fi这几条命令实现分支控制。

下面看一段示例code

#! /bin/sh
if [ -f /bin/bash ]
then 
  echo "/bin/bash is a file"
else 
  echo "/bin/bash is NOT a file"
fi
if :; then echo "always true"; fi

与C类似的语法,并不难理解。

“:”是一个特殊的命令,称为空命令,该命令不做任何事,但Exit Status总是真。此外,也可以执行/bin/true或/bin/false得到真或假的Exit Status。

1.4.2 case/esac

case语法如下:

case $变量名 in            
  'value1')                
     statement                
     ...                
 ;;            
  'value2')                
    statement                
    ...                
 ;;            
*)                
    statement                
    ..                
 ;;        
esac
#case支持的通配符:
    *           //任意长度任意字符
    ?           //任意单个字符
    []          //指字范围内的任意单个字符
   start|START  //俩种选择

case命令可类比C语言的switch/case语句,esac表示case语句块的结束。C语言的case只能匹配整型或字符型常量表达式,而Shell脚本的case可以匹配字符串和Wildcard,每个匹配分支可以有若干条命令,末尾必须以;;结束,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出。

#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
  echo "Good Morning!";;
[nN]*)
  echo "Good Afternoon!";;
*)
  echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
  return 1;;
esac
return 0

1.5 循环

1.5.1 for/do/done

for语法如下:

第一种:
for  ((expr1;expr2;expr3))      # expr1:初始值条件  
                #expr2:循环的范围进行退出  
                #expr3:变量的值使用
{
  循环体
}
for ((expr1;expr2;expr3));do            
  循环体
done
第二种:
for  变量  in 列表; do
  循环体
done

Shell脚本的for循环结构和C语言很不一样,它类似于某些编程语言的foreach循环。例如:

#! /bin/sh
for FRUIT in apple banana pear; do
  echo "I like $FRUIT"
done

1.5.2 while/do/done

while语法如下:

while循环用于不知道循环次数的场景,注意有退出条件
while [ 条件 ];do
  statement
  .....
done

while的用法和C语言类似。比如一个验证密码的脚本:

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

1.5.3 break和continue

break[n]可以指定跳出几层循环;continue跳过本次循环,但不会跳出循环。

即break跳出,continue跳过。

2 位置参数和特殊变量

shell中常用的位置参数和特殊变量总结:

$0      相当于C语言main函数的argv[0]
$1、$2...  这些称为位置参数(Positional Parameter),相当于C语言main函数的argv[1]、argv[2]...
$#      相当于C语言main函数的argc - 1,注意这里的#后面不表示注释
$@      表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面。
$*      表示参数列表"$1" "$2" ...,同上
$?      上一条命令的Exit Status
$$      当前进程号
#!/bin/bash
echo ------start--------
echo $0 $1 $2 
echo $*
echo $@
echo $$
echo $#
echo ------end--------

输出如下:

可以在shell脚本中使用shift丢弃一些参数

#!/bin/bash
echo ------start--------
echo ------before shift--------
echo $0 $1 $2 $3 $4 $5
echo $#
shift
shift
shift
echo ------after shift--------
echo $0 $1 $2 $3 $4 $5
echo $#
echo ------end--------

输出如下:

3. 输入和输出

3.1 echo

显示文本行或变量,或者把字符串输入到文件。

echo [option] string
-e 解析转义字符
-n 不回车换行。默认情况echo回显的内容后面跟一个回车换行。

3.2 tee

tee命令把结果输出到标准输出,另一个副本输出到相应文件。

overboom@overboom:~/Work$ ls -al
total 52
drwxrwxr-x 11 overboom overboom 4096 9月  12 22:47 .
drwxr-xr-x 22 overboom overboom 4096 9月  12 22:47 ..
drwxr-xr-x  7 root     root     4096 7月   4 18:39 beanCode
drwxrwxr-x  2 overboom overboom 4096 3月  27 23:45 c_code
drwxrwxr-x  3 overboom overboom 4096 8月  10 21:07 fdbus
drwxrwxr-x  3 overboom overboom 4096 8月  10 23:16 git_usage
drwxrwxr-x  4 overboom overboom 4096 6月   2 00:08 jsoncpp
drwxrwxr-x  2 overboom overboom 4096 8月   4 21:20 minixml
-rwxrwxr-x  1 overboom overboom  213 9月  12 22:45 position.sh
drwxrwxr-x  2 overboom overboom 4096 5月  27 23:43 test_C++Sqlite
drwxrwxr-x  6 overboom overboom 4096 5月  25 21:37 test_find
-rwxrwxr-x  1 overboom overboom  141 9月  12 21:23 test.sh
drwxrwxr-x  3 overboom overboom 4096 4月  10 17:36 xwp_linux
overboom@overboom:~/Work$ ls -al | tee out_file
total 52
drwxrwxr-x 11 overboom overboom 4096 9月  12 23:02 .
drwxr-xr-x 22 overboom overboom 4096 9月  12 22:47 ..
drwxr-xr-x  7 root     root     4096 7月   4 18:39 beanCode
drwxrwxr-x  2 overboom overboom 4096 3月  27 23:45 c_code
drwxrwxr-x  3 overboom overboom 4096 8月  10 21:07 fdbus
drwxrwxr-x  3 overboom overboom 4096 8月  10 23:16 git_usage
drwxrwxr-x  4 overboom overboom 4096 6月   2 00:08 jsoncpp
drwxrwxr-x  2 overboom overboom 4096 8月   4 21:20 minixml
-rwxrwxr-x  1 overboom overboom  213 9月  12 22:45 position.sh
drwxrwxr-x  2 overboom overboom 4096 5月  27 23:43 test_C++Sqlite
drwxrwxr-x  6 overboom overboom 4096 5月  25 21:37 test_find
-rwxrwxr-x  1 overboom overboom  141 9月  12 21:23 test.sh
drwxrwxr-x  3 overboom overboom 4096 4月  10 17:36 xwp_linux
overboom@overboom:~/Work$ ll
total 56
drwxrwxr-x 11 overboom overboom 4096 9月  12 23:02 ./
drwxr-xr-x 22 overboom overboom 4096 9月  12 22:47 ../
drwxr-xr-x  7 root     root     4096 7月   4 18:39 beanCode/
drwxrwxr-x  2 overboom overboom 4096 3月  27 23:45 c_code/
drwxrwxr-x  3 overboom overboom 4096 8月  10 21:07 fdbus/
drwxrwxr-x  3 overboom overboom 4096 8月  10 23:16 git_usage/
drwxrwxr-x  4 overboom overboom 4096 6月   2 00:08 jsoncpp/
drwxrwxr-x  2 overboom overboom 4096 8月   4 21:20 minixml/
-rw-rw-r--  1 overboom overboom  793 9月  12 23:02 out_file
-rwxrwxr-x  1 overboom overboom  213 9月  12 22:45 position.sh*
drwxrwxr-x  2 overboom overboom 4096 5月  27 23:43 test_C++Sqlite/
drwxrwxr-x  6 overboom overboom 4096 5月  25 21:37 test_find/
-rwxrwxr-x  1 overboom overboom  141 9月  12 21:23 test.sh*
drwxrwxr-x  3 overboom overboom 4096 4月  10 17:36 xwp_linux/
overboom@overboom:~/Work$ cat out_file 
total 52
drwxrwxr-x 11 overboom overboom 4096 9月  12 23:02 .
drwxr-xr-x 22 overboom overboom 4096 9月  12 22:47 ..
drwxr-xr-x  7 root     root     4096 7月   4 18:39 beanCode
drwxrwxr-x  2 overboom overboom 4096 3月  27 23:45 c_code
drwxrwxr-x  3 overboom overboom 4096 8月  10 21:07 fdbus
drwxrwxr-x  3 overboom overboom 4096 8月  10 23:16 git_usage
drwxrwxr-x  4 overboom overboom 4096 6月   2 00:08 jsoncpp
drwxrwxr-x  2 overboom overboom 4096 8月   4 21:20 minixml
-rwxrwxr-x  1 overboom overboom  213 9月  12 22:45 position.sh
drwxrwxr-x  2 overboom overboom 4096 5月  27 23:43 test_C++Sqlite
drwxrwxr-x  6 overboom overboom 4096 5月  25 21:37 test_find
-rwxrwxr-x  1 overboom overboom  141 9月  12 21:23 test.sh
drwxrwxr-x  3 overboom overboom 4096 4月  10 17:36 xwp_linux

3.3 文件重定向

cmd > file        把标准输出重定向到新文件中
cmd >> file       追加
cmd > file 2>&1     标准出错也重定向到1所指向的file里
cmd >> file 2>&1
cmd < file1 > file2   输入输出都定向到文件里
cmd < &fd         把文件描述符fd作为标准输入
cmd > &fd         把文件描述符fd作为标准输出
cmd < &-        关闭标准输入  

4.函数

和C语言类似,Shell中也有函数的概念,但是函数定义中没有返回值也没有参数列表。

Shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样是用$0、$1、$2等变量来提取参数,函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$0、$1、$2等变量。函数中可以用return命令返回,如果return后面跟一个数字则表示函数的Exit Status。

5. shell脚本调试方法

Shell提供了一些用于调试脚本的选项,如:

-n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。

-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出。

-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。

这些选项有三种常见的使用方法:

1.在命令行提供参数。如:
$ sh -x ./script.sh
2.在脚本开头提供参数。如:
#! /bin/sh -x
3.在脚本中用set命令启用或禁用参数。如:
#! /bin/sh
if [ -z "$1" ]; then
  set -x
  echo "ERROR: Insufficient Args."
  exit 1
  set +x
fi

set -x和set +x分别表示启用和禁用-x参数,这样可以只对脚本中的某一段进行跟踪调试。


相关文章
|
5月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
5月前
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
3月前
|
运维 监控 Shell
深入理解Linux系统下的Shell脚本编程
【10月更文挑战第24天】本文将深入浅出地介绍Linux系统中Shell脚本的基础知识和实用技巧,帮助读者从零开始学习编写Shell脚本。通过本文的学习,你将能够掌握Shell脚本的基本语法、变量使用、流程控制以及函数定义等核心概念,并学会如何将这些知识应用于实际问题解决中。文章还将展示几个实用的Shell脚本例子,以加深对知识点的理解和应用。无论你是运维人员还是软件开发者,这篇文章都将为你提供强大的Linux自动化工具。
|
4月前
|
Shell
Shell编程(下)
Shell编程(下)
123 1
|
4月前
|
Shell Linux Windows
Shell编程(上)
Shell编程(上)
65 1
|
4月前
|
Shell Linux 开发工具
|
4月前
|
监控 Unix Shell
shell脚本编程学习
【10月更文挑战第1天】shell脚本编程
103 12
|
5月前
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
5月前
|
存储 Unix Shell
shell脚本编程基础
【9月更文挑战第4天】
70 12
|
5月前
|
Shell Linux
Shell 编程 编写hello word
Shell 编写hello word
66 5