自动化的基础:shell 脚本

858 阅读4分钟

前置条件

  • 一台 linux 或 windows 服务器
  • 会使用 linux 和 windows 下的终端(windows 得安装支持 bash 的终端)

内容主体

常见的 linux 目录含义

目录用途
/根目录
/bin二进制目录,存放用户级的 GNU 工具
/boot启动目录,存放启动文件
/dev设备目录,linux 在这里创建设备节点
/etc系统配置文件
/home主目录,linux 在这里创建用户目录
/lib库目录,存放系统和应用程序的库文件
/media媒体目录,可移动媒体设备的常用挂载点
/nmt挂载目录,另一个可移动媒体设备的常用挂载点
/opt可选目录,常用于存放第三方软件包和数据文件
/proc进程目录,存放现有硬件及当前进程的相关信息
/rootroot 用户的主目录
/sbin系统二进制目录,存放许多 GNU 管理员级工具
/run运行目录,存放系统运作时的运行时数据
/srv服务目录,存放本地服务的相关文件
/sys系统目录,存放系统硬件信息的相关文件
/tmp临时目录,可以在该目录中创建和删除临时工作文件
/usr用户二进制目录,大量用户级的 GNU 工具和数据文件都存储在这里
/var可变目录,用以存放经常变化的文件,比如日志文件

目录命令

显示所有文件(区分文件夹与文件)

ls -F  

显示文件或文件夹的详细信息(权限、用户组、文件类型)

ll 

搜索目录下 content

ll *t?nt

创建目录

mkdir folder

复制目录

cp -R souce destination

删除目录

rmdir folder
rm -rf folder

显示目录树

tree folder

文件命令

创建

touch 1.txt

复制

cp -R source destination

重命名

mv from  to

删除

rm -i file

查看文件内容类型

file fileExample

查看部分文件

more file
tail file

进程命令

显示当前所有进程

ps -ef
  • -e 参数指定所有运行在系统上的进程
  • -f 参数扩展输出的进程消息
UID: 启 动 这 些 进 程 的 用 户。
PID: 进 程 的 进 程 ID。
PPID: 父 进 程 的 进 程 号( 如 果 该 进 程 是 由 另 一 个 进 程 启 动 的)。
C: 进 程 生 命 周 期 中 的 CPU 利 用 率。 
STIME: 进 程 启 动 时 的 系 统 时 间。
TTY: 进 程 启 动 时 的 终 端 设 备。 
TIME: 运 行 进 程 需 要 的 累 计 CPU 时 间。
CMD: 启 动 的 程 序 名 称。

结束进程

kill pid
kill -s pid # 强制关闭
killall 进程名 # 关闭进程名的所有进程

父子shell

进程列表,用 () 表示子 shell

(pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls ; echo $ BASH_SUBSHELL)

变量

设置变量

echo $my_variable 
my_variable = Hello #变量赋值
echo $my_variable   #打印变量
unset my_variable   #删除变量

yum 软件安装管理

列出已经安装的包

yum list installed

安装软件

yum install package_name

更新软件

yum update package_name
yum updates

卸载软件

yum remove package_name #保留数据和配置文件
yum erase package_name  #删除软件和配置文件

手动安装软件

#下载tar
tar -zxvf tar包
make
make install

Vim 编辑器

快捷键

:set nu #打开行号
:set nonu #隐藏行号	
G   #移到最后一行
gg  #移动到第一行
dd  #删除当前行
d5d #删除光标后5行
p   #粘贴到某地
/值  #查找

Shell 基础

创建 shell 脚本文件

#!/bin/bash  
#文件开头
date
who

用户变量

name=william  #注意 = 之间不能有空格

value1=10 
value2=$value1

命令输出给变量

test=`date`
test=$(date)

date +%y%m%d #201125

输出重定向

who > 123.txt  # 创建或覆盖原有内容
who >> 123.txt # 追加内容

输入重定向

wc < 123.txt  #输出 行数、词数、字节数

wc << EOF     #内联输入重定向,只需要在对内联输入的提供
> test 1
> test 2
> test 3
>EOF          # 输入结束符

管道

ls | more

整数运算

var1 = 100 
var2 = 45 
var3 = $[ $var1 / $var2]

输出 2

浮点运算

#!/bin/bash 
var1=100 
var2=45 
var3=$( echo "scale = 4; $var1/$var2" | bc) 
echo The answer for this is $ var3

输出:2.2222

退出脚本

echo $?  # 查看退出状态 0为成功
# 退出码 0~255
exit 5

结构化语句

if-then 语句

if pwd        #pwd 命令返回 0 则执行then
then echo "It worked" 
fi

if-then-else 语句

if  command	
then command
else command
fi

嵌套 if

if  command	
then command
else 
	if command
	then command
	fi
fi

elif

if command1 
then 
	commands 
elif command2  #状态码为0则执行then中语句
then 
	more commands 
fi

test 命令

test condition

if test condition     #
then 
	echo "No expression returns a True" 
else 
	echo "No expression returns a False" 
fi

条件 [ ]

if [ $num -gt 2 ] 
then 
        echo "True" 
else 
        echo "False" 
fi

[ ] 也可以进行字符串比较

str1=name
str2=sex
[ $str1 = $str2 ]
[ $str1 \> $str2 ] #转义大写

str1=william
str2=''
[ -n $str1 ] 
#判 断 str1 变 量 是 否 长 度 非 0, 而 它 的 长 度 正 好 非 0, 所 以 then 部 分 被 执 行 了。
[ -z $str2 ] 
#判 断 str1 变 量 是 否 长 度 为 0, 而 它 的 长 度 正 好 为 0, 所 以 then 部 分 被 执 行 了。

文件比较

-d file 检 查 file 是 否 存 在 并 是 一 个 目 录 
-e file 检 查 file 是 否 存 在 
-f file 检 查 file 是 否 存 在 并 是 一 个 文 件 
-r file 检 查 file 是 否 存 在 并 可 读 
-s file 检 查 file 是 否 存 在 并 非 空 
-w file 检 查 file 是 否 存 在 并 可 写 
-x file 检 查 file 是 否 存 在 并 可 执 行 
-O file 检 查 file 是 否 存 在 并 属 当 前 用 户 所 有 
-G file 检 查 file 是 否 存 在 并 且 默 认 组 与 当 前 用 户 相 同 
file1 -nt file2 检 查 file1 是 否 比 file2 新 
file1 -ot file2 检 查 file1 是 否 比 file2 旧

检查目录是否存在 -d

#!/bin/bash 
jump_directory=/home/shell 
if [ -d $jump_directory ] 
then 
	echo "The $jump_directory directory exists" 
	cd $jump_directory 
	ls 
else 
	echo "The $jump_directory directory does not exist" 
fi

检查对象是否存在-e

#!/bin/bash
location=$HOME
file_name="123"
if [ -e $location ]
then 
	echo "OK on the $location directory."
	echo "Now checking on the file, $file_name."
	
	if [ -e $location/$file_name ]
	then
		echo "Ok on the filename"
		echo "Updating current Date"
		date >> $location/$file_name
	else
		echo "File does not exist"
		echo "Nothing to update"
	fi
else
	echo "The $location directory does not exist."
	echo "Nothing to update"
fi

复合条件

if-then 语句中加入复合条件

[ condition1 ] && [ condition2 ] 
[ condition1 ] | | [ condition2 ]

使用双括号 (())

val1=10
if (( $val1**2>90 )) 
then 
	(( val2=$val1**2 )) 
	echo "The square of $val1 is $val2" 
fi

case 命令

case variable in 
pattern1 | pattern2) commands1;; 
pattern3) commands2;; 
*) default commands;; 
esac

循环

for 循环

for var in list 
do commands 
done

例子

for test in Alabama Alaska Arizona Arkansas California Colorado 
do echo The next state is $ test 
done 

拼接list

list="Alabama Alaska Arizona Arkansas Colorado" 
list=$list" 123" 
for state in $list 
do echo "Have you ever visited $state?" 
done

从命令中读取列表

file=`pwd` 
for state in $( ls $file)   #ls、cat等
do echo "Visit beautiful $state" 
done

内部字段分隔符

file=`pwd`
IFS=$'\n' #告 诉 bash shell 在 数 据 值 中 忽 略 空 格 和 制 表 符。采用换行符分割list
IFS=: #用:分号分割列表
IFS = $'\ n':;" #多个 IFS 值

for state in $( ls $ file) 
do echo "Visit beautiful $state" 
done

使用通配符读取目录

mulu=/home/*
for file in $mulu 
do 
	if [ -d "$file" ] 
	then echo "$file is a directory" 
	elif [ -f "$file" ] 
	then echo "$file is a file" 
	fi 
done

C 语言类型的 for 循环

for (( i=1; i <= 10; i ++ )) 
do echo "The next number is $i" 
done

while 命令

while test command 
do other commands 
done	
while [ $var1 -gt 0 ] #迭代条件必须能够跳出,否则进入死循环
var1 = $[ $var1 - 1 ]


while 使用多个测试命令

var1=10 
while echo $var1 
	[ $var1 -ge 0 ] 
do 
	echo "This is inside the loop" 
	var1=$[ $var1 - 1 ] 
done

util 命令

until test commands #一旦测试命令返回了0状态码,则不再执行 do 方法 
do other commands 
done
var1=100 
until [ $var1 -eq 0 ] 
do echo $var1 
var1=$[ $var1 - 25 ] 
done

循环控制

break   #跳出内部循环,外部循环仍在运行
continue #符合某个条件,直接进入下一次循环

循环的输出重定向到文件

done -> output.txt  #循环的输出直接到哦 output.txt 命令中

得到可在命令行中可执行文件的列表

IFS=: 
for folder in $PATH 
do 
	echo "$ folder:" 
	for file in $folder/* 
	do 
		if [ -x $file ] 
		then 
		echo " $file" 
		fi 
	done 
done

Shell 循环案例

从 .csv 读取文件

input="users.csv" 
while IFS=',' read -r userid name 
do 
	echo "adding $userid" 
	useradd -c "$name" -m $userid
done < "$input"


#user.csv内容
users.csv 

rich, Richard Blum 
christine, Christine Bresnahan 
barbara, Barbara Blum 
tim, Timothy Bresnahan


处理用户输入

读取参数

total=$[ $1 * $2 ] 
echo The first parameter is $1. 
cho The second parameter is $2. 
echo The total value is $total. 

./ test2. sh 2 5 
The first parameter is 2.
# 若参数中包含空格必须使用单引号或双引号

获取当前运行的脚本名

name=$( basename $0) 
echo 
echo The script name is: $name

The script name is: test5b.sh

获取当前输入参数的数量

echo There were $# parameters supplied. # $# 含有脚本运行时携带的命令行参数的个数。
echo this is $2 parameters # $2 表示输入的第二个变量

# 你 不 能 在 花 括 号 内 使 用 美 元 符。 必 须 将 美 元 符 换 成 感 叹 号。
# 获取输入变量的最后一个变量
echo The last parameter is ${!#}

一次获取所有参数

count=1 

for param in "$*" 
do 
	echo "\$* Parameter #$count=$param" 
	count=$[ $count + 1 ] 
done 
####################################################

count=1 

for param in "$@" 
do 
	echo "\$@ Parameter #$count=$param" 
	count=$[ $count + 1 ]
done

处理选项

while [ -n "$1" ] 
do 
	case "$1" in 
	-a) echo "Found the -a option" ;; 
	-b) echo "Found the -b option" ;; 
	-c) echo "Found the -c option" ;; 
	*) echo "$1 is not an option" ;; 
	esac 
	shift 
done

# shift 命 令 时, 默 认 情 况 下 它 会 将 每 个 参 数 变 量 向 左 移 动 一 个 位 置。 所 以, 变 量 $ 3 的 值 会 移 到 $ 2 中, 变 量 $ 2 的 值 会 移 到 $ 1 中, 而 变 量 $ 1 的 值 则 会 被 删 除( 注 意, 变 量 $ 0 的 值, 也 就 是 程 序 名, 不 会 改 变)。

分 离 参 数 和 选 项

while [ -n "$1" ] 
do
	case "$1" in 
        -a) echo "Found the -a option" ;; 
        -b) echo "Found the -b option";; 
        -c) echo "Found the -c option" ;; 
        --) shift 
        	break ;; 
        *) echo "$1 is not an option";; 
        esac 
        shift 
done

count=1 
for param in $@ 
do 
	echo "Parameter #$count: $param" 
	count=$[ $count + 1 ] 
done


用户输入

echo -n "Enter your name: " 
read name 
echo "Hello $name, welcome to my program. "
#####################
read -p "Please enter your age: " age 
days=$[ $age * 365 ] 
echo "That makes you over $days days old! "

实际应用

windows 平台下的 jar 包一键启动脚本

@echo off

set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_261
set APP_HOME=D:\sqjzService
set PROFILE=dev
set JVM_XMS=1g
set JVM_XMX=4g
set PORT=7788

title sqjzService_%PORT%
set classpath=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%lib\tools.jar;
set path=%JAVA_HOME%\bin

java -Xms%JVM_XMS% -Xmx%JVM_XMX%   -jar %APP_HOME%\sqjzService.jar  --spring.profiles.active=%PROFILE%  > %APP_HOME%/logs/batchLog%date:~0,4%%date:~5,2%%date:~8,2%.log

@pause

目录结构

./
logs/
sqjzService.jar
start.bat
start.sh
stop.bat
stop.sh

Linux 系统下 jar 包启动脚本

#!/bin/bash

JAVA_HOME=/opt/java/jdk1.8.0_144
APP_HOME=/opt/jar/sqjzService
PROFILE=prod
JVM_XMS=1g
JVM_XMX=4g

nohup $JDK/java -Xms$JVM_XMS -Xmx$JVM_XMX -jar $APP_HOME/sqjzService.jar \
--spring.profiles.active=$PROFILE \
>/dev/null 2>&1 &

windows 下一键关闭脚本

@echo off

set PORT=7788

for /f "tokens=5" %%i in ('netstat -ano^|findstr ":%PORT%"') do (
    echo kill the process %%i who use the port %port%
    taskkill /pid %%i -t -f
)

linux 下一键关闭脚本

#!/bin/bash

APP=sqjzService

pid=`ps -ef | grep $APP.jar | grep -v grep | awk '{print $2}'`
if [ -n "$pid" ]
then
   kill -9 $pid
fi