This commit is contained in:
Zhang Peng 2020-03-10 22:54:15 +08:00
parent e1773d60e6
commit 18d2a58725
21 changed files with 1731 additions and 355 deletions

View File

@ -0,0 +1,29 @@
#!/usr/bin/expect -f
# -----------------------------------------------------------------------------------------------------
# expect 交互式脚本示例 - 自动远程登录,并在其他机器上创建一个文件
# @author Zhang Peng
# -----------------------------------------------------------------------------------------------------
# 设置变量
set USER "root"
set PWD "XXXXXX"
set HOST "127.0.0.2"
# 设置超时时间
set timeout 400
# 远程登录
spawn ssh -p 22 $USER@$HOST
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$PWD\r" }
}
# 在其他机器上创建
expect "#"
send "touch /home/demo.txt\r"
expect "#"
send "echo hello world >> /home/demo.txt\r"
expect "#"
# 退出
send "exit\r"

View File

@ -1,25 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ------------------------------------------------------------------------------
# 构建 Docker 镜像 脚本
# @author Zhang Peng
# @since 2020/1/14
# ------------------------------------------------------------------------------
# 装载其它库
LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}`
source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh
dockerBuild() { dockerBuild() {
if [[ ! $1 ]] || [[ ! $2 ]] || [[ ! $3 ]]; then if [[ ! $1 ]] || [[ ! $2 ]] || [[ ! $3 ]]; then
logError "you must input following params in order:" logError "you must input following params in order:"
echo -e "${C_B_RED}" echo -e "${ENV_COLOR_B_RED}"
echo " (1) source" echo " (1) source"
echo " (2) repository" echo " (2) repository"
echo " (3) tag" echo " (3) tag"
echo -e "\nEg. dockerBuild /home/workspace dunwu/dockerApp 0.0.1" echo -e "\nEg. dockerBuild /home/workspace tdh60dev01:5000/fide/fide-processor fide-0.0.6-SNAPSHOT"
echo -e "${C_RESET}" echo -e "${ENV_COLOR_RESET}"
return ${FAILED} return ${ENV_FAILED}
fi fi
local source=$1 local source=$1
@ -27,15 +20,15 @@ dockerBuild() {
local tag=$3 local tag=$3
dockerCheck ${source} dockerCheck ${source}
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
return ${FAILED} return ${ENV_FAILED}
fi fi
cd ${source} cd ${source}
callAndLog docker build -t ${repository}:${tag} . callAndLog docker build -t ${repository}:${tag} .
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
logError "docker build -t ${repository}:${tag} failed" logError "docker build -t ${repository}:${tag} failed"
return ${FAILED} return ${ENV_FAILED}
fi fi
cd - cd -
@ -44,18 +37,17 @@ dockerBuild() {
dockerPush() { dockerPush() {
if [[ ! $1 ]] || [[ ! $2 ]]; then if [[ ! $1 ]] || [[ ! $2 ]]; then
logError "you must input following params in order:" logError "you must input following params in order:"
echo -e "${C_B_RED}" echo -e "${ENV_COLOR_B_RED}"
echo " (1) repository" echo " (1) repository"
echo " (2) tag" echo " (2) tag"
echo -e "\nEg. dockerBuild dunwu/dockerApp 0.0.1" echo -e "\nEg. dockerBuild tdh60dev01:5000/fide/fide-processor fide-0.0.6-SNAPSHOT"
echo -e "${C_RESET}" echo -e "${ENV_COLOR_RESET}"
return ${FAILED} return ${ENV_FAILED}
fi fi
local repository=$1 local repository=$1
local tag=$2 local tag=$2
# 如果 docker 镜像已存在,则删除镜像
local dockerHashId=$(docker image ls | grep ${repository} | grep ${tag} | awk '{print $3}') local dockerHashId=$(docker image ls | grep ${repository} | grep ${tag} | awk '{print $3}')
if [[ ! ${dockerHashId} ]]; then if [[ ! ${dockerHashId} ]]; then
logInfo "try to delete existed image: ${repository}:${tag}" logInfo "try to delete existed image: ${repository}:${tag}"
@ -66,22 +58,22 @@ dockerPush() {
callAndLog docker push ${repository}:${tag} callAndLog docker push ${repository}:${tag}
} }
# 判断指定路径下是否为 docker 工程 # check Dockerfile
# @param $1: 第一个参数为 docker 项目路径 # @param $1: project path
dockerCheck() { dockerCheck() {
local source=$1 local source=$1
if [[ -d "${source}" ]]; then if [[ -d "${source}" ]]; then
cd ${source} cd ${source}
if [[ -f "${source}/Dockerfile" ]]; then if [[ -f "${source}/Dockerfile" ]]; then
return ${YES} return ${ENV_YES}
else else
logError "Dockerfile is not exists" logError "Dockerfile is not exists"
return ${NO} return ${ENV_NO}
fi fi
cd - cd -
return ${YES} return ${ENV_YES}
else else
logError "${source} is not valid docker project" logError "${source} is not valid docker project"
return ${NO} return ${ENV_NO}
fi fi
} }

View File

@ -1,21 +1,23 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ------------------------------------------------------------------------------ # -----------------------------------------------------------------------------------------------------
# Git 基本操作脚本 # git operation utils
# @author Zhang Peng # @author Zhang Peng
# ------------------------------------------------------------------------------ # -----------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ load libs
# 装载其它库
LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}`
if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then
logError "必要脚本库 ${LINUX_SCRIPTS_LIB_DIR}/utils.sh 不存在!" logError "${LINUX_SCRIPTS_LIB_DIR}/utils.sh not exists!"
exit 1 exit 1
fi fi
source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh
# ------------------------------------------------------------------------------ git 操作函数
# ------------------------------------------------------------------------------ functions
GIT_LOCAL_BRANCH= GIT_LOCAL_BRANCH=
getGitLocalBranch() { getGitLocalBranch() {
@ -27,37 +29,36 @@ getGitOriginBranch() {
GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}") GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}")
} }
# 检查指定的路径是不是一个 git 项目 # check specified path is git project or not
checkGit() { checkGit() {
local source=$1 local source=$1
if [[ -d "${source}" ]]; then if [[ -d "${source}" ]]; then
cd ${source} || return ${NO} cd ${source} || return ${ENV_NO}
# 1删除git状态零时文件 # (1) delete gitstatus.tmp
if [[ -f "gitstatus.tmp" ]]; then if [[ -f "gitstatus.tmp" ]]; then
rm -rf gitstatus.tmp rm -rf gitstatus.tmp
fi fi
# 2判断git是否可用 # (2) check git status
git status &> gitstatus.tmp git status &> gitstatus.tmp
local gitStatus=false local gitStatus=false
grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true
rm -rf gitstatus.tmp rm -rf gitstatus.tmp
if [[ ${gitStatus} == true ]]; then if [[ ${gitStatus} == true ]]; then
return ${YES} return ${ENV_YES}
else else
return ${NO} return ${ENV_NO}
fi fi
return ${NO} return ${ENV_NO}
fi fi
logError "${source} is invalid dir." logWarn "${source} is not exists."
return ${NO} return ${ENV_NO}
} }
# clone 或 fetch 操作 # execute git clone or fetch
# 如果本地代码目录已经是 git 仓库,执行 pull若不是则执行 clone # params: Git repository, git group, git project, git branch, local path
# 依次传入 Git 仓库、Git 项目组、Git 项目名、分支、本地代码目录
cloneOrPullGit() { cloneOrPullGit() {
local repository=$1 local repository=$1
@ -68,12 +69,12 @@ cloneOrPullGit() {
if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then
logError "Please input root, group, project, branch." logError "Please input root, group, project, branch."
return ${FAILED} return ${ENV_FAILED}
fi fi
if [[ ! -d "${root}" ]]; then if [[ ! -d "${root}" ]]; then
logError "${root} is not directory." logError "${root} is not directory."
return ${FAILED} return ${ENV_FAILED}
fi fi
local source=${root}/${group}/${project} local source=${root}/${group}/${project}
@ -82,52 +83,49 @@ cloneOrPullGit() {
mkdir -p ${root}/${group} mkdir -p ${root}/${group}
checkGit ${source} checkGit ${source}
if [[ "${YES}" == "$?" ]]; then if [[ "${ENV_YES}" == "$?" ]]; then
# 如果 ${source} 是 git 项目,执行 pull 操作 cd ${source} || return ${ENV_FAILED}
cd ${source} || return ${FAILED}
git fetch --all
git checkout -f ${branch} git checkout -f ${branch}
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
logError "<<<< git checkout ${branch} failed." logError "<<<< git checkout ${branch} failed."
return ${FAILED} return ${ENV_FAILED}
fi fi
logInfo "git checkout ${branch} succeed." logInfo "git checkout ${branch} succeed."
getGitOriginBranch getGitOriginBranch
git fetch --all
git reset --hard ${GIT_ORIGIN_BRANCH} git reset --hard ${GIT_ORIGIN_BRANCH}
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
logError "<<<< git reset --hard ${GIT_ORIGIN_BRANCH} failed." logError "<<<< git reset --hard ${GIT_ORIGIN_BRANCH} failed."
return ${FAILED} return ${ENV_FAILED}
fi fi
logInfo "git reset --hard ${GIT_ORIGIN_BRANCH} succeed." logInfo "git reset --hard ${GIT_ORIGIN_BRANCH} succeed."
git pull git pull
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
logError "<<<< git pull failed." logError "<<<< git pull failed."
return ${FAILED} return ${ENV_FAILED}
fi fi
logInfo "git pull succeed." logInfo "git pull succeed."
else else
# 如果 ${source} 不是 git 项目,执行 clone 操作
git clone "${repository}:${group}/${project}.git" ${source} git clone "${repository}:${group}/${project}.git" ${source}
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
logError "<<<< git clone ${project} failed." logError "<<<< git clone ${project} failed."
return ${FAILED} return ${ENV_FAILED}
fi fi
logInfo "git clone ${project} succeed." logInfo "git clone ${project} succeed."
cd ${source} || return ${FAILED} cd ${source} || return ${ENV_FAILED}
git checkout -f ${branch} git checkout -f ${branch}
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
logError "<<<< git checkout ${branch} failed." logError "<<<< git checkout ${branch} failed."
return ${FAILED} return ${ENV_FAILED}
fi fi
logInfo "git checkout ${branch} succeed." logInfo "git checkout ${branch} succeed."
fi fi
logInfo "Clone or pull git project [$2/$3:$4] succeed." logInfo "Clone or pull git project [$2/$3:$4] succeed."
return ${SUCCEED} return ${ENV_SUCCEED}
} }

View File

@ -94,7 +94,7 @@ startServer() {
# >>>> 3. create log dir and console log file # >>>> 3. create log dir and console log file
mkdir -p ${LOG_PATH} mkdir -p ${LOG_PATH}
if [[ ! -f ${CONSOLE_LOG} ]]; then if [[ ! -x ${CONSOLE_LOG} ]]; then
touch ${CONSOLE_LOG} touch ${CONSOLE_LOG}
fi fi

View File

@ -1,37 +1,41 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ------------------------------------------------------------------------------ # -----------------------------------------------------------------------------------------------------
# maven 项目操作脚本 # maven operation utils
# @author Zhang Peng # @author Zhang Peng
# ------------------------------------------------------------------------------ # -----------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ load libs
# 装载其它库
LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}`
if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then
echo "必要脚本库 ${LINUX_SCRIPTS_LIB_DIR}/utils.sh 不存在!" echo "${LINUX_SCRIPTS_LIB_DIR}/utils.sh not exists!"
exit 1 exit 1
fi fi
source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh
# 执行 maven 操作
# @param $1: 第一个参数为 maven 项目路径 # ------------------------------------------------------------------------------ functions
# @param $2: 第二个参数为 maven 操作,如 package、install、deploy
# @param $3: 第三个参数为 maven profile 【非必填】 # execute maven lifecycle operation
# @param $1: maven project path
# @param $2: maven lifecycle, eg. package、install、deploy
# @param $3: maven profile [optional]
mavenOperation() { mavenOperation() {
local source=$1 local source=$1
local lifecycle=$2 local lifecycle=$2
local profile=$3 local profile=$3
mavenCheck ${source} mavenCheck ${source}
if [[ "${SUCCEED}" != "$?" ]]; then if [[ "${ENV_SUCCEED}" != "$?" ]]; then
return ${FAILED} return ${ENV_FAILED}
fi fi
if [[ ! "${lifecycle}" ]]; then if [[ ! "${lifecycle}" ]]; then
logError "please input maven lifecycle" logError "please input maven lifecycle"
return ${FAILED} return ${ENV_FAILED}
fi fi
local mvnCli="mvn clean ${lifecycle} -DskipTests=true -B -U" local mvnCli="mvn clean ${lifecycle} -DskipTests=true -B -U"
@ -47,25 +51,25 @@ mavenOperation() {
callAndLog "${mvnCli}" callAndLog "${mvnCli}"
cd - cd -
return ${SUCCEED} return ${ENV_SUCCEED}
} }
# 判断指定路径下是否为 maven 工程 # check specified path is maven project or not
# @param $1: 第一个参数为 maven 项目路径 # @param $1: maven project path
mavenCheck() { mavenCheck() {
local source=$1 local source=$1
if [[ -d "${source}" ]]; then if [[ -d "${source}" ]]; then
cd ${source} cd ${source}
if [[ -f "${source}/pom.xml" ]]; then if [[ -f "${source}/pom.xml" ]]; then
return ${YES} return ${ENV_YES}
else else
logError "pom.xml is not exists" logError "pom.xml is not exists"
return ${NO} return ${ENV_NO}
fi fi
cd - cd -
return ${YES} return ${ENV_YES}
else else
logError "please input valid maven project path" logError "please input valid maven project path"
return ${NO} return ${ENV_NO}
fi fi
} }

154
codes/linux/lib/mysql.sh Normal file
View File

@ -0,0 +1,154 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------------------------------
# 数据库操作脚本
# @author Zhang Peng
# -----------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ 1. env
## 数据库IP
#ENV_MYSQL_HOST="127.0.0.1"
## 数据库用户名
#ENV_MYSQL_USERNAME="root"
## 数据密码
#ENV_MYSQL_PASSWORD="Tw#123456"
if [[ ! ${ENV_MYSQL_HOST} ]] || [[ ! ${ENV_MYSQL_USERNAME} ]] || [[ ! ${ENV_MYSQL_PASSWORD} ]]; then
logError "执行本脚本前必须先 export 环境变量: ENV_MYSQL_HOST, ENV_MYSQL_USERNAME, ENV_MYSQL_PASSWORD."
exit ${ENV_FAILED}
fi
# 备份模式:备份所有数据库(--all-databases|备份指定数据库列表
MYSQL_DATABASES="${ENV_MYSQL_DATABASES:---all-databases}"
#备份路径
MYSQL_BACKUP_DIR="${ENV_MYSQL_BACKUP_DIR:-/var/lib/mysql/backup}"
#备份日志路径
export ENV_LOG_PATH="${MYSQL_BACKUP_DIR}/mysql-backup.log"
# ------------------------------------------------------------------------------ 2. libs
# 装载其它库
LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}`
if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then
logError "${LINUX_SCRIPTS_LIB_DIR}/utils.sh not exists!"
exit 1
fi
source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh
# ------------------------------------------------------------------------------ 3. global var
# 备份文件最大数量
BACKUP_ARTIFACTS_MAX_NUM=7
# ------------------------------------------------------------------------------ 4. functions
backupAllDatabase() {
#时间戳
local timestamp=$(date +"%Y%m%d")
#备份所有数据库
logInfo "正在备份所有数据库"
mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} --all-databases > "${MYSQL_BACKUP_DIR}/all-${timestamp}.sql" 2>> ${ENV_LOG_PATH};
#检查备份结果是否成功
if [[ "$?" != 0 ]]; then
logError "<<<< 备份所有数据库失败"
return ${ENV_FAILED}
fi
# 压缩备份sql文件删除旧的备份文件
cd "${MYSQL_BACKUP_DIR}"
if [[ ! -f "${MYSQL_BACKUP_DIR}/all-${timestamp}.sql" ]]; then
logError "备份文件 ${MYSQL_BACKUP_DIR}/all-${timestamp}.sql 不存在"
return ${ENV_FAILED}
fi
#为节约硬盘空间,将数据库压缩
sudo tar zcf "all-${timestamp}.tar.gz" "all-${timestamp}.sql" > /dev/null
#删除原始文件,只留压缩后文件
sudo rm -f "all-${timestamp}.sql"
#删除七天前备份也就是只保存7天内的备份
find "${MYSQL_BACKUP_DIR} -name all-*.tar.gz -type f -mtime +${BACKUP_ARTIFACTS_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1
logInfo "<<<< 备份所有数据库成功"
return ${ENV_SUCCEED}
}
backupSelectedDatabase() {
#时间戳
local timestamp=$(date +"%Y%m%d")
#数据库,如有多个库用空格分开
databaseList="${MYSQL_DATABASES}"
#备份指定数据库列表
for database in ${databaseList}; do
logInfo "正在备份数据库:${database}"
mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} "${database}" > "${MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" 2>> ${ENV_LOG_PATH};
if [[ "$?" != 0 ]]; then
logError "<<<< 备份 ${database} 失败"
return ${ENV_FAILED}
fi
# 压缩备份sql文件删除旧的备份文件
cd "${MYSQL_BACKUP_DIR}"
if [[ ! -f "${MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" ]]; then
logError "备份文件 ${MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql 不存在"
return ${ENV_FAILED}
fi
#为节约硬盘空间,将数据库压缩
sudo tar zcf "${database}-${timestamp}.tar.gz" "${database}-${timestamp}.sql" > /dev/null
#删除原始文件,只留压缩后文件
sudo rm -f "${database}-${timestamp}.sql"
#删除七天前备份也就是只保存7天内的备份
find "${MYSQL_BACKUP_DIR} -name ${database}-*.tar.gz -type f -mtime +${BACKUP_ARTIFACTS_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1
done
logInfo "<<<< 备份数据库 ${MYSQL_DATABASES} 成功"
return ${ENV_SUCCEED}
}
backupMysql() {
#日志记录头部
sudo mkdir -p ${MYSQL_BACKUP_DIR}
touch ${ENV_LOG_PATH}
logInfo "------------------------------------------------------------------"
logInfo ">>>> 备份数据库开始"
#正式备份数据库
if [[ ${MYSQL_DATABASES} == "--all-databases" ]]; then
backupAllDatabase
else
backupSelectedDatabase
fi
}
recoveryMysql() {
logInfo "------------------------------------------------------------------"
logInfo ">>>> 恢复数据库开始"
if [[ ! -f ${ENV_SQL_FILE_PATH} ]]; then
logError "sql 文件 ${ENV_SQL_FILE_PATH} 不存在"
return ${ENV_FAILED}
fi
mysql -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} < ${ENV_SQL_FILE_PATH}
if [[ "$?" != 0 ]]; then
logError "<<<< 恢复数据库失败"
return ${ENV_FAILED}
fi
logInfo "<<<< 恢复数据库成功"
return ${ENV_SUCCEED}
}

View File

@ -1,118 +1,113 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ------------------------------------------------------------------------------ # -----------------------------------------------------------------------------------------------------
# Shell Utils # Shell Utils
# @author Zhang Peng # @author Zhang Peng
# ------------------------------------------------------------------------------ # -----------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ 颜色状态 # ------------------------------------------------------------------------------ env
# Regular Color # Regular Color
export C_BLACK="\033[0;30m" export ENV_COLOR_BLACK="\033[0;30m"
export C_RED="\033[0;31m" export ENV_COLOR_RED="\033[0;31m"
export C_GREEN="\033[0;32m" export ENV_COLOR_GREEN="\033[0;32m"
export C_YELLOW="\033[0;33m" export ENV_COLOR_YELLOW="\033[0;33m"
export C_BLUE="\033[0;34m" export ENV_COLOR_BLUE="\033[0;34m"
export C_MAGENTA="\033[0;35m" export ENV_COLOR_MAGENTA="\033[0;35m"
export C_CYAN="\033[0;36m" export ENV_COLOR_CYAN="\033[0;36m"
export C_WHITE="\033[0;37m" export ENV_COLOR_WHITE="\033[0;37m"
# Bold Color # Bold Color
export C_B_BLACK="\033[1;30m" export ENV_COLOR_B_BLACK="\033[1;30m"
export C_B_RED="\033[1;31m" export ENV_COLOR_B_RED="\033[1;31m"
export C_B_GREEN="\033[1;32m" export ENV_COLOR_B_GREEN="\033[1;32m"
export C_B_YELLOW="\033[1;33m" export ENV_COLOR_B_YELLOW="\033[1;33m"
export C_B_BLUE="\033[1;34m" export ENV_COLOR_B_BLUE="\033[1;34m"
export C_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_MAGENTA="\033[1;35m"
export C_B_CYAN="\033[1;36m" export ENV_COLOR_B_CYAN="\033[1;36m"
export C_B_WHITE="\033[1;37m" export ENV_COLOR_B_WHITE="\033[1;37m"
# Underline Color # Underline Color
export C_U_BLACK="\033[4;30m" export ENV_COLOR_U_BLACK="\033[4;30m"
export C_U_RED="\033[4;31m" export ENV_COLOR_U_RED="\033[4;31m"
export C_U_GREEN="\033[4;32m" export ENV_COLOR_U_GREEN="\033[4;32m"
export C_U_YELLOW="\033[4;33m" export ENV_COLOR_U_YELLOW="\033[4;33m"
export C_U_BLUE="\033[4;34m" export ENV_COLOR_U_BLUE="\033[4;34m"
export C_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_MAGENTA="\033[4;35m"
export C_U_CYAN="\033[4;36m" export ENV_COLOR_U_CYAN="\033[4;36m"
export C_U_WHITE="\033[4;37m" export ENV_COLOR_U_WHITE="\033[4;37m"
# Background Color # Background Color
export C_BG_BLACK="\033[40m" export ENV_COLOR_BG_BLACK="\033[40m"
export C_BG_RED="\033[41m" export ENV_COLOR_BG_RED="\033[41m"
export C_BG_GREEN="\033[42m" export ENV_COLOR_BG_GREEN="\033[42m"
export C_BG_YELLOW="\033[43m" export ENV_COLOR_BG_YELLOW="\033[43m"
export C_BG_BLUE="\033[44m" export ENV_COLOR_BG_BLUE="\033[44m"
export C_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_MAGENTA="\033[45m"
export C_BG_CYAN="\033[46m" export ENV_COLOR_BG_CYAN="\033[46m"
export C_BG_WHITE="\033[47m" export ENV_COLOR_BG_WHITE="\033[47m"
# Reset Color # Reset Color
export C_RESET="$(tput sgr0)" export ENV_COLOR_RESET="$(tput sgr0)"
# ------------------------------------------------------------------------------ 常用状态值 # status
export ENV_YES=0
export ENV_NO=1
export ENV_SUCCEED=0
export ENV_FAILED=1
export YES=0
export NO=1
export SUCCEED=0
export FAILED=1
# ------------------------------------------------------------------------------ 常用状态值 # ------------------------------------------------------------------------------ functions
# 显示打印日志的时间 # 显示打印日志的时间
DATE=$(date "+%Y-%m-%d %H:%M:%S") SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
# 那个用户在操作 # 那个用户在操作
USER=$(whoami) USER=$(whoami)
# 日志路径 # 日志路径
LOG_DIR=/var/log/dunwu LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log}
LOG_PATH=${LOG_DIR}/shell.log # 日志目录
LOG_DIR=${LOG_PATH%/*}
createLogFileIfNotExists() { createLogFileIfNotExists() {
if [[ ! -f "${LOG_PATH}" ]]; then if [[ ! -x "${LOG_PATH}" ]]; then
sudo mkdir -p "${LOG_DIR}" mkdir -p "${LOG_DIR}"
touch "${LOG_PATH}" touch "${LOG_PATH}"
fi fi
} }
logInfo() { logInfo() {
echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}"
createLogFileIfNotExists createLogFileIfNotExists
echo "[${DATE}] [${USER}] [INFO] [$0] [$@] execute succeed." >> "${LOG_PATH}" echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}"
} }
logWarn() { logWarn() {
echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}"
createLogFileIfNotExists createLogFileIfNotExists
echo "[${DATE}] [${USER}] [WARN] [$0] [$@] execute succeed." >> "${LOG_PATH}" echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}"
} }
logError() { logError() {
echo -e "${C_B_RED}[ERROR] $@${C_RESET}" echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}"
createLogFileIfNotExists createLogFileIfNotExists
echo "[${DATE}] [${USER}] [ERROR] [$0] [$@] execute failed." >> "${LOG_PATH}" echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}"
} }
printInfo() { printInfo() {
echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}"
} }
printWarn() { printWarn() {
echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}"
} }
printError() { printError() {
echo -e "${C_B_RED}[ERROR] $@${C_RESET}" echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}"
} }
callAndLog () { callAndLog () {
$* $*
if [[ $? -eq ${SUCCEED} ]]; then if [[ $? -eq ${ENV_SUCCEED} ]]; then
logInfo "$@ succeed" logInfo "$@"
echo -e "${C_B_GREEN}[INFO] [$0] [$@] execute succeed.${C_RESET}" return ${ENV_SUCCEED}
return ${SUCCEED}
else else
logError "$@ failed" logError "$@ EXECUTE FAILED"
echo -e "${C_B_RED}[ERROR] [$0] [$@] execute failed.${C_RESET}" return ${ENV_FAILED}
return ${FAILED}
fi fi
} }

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
path=/dir1/dir2/dir3/test.txt
echo ${path##*/} 获取文件名 test.txt
echo ${path##*.} 获取后缀 txt
#不带后缀的文件名
temp=${path##*/}
echo ${temp%.*} test
#获取目录
echo ${path%/*} /dir1/dir2/dir3

View File

@ -30,7 +30,7 @@ module.exports = {
}, { }, {
text: "Docker 教程", link: "/docker/", text: "Docker 教程", link: "/docker/",
}, { }, {
text: "博客", link: "https://github.com/dunwu/blog", target: "_blank", rel: "" text: "🎯 博客", link: "https://github.com/dunwu/blog", target: "_blank", rel: ""
}], sidebar: "auto", sidebarDepth: 2 }], sidebar: "auto", sidebarDepth: 2
} }
} }

View File

@ -19,31 +19,31 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu
### Linux 命令 ### Linux 命令
> 学习 Linux 的第一步:当然是从 [Linux 命令](docs/linux/cli/README.md) 入手了。 > 学习 Linux 的第一步:当然是从 [Linux 命令](linux/cli/README.md) 入手了。
- [查看 Linux 命令帮助信息](docs/linux/cli/linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - [查看 Linux 命令帮助信息](linux/cli/linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man`
- [Linux 文件目录管理](docs/linux/cli/linux-cli-dir.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` - [Linux 文件目录管理](linux/cli/linux-cli-dir.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm`
- [Linux 文件内容查看命令](docs/linux/cli/linux-cli-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` - [Linux 文件内容查看命令](linux/cli/linux-cli-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep`
- [Linux 文件压缩和解压](docs/linux/cli/linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` - [Linux 文件压缩和解压](linux/cli/linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip`
- [Linux 用户管理](docs/linux/cli/linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` - [Linux 用户管理](linux/cli/linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo`
- [Linux 系统管理](docs/linux/cli/linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` - [Linux 系统管理](linux/cli/linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab`
- [Linux 网络管理](docs/linux/cli/linux-cli-net.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` - [Linux 网络管理](linux/cli/linux-cli-net.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat`
- [Linux 硬件管理](docs/linux/cli/linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` - [Linux 硬件管理](linux/cli/linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop`
- [Linux 软件管理](docs/linux/cli/linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get` - [Linux 软件管理](linux/cli/linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get`
### Linux 运维 ### Linux 运维
> Linux 系统的常见运维工作。 > Linux 系统的常见运维工作。
- [网络运维](docs/linux/ops/network-ops.md) - [网络运维](linux/ops/network-ops.md)
- [Samba](docs/linux/ops/samba.md) - [Samba](linux/ops/samba.md)
- [NTP](docs/linux/ops/ntp.md) - [NTP](linux/ops/ntp.md)
- [Firewalld](docs/linux/ops/firewalld.md) - [Firewalld](linux/ops/firewalld.md)
- [Crontab](docs/linux/ops/crontab.md) - [Crontab](linux/ops/crontab.md)
- [Systemd](docs/linux/ops/systemd.md) - [Systemd](linux/ops/systemd.md)
- [Vim](docs/linux/ops/vim.md) - [Vim](linux/ops/vim.md)
- [Iptables](docs/linux/ops/iptables.md) - [Iptables](linux/ops/iptables.md)
- [oh-my-zsh](docs/linux/ops/zsh.md) - [oh-my-zsh](linux/ops/zsh.md)
### 软件运维 ### 软件运维
@ -52,34 +52,34 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu
> 配套安装脚本:⌨ [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) > 配套安装脚本:⌨ [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft)
- 开发环境 - 开发环境
- [JDK 安装](docs/linux/soft/jdk-install.md) - [JDK 安装](linux/soft/jdk-install.md)
- [Maven 安装](docs/linux/soft/maven-install.md) - [Maven 安装](linux/soft/maven-install.md)
- [Nodejs 安装](docs/linux/soft/nodejs-install.md) - [Nodejs 安装](linux/soft/nodejs-install.md)
- 开发工具 - 开发工具
- [Nexus 运维](docs/linux/soft/nexus-ops.md) - [Nexus 运维](linux/soft/nexus-ops.md)
- [Gitlab 运维](docs/linux/soft/kafka-install.md) - [Gitlab 运维](linux/soft/kafka-install.md)
- [Jenkins 运维](docs/linux/soft/jenkins.md) - [Jenkins 运维](linux/soft/jenkins.md)
- [Svn 运维](docs/linux/soft/svn-ops.md) - [Svn 运维](linux/soft/svn-ops.md)
- [YApi 运维](docs/linux/soft/yapi-ops.md) - [YApi 运维](linux/soft/yapi-ops.md)
- 中间件服务 - 中间件服务
- [Elastic 运维](docs/linux/soft/elastic) - [Elastic 运维](linux/soft/elastic)
- [Kafka 运维](docs/linux/soft/kafka-install.md) - [Kafka 运维](linux/soft/kafka-install.md)
- [RocketMQ 运维](docs/linux/soft/rocketmq-install.md) - [RocketMQ 运维](linux/soft/rocketmq-install.md)
- [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md)
- [Nacos 运维](docs/linux/soft/nacos-install.md) - [Nacos 运维](linux/soft/nacos-install.md)
- 服务器 - 服务器
- [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚 - [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚
- [Tomcat 运维](docs/linux/soft/tomcat-install.md) - [Tomcat 运维](linux/soft/tomcat-install.md)
- [数据库](https://github.com/dunwu/db-tutorial) 📚 - [数据库](https://github.com/dunwu/db-tutorial) 📚
- [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md) - [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md)
- [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md) - [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md)
### Docker ### Docker
- [Docker 快速入门](docs/docker/docker-quickstart.md) - [Docker 快速入门](docker/docker-quickstart.md)
- [Dockerfile 最佳实践](docs/docker/docker-dockerfile.md) - [Dockerfile 最佳实践](docker/docker-dockerfile.md)
- [Docker Cheat Sheet](docs/docker/docker-cheat-sheet.md) - [Docker Cheat Sheet](docker/docker-cheat-sheet.md)
- [Kubernetes 应用指南](docs/docker/kubernetes.md) - [Kubernetes 应用指南](docker/kubernetes.md)
### 其他 ### 其他
@ -103,4 +103,4 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu
## 🚪 传送门 ## 🚪 传送门
◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ ◾ 🏠 [LINUX-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾

155
docs/linux/cli/free.md Normal file
View File

@ -0,0 +1,155 @@
# free
显示内存的使用情况
## 补充说明
**free 命令** 可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。
### 语法
```shell
free(选项)
```
### 选项
```shell
-b # 以Byte为单位显示内存使用情况
-k # 以KB为单位显示内存使用情况
-m # 以MB为单位显示内存使用情况
-g # 以GB为单位显示内存使用情况。
-o # 不显示缓冲区调节列;
-s<间隔秒数> # 持续观察内存使用状况;
-t # 显示内存总和列;
-V # 显示版本信息。
```
### 实例
```shell
free -t # 以总和的形式显示内存的使用信息
free -s 10 # 周期性的查询内存使用信息每10s 执行一次命令
```
显示内存使用情况
```shell
free -m
total used free shared buffers cached
Mem: 2016 1973 42 0 163 1497
-/+ buffers/cache: 312 1703
Swap: 4094 0 4094
```
**第一部分 Mem 行解释:**
```shell
total内存总数
used已经使用的内存数
free空闲的内存数
shared当前已经废弃不用
buffers Buffer缓存内存数
cached Page缓存内存数。
```
关系total = used + free
**第二部分(-/+ buffers/cache)解释:**
```shell
(-buffers/cache) used内存数第一部分Mem行中的 used buffers cached
(+buffers/cache) free内存数: 第一部分Mem行中的 free + buffers + cached
```
可见-buffers/cache 反映的是被程序实实在在吃掉的内存,而+buffers/cache 反映的是可以挪用的内存总数。
第三部分是指交换分区。
输出结果的第四行是交换分区 SWAP 的,也就是我们通常所说的虚拟内存。
区别:第二行(mem)的 used/free 与第三行(-/+ buffers/cache) used/free 的区别。 这两个的区别在于使用的角度来看,第一行是从 OS 的角度来看,因为对于 OSbuffers/cached 都是属于被使用,所以他的可用内存是 2098428KB,已用内存是 30841684KB,其中包括内核OS使用+Application(X, oracle,etc)使用的+buffers+cached.
第三行所指的是从应用程序角度来看对于应用程序来说buffers/cached 是等于可用的,因为 buffer/cached 是为了提高文件读取的性能当应用程序需在用到内存的时候buffer/cached 会很快地被回收。
所以从应用程序的角度来说,可用内存=系统 free memory+buffers+cached。
如本机情况的可用内存为:
18007156=2098428KB+4545340KB+11363424KB
接下来解释什么时候内存会被交换,以及按什么方交换。
当可用内存少于额定值的时候,就会开会进行交换。如何看额定值:
```shell
cat /proc/meminfo
MemTotal: 16140816 kB
MemFree: 816004 kB
MemAvailable: 2913824 kB
Buffers: 17912 kB
Cached: 2239076 kB
SwapCached: 0 kB
Active: 12774804 kB
Inactive: 1594328 kB
Active(anon): 12085544 kB
Inactive(anon): 94572 kB
Active(file): 689260 kB
Inactive(file): 1499756 kB
Unevictable: 116888 kB
Mlocked: 116888 kB
SwapTotal: 8191996 kB
SwapFree: 8191996 kB
Dirty: 56 kB
Writeback: 0 kB
AnonPages: 12229228 kB
Mapped: 117136 kB
Shmem: 58736 kB
Slab: 395568 kB
SReclaimable: 246700 kB
SUnreclaim: 148868 kB
KernelStack: 30496 kB
PageTables: 165104 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 16262404 kB
Committed_AS: 27698600 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 311072 kB
VmallocChunk: 34350899200 kB
HardwareCorrupted: 0 kB
AnonHugePages: 3104768 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 225536 kB
DirectMap2M: 13279232 kB
DirectMap1G: 5242880 kB
```
交换将通过三个途径来减少系统中使用的物理页面的个数:
1. 减少缓冲与页面 cache 的大小,
2. 将系统 V 类型的内存页面交换出去,
3. 换出或者丢弃页面。(Application 占用的内存页,也就是物理内存不足)。
事实上,少量地使用 swap 是不是影响到系统性能的。
那 buffers 和 cached 都是缓存,两者有什么区别呢?
为了提高磁盘存取效率, Linux 做了一些精心的设计, 除了对 dentry 进行缓存(用于 VFS,加速文件路径名到 inode 的转换), 还采取了两种主要 Cache 方式:
Buffer Cache 和 Page Cache。前者针对磁盘块的读写后者针对文件 inode 的读写。这些 Cache 有效缩短了 I/O 系统调用(比如 read,write,getdents)的时间。
磁盘的操作有逻辑级(文件系统)和物理级(磁盘块),这两种 Cache 就是分别缓存逻辑和物理级数据的。
Page cache 实际上是针对文件系统的,是文件的缓存,在文件层面上的数据会缓存到 page cache。文件的逻辑层需要映射到实际的物理磁盘这种映射关系由文件系统来完成。当 page cache 的数据需要刷新时page cache 中的数据交给 buffer cache因为 Buffer Cache 就是缓存磁盘块的。但是这种处理在 2.6 版本的内核之后就变的很简单了,没有真正意义上的 cache 操作。
Buffer cache 是针对磁盘块的缓存,也就是在没有文件系统的情况下,直接对磁盘进行操作的数据会缓存到 buffer cache 中,例如,文件系统的元数据都会缓存到 buffer cache 中。
简单说来page cache 用来缓存文件数据buffer cache 用来缓存磁盘数据。在有文件系统的情况下,对文件操作,那么数据会缓存到 page cache如果直接采用 dd 等工具对磁盘进行读写,那么数据会缓存到 buffer cache。
所以我们看 linux,只要不用 swap 的交换空间,就不用担心自己的内存太少.如果常常 swap 用很多,可能你就要考虑加物理内存了.这也是 linux 看内存是否够用的标准.
如果是应用服务器的话,一般只看第二行,+buffers/cache,即对应用程序来说 free 的内存太少了,也是该考虑优化程序或加内存了。

245
docs/linux/cli/grep.md Normal file
View File

@ -0,0 +1,245 @@
# grep
强大的文本搜索工具
## 补充说明
**grep** global search regular expression(RE) and print out the line全面搜索正则表达式并把行打印出来是一种强大的文本搜索工具它能使用正则表达式搜索文本并把匹配的行打印出来。用于过滤/搜索的特定字符。可使用正则表达式能多种命令配合使用,使用上十分灵活。
### 选项
```shell
-a --text # 不要忽略二进制数据。
-A <显示行数> --after-context=<显示行数> # 除了显示符合范本样式的那一行之外,并显示该行之后的内容。
-b --byte-offset # 在显示符合范本样式的那一行之外,并显示该行之前的内容。
-B<显示行数> --before-context=<显示行数> # 除了显示符合样式的那一行之外,并显示该行之前的内容。
-c --count # 计算符合范本样式的列数。
-C<显示行数> --context=<显示行数>或-<显示行数> # 除了显示符合范本样式的那一列之外,并显示该列之前后的内容。
-d<进行动作> --directories=<动作> # 当指定要查找的是目录而非文件时必须使用这项参数否则grep命令将回报信息并停止动作。
-e<范本样式> --regexp=<范本样式> # 指定字符串作为查找文件内容的范本样式。
-E --extended-regexp # 将范本样式为延伸的普通表示法来使用,意味着使用能使用扩展正则表达式。
-f<范本文件> --file=<规则文件> # 指定范本文件其内容有一个或多个范本样式让grep查找符合范本条件的文件内容格式为每一列的范本样式。
-F --fixed-regexp # 将范本样式视为固定字符串的列表。
-G --basic-regexp # 将范本样式视为普通的表示法来使用。
-h --no-filename # 在显示符合范本样式的那一列之前,不标示该列所属的文件名称。
-H --with-filename # 在显示符合范本样式的那一列之前,标示该列的文件名称。
-i --ignore-case # 忽略字符大小写的差别。
-l --file-with-matches # 列出文件内容符合指定的范本样式的文件名称。
-L --files-without-match # 列出文件内容不符合指定的范本样式的文件名称。
-n --line-number # 在显示符合范本样式的那一列之前,标示出该列的编号。
-P --perl-regexp # PATTERN 是一个 Perl 正则表达式
-q --quiet或--silent # 不显示任何信息。
-R/-r --recursive # 此参数的效果和指定“-d recurse”参数相同。
-s --no-messages # 不显示错误信息。
-v --revert-match # 反转查找。
-V --version # 显示版本信息。
-w --word-regexp # 只显示全字符合的列。
-x --line-regexp # 只显示全列符合的列。
-y # 此参数效果跟“-i”相同。
-o # 只输出文件中匹配到的部分。
-m <num> --max-count=<num> # 找到num行结果后停止查找用来限制匹配行数
```
### 规则表达式
```shell
^ # 锚定行的开始 如:'^grep'匹配所有以grep开头的行。
$ # 锚定行的结束 如:'grep$' 匹配所有以grep结尾的行。
. # 匹配一个非换行符的字符 如:'gr.p'匹配gr后接一个任意字符然后是p。
* # 匹配零个或多个先前字符 如:'*grep'匹配所有一个或多个空格后紧跟grep的行。
.* # 一起用代表任意字符。
[] # 匹配一个指定范围内的字符,如'[Gg]rep'匹配Grep和grep。
[^] # 匹配一个不在指定范围内的字符,如:'[^A-FH-Z]rep'匹配不包含A-R和T-Z的一个字母开头紧跟rep的行。
\(..\) # 标记匹配字符,如'\(love\)'love被标记为1。
\< # 锚定单词的开始,如:'\<grep'匹配包含以grep开头的单词的行。
\> # 锚定单词的结束,如'grep\>'匹配包含以grep结尾的单词的行。
x\{m\} # 重复字符xm次'0\{5\}'匹配包含5个o的行。
x\{m,\} # 重复字符x,至少m次'o\{5,\}'匹配至少有5个o的行。
x\{m,n\} # 重复字符x至少m次不多于n次'o\{5,10\}'匹配5--10个o的行。
\w # 匹配文字和数字字符,也就是[A-Za-z0-9],如:'G\w*p'匹配以G后跟零个或多个文字或数字字符然后是p。
\W # \w的反置形式匹配一个或多个非单词字符如点号句号等。
\b # 单词锁定符,如: '\bgrep\b'只匹配grep。
```
## grep 命令常见用法
在文件中搜索一个单词,命令会返回一个包含 **“match_pattern”** 的文本行:
```shell
grep match_pattern file_name
grep "match_pattern" file_name
```
在多个文件中查找:
```shell
grep "match_pattern" file_1 file_2 file_3 ...
```
输出除之外的所有行 **-v** 选项:
```shell
grep -v "match_pattern" file_name
```
标记匹配颜色 **--color=auto** 选项:
```shell
grep "match_pattern" file_name --color=auto
```
使用正则表达式 **-E** 选项:
```shell
grep -E "[1-9]+"
# 或
egrep "[1-9]+"
```
使用正则表达式 **-P** 选项:
```shell
grep -P "(\d{3}\-){2}\d{4}" file_name
```
只输出文件中匹配到的部分 **-o** 选项:
```shell
echo this is a test line. | grep -o -E "[a-z]+\."
line.
echo this is a test line. | egrep -o "[a-z]+\."
line.
```
统计文件或者文本中包含匹配字符串的行数 **-c** 选项:
```shell
grep -c "text" file_name
```
输出包含匹配字符串的行数 **-n** 选项:
```shell
grep "text" -n file_name
# 或
cat file_name | grep "text" -n
#多个文件
grep "text" -n file_1 file_2
```
打印样式匹配所位于的字符或字节偏移:
```shell
echo gun is not unix | grep -b -o "not"
7:not
#一行中字符串的字符便宜是从该行的第一个字符开始计算起始值为0。选项 **-b -o** 一般总是配合使用。
```
搜索多个文件并查找匹配文本在哪些文件中:
```shell
grep -l "text" file1 file2 file3...
```
### grep 递归搜索文件
在多级目录中对文本进行递归搜索:
```shell
grep "text" . -r -n
# .表示当前目录。
```
忽略匹配样式中的字符大小写:
```shell
echo "hello world" | grep -i "HELLO"
# hello
```
选项 **-e** 制动多个匹配样式:
```shell
echo this is a text line | grep -e "is" -e "line" -o
is
line
#也可以使用 **-f** 选项来匹配多个样式,在样式文件中逐行写出需要匹配的字符。
cat patfile
aaa
bbb
echo aaa bbb ccc ddd eee | grep -f patfile -o
```
在 grep 搜索结果中包括或者排除指定文件:
```shell
# 只在目录中所有的.php和.html文件中递归搜索字符"main()"
grep "main()" . -r --include *.{php,html}
# 在搜索结果中排除所有README文件
grep "main()" . -r --exclude "README"
# 在搜索结果中排除filelist文件列表里的文件
grep "main()" . -r --exclude-from filelist
```
使用 0 值字节后缀的 grep 与 xargs
```shell
# 测试文件:
echo "aaa" > file1
echo "bbb" > file2
echo "aaa" > file3
grep "aaa" file* -lZ | xargs -0 rm
# 执行后会删除file1和file3grep输出用-Z选项来指定以0值字节作为终结符文件名\0xargs -0 读取输入并用0值字节终结符分隔文件名然后删除匹配文件-Z通常和-l结合使用。
```
grep 静默输出:
```shell
grep -q "test" filename
# 不会输出任何信息如果命令运行成功返回0失败则返回非0值。一般用于条件测试。
```
打印出匹配文本之前或者之后的行:
```shell
# 显示匹配某个结果之后的3行使用 -A 选项:
seq 10 | grep "5" -A 3
5
6
7
8
# 显示匹配某个结果之前的3行使用 -B 选项:
seq 10 | grep "5" -B 3
2
3
4
5
# 显示匹配某个结果的前三行和后三行,使用 -C 选项:
seq 10 | grep "5" -C 3
2
3
4
5
6
7
8
# 如果匹配结果有多个,会用“--”作为各匹配结果之间的分隔符:
echo -e "a\nb\nc\na\nb\nc" | grep a -A 1
a
b
--
a
b
```

70
docs/linux/cli/iostat.md Normal file
View File

@ -0,0 +1,70 @@
# iostat
监视系统输入输出设备和 CPU 的使用情况
## 补充说明
**iostat 命令** 被用于监视系统输入输出设备和 CPU 的使用情况。它的特点是汇报磁盘活动统计情况,同时也会汇报出 CPU 使用情况。同 vmstat 一样iostat 也有一个弱点,就是它不能对某个进程进行深入分析,仅对系统的整体情况进行分析。
### 语法
```shell
iostat(选项)(参数)
```
### 选项
```shell
-c仅显示CPU使用情况
-d仅显示设备利用率
-k显示状态以千字节每秒为单位而不使用块每秒
-m显示状态以兆字节每秒为单位
-p仅显示块设备和所有被使用的其他分区的状态
-t显示每个报告产生时的时间
-V显示版号并退出
-x显示扩展状态。
```
### 参数
- 间隔时间:每次报告的间隔时间(秒);
- 次数:显示报告的次数。
### 实例
用`iostat -x /dev/sda1`来观看磁盘 I/O 的详细情况:
```shell
iostat -x /dev/sda1
Linux 2.6.18-164.el5xen (localhost.localdomain)
2010年03月26日
avg-cpu: %user %nice %system %iowait
%steal %idle
0.11 0.02 0.18 0.35
0.03 99.31
Device: tps Blk_read/s Blk_wrtn/s
Blk_read Blk_wrtn
sda1 0.02 0.08
0.00 2014 4
```
详细说明:第二行是系统信息和监测时间,第三行和第四行显示 CPU 使用情况(具体内容和 mpstat 命令相同)。这里主要关注后面 I/O 输出的信息,如下所示:
| 标示 | 说明 |
| -------- | ----------------------------------- |
| Device | 监测设备名称 |
| rrqm/s | 每秒需要读取需求的数量 |
| wrqm/s | 每秒需要写入需求的数量 |
| r/s | 每秒实际读取需求的数量 |
| w/s | 每秒实际写入需求的数量 |
| rsec/s | 每秒读取区段的数量 |
| wsec/s | 每秒写入区段的数量 |
| rkB/s | 每秒实际读取的大小,单位为 KB |
| wkB/s | 每秒实际写入的大小,单位为 KB |
| avgrq-sz | 需求的平均大小区段 |
| avgqu-sz | 需求的平均队列长度 |
| await | 等待 I/O 平均的时间milliseconds |
| svctm | I/O 需求完成的平均时间 |
| %util | 被 I/O 需求消耗的 CPU 百分比 |

79
docs/linux/cli/iotop.md Normal file
View File

@ -0,0 +1,79 @@
# iotop
用来监视磁盘 I/O 使用状况的工具
## 补充说明
**iotop 命令** 是一个用来监视磁盘 I/O 使用状况的 top 类工具。iotop 具有与 top 相似的 UI其中包括 PID、用户、I/O、进程等相关信息。Linux 下的 IO 统计工具如 iostatnmon 等大多数是只能统计到 per 设备的读写情况,如果你想知道每个进程是如何使用 IO 的就比较麻烦,使用 iotop 命令可以很方便的查看。
iotop 使用 Python 语言编写而成,要求 Python2.5(及以上版本)和 Linux kernel2.6.20及以上版本。iotop 提供有源代码及 rpm 包,可从其官方主页下载。
### 安装
**Ubuntu**
```shell
apt-get install iotop
```
**CentOS**
```shell
yum install iotop
```
**编译安装**
```shell
wget http://guichaz.free.fr/iotop/files/iotop-0.4.4.tar.gz
tar zxf iotop-0.4.4.tar.gz
python setup.py build
python setup.py install
```
### 语法
```shell
iotop选项
```
### 选项
```shell
-o只显示有io操作的进程
-b批量显示无交互主要用作记录到文件。
-n NUM显示NUM次主要用于非交互式模式。
-d SEC间隔SEC秒显示一次。
-p PID监控的进程pid。
-u USER监控的进程用户。
```
**iotop 常用快捷键:**
1. 左右箭头:改变排序方式,默认是按 IO 排序。
2. r改变排序顺序。
3. o只显示有 IO 输出的进程。
4. p进程/线程的显示方式的切换。
5. a显示累积使用量。
6. q退出。
### 实例
直接执行 iotop 就可以看到效果了:
```shell
Total DISK read: 0.00 B/s | Total DISK write: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> command
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init [3]
2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd]
3 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/0]
4 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0]
5 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/0]
6 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/1]
7 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/1]
8 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/1]
9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/0]
10 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/1]
11 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [khelper]
2572 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [bluetooth]
```

83
docs/linux/cli/scp.md Normal file
View File

@ -0,0 +1,83 @@
# scp
加密的方式在本地主机和远程主机之间复制文件
## 补充说明
**scp 命令** 用于在 Linux 下进行远程拷贝文件的命令,和它类似的命令有 cp不过 cp 只是在本机进行拷贝不能跨服务器,而且 scp 传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system 时,用 scp 可以帮你把文件移出来。另外scp 还非常不占资源不会提高多少系统负荷在这一点上rsync 就远远不及它了。虽然  rsync 比 scp 会快一点但当小文件众多的情况下rsync 会导致硬盘 I/O 非常高,而 scp 基本不影响系统正常使用。
### 语法
```shell
scp(选项)(参数)
```
### 选项
```shell
-1使用ssh协议版本1
-2使用ssh协议版本2
-4使用ipv4
-6使用ipv6
-B以批处理模式运行
-C使用压缩
-F指定ssh配置文件
-iidentity_file 从指定文件中读取传输时使用的密钥文件例如亚马逊云pem此参数直接传递给ssh
-l指定宽带限制
-o指定使用的ssh选项
-P指定远程主机的端口号
-p保留文件的最后修改时间最后访问时间和权限模式
-q不显示复制进度
-r以递归方式复制。
```
### 参数
- 源文件:指定要复制的源文件。
- 目标文件:目标文件。格式为`user@hostfilename`(文件名为目标文件的名称)。
### 实例
从远程复制到本地的 scp 命令与上面的命令雷同,只要将从本地复制到远程的命令后面 2 个参数互换顺序就行了。
**从远处复制文件到本地目录**
```shell
scp root@10.10.10.10:/opt/soft/nginx-0.5.38.tar.gz /opt/soft/
```
从 10.10.10.10 机器上的`/opt/soft/`的目录中下载 nginx-0.5.38.tar.gz  文件到本地`/opt/soft/`目录中。
**从亚马逊云复制 OpenVPN 到本地目录**
```shell
scp -i amazon.pem ubuntu@10.10.10.10:/usr/local/openvpn_as/etc/exe/openvpn-connect-2.1.3.110.dmg openvpn-connect-2.1.3.110.dmg
```
从 10.10.10.10 机器上下载 openvpn 安装文件到本地当前目录来。
**从远处复制到本地**
```shell
scp -r root@10.10.10.10:/opt/soft/mongodb /opt/soft/
```
从 10.10.10.10 机器上的`/opt/soft/`中下载 mongodb 目录到本地的`/opt/soft/`目录来。
**上传本地文件到远程机器指定目录**
```shell
scp /opt/soft/nginx-0.5.38.tar.gz root@10.10.10.10:/opt/soft/scptest
# 指定端口 2222
scp -rp -P 2222 /opt/soft/nginx-0.5.38.tar.gz root@10.10.10.10:/opt/soft/scptest
```
复制本地`/opt/soft/`目录下的文件 nginx-0.5.38.tar.gz 到远程机器 10.10.10.10 的`opt/soft/scptest`目录。
**上传本地目录到远程机器指定目录**
```shell
scp -r /opt/soft/mongodb root@10.10.10.10:/opt/soft/scptest
```
上传本地目录`/opt/soft/mongodb`到远程机器 10.10.10.10 上`/opt/soft/scptest`的目录中去。

88
docs/linux/cli/top.md Normal file
View File

@ -0,0 +1,88 @@
# top
显示或管理执行中的程序
## 补充说明
**top 命令** 可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过 top 命令所提供的互动式界面,用热键可以管理。
### 语法
```shell
top(选项)
```
### 选项
```shell
-b以批处理模式操作
-c显示完整的治命令
-d屏幕刷新间隔时间
-I忽略失效过程
-s保密模式
-S累积模式
-i<时间>:设置间隔时间;
-u<用户名>:指定用户名;
-p<进程号>:指定进程;
-n<次数>:循环显示的次数。
```
### top 交互命令
在 top 命令执行过程中可以使用的一些交互命令。这些命令都是单字母的,如果在命令行中使用了-s 选项,  其中一些命令可能会被屏蔽。
```shell
h显示帮助画面给出一些简短的命令总结说明
k终止一个进程
i忽略闲置和僵死进程这是一个开关式命令
q退出程序
r重新安排一个进程的优先级别
S切换到累计模式
s改变两次刷新之间的延迟时间单位为s如果有小数就换算成ms。输入0值则系统将不断刷新默认值是5s
f或者F从当前显示中添加或者删除项目
o或者O改变显示项目的顺序
l切换显示平均负载和启动时间信息
m切换显示内存信息
t切换显示进程和CPU状态信息
c切换显示命令名称和完整命令行
M根据驻留内存大小进行排序
P根据CPU使用百分比大小进行排序
T根据时间/累计时间进行排序;
w将当前设置写入~/.toprc文件中。
```
### 实例
```shell
top - 09:44:56 up 16 days, 21:23, 1 user, load average: 9.59, 4.75, 1.92
Tasks: 145 total, 2 running, 143 sleeping, 0 stopped, 0 zombie
Cpu(s): 99.8%us, 0.1%sy, 0.0%ni, 0.2%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 4147888k total, 2493092k used, 1654796k free, 158188k buffers
Swap: 5144568k total, 56k used, 5144512k free, 2013180k cached
```
**解释:**
- top - 09:44:56[当前系统时间],
- 16 days[系统已经运行了 16 天],
- 1 user[个用户当前登录],
- load average: 9.59, 4.75, 1.92[系统负载,即任务队列的平均长度]
- Tasks: 145 total[总进程数],
- 2 running[正在运行的进程数],
- 143 sleeping[睡眠的进程数],
- 0 stopped[停止的进程数],
- 0 zombie[冻结进程数],
- Cpu(s): 99.8%us[用户空间占用 CPU 百分比],
- 0.1%sy[内核空间占用 CPU 百分比],
- 0.0%ni[用户进程空间内改变过优先级的进程占用 CPU 百分比],
- 0.2%id[空闲 CPU 百分比], 0.0%wa[等待输入输出的 CPU 时间百分比],
- 0.0%hi[],
- 0.0%st[],
- Mem: 4147888k total[物理内存总量],
- 2493092k used[使用的物理内存总量],
- 1654796k free[空闲内存总量],
- 158188k buffers[用作内核缓存的内存量]
- Swap:  5144568k total[交换区总量],
- 56k used[使用的交换区总量],
- 5144512k free[空闲交换区总量],
- 2013180k cached[缓冲的交换区总量],

95
docs/linux/cli/vmstat.md Normal file
View File

@ -0,0 +1,95 @@
# vmstat
显示虚拟内存状态
## 补充说明
**vmstat 命令** 的含义为显示虚拟内存状态“Viryual Memor Statics”但是它可以报告关于进程、内存、I/O 等系统整体运行状态。
### 语法
```shell
vmstat(选项)(参数)
```
### 选项
```shell
-a显示活动内页
-f显示启动后创建的进程总数
-m显示slab信息
-n头信息仅显示一次
-s以表格方式显示事件计数器和内存状态
-d报告磁盘状态
-p显示指定的硬盘分区状态
-S输出信息的单位。
```
### 参数
- 事件间隔:状态信息刷新的时间间隔;
- 次数:显示报告的次数。
### 实例
```shell
vmstat 3
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 320 42188 167332 1534368 0 0 4 7 1 0 0 0 99 0 0
0 0 320 42188 167332 1534392 0 0 0 0 1002 39 0 0 100 0 0
0 0 320 42188 167336 1534392 0 0 0 19 1002 44 0 0 100 0 0
0 0 320 42188 167336 1534392 0 0 0 0 1002 41 0 0 100 0 0
0 0 320 42188 167336 1534392 0 0 0 0 1002 41 0 0 100 0 0
```
**字段说明:**
Procs进程
- r: 运行队列中进程数量,这个值也可以判断是否需要增加 CPU。长期大于 1
- b: 等待 IO 的进程数量。
Memory内存
- swpd: 使用虚拟内存大小,如果 swpd 的值不为 0但是 SISO 的值长期为 0这种情况不会影响系统性能。
- free: 空闲物理内存大小。
- buff: 用作缓冲的内存大小。
- cache: 用作缓存的内存大小,如果 cache 的值大的时候,说明 cache 处的文件数多,如果频繁访问到的文件都能被 cache 处,那么磁盘的读 IO bi 会非常小。
Swap
- si: 每秒从交换区写到内存的大小,由磁盘调入内存。
- so: 每秒写入交换区的内存大小,由内存调入磁盘。
注意:内存够用的时候,这 2 个值都是 0如果这 2 个值长期大于 0 时,系统性能会受到影响,磁盘 IO 和 CPU 资源都会被消耗。有些朋友看到空闲内存free很少的或接近于 0 时,就认为内存不够用了,不能光看这一点,还要结合 si 和 so如果 free 很少,但是 si 和 so 也很少(大多时候是 0那么不用担心系统性能这时不会受到影响的。
IO现在的 Linux 版本块的大小为 1kb
- bi: 每秒读取的块数
- bo: 每秒写入的块数
注意:随机磁盘读写的时候,这 2 个值越大(如超出 1024k),能看到 CPU 在 IO 等待的值也会越大。
system系统
- in: 每秒中断数,包括时钟中断。
- cs: 每秒上下文切换数。
注意:上面 2 个值越大,会看到由内核消耗的 CPU 时间会越大。
CPU以百分比表示
- us: 用户进程执行时间百分比(user time)
us 的值比较高时,说明用户进程消耗的 CPU 时间多,但是如果长期超 50%的使用,那么我们就该考虑优化程序算法或者进行加速。
- sy: 内核系统进程执行时间百分比(system time)
sy 的值高时,说明系统内核消耗的 CPU 资源多,这并不是良性表现,我们应该检查原因。
- wa: IO 等待时间百分比
wa 的值高时,说明 IO 等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。
- id: 空闲时间百分比

180
docs/linux/expect.md Normal file
View File

@ -0,0 +1,180 @@
# expect shell 脚本
## expect 简介
`expect` 是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。
在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用 `expect`,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。这就是 `expect`
expect 自动交互流程:
1. spawn 启动指定进程
2. expect 获取指定关键字
3. send 向指定程序发送指定字符
4. 执行完成退出
## expect 安装
### yum 安装
执行命令:
```shell
yum -y install expect
```
### 手动安装
expect 依赖 tcl所以需要先安装 tcl
```shell
wget https://nchc.dl.sourceforge.net/project/tcl/Tcl/8.6.9/tcl8.6.9-src.tar.gz
tar xf tcl8.6.9-src.tar.gz
cd tcl8.6.9/unix/
./configure && make && sudo make install
```
再安装 expect
```shell
wget https://nchc.dl.sourceforge.net/project/expect/Expect/5.45.4/expect5.45.4.tar.gz
tar xf expect5.45.4.tar.gz
cd ./expect5.45.4
./configure && make && sudo make install
```
## expect 参数
启用选项:
- `-c` - 执行脚本前先执行的命令,可多次使用。
- `-d` - debug 模式,可以在运行时输出一些诊断信息,与在脚本开始处使用 `exp_internal 1` 相似。
- `-D` - 启用交换调式器,可设一整数参数。
- `-f` - 从文件读取命令,仅用于使用 `#!` 时。如果文件名为 `-`,则从 stdin 读取(使用 `./-` 从文件名为-的文件读取)。
- `-i` - 交互式输入命令,使用 `exit``EOF` 退出输入状态。
- `--` - 标示选项结束(如果你需要传递与 `expect` 选项相似的参数给脚本时),可放到 `#!` 行: `#!/usr/bin/expect --`
- `-v` - 显示 `expect` 版本信息。
## expect 命令
- `spawn` - 命令用来启动新的进程,`spawn`后的`send`和`expect`命令都是和使用`spawn`打开的进程进行交互。
- `expect` - 获取匹配信息,匹配成功则执行 `expect` 后面的程序动作。
- `exp_continue` - 在 `expect` 中多次匹配就需要用到。
- `send` - 命令接收一个字符串参数,并将该参数发送到进程。
- `send exp_send` - 用于发送指定的字符串信息。
- `interact` - 命令用的其实不是很多,一般情况下使用`spawn`、`send`和`expect`命令就可以很好的完成我们的任务;但在一些特殊场合下还是需要使用`interact`命令的,`interact`命令主要用于退出自动化,进入人工交互。比如我们使用`spawn`、`send`和`expect`命令完成了 ftp 登陆主机,执行下载文件任务,但是我们希望在文件下载结束以后,仍然可以停留在 ftp 命令行状态,以便手动的执行后续命令,此时使用`interact`命令就可以很好的完成这个任务。
- `send_user` - 用来打印输出 相当于 shell 中的 echo
- `set` - 定义变量。
- `set timeout` - 设置超时时间。
- `puts` - 输出变量。
- `exit` - 退出 expect 脚本
- `eof` - expect 执行结束,退出。
## 示例场景
远程登录
1ssh 登录远程主机执行命令,执行方法 `expect 1.sh` 或者 `source 1.sh`
```shell
#!/usr/bin/expect
spawn ssh saneri@192.168.56.103 df -Th
expect "*password"
send "123456\n"
expect eof
```
2ssh 远程登录主机执行命令,在 shell 脚本中执行 expect 命令,执行方法 sh 2.sh、bash 2.sh 或./2.sh 都可以执行.
```
#!/bin/bash
passwd='123456'
/usr/bin/expect <<-EOF
set time 30
spawn ssh saneri@192.168.56.103 df -Th
expect {
"*yes/no" { send "yes\r"; exp_continue }
"*password:" { send "$passwd\r" }
}
expect eof
EOF
```
3expect 执行多条命令
```
#!/usr/bin/expect -f
set timeout 10
spawn sudo su - root
expect "*password*"
send "123456\r"
expect "#*"
send "ls\r"
expect "#*"
send "df -Th\r"
send "exit\r"
expect eof
```
4创建 ssh key将 id_rsa 和 id_rsa.pub 文件分发到各台主机上面。
```shell
#!/bin/bash
# 判断id_rsa密钥文件是否存在
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
else
echo "id_rsa has created ..."
fi
#分发到各个节点,这里分发到host文件中的主机中.
while read line
do
user=`echo $line | cut -d " " -f 2`
ip=`echo $line | cut -d " " -f 1`
passwd=`echo $line | cut -d " " -f 3`
expect <<EOF
set timeout 10
spawn ssh-copy-id $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$passwd\n" }
}
expect "password" { send "$passwd\n" }
EOF
done < hosts
```
5shell 调用 expect 执行多行命令.
```
#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "touch /tmp/test.txt\n" }
expect "]#" { send "exit\n" } expect eof
EOF
#./ssh5.sh 192.168.1.10 root 123456
```
## 参考资料
- [linux expect 自动交互脚本用法](https://blog.csdn.net/u010820857/article/details/89925274)

View File

@ -4,7 +4,7 @@
### 1.1. Zsh 是什么 ### 1.1. Zsh 是什么
使用 Linux 的人都知道:**_Shell_ 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。_Shell_ 既是一种命令语言,又是一种程序设计语言**。 使用 Linux 的人都知道:***Shell_ 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。_Shell_ 既是一种命令语言,又是一种程序设计语言**。
Shell 的类型有很多种linux 下默认的是 bash虽然 bash 的功能已经很强大但对于以懒惰为美德的程序员来说bash 的提示功能不够强大,界面也不够炫,并非理想工具。 Shell 的类型有很多种linux 下默认的是 bash虽然 bash 的功能已经很强大但对于以懒惰为美德的程序员来说bash 的提示功能不够强大,界面也不够炫,并非理想工具。

View File

@ -0,0 +1,362 @@
# Jenkins 运维
> 环境要求
>
> - JDKJDK7+,官网推荐是 JDK 8
> - Jenkins2.190.1
## Jenkins 简介
### Jenkins 是什么
Jenkins 是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。
Jenkins 支持各种运行方式可通过系统包、Docker 或者通过一个独立的 Java 程序。
### CI/CD 是什么
CI(Continuous integration中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。借用网络图片对 CI 加以理解。
![img](http://dunwu.test.upcdn.net/snap/20200310174528.png)
CD(Continuous Delivery 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的 Staging 环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。下图反应的是 CI/CD 的大概工作模式。
![img](http://dunwu.test.upcdn.net/snap/20200310174544.png)
## Jenkins 安装
> 更详细内容请参考:[Jenkins 官方安装文档](https://jenkins.io/zh/doc/book/installing/)
### War 包部署
安装步骤如下:
1下载并解压到本地
进入[官网下载地址](https://jenkins.io/zh/download/),选择合适的版本下载。
我选择的是最新稳定 war 版本 2.89.4http://mirrors.jenkins.io/war-stable/latest/jenkins.war
我个人喜欢存放在:`/opt/software/jenkins`
```bash
mkdir -p /opt/software/jenkins
wget -O /opt/software/jenkins/jenkins.war http://mirrors.jenkins.io/war-stable/latest/jenkins.wa
```
2启动
如果你和我一样,选择 war 版本,那么你可以将 war 移到 Tomcat 的 webapps 目录下,通过 Tomcat 来启动。
当然,也可以通过 `java -jar` 方式来启动。
```bash
cd /opt/software/jenkins
nohup java -jar jenkins.war --httpPort=8080 >> nohup.out 2>&1 &
```
### rpm 包部署
1下载安装
```bash
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
yum install jenkins
```
2启动
```bash
systemctl start jenkins
```
### 访问
1. 打开浏览器进入链接 `http://localhost:8080`.
2. 按照说明完成安装.
## Jenkins 基本使用
Jenkins 是一个强大的 CI 工具,虽然本身使用 Java 开发,但也能用来做其他语言开发的项目 CI。下面讲解如何使用 Jenkins 创建一个构建任务。
登录 Jenkins 点击左侧的新建,创建新的构建任务。
![img](https:////upload-images.jianshu.io/upload_images/6464255-22b3c49af599565d.png?imageMogr2/auto-orient/strip|imageView2/2/w/374/format/webp)
跳转到如下界面。任务名称可以自行设定,但需要全局唯一。输入名称后选择构建一个自由风格的软件项目(其他选项不作介绍)。并点击下方的确定按钮即创建了一个构建任务。之后会自动跳转到该 job 的配置页面。
![img](https:////upload-images.jianshu.io/upload_images/6464255-0febc0bc4ca3cadd.png?imageMogr2/auto-orient/strip|imageView2/2/w/1044/format/webp)
新建自由风格的软件项目
下图是构建任务设置界面,可以看到上方的几个选项**"General", "源码管理" "构建触发器""构建环境" "构建" "构建后操作"**。下面逐一介绍。
![img](https:////upload-images.jianshu.io/upload_images/6464255-77998a3e6a70b83f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1032/format/webp)
### General
General 是构建任务的一些基本配置。名称,描述之类的。
![](http://dunwu.test.upcdn.net/snap/20200310221814.png)
重要配置项:
- **Description**:对构建任务的描述。
- **Discard old builds**:服务器资源是有限的,有时候保存了太多的历史构建,会导致 Jenkins 速度变慢,并且服务器硬盘资源也会被占满。当然下方的"保持构建天数" 和 保持构建的最大个数是可以自定义的,需要根据实际情况确定一个合理的值。
点击右方的问号图标可以查看帮助信息。
### Source Code Management
**Source Code Management**,即源码管理,就是配置你代码的存放位置。
![](http://dunwu.test.upcdn.net/snap/20200310222110.png)
- **Git:** 支持主流的 Github 和 Gitlab 代码仓库。因我们的研发团队使用的是 gitlab所以下面我只会对该项进行介绍。
- **Repository URL**:仓库地址。
- **Credentials**:凭证。可以使用 HTTP 方式的用户名密码,也可以是 RSA 文件。 但要通过后面的"ADD"按钮添加凭证。
- **Branches to build**:构建的分支。`*/master` 表示 master 分支,也可以设置为其他分支。
- **Repository browser**:你所使用的代码仓库管理工具,如 Github、Gitlab.
- **Subversion**:即 SVN这里不作介绍。
### Build Triggers
**Build Triggers**,即构建触发器,用于构建任务的触发器。
![](http://dunwu.test.upcdn.net/snap/20200310222608.png)
配置说明:
- **Trigger builds remotely (e.g., from scripts)**:触发远程构建(例如,使用脚本)。该选项会提供一个接口,可以用来在代码层面触发构建。
- **Build after other projects are built**:该选项意思是"在其他项目构建后再构建"。
- **Build periodically**:周期性的构建。就是每隔一段时间进行构建。日程表类似 linux crontab 书写格式。如:`H/30 * * * *`,表示每隔 30 分钟进行一次构建。
- **Build when a change is pushed to GitLab**当有 git push 到 Gitlab 仓库,即触发构建。后面会有一个触发构建的地址,一般被称为 webhooks。需要将这个地址配置到 gitlab 中webhooks 如何配置后面介绍。这个是常用的构建触发器。
- **Poll SCM**该选项是配合上面这个选项使用的。当代码仓库发生改动jenkins 并不知道。需要配置这个选项,周期性的去检查代码仓库是否发生改动。
### Build Environment
**Build Environment**,即构建环境,配置构建前的一些准备工作,如指定构建工具。
![](http://dunwu.test.upcdn.net/snap/20200310223004.png)
### Build
Build即构建。
点击下图中的 Add build step 按钮,会弹出一个构建任务菜单,可以根据实际需要来选择。
![](http://dunwu.test.upcdn.net/snap/20200310223241.png)
【说明】
- **Copy artifacts from another project**:从其他项目获取构建。一般当本任务有上游任务,需要获取上游任务的构件时使用。比如:有个 Java Web 项目,需要依赖于上一个前端构建任务输出的静态文件压缩包。
- Eexcute NodeJS script执行 Nodejs 脚本。默认支持 nodejs、npm 命令。
- **Eexcute shell** 执行 shell 脚本。用于 Linux 环境。
- **Execute Windows batch command**:执行 batch 脚本。用于 Windows 环境。
- **Invoke Ant**Ant 是一款 java 项目构建工具。
- **Invoke Gradle script**Gradle 构建项目。
- **Invoke top-level Maven targets**Maven 构建项目。
### Post-build Actions
**Post-build Actions**,即构建后操作,用于构建完本项目的一些后续操作,比如生成相应的代码测试报告。
![](http://dunwu.test.upcdn.net/snap/20200310224106.png)
![](http://dunwu.test.upcdn.net/snap/20200310224254.png)
![](http://dunwu.test.upcdn.net/snap/20200310224331.png)
个人较常用的配置:
- **Archive the artifacts**:归档构件。
- **Build other projects**:构建其他项目。
- **Trigger parameterized build on other projects**:构建其他项目,并传输构建参数。
- **Publish JUnit test result report**:发布 Junit 测试报告。
- **E-mail Notification**:邮件通知,构建完成后发邮件到指定的邮箱。
---
**以上配置完成后,点击保存即可。**
### 开始构建
![](http://dunwu.test.upcdn.net/snap/20200310224927.png)
如上图所示,一切配置好后,即可点击 **Build Now** 开始构建。
### 构建结果
![](http://dunwu.test.upcdn.net/snap/20200310225234.png)
- **构建状态**
- **Successful 蓝色**:构建完成,并且被认为是稳定的。
- **Unstable 黄色**:构建完成,但被认为是不稳定的。
- **Failed 红色**:构建失败。
- **Disable 灰色**:构建已禁用
- **构建稳定性**
- 构建稳定性用天气表示:**晴、晴转多云、多云、小雨、雷阵雨**。天气越好表示构建越稳定,反之亦然。
- 构建历史界面
- **console output**:输出构建的日志信息
## 其他相关配置
### SSH Server 配置
登录 jenkins -> 系统管理 -> 系统设置
配置请看下图:
![img](https:////upload-images.jianshu.io/upload_images/6464255-15476f9e273daa58.png?imageMogr2/auto-orient/strip|imageView2/2/w/1108/format/webp)
重要配置:
- **SSH Servers:** 由于 jenkins 服务器公钥文件我已经配置好,所以之后新增 SSH Servers 只需要配置这一项即可。
- **Name** 自定义,需要全局唯一。
- **HostName:** 主机名,直接用 ip 地址即可。
- **Username:** 新增 Server 的用户名,这里配置的是 root。
- **Remote Directory:** 远程目录。jenkins 服务器发送文件给新增的 server 默认是在这个目录。
### 配置 Gitlab webhooks
在 gitlab 的 project 页面 打开**settings**,再打开 **web hooks** 。点击**"ADD WEB HOOK"** 添加 webhook。把之前 jenkins 配置中的那个 url 添加到这里,添加完成后,点击**"TEST HOOK"**进行测试,如果显示 SUCCESS 则表示添加成功。
![img](https:////upload-images.jianshu.io/upload_images/6464255-9f8d04a1400556f9.png?imageMogr2/auto-orient/strip|imageView2/2/w/246/format/webp)
![img](https:////upload-images.jianshu.io/upload_images/6464255-154a62db330c819b.png?imageMogr2/auto-orient/strip|imageView2/2/w/240/format/webp)
![img](https:////upload-images.jianshu.io/upload_images/6464255-e4d1ea1e1dbde812.png?imageMogr2/auto-orient/strip|imageView2/2/w/1036/format/webp)
![img](https:////upload-images.jianshu.io/upload_images/6464255-c7a687207b2c26fc.png?imageMogr2/auto-orient/strip|imageView2/2/w/1106/format/webp)
![img](https:////upload-images.jianshu.io/upload_images/6464255-ce8ae810bc2cb0d4.png?imageMogr2/auto-orient/strip|imageView2/2/w/1154/format/webp)
配置 phpunit.xml
phpunit.xml 是 phpunit 这个工具用来单元测试所需要的配置文件。这个文件的名称同样也是可以自定义的,但是要在"build.xml"中配置好名字就行。默认情况下,用"phpunit.xml", 则不需要在"build.xml"中配置文件名。
![img](https:////upload-images.jianshu.io/upload_images/6464255-aa212d3b3eaff548.png?imageMogr2/auto-orient/strip|imageView2/2/w/798/format/webp)
build.xml 中 phpunit 配置
fileset dir 指定单元测试文件所在路径include 指定包含哪些文件,支持通配符匹配。当然也可以用 exclude 关键字指定不包含的文件。
![img](https:////upload-images.jianshu.io/upload_images/6464255-dbc0084f6d50a240.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)
### jenkins 权限管理
由于 jenkins 默认的权限管理体系不支持用户组或角色的配置,因此需要安装第三发插件来支持角色的配置,本文将使用 Role Strategy Plugin。基于这个插件的权限管理设置请参考这篇文章:[http://blog.csdn.net/russ44/article/details/52276222](https://link.jianshu.com?t=http%3A%2F%2Fblog.csdn.net%2Fruss44%2Farticle%2Fdetails%2F52276222),这里不作详细介绍。
至此,就可以用 jenkins 周而复始的进行 CI 了,当然 jenkins 是一个强大的工具,功能绝不仅仅是以上这些,其他方面要是以后用到,我会更新到这篇文章中。有疑问欢迎在下方留言。
## Jenkins FAQ
### 登录密码
如果不知道初始登录密码,可以通过以下方式查看:
执行命令 `cat /root/.jenkins/secrets/initialAdminPassword`,打印出来的即是初始登录密码。
### 忘记密码
1.执行 `vim /root/.jenkins/config.xml` ,删除以下内容
```xml
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
<denyAnonymousReadAccess>true</denyAnonymousReadAccess>
</authorizationStrategy>
<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
<disableSignup>true</disableSignup>
<enableCaptcha>false</enableCaptcha>
</securityRealm>
```
2.重启 Jenkins 服务;
3.进入首页>“系统管理”>“Configure Global Security”
4.勾选“启用安全”;
5.点选“Jenkins 专有用户数据库”,并点击“保存”;
6.重新点击首页>“系统管理”,发现此时出现“管理用户”;
7.点击进入展示“用户列表”;
8.点击右侧进入修改密码页面,修改后即可重新登录。
### 卡在 check 页面
**现象**:输入密码后,卡在 check 页面
**原因**jenkins 在安装插件前总是尝试连接 www.google.com来判断网络是否连通。谷歌的网站在大陆是连不上的所以会出现这个问题。
**解决方案**:执行`vim /root/.jenkins/updates/default.json`,将 `connectionCheckUrl` 后的 `www.google.com` 改为 `www.baidu.com` 。然后重启即可。
或者直接执行命令:
```bash
sed -i 's/www.google.com/www.baidu.com/g' /root/.jenkins/updates/default.json
```
### 卡在 getting startted 页面
**现象**:卡在 getting startted 页面
**原因**jenkins 默认的插件下载服务器地址在国外,如果不翻墙下载不了。
**解决方案**:执行`vim /root/.jenkins/hudson.model.UpdateCenter.xml`,将 `<url>` 改为 `http://mirror.xmission.com/jenkins/updates/update-center.json` 。然后重启即可。
或者直接执行命令:
```bash
sed -i '/^<url>/s/.*/<url>http:\/\/mirror.xmission.com\/jenkins\/updates\/update-center.json<\/url>/g' /root/.jenkins/hudson.model.UpdateCenter.xml
```
### 以 root 用户运行
1修改 jenkins 用户
```bash
vim /etc/sysconfig/jenkins
```
修改用户
```bash
$JENKINS_USER="root"
```
2修改 `Jenkins` 相关文件夹用户权限
```bash
chown -R root:root /var/lib/jenkins
chown -R root:root /var/cache/jenkins
chown -R root:root /var/log/jenkins
```
3重启 Jenkins
```
systemctl restart jenkins
```
## 参考资料
- **官方**
- [Jenkins 官网](https://jenkins.io/zh/)
- [Jenkins 中文文档](https://jenkins.io/zh/doc/tutorials/)
- **引申**
- [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
- **文章**
- https://jenkins.io/doc/pipeline/tour/getting-started/
- https://www.cnblogs.com/austinspark-jessylu/p/6894944.html
- http://blog.csdn.net/jlminghui/article/details/54952148
- [Jenkins 详细教程](https://www.jianshu.com/p/5f671aca2b5a)

View File

@ -1,165 +0,0 @@
# Jenkins 安装
> 环境要求
>
> - JDKJDK7+,官网推荐是 JDK 8
## 部署
> 参考:[官方安装文档](https://jenkins.io/zh/doc/book/installing/)
### War 包部署
安装步骤如下:
1下载并解压到本地
进入[官网下载地址](https://jenkins.io/zh/download/),选择合适的版本下载。
我选择的是最新稳定 war 版本 2.89.4http://mirrors.jenkins.io/war-stable/latest/jenkins.war
我个人喜欢存放在:`/opt/software/jenkins`
```bash
mkdir -p /opt/software/jenkins
wget -O /opt/software/jenkins/jenkins.war http://mirrors.jenkins.io/war-stable/latest/jenkins.wa
```
2启动
如果你和我一样,选择 war 版本,那么你可以将 war 移到 Tomcat 的 webapps 目录下,通过 Tomcat 来启动。
当然,也可以通过 `java -jar` 方式来启动。
```bash
cd /opt/software/jenkins
nohup java -jar jenkins.war --httpPort=8080 >> nohup.out 2>&1 &
```
### rpm 包部署
1下载安装
```bash
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
yum install jenkins
```
2启动
```bash
systemctl start jenkins
```
### 访问
1. 打开浏览器进入链接 `http://localhost:8080`.
2. 按照说明完成安装.
## 系统配置
## FAQ
### 登录密码
如果不知道初始登录密码,可以通过以下方式查看:
执行命令 `cat /root/.jenkins/secrets/initialAdminPassword`,打印出来的即是初始登录密码。
### 忘记密码
1.执行 `vim /root/.jenkins/config.xml` ,删除以下内容
```xml
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
<denyAnonymousReadAccess>true</denyAnonymousReadAccess>
</authorizationStrategy>
<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
<disableSignup>true</disableSignup>
<enableCaptcha>false</enableCaptcha>
</securityRealm>
```
2.重启 Jenkins 服务;
3.进入首页>“系统管理”>“Configure Global Security”
4.勾选“启用安全”;
5.点选“Jenkins 专有用户数据库”,并点击“保存”;
6.重新点击首页>“系统管理”,发现此时出现“管理用户”;
7.点击进入展示“用户列表”;
8.点击右侧进入修改密码页面,修改后即可重新登录。
### 卡在 check 页面
**现象**:输入密码后,卡在 check 页面
**原因**jenkins 在安装插件前总是尝试连接 www.google.com来判断网络是否连通。谷歌的网站在大陆是连不上的所以会出现这个问题。
**解决方案**:执行`vim /root/.jenkins/updates/default.json`,将 `connectionCheckUrl` 后的 `www.google.com` 改为 `www.baidu.com` 。然后重启即可。
或者直接执行命令:
```bash
sed -i 's/www.google.com/www.baidu.com/g' /root/.jenkins/updates/default.json
```
### 卡在 getting startted 页面
**现象**:卡在 getting startted 页面
**原因**jenkins 默认的插件下载服务器地址在国外,如果不翻墙下载不了。
**解决方案**:执行`vim /root/.jenkins/hudson.model.UpdateCenter.xml`,将 `<url>` 改为 `http://mirror.xmission.com/jenkins/updates/update-center.json` 。然后重启即可。
或者直接执行命令:
```bash
sed -i '/^<url>/s/.*/<url>http:\/\/mirror.xmission.com\/jenkins\/updates\/update-center.json<\/url>/g' /root/.jenkins/hudson.model.UpdateCenter.xml
```
### 以 root 用户运行
1修改 jenkins 用户
```bash
vim /etc/sysconfig/jenkins
```
修改用户
```bash
$JENKINS_USER="root"
```
2修改 `Jenkins` 相关文件夹用户权限
```bash
chown -R root:root /var/lib/jenkins
chown -R root:root /var/cache/jenkins
chown -R root:root /var/log/jenkins
```
3重启 Jenkins
```
systemctl restart jenkins
```
## 参考资料
- **官方**
- [Jenkins 官网](https://jenkins.io/zh/)
- **引申**
- [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
- **引用**
- https://jenkins.io/doc/pipeline/tour/getting-started/
- https://www.cnblogs.com/austinspark-jessylu/p/6894944.html
- http://blog.csdn.net/jlminghui/article/details/54952148