目录
  1. Linux Shell 脚本攻略
    1. 第一章 小试牛刀
      1. 1.1 Shell发展的时间线
      2. 1.2 终端中的显示输出
        1. 界面介绍:
        2. shell 脚本
        3. 打印
        4. 环境变量
      3. 1.5 Shell 进行数学运算
        1. 反引号的作用:存储命令
        2. 整数运算
        3. 浮点数
      4. 1.6 重定向与追加模式
      5. 1.7 数组与关联数组
        1. 普通数组
        2. 关联数组
      6. 1.8 alias定义别名
      7. 1.12 函数和参数
        1. 定义函数
        2. 递归函数
        3. 导出函数
        4. 向命令传递参数
      8. 1.17 比较与测试
        1. 条件语句
        2. 算法比较
        3. 文件系统相关测试
        4. 字符串比较
Linux Shell 脚本攻略

Linux Shell 脚本攻略

第一章 小试牛刀

1.1 Shell发展的时间线

  • 早期计算机没有操作系统,也无图形化界面。
  • 20世纪60年代,支持交互式界面
  • 贝尔实验室,开发交互式用户界面,使得通过文本文件去执行程序,而非一堆命令。这些文件就是shell脚本,这带来了效率的巨大提升。
  • 早期Unix只支持一种交互式 shell 【sh】
  • 1989年,推出了一种全新的 shell 【bash】
  • 随着Linux的发展,bash 逐渐成为标准shell

1.2 终端中的显示输出

界面介绍:

jacky@jacky-virtual-machine:~$

形式为:username@hostname$

$ 表示普通该用户,# 表示管理员用户root

shell 脚本

  • 通常以shebang来定义该脚本所使用的解释器#!/bin/bash

    内核会读取脚本的首行,自动识别/bin/bash

  • 可执行文件

    # 通过Bash 执行
    bash myScript.sh

    # 修改权限后,执行文件
    chmod 755 myScript.sh
    或者 chmod a+x sample.sh
    ./myScript.sh

打印

# 不加引号echo 可以打印
echo Hello world
# 特殊字符无法打出,需要加引号
echo "Hello world ;world"
# printf 可以格式化输出
printf "%-5s %-10s %-4s\n" No Name Mark
printf "%-5s %-10s %-4.2f\n" 1 Sarath 80.3456
printf "%-5s %-10s %-4.2f\n" 2 James 90.9989
printf "%-5s %-10s %-4.2f\n" 3 Jeff 77.564
# echo -e 转义输出
echo -e "1\t2\t3"
# echo 花括号可以限制打印范围
count=5
fruit=apple
echo "We have $count ${fruit}(s)"

输出:

Hello world
-------------------------------
Hello world ;world
-------------------------------
No Name Mark
1 Sarath 80.35
2 James 91.00
3 Jeff 77.56
--------------------------------
1 2 3
--------------------------------
We have 5 apple(s)

彩色打印:

echo -e "\e[1;31m This is red text \e[0m"

文字:重置=0,黑色=30,红色31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37。

背景:重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47。

环境变量

# 查看环境变量
# 1. 查看系统的环境变量
env
# 2. 查看进程的环境变量
$ pgrep gedit # pgrep查找进程的ID
cat /prop/$PID/environ | tr '\0' '\n' # 根据进程的ID,查看进程的环境变量

# 获取字符串的长度
var=1234567890
echo ${#var}

# 获取当前所使用的shell
echo $SHELL
echo $0

# 获取当前的用户类型,0代表root用户
echo $UID

export 命令生命了将由子进程所继承的一个或多个变量,这些变量被导出后,当前shell脚本所执行的任何应用程序都会获得这个变量。

1.5 Shell 进行数学运算

反引号的作用:存储命令

# 存储命令
cmd_output=`ls | cat -n`
echo $cmd_output

在执行一条命令时,会先将其中的``中的语句当作命令执行一遍,再将结果加入到原命令中重新执行,例如:

date=`date -d '1 day ago' "+%Y-%m-%d"`
echo $date
#结果:2017-01-09

date=$(date -d '1 day ago' "+%Y-%m-%d")
echo $date
#结果:2017-01-09

# Shell 脚本
for path in `find $HADOOP_HOME -name "*.jar"`
do
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$path
done

整数运算

使用let(())[]作为基本操作

# let  操作
no1=4;
no2=5;
let result=no1+no2
echo $result
# let 的简化写法
let no+=6
let no1++

# []的使用
result=$[ no1 + no2 ]

#expr 的使用
result=`expr 3 + 4`
result=$(expr $no1 +5)

浮点数

# 高级操作: | bc 
echo "4 * 0.56" | bc

# 单引号可以保存命令
result=`echo "$no * 1.5" | bc`
echo $result

# 控制前缀
## 控制输出的小数点
echo "scale=2;22/7" | bc
## 进制转换
no=100
echo "obase=2;$no" | bc # 转换为2进制
echo "obase=10;$no" | bc # 转换为10进制

# 计算平方以及平方根
echo "sqrt(100)" | bc
echo "10^10" | bc

1.6 重定向与追加模式

# > 重定向
echo "This is a sample text 1" > temp.txt
# >> 追加模式
echo "This is a sample text 1" > temp.txt

# 输出错误内容
ls + 2 > out.txt
0 -- stdin (标准输入)
1 -- stdout(标准输出)
2-- stderr(标准错误)

# 默认 > 等价于 1>
# 默认 >> 等价于 1>>

1.7 数组与关联数组

Bash支持普通数组和关联数组。普通数组以整数作为索引,后者使用字符串作为索引。

普通数组

array_var=(test1 test2 test3 test4)

# 同理可以定义 "索引-值"
array_var[0]="test1"
array_var[1]="test2"
array_var[2]="test3"
array_var[3]="test4"

# 读取
echo ${array_var[0]} # 不加花括号,直接输出第一个索引值

# 套娃索引
index=3
echo ${array_var[$index]}

#打印所有数组
echo ${array_var[*]}
echo ${array_var[@]}

# 打印长度
echo ${#array_var[*]}

关联数组

# 首先需要定义关联数组 
declare -A ass_array
# 1. [索引值]--值
ass_array=([index1]=val1 [index2]=val2)
# 2. 索引赋值模式
ass_array[index1]=val1
ass_array[index2]=val2
# 查看的时候记得加花括号{}
echo "${ass_array[index1]}"
# 输出索引
echo ${!ass_array[*]}

注:如果没有声明,则默认是数值索引数组。

1.8 alias定义别名

# 定义好别名后,我们就可以用install来代替sudo apt-get install 了。
alias install='sudo apt-get install'

# 列出当前所有的别名
alias

alias是暂时的,一旦关闭终端,别名就会失效,若要永久使用可以把定义语句写入~/.bashrc文件中。每当一个新的交互式shell进程生成时,都会执行~/.bashrc文件。

echo 'alias new_command="command sequence"' >> ~./bashrc'

创建别名时,如果已经有同名的别名存在,那么原有的别名设置将被新的设置取代。

1.12 函数和参数

定义函数

function fname()
{
statements;
}
# 可以省略function
fname()
{
echo $1, $2; # 访问参数
echo "$@" ;# 输出所有输入的实体
echo "$*" ;#把所有输入单实体输出
return 0 ; # 返回值
}
# 行内function,注意花括号前后需要一个空格。
fname() { statement; }


# 调用
fname a b

读取外部输入:

输入参数 说明
$0 脚本的名称
$1 ,$2 ,$n 第n个参数
“$@” 被扩展成"$1" , “$2” , “$3”
“$*” 被扩展乘"$1c$2c$3"
$? 返回命令返回值

建议使用$@,可以一个一个输出

递归函数

函数在编写的过程中可以调用自己,如下:

F() { echo $1; F hello; sleep 1 ; }

导出函数

$function getIP() { ifconfig $1 | grep 'inet'; }
$echo "getIP ens33">test.sh # getIP这个命令是文件中的内容,终端无法识别
# 若直接运行
(base) jacky@jacky-virtual-machine:~/桌面$ bash test.sh
test.sh: 行 1: getIP: 未找到命令

# 导出函数
export -f getIP

# 再运行一次sh脚本
(base) jacky@jacky-virtual-machine:~/桌面$ bash test.sh
inet 地址:192.168.6.129 广播:192.168.6.255 掩码:255.255.255.0
inet6 地址: fe80::d74:deb0:c82e:1f79/64 Scope:Link

向命令传递参数

#! /bin/bash
for i in `seq 1 $#`
do
echo $i is $1
shift
done

# 结果查看
(base) jacky@jacky-virtual-machine:~/桌面$ bash test.sh a b c
1 is a
2 is b
3 is c

shift命令将$1参数一次向左移动一个位置,让脚本使用$1访问每一个参数。

1.17 比较与测试

条件语句

# 只有一个条件
if condition;
then
commands;
fi
# else情况
if condition;
then
commands;
else if condition; then
commands;
else
commands;
if

if 和 else 语句能够嵌套使用。if的条件判断可能导致代码非常长,可以通过逻辑运算符将它变得简洁一些,如下:

# 使用技巧:
[ condition ] && action # 如果condition为真,则执行action
[ condition ] | | action # 如果condition为假,则执行action

算法比较

[  $var -eq 0 ] or [ $var -ne 0  ]
# 其他重要的操作符:
-gt : 大于(greater than)
-lt : 小于(less than)
-ge : 大于等于
-le : 小于等于

-a : 逻辑与操作符
-0 : 逻辑或操作符
# 使用方法
[ $var 1 -ne 0 -a $var2 -gt 2 ] # 使用逻辑与 -a

文件系统相关测试

[ -f $file_var ]: 是否是一个正常的文件路径或者是文件名
[ -x $var ]: 文件是否可执行
[ -d $var ]: 是否是目录
[ -e $var ]: 是否存在文件
[ -c $var ]: 是否包含字符设备文件
[ -b $var ]: 是否包含块设备文件的路径
[ -w $var ]: 文件是否可写
[ -r $var ]: 文件是否可读
[ -L $var ]: 是否包含符号链接

例子:

fpath="/etc/passwd"
if [ -e $fpath ] ; then
echo File exists ;
else
echo Does not exits;
fi

字符串比较

[[ $str1 == $str2]]  # 是否字符串相同
[[ $str1 != $str2 ]] # 不相同
[[ -z $str1 ]] # 是否为空串
[[ -n $str1 ]] # 是否不为空串

利用&&||能够组合条件:

if [[ $str1 == $str2]]  &&  [[ $str1 == $str2]] ;
then
commands;
fi