diff --git a/codes/expect/远程登录.sh b/codes/expect/远程登录.sh new file mode 100644 index 0000000..045a076 --- /dev/null +++ b/codes/expect/远程登录.sh @@ -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" diff --git a/codes/linux/lib/docker.sh b/codes/linux/lib/docker.sh index 3fca3e6..7ef09f3 100644 --- a/codes/linux/lib/docker.sh +++ b/codes/linux/lib/docker.sh @@ -1,25 +1,18 @@ #!/usr/bin/env bash -# ------------------------------------------------------------------------------ -# 构建 Docker 镜像 脚本 -# @author Zhang Peng -# @since 2020/1/14 -# ------------------------------------------------------------------------------ - -# 装载其它库 LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh dockerBuild() { if [[ ! $1 ]] || [[ ! $2 ]] || [[ ! $3 ]]; then logError "you must input following params in order:" - echo -e "${C_B_RED}" + echo -e "${ENV_COLOR_B_RED}" echo " (1) source" echo " (2) repository" echo " (3) tag" - echo -e "\nEg. dockerBuild /home/workspace dunwu/dockerApp 0.0.1" - echo -e "${C_RESET}" - return ${FAILED} + echo -e "\nEg. dockerBuild /home/workspace tdh60dev01:5000/fide/fide-processor fide-0.0.6-SNAPSHOT" + echo -e "${ENV_COLOR_RESET}" + return ${ENV_FAILED} fi local source=$1 @@ -27,15 +20,15 @@ dockerBuild() { local tag=$3 dockerCheck ${source} - if [[ "${SUCCEED}" != "$?" ]]; then - return ${FAILED} + if [[ "${ENV_SUCCEED}" != "$?" ]]; then + return ${ENV_FAILED} fi cd ${source} callAndLog docker build -t ${repository}:${tag} . - if [[ "${SUCCEED}" != "$?" ]]; then + if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "docker build -t ${repository}:${tag} failed" - return ${FAILED} + return ${ENV_FAILED} fi cd - @@ -44,18 +37,17 @@ dockerBuild() { dockerPush() { if [[ ! $1 ]] || [[ ! $2 ]]; then logError "you must input following params in order:" - echo -e "${C_B_RED}" + echo -e "${ENV_COLOR_B_RED}" echo " (1) repository" echo " (2) tag" - echo -e "\nEg. dockerBuild dunwu/dockerApp 0.0.1" - echo -e "${C_RESET}" - return ${FAILED} + echo -e "\nEg. dockerBuild tdh60dev01:5000/fide/fide-processor fide-0.0.6-SNAPSHOT" + echo -e "${ENV_COLOR_RESET}" + return ${ENV_FAILED} fi local repository=$1 local tag=$2 - # 如果 docker 镜像已存在,则删除镜像 local dockerHashId=$(docker image ls | grep ${repository} | grep ${tag} | awk '{print $3}') if [[ ! ${dockerHashId} ]]; then logInfo "try to delete existed image: ${repository}:${tag}" @@ -66,22 +58,22 @@ dockerPush() { callAndLog docker push ${repository}:${tag} } -# 判断指定路径下是否为 docker 工程 -# @param $1: 第一个参数为 docker 项目路径 +# check Dockerfile +# @param $1: project path dockerCheck() { local source=$1 if [[ -d "${source}" ]]; then cd ${source} if [[ -f "${source}/Dockerfile" ]]; then - return ${YES} + return ${ENV_YES} else logError "Dockerfile is not exists" - return ${NO} + return ${ENV_NO} fi cd - - return ${YES} + return ${ENV_YES} else logError "${source} is not valid docker project" - return ${NO} + return ${ENV_NO} fi } diff --git a/codes/linux/lib/git.sh b/codes/linux/lib/git.sh index 3948da0..1846174 100644 --- a/codes/linux/lib/git.sh +++ b/codes/linux/lib/git.sh @@ -1,21 +1,23 @@ #!/usr/bin/env bash -# ------------------------------------------------------------------------------ -# Git 基本操作脚本 +# ----------------------------------------------------------------------------------------------------- +# git operation utils # @author Zhang Peng -# ------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------ load 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 不存在!" + logError "${LINUX_SCRIPTS_LIB_DIR}/utils.sh not exists!" exit 1 fi source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh -# ------------------------------------------------------------------------------ git 操作函数 + +# ------------------------------------------------------------------------------ functions GIT_LOCAL_BRANCH= getGitLocalBranch() { @@ -27,37 +29,36 @@ getGitOriginBranch() { GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}") } -# 检查指定的路径是不是一个 git 项目 +# check specified path is git project or not checkGit() { local source=$1 if [[ -d "${source}" ]]; then - cd ${source} || return ${NO} - # (1)删除git状态零时文件 + cd ${source} || return ${ENV_NO} + # (1) delete gitstatus.tmp if [[ -f "gitstatus.tmp" ]]; then rm -rf gitstatus.tmp fi - # (2)判断git是否可用 + # (2) check git status git status &> gitstatus.tmp local gitStatus=false grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true rm -rf gitstatus.tmp if [[ ${gitStatus} == true ]]; then - return ${YES} + return ${ENV_YES} else - return ${NO} + return ${ENV_NO} fi - return ${NO} + return ${ENV_NO} fi - logError "${source} is invalid dir." - return ${NO} + logWarn "${source} is not exists." + return ${ENV_NO} } -# clone 或 fetch 操作 -# 如果本地代码目录已经是 git 仓库,执行 pull;若不是,则执行 clone -# 依次传入 Git 仓库、Git 项目组、Git 项目名、分支、本地代码目录 +# execute git clone or fetch +# params: Git repository, git group, git project, git branch, local path cloneOrPullGit() { local repository=$1 @@ -68,12 +69,12 @@ cloneOrPullGit() { if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then logError "Please input root, group, project, branch." - return ${FAILED} + return ${ENV_FAILED} fi if [[ ! -d "${root}" ]]; then logError "${root} is not directory." - return ${FAILED} + return ${ENV_FAILED} fi local source=${root}/${group}/${project} @@ -82,52 +83,49 @@ cloneOrPullGit() { mkdir -p ${root}/${group} checkGit ${source} - if [[ "${YES}" == "$?" ]]; then - # 如果 ${source} 是 git 项目,执行 pull 操作 - cd ${source} || return ${FAILED} + if [[ "${ENV_YES}" == "$?" ]]; then + cd ${source} || return ${ENV_FAILED} + git fetch --all git checkout -f ${branch} - if [[ "${SUCCEED}" != "$?" ]]; then + if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "<<<< git checkout ${branch} failed." - return ${FAILED} + return ${ENV_FAILED} fi logInfo "git checkout ${branch} succeed." getGitOriginBranch - git fetch --all git reset --hard ${GIT_ORIGIN_BRANCH} - if [[ "${SUCCEED}" != "$?" ]]; then + if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "<<<< git reset --hard ${GIT_ORIGIN_BRANCH} failed." - return ${FAILED} + return ${ENV_FAILED} fi logInfo "git reset --hard ${GIT_ORIGIN_BRANCH} succeed." git pull - if [[ "${SUCCEED}" != "$?" ]]; then + if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "<<<< git pull failed." - return ${FAILED} + return ${ENV_FAILED} fi logInfo "git pull succeed." else - # 如果 ${source} 不是 git 项目,执行 clone 操作 - git clone "${repository}:${group}/${project}.git" ${source} - if [[ "${SUCCEED}" != "$?" ]]; then + if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "<<<< git clone ${project} failed." - return ${FAILED} + return ${ENV_FAILED} fi logInfo "git clone ${project} succeed." - cd ${source} || return ${FAILED} + cd ${source} || return ${ENV_FAILED} git checkout -f ${branch} - if [[ "${SUCCEED}" != "$?" ]]; then + if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "<<<< git checkout ${branch} failed." - return ${FAILED} + return ${ENV_FAILED} fi logInfo "git checkout ${branch} succeed." fi logInfo "Clone or pull git project [$2/$3:$4] succeed." - return ${SUCCEED} + return ${ENV_SUCCEED} } diff --git a/codes/linux/lib/java.sh b/codes/linux/lib/java.sh index 976326e..84276bc 100644 --- a/codes/linux/lib/java.sh +++ b/codes/linux/lib/java.sh @@ -94,7 +94,7 @@ startServer() { # >>>> 3. create log dir and console log file mkdir -p ${LOG_PATH} - if [[ ! -f ${CONSOLE_LOG} ]]; then + if [[ ! -x ${CONSOLE_LOG} ]]; then touch ${CONSOLE_LOG} fi diff --git a/codes/linux/lib/maven.sh b/codes/linux/lib/maven.sh index 01cbcb4..b55907f 100644 --- a/codes/linux/lib/maven.sh +++ b/codes/linux/lib/maven.sh @@ -1,37 +1,41 @@ #!/usr/bin/env bash -# ------------------------------------------------------------------------------ -# maven 项目操作脚本 +# ----------------------------------------------------------------------------------------------------- +# maven operation utils # @author Zhang Peng -# ------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------ load libs -# 装载其它库 LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` 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 fi source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh -# 执行 maven 操作 -# @param $1: 第一个参数为 maven 项目路径 -# @param $2: 第二个参数为 maven 操作,如 package、install、deploy -# @param $3: 第三个参数为 maven profile 【非必填】 + +# ------------------------------------------------------------------------------ functions + +# execute maven lifecycle operation +# @param $1: maven project path +# @param $2: maven lifecycle, eg. package、install、deploy +# @param $3: maven profile [optional] mavenOperation() { local source=$1 local lifecycle=$2 local profile=$3 mavenCheck ${source} - if [[ "${SUCCEED}" != "$?" ]]; then - return ${FAILED} + if [[ "${ENV_SUCCEED}" != "$?" ]]; then + return ${ENV_FAILED} fi if [[ ! "${lifecycle}" ]]; then logError "please input maven lifecycle" - return ${FAILED} + return ${ENV_FAILED} fi local mvnCli="mvn clean ${lifecycle} -DskipTests=true -B -U" @@ -47,25 +51,25 @@ mavenOperation() { callAndLog "${mvnCli}" cd - - return ${SUCCEED} + return ${ENV_SUCCEED} } -# 判断指定路径下是否为 maven 工程 -# @param $1: 第一个参数为 maven 项目路径 +# check specified path is maven project or not +# @param $1: maven project path mavenCheck() { local source=$1 if [[ -d "${source}" ]]; then cd ${source} if [[ -f "${source}/pom.xml" ]]; then - return ${YES} + return ${ENV_YES} else logError "pom.xml is not exists" - return ${NO} + return ${ENV_NO} fi cd - - return ${YES} + return ${ENV_YES} else logError "please input valid maven project path" - return ${NO} + return ${ENV_NO} fi } diff --git a/codes/linux/lib/mysql.sh b/codes/linux/lib/mysql.sh new file mode 100644 index 0000000..4349344 --- /dev/null +++ b/codes/linux/lib/mysql.sh @@ -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} +} diff --git a/codes/linux/lib/utils.sh b/codes/linux/lib/utils.sh index 2900760..e96a8e5 100644 --- a/codes/linux/lib/utils.sh +++ b/codes/linux/lib/utils.sh @@ -1,118 +1,113 @@ #!/usr/bin/env bash -# ------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------------------------------- # Shell Utils # @author Zhang Peng -# ------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------------------------------- -# ------------------------------------------------------------------------------ 颜色状态 +# ------------------------------------------------------------------------------ env # Regular Color -export C_BLACK="\033[0;30m" -export C_RED="\033[0;31m" -export C_GREEN="\033[0;32m" -export C_YELLOW="\033[0;33m" -export C_BLUE="\033[0;34m" -export C_MAGENTA="\033[0;35m" -export C_CYAN="\033[0;36m" -export C_WHITE="\033[0;37m" - +export ENV_COLOR_BLACK="\033[0;30m" +export ENV_COLOR_RED="\033[0;31m" +export ENV_COLOR_GREEN="\033[0;32m" +export ENV_COLOR_YELLOW="\033[0;33m" +export ENV_COLOR_BLUE="\033[0;34m" +export ENV_COLOR_MAGENTA="\033[0;35m" +export ENV_COLOR_CYAN="\033[0;36m" +export ENV_COLOR_WHITE="\033[0;37m" # Bold Color -export C_B_BLACK="\033[1;30m" -export C_B_RED="\033[1;31m" -export C_B_GREEN="\033[1;32m" -export C_B_YELLOW="\033[1;33m" -export C_B_BLUE="\033[1;34m" -export C_B_MAGENTA="\033[1;35m" -export C_B_CYAN="\033[1;36m" -export C_B_WHITE="\033[1;37m" - +export ENV_COLOR_B_BLACK="\033[1;30m" +export ENV_COLOR_B_RED="\033[1;31m" +export ENV_COLOR_B_GREEN="\033[1;32m" +export ENV_COLOR_B_YELLOW="\033[1;33m" +export ENV_COLOR_B_BLUE="\033[1;34m" +export ENV_COLOR_B_MAGENTA="\033[1;35m" +export ENV_COLOR_B_CYAN="\033[1;36m" +export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color -export C_U_BLACK="\033[4;30m" -export C_U_RED="\033[4;31m" -export C_U_GREEN="\033[4;32m" -export C_U_YELLOW="\033[4;33m" -export C_U_BLUE="\033[4;34m" -export C_U_MAGENTA="\033[4;35m" -export C_U_CYAN="\033[4;36m" -export C_U_WHITE="\033[4;37m" - +export ENV_COLOR_U_BLACK="\033[4;30m" +export ENV_COLOR_U_RED="\033[4;31m" +export ENV_COLOR_U_GREEN="\033[4;32m" +export ENV_COLOR_U_YELLOW="\033[4;33m" +export ENV_COLOR_U_BLUE="\033[4;34m" +export ENV_COLOR_U_MAGENTA="\033[4;35m" +export ENV_COLOR_U_CYAN="\033[4;36m" +export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color -export C_BG_BLACK="\033[40m" -export C_BG_RED="\033[41m" -export C_BG_GREEN="\033[42m" -export C_BG_YELLOW="\033[43m" -export C_BG_BLUE="\033[44m" -export C_BG_MAGENTA="\033[45m" -export C_BG_CYAN="\033[46m" -export C_BG_WHITE="\033[47m" - +export ENV_COLOR_BG_BLACK="\033[40m" +export ENV_COLOR_BG_RED="\033[41m" +export ENV_COLOR_BG_GREEN="\033[42m" +export ENV_COLOR_BG_YELLOW="\033[43m" +export ENV_COLOR_BG_BLUE="\033[44m" +export ENV_COLOR_BG_MAGENTA="\033[45m" +export ENV_COLOR_BG_CYAN="\033[46m" +export ENV_COLOR_BG_WHITE="\033[47m" # 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) # 日志路径 -LOG_DIR=/var/log/dunwu -LOG_PATH=${LOG_DIR}/shell.log +LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} +# 日志目录 +LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { - if [[ ! -f "${LOG_PATH}" ]]; then - sudo mkdir -p "${LOG_DIR}" + if [[ ! -x "${LOG_PATH}" ]]; then + mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } logInfo() { - echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists - echo "[${DATE}] [${USER}] [INFO] [$0] [$@] execute succeed." >> "${LOG_PATH}" + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { - echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists - echo "[${DATE}] [${USER}] [WARN] [$0] [$@] execute succeed." >> "${LOG_PATH}" + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { - echo -e "${C_B_RED}[ERROR] $@${C_RESET}" + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists - echo "[${DATE}] [${USER}] [ERROR] [$0] [$@] execute failed." >> "${LOG_PATH}" + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { - echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { - echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { - echo -e "${C_B_RED}[ERROR] $@${C_RESET}" + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* - if [[ $? -eq ${SUCCEED} ]]; then - logInfo "$@ succeed" - echo -e "${C_B_GREEN}[INFO] [$0] [$@] execute succeed.${C_RESET}" - return ${SUCCEED} + if [[ $? -eq ${ENV_SUCCEED} ]]; then + logInfo "$@" + return ${ENV_SUCCEED} else - logError "$@ failed" - echo -e "${C_B_RED}[ERROR] [$0] [$@] execute failed.${C_RESET}" - return ${FAILED} + logError "$@ EXECUTE FAILED" + return ${ENV_FAILED} fi } diff --git a/codes/shell/文件操作/文件路径操作.sh b/codes/shell/文件操作/文件路径操作.sh new file mode 100644 index 0000000..a246d25 --- /dev/null +++ b/codes/shell/文件操作/文件路径操作.sh @@ -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 diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index bdaa934..c5f617b 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -30,7 +30,7 @@ module.exports = { }, { 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 } } diff --git a/docs/README.md b/docs/README.md index b25c52b..bef89e1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,31 +19,31 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu ### 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 文件目录管理](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 文件内容查看命令](docs/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 用户管理](docs/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 网络管理](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 硬件管理](docs/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-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` +- [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 文件内容查看命令](linux/cli/linux-cli-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` +- [Linux 文件压缩和解压](linux/cli/linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` +- [Linux 用户管理](linux/cli/linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` +- [Linux 系统管理](linux/cli/linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` +- [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 硬件管理](linux/cli/linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` +- [Linux 软件管理](linux/cli/linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get` ### Linux 运维 > Linux 系统的常见运维工作。 -- [网络运维](docs/linux/ops/network-ops.md) -- [Samba](docs/linux/ops/samba.md) -- [NTP](docs/linux/ops/ntp.md) -- [Firewalld](docs/linux/ops/firewalld.md) -- [Crontab](docs/linux/ops/crontab.md) -- [Systemd](docs/linux/ops/systemd.md) -- [Vim](docs/linux/ops/vim.md) -- [Iptables](docs/linux/ops/iptables.md) -- [oh-my-zsh](docs/linux/ops/zsh.md) +- [网络运维](linux/ops/network-ops.md) +- [Samba](linux/ops/samba.md) +- [NTP](linux/ops/ntp.md) +- [Firewalld](linux/ops/firewalld.md) +- [Crontab](linux/ops/crontab.md) +- [Systemd](linux/ops/systemd.md) +- [Vim](linux/ops/vim.md) +- [Iptables](linux/ops/iptables.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) - 开发环境 - - [JDK 安装](docs/linux/soft/jdk-install.md) - - [Maven 安装](docs/linux/soft/maven-install.md) - - [Nodejs 安装](docs/linux/soft/nodejs-install.md) + - [JDK 安装](linux/soft/jdk-install.md) + - [Maven 安装](linux/soft/maven-install.md) + - [Nodejs 安装](linux/soft/nodejs-install.md) - 开发工具 - - [Nexus 运维](docs/linux/soft/nexus-ops.md) - - [Gitlab 运维](docs/linux/soft/kafka-install.md) - - [Jenkins 运维](docs/linux/soft/jenkins.md) - - [Svn 运维](docs/linux/soft/svn-ops.md) - - [YApi 运维](docs/linux/soft/yapi-ops.md) + - [Nexus 运维](linux/soft/nexus-ops.md) + - [Gitlab 运维](linux/soft/kafka-install.md) + - [Jenkins 运维](linux/soft/jenkins.md) + - [Svn 运维](linux/soft/svn-ops.md) + - [YApi 运维](linux/soft/yapi-ops.md) - 中间件服务 - - [Elastic 运维](docs/linux/soft/elastic) - - [Kafka 运维](docs/linux/soft/kafka-install.md) - - [RocketMQ 运维](docs/linux/soft/rocketmq-install.md) + - [Elastic 运维](linux/soft/elastic) + - [Kafka 运维](linux/soft/kafka-install.md) + - [RocketMQ 运维](linux/soft/rocketmq-install.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) 📚 - - [Tomcat 运维](docs/linux/soft/tomcat-install.md) + - [Tomcat 运维](linux/soft/tomcat-install.md) - [数据库](https://github.com/dunwu/db-tutorial) 📚 - [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) ### Docker -- [Docker 快速入门](docs/docker/docker-quickstart.md) -- [Dockerfile 最佳实践](docs/docker/docker-dockerfile.md) -- [Docker Cheat Sheet](docs/docker/docker-cheat-sheet.md) -- [Kubernetes 应用指南](docs/docker/kubernetes.md) +- [Docker 快速入门](docker/docker-quickstart.md) +- [Dockerfile 最佳实践](docker/docker-dockerfile.md) +- [Docker Cheat Sheet](docker/docker-cheat-sheet.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) ◾ diff --git a/docs/linux/cli/free.md b/docs/linux/cli/free.md new file mode 100644 index 0000000..385ea0e --- /dev/null +++ b/docs/linux/cli/free.md @@ -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 的角度来看,因为对于 OS,buffers/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 的内存太少了,也是该考虑优化程序或加内存了。 diff --git a/docs/linux/cli/grep.md b/docs/linux/cli/grep.md new file mode 100644 index 0000000..37508fd --- /dev/null +++ b/docs/linux/cli/grep.md @@ -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 --max-count= # 找到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结尾的单词的行。 +x\{m\} # 重复字符x,m次,如:'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和file3,grep输出用-Z选项来指定以0值字节作为终结符文件名(\0),xargs -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 +``` diff --git a/docs/linux/cli/iostat.md b/docs/linux/cli/iostat.md new file mode 100644 index 0000000..c4fce1e --- /dev/null +++ b/docs/linux/cli/iostat.md @@ -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 百分比 | diff --git a/docs/linux/cli/iotop.md b/docs/linux/cli/iotop.md new file mode 100644 index 0000000..42dd735 --- /dev/null +++ b/docs/linux/cli/iotop.md @@ -0,0 +1,79 @@ +# iotop + +用来监视磁盘 I/O 使用状况的工具 + +## 补充说明 + +**iotop 命令** 是一个用来监视磁盘 I/O 使用状况的 top 类工具。iotop 具有与 top 相似的 UI,其中包括 PID、用户、I/O、进程等相关信息。Linux 下的 IO 统计工具如 iostat,nmon 等大多数是只能统计到 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] +``` diff --git a/docs/linux/cli/scp.md b/docs/linux/cli/scp.md new file mode 100644 index 0000000..1af18c8 --- /dev/null +++ b/docs/linux/cli/scp.md @@ -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配置文件; +-i:identity_file 从指定文件中读取传输时使用的密钥文件(例如亚马逊云pem),此参数直接传递给ssh; +-l:指定宽带限制; +-o:指定使用的ssh选项; +-P:指定远程主机的端口号; +-p:保留文件的最后修改时间,最后访问时间和权限模式; +-q:不显示复制进度; +-r:以递归方式复制。 +``` + +### 参数 + +- 源文件:指定要复制的源文件。 +- 目标文件:目标文件。格式为`user@host:filename`(文件名为目标文件的名称)。 + +### 实例 + +从远程复制到本地的 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`的目录中去。 diff --git a/docs/linux/cli/top.md b/docs/linux/cli/top.md new file mode 100644 index 0000000..6b5f575 --- /dev/null +++ b/docs/linux/cli/top.md @@ -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[缓冲的交换区总量], diff --git a/docs/linux/cli/vmstat.md b/docs/linux/cli/vmstat.md new file mode 100644 index 0000000..c8d52c0 --- /dev/null +++ b/docs/linux/cli/vmstat.md @@ -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,但是 SI,SO 的值长期为 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: 空闲时间百分比 diff --git a/docs/linux/expect.md b/docs/linux/expect.md new file mode 100644 index 0000000..24c7d39 --- /dev/null +++ b/docs/linux/expect.md @@ -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 执行结束,退出。 + +## 示例场景 + +远程登录 + +(1)ssh 登录远程主机执行命令,执行方法 `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 +``` + +(2)ssh 远程登录主机执行命令,在 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 +``` + +(3)expect 执行多条命令 + +``` +#!/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 < 环境要求 +> +> - JDK:JDK7+,官网推荐是 JDK 8 +> - Jenkins:2.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.4:http://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 +true + + true + + + true + false + +``` + +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`,将 `` 改为 `http://mirror.xmission.com/jenkins/updates/update-center.json` 。然后重启即可。 + +或者直接执行命令: + +```bash +sed -i '/^/s/.*/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) diff --git a/docs/linux/soft/jenkins.md b/docs/linux/soft/jenkins.md deleted file mode 100644 index de9106d..0000000 --- a/docs/linux/soft/jenkins.md +++ /dev/null @@ -1,165 +0,0 @@ -# Jenkins 安装 - -> 环境要求 -> -> - JDK:JDK7+,官网推荐是 JDK 8 - -## 部署 - -> 参考:[官方安装文档](https://jenkins.io/zh/doc/book/installing/) - -### War 包部署 - -安装步骤如下: - -(1)下载并解压到本地 - -进入[官网下载地址](https://jenkins.io/zh/download/),选择合适的版本下载。 - -我选择的是最新稳定 war 版本 2.89.4:http://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 -true - - true - - - true - false - -``` - -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`,将 `` 改为 `http://mirror.xmission.com/jenkins/updates/update-center.json` 。然后重启即可。 - -或者直接执行命令: - -```bash -sed -i '/^/s/.*/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