From 50b09e27431c641b35cbd4c12b3d419979fa7f26 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 8 Aug 2019 14:05:30 +0800 Subject: [PATCH 01/64] update codes --- codes/linux/soft/README.md | 11 ++++ codes/linux/soft/fastdfs-install.sh | 3 +- codes/shell/demos/variable-demo.sh | 4 +- ...72\346\234\254\344\275\277\347\224\250.sh" | 22 ++++++++ ...74\347\273\231\345\217\230\351\207\217.sh" | 4 ++ ...73\347\273\237\345\217\230\351\207\217.sh" | 5 ++ ...32\344\271\211\345\217\230\351\207\217.sh" | 3 +- ...62\346\210\220\346\225\260\347\273\204.sh" | 19 +++++++ ...72\346\234\254\344\275\277\347\224\250.sh" | 55 +++++++++++++++++++ ...72\346\234\254\344\275\277\347\224\250.sh" | 55 +++++++++++++++++++ 10 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" => "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" (79%) create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" diff --git a/codes/linux/soft/README.md b/codes/linux/soft/README.md index 56deb72d..4ba17d88 100644 --- a/codes/linux/soft/README.md +++ b/codes/linux/soft/README.md @@ -233,3 +233,14 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fa curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/docker-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/docker-install.sh | bash ``` + +## FastDFS 安装 + +说明: + +使用方法:执行以下任意命令即可执行脚本。 + +```sh +curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash +wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash +``` diff --git a/codes/linux/soft/fastdfs-install.sh b/codes/linux/soft/fastdfs-install.sh index 4ac701bd..0ad79b42 100644 --- a/codes/linux/soft/fastdfs-install.sh +++ b/codes/linux/soft/fastdfs-install.sh @@ -47,7 +47,7 @@ nginx_version=1.16.0 nginx_path=/opt/nginx printf "${GREEN}>>>>>>>> install required libs.${RESET}\n\n" -yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vim -y +yum install -y git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vim unzip # download and decompression mkdir -p ${path} @@ -95,6 +95,7 @@ make && make install printf "${GREEN}>>>>>>>>> fastdfs 配置文件准备${RESET}\n" # 配置修改参考:https://github.com/happyfish100/fastdfs/wiki +mkdir -p /etc/fdfs cp ${path}/fastdfs/conf/http.conf /etc/fdfs/ #供nginx访问使用 cp ${path}/fastdfs/conf/mime.types /etc/fdfs/ #供nginx访问使用 cp ${path}/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs diff --git a/codes/shell/demos/variable-demo.sh b/codes/shell/demos/variable-demo.sh index c7acaa17..3cb6fdf7 100644 --- a/codes/shell/demos/variable-demo.sh +++ b/codes/shell/demos/variable-demo.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash ################### 声明变量 ################### -word="hello" -echo ${word} +name="world" +echo "hello ${name}" # Output: hello ################### 只读变量 ################### diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" new file mode 100644 index 00000000..22c68775 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +################### 声明变量 ################### +name="world" +echo "hello ${name}" +# Output: hello world + +################### 只读变量 ################### +readonly_var="hello" +echo ${readonly_var} +# Output: hello +readonly readonly_var +# rword="bye" # 如果放开注释,执行时会报错 + +################### 删除变量 ################### +dword="hello" # 声明变量 +echo ${dword} # 输出变量值 +# Output: hello + +unset dword # 删除变量 +echo ${dword} +# Output: (空) diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" new file mode 100644 index 00000000..b9358593 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +folder=$(pwd) +echo "current path: ${folder}" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" new file mode 100644 index 00000000..3c9bb1c2 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "User info fro userId:$USER" +echo UID:$UID +echo HOME:$HOME diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" similarity index 79% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" rename to "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" index 5dd633d4..854b16de 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" @@ -1,5 +1,4 @@ -#!/bin/bash -#testing variables +#!/usr/bin/env bash days=10 guest="Katie" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" new file mode 100644 index 00000000..5f770fa4 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# ---------------------------------------------------------------------------------- +# 根据特定字符将一个字符串分割成数组 +# ---------------------------------------------------------------------------------- + +str="0.0.0.1" +OLD_IFS="$IFS" +IFS="." +array=(${str}) +IFS="$OLD_IFS" +size=${#array[*]} +lastIndex=`expr ${size} - 1` +echo "数组长度:${size}" +echo "最后一个数组元素:${array[${lastIndex}]}" +for item in ${array[@]} +do + echo "$item" +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" new file mode 100644 index 00000000..94018761 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +################### 单引号和双引号 ################### +################### 拼接字符串 ################### +# 使用单引号拼接 +name1='white' +str1='hello, '${name1}'' +str2='hello, ${name1}' +echo ${str1}_${str2} +# Output: +# hello, white_hello, ${name1} + +# 使用双引号拼接 +name2="black" +str3="hello, "${name2}"" +str4="hello, ${name2}" +echo ${str3}_${str4} +# Output: +# hello, black_hello, black + +################### 获取字符串长度 ################### +text="12345" +echo "${text} length is: ${#text}" +# Output: +# 12345 length is: 5 + +################### 获取字符串长度 ################### +text="12345" +echo ${text:2:2} +# Output: +# 34 + +################### 查找子字符串 ################### +text="hello" +echo `expr index "${text}" ll` +# Output: +# 3 + +################### 截取关键字左边内容 ################### +full_branch="feature/1.0.0" +branch=`echo ${full_branch#feature/}` +echo "branch is ${branch}" + +################### 截取关键字右边内容 ################### +full_version="0.0.1-SNAPSHOT" +version=`echo ${full_version%-SNAPSHOT}` +echo "version is ${version}" + +################### 判断字符串中是否包含子字符串 ################### +result=$(echo "${str}" | grep "feature/") +if [[ "$result" != "" ]] ; then + echo "feature/ 是 ${str} 的子字符串" +else + echo "feature/ 不是 ${str} 的子字符串" +fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" new file mode 100644 index 00000000..65ff2ef7 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# 创建数组 +nums=([2]=2 [0]=0 [1]=1) +colors=(red yellow "dark blue") + +# 访问数组的单个元素 +echo ${nums[1]} +# Output: 1 + +# 访问数组的所有元素 +echo ${colors[*]} +# Output: red yellow dark blue + +echo ${colors[@]} +# Output: red yellow dark blue + +printf "+ %s\n" ${colors[*]} +# Output: +# + red +# + yellow +# + dark +# + blue + +printf "+ %s\n" "${colors[*]}" +# Output: +# + red yellow dark blue + +printf "+ %s\n" "${colors[@]}" +# Output: +# + red +# + yellow +# + dark blue + +# 访问数组的部分元素 +echo ${nums[@]:0:2} +# Output: +# 0 1 + +# 访问数组长度 +echo ${#nums[*]} +# Output: +# 3 + +# 向数组中添加元素 +colors=(white "${colors[@]}" green black) +echo ${colors[@]} +# Output: +# white red yellow dark blue green black + +# 从数组中删除元素 +unset nums[0] +echo ${nums[@]} +# Output: +# 1 2 From 700888cecfd1049ee669801cc82ad9d1cc5449cb Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 19 Aug 2019 19:19:57 +0800 Subject: [PATCH 02/64] update codes --- README.md | 2 +- .../soft/config/redis-remote-access.conf | 6 +- codes/linux/soft/nexus-install.sh | 22 +++- codes/linux/soft/redis-install.sh | 36 ++++-- docs/README.md | 2 +- docs/coverpage.md | 2 +- docs/linux/soft/README.md | 2 +- .../soft/{nexus-install.md => nexus-ops.md} | 114 +++++++++++++++--- docs/linux/soft/redis-ops.md | 11 +- 9 files changed, 158 insertions(+), 39 deletions(-) rename docs/linux/soft/{nexus-install.md => nexus-ops.md} (56%) diff --git a/README.md b/README.md index 6963a22b..fd536cf8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ - [Maven 安装](docs/linux/soft/maven-install.md) - [Nodejs 安装](docs/linux/soft/nodejs-install.md) - 开发工具 - - [Nexus 运维](docs/linux/soft/nexus-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) diff --git a/codes/linux/soft/config/redis-remote-access.conf b/codes/linux/soft/config/redis-remote-access.conf index 9744238a..e77635a1 100644 --- a/codes/linux/soft/config/redis-remote-access.conf +++ b/codes/linux/soft/config/redis-remote-access.conf @@ -66,7 +66,7 @@ # IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES # JUST COMMENT THE FOLLOWING LINE. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# bind 127.0.0.1 +bind 0.0.0.0 # Protected mode is a layer of security protection, in order to avoid that # Redis instances left open on the internet are accessed and exploited. @@ -499,12 +499,12 @@ replica-priority 100 # # This should stay commented out for backward compatibility and because most # people do not need auth (e.g. they run their own servers). -# +#:q # Warning: since Redis is pretty fast an outside user can try up to # 150k passwords per second against a good box. This means that you should # use a very strong password otherwise it will be very easy to break. # -requirepass 123456 +# requirepass # Command renaming. # diff --git a/codes/linux/soft/nexus-install.sh b/codes/linux/soft/nexus-install.sh index 1ec25401..39d06874 100644 --- a/codes/linux/soft/nexus-install.sh +++ b/codes/linux/soft/nexus-install.sh @@ -1,5 +1,20 @@ #!/usr/bin/env bash +################################################################################### +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +################################################################################### + +printf "${BLUE}" +cat << EOF + ################################################################################### # 安装 sonatype nexus(用于搭建 maven 私服) 脚本 # @system: 适用于所有 linux 发行版本。 @@ -8,7 +23,10 @@ # @author: Zhang Peng ################################################################################### -echo -e "\n>>>>>>>>> install sonatype nexus" +EOF +printf "${RESET}" + +printf "${GREEN}>>>>>>>> install nexus begin.${RESET}\n" mkdir -p /opt/maven cd /opt/maven @@ -16,3 +34,5 @@ cd /opt/maven version=3.13.0-01 curl -o /opt/maven/nexus-unix.tar.gz http://download.sonatype.com/nexus/3/nexus-${version}-unix.tar.gz tar -zxf nexus-unix.tar.gz + +printf "${GREEN}<<<<<<<< install nexus end.${RESET}\n" diff --git a/codes/linux/soft/redis-install.sh b/codes/linux/soft/redis-install.sh index 07c0d430..f1033250 100644 --- a/codes/linux/soft/redis-install.sh +++ b/codes/linux/soft/redis-install.sh @@ -1,5 +1,18 @@ #!/usr/bin/env bash +################################################################################### +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +################################################################################### + +printf "${BLUE}" cat << EOF ################################################################################### @@ -9,6 +22,7 @@ cat << EOF ################################################################################### EOF +printf "${RESET}" command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } @@ -37,37 +51,41 @@ if [[ -n $4 ]]; then password=$4 fi -echo "Current execution: install redis ${version} to ${root}, service port = ${port}, password = ${password}" -echo -e "\n>>>>>>>>> install libs" +printf "${GREEN}>>>>>>>> install redis begin.${RESET}\n" + +printf "\t${GREEN}Current execution: install redis ${version} to ${root}, service port = ${port}, password = ${password}${RESET}\n" yum install -y zlib zlib-devel gcc-c++ libtool openssl openssl-devel tcl -echo -e "\n>>>>>>>>> download redis" mkdir -p ${root} curl -o ${root}/redis-${version}.tar.gz http://download.redis.io/releases/redis-${version}.tar.gz -echo -e "\n>>>>>>>>> install redis" path=${root}/redis-${version} tar zxf ${root}/redis-${version}.tar.gz -C ${root} cd ${path} make && make install cd - -echo -e "\n>>>>>>>>> config redis" +printf "\n${CYAN}>>>>>>>>> config redis${RESET}\n" cp ${path}/redis.conf ${path}/redis.conf.default wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis-remote-access.conf -O ${path}/redis.conf +mkdir -p /etc/redis cp ${path}/redis.conf /etc/redis/${port}.conf sed -i "s/^port 6379/port ${port}/g" /etc/redis/${port}.conf -sed -i "s/^requirepass 123456/requirepass ${password}/g" /etc/redis/${port}.conf +if [[ -n ${password} ]]; then + sed -i "s/^# requirepass/requirepass ${password}/g" /etc/redis/${port}.conf +fi -echo -e "\n>>>>>>>>> add firewall port" +printf "\n${CYAN}>>>>>>>>> add firewall port${RESET}\n" firewall-cmd --zone=public --add-port=${port}/tcp --permanent firewall-cmd --reload -echo -e "\n>>>>>>>>> add redis service" +printf "\n${CYAN}>>>>>>>>> add redis service${RESET}\n" # 注册 redis 服务,并设置开机自启动 -cp ${path}/utils/redis_init_script /etc/init.d/redis_${port} +cp ${path}/utils/redis_init_script /etc/init.d/ +mv /etc/init.d/redis_init_script /etc/init.d/redis_${port} sed -i "s/^REDISPORT=.*/REDISPORT=${port}/g" /etc/init.d/redis_${port} chmod +x /etc/init.d/redis_${port} chkconfig --add redis_${port} service redis_${port} start +printf "\n${GREEN}<<<<<<<< install redis end.${RESET}\n" diff --git a/docs/README.md b/docs/README.md index e41a86b9..88ae81fa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,7 +17,7 @@ - [Maven 安装](linux/soft/maven-install.md) - [Nodejs 安装](linux/soft/nodejs-install.md) - 开发工具 - - [Nexus 运维](linux/soft/nexus-install.md) + - [Nexus 运维](linux/soft/nexus-ops.md) - [Gitlab 运维](linux/soft/kafka-install.md) - [Jenkins 运维](linux/soft/jenkins.md) - [Svn 运维](linux/soft/svn-ops.md) diff --git a/docs/coverpage.md b/docs/coverpage.md index 6a08035d..0de646b9 100644 --- a/docs/coverpage.md +++ b/docs/coverpage.md @@ -1,6 +1,6 @@
-# Linux 教程 +# Linux Tutorial > Linux 教程 diff --git a/docs/linux/soft/README.md b/docs/linux/soft/README.md index 80d3683e..f9232db0 100644 --- a/docs/linux/soft/README.md +++ b/docs/linux/soft/README.md @@ -6,7 +6,7 @@ - [Jenkins 安装](jenkins-install.md) - [Kafka 安装](kafka-install.md) - [Maven 安装](maven-install.md) -- [Nexus 安装](nexus-install.md) +- [Nexus 安装](nexus-ops.md) - [Nodejs 安装](nodejs-install.md) - [RocketMQ 安装](rocketmq-install.md) - [Svn 安装](svn-ops.md) diff --git a/docs/linux/soft/nexus-install.md b/docs/linux/soft/nexus-ops.md similarity index 56% rename from docs/linux/soft/nexus-install.md rename to docs/linux/soft/nexus-ops.md index 5f5337ca..ea31a55e 100644 --- a/docs/linux/soft/nexus-install.md +++ b/docs/linux/soft/nexus-ops.md @@ -1,5 +1,7 @@ -# 部署并使用 Nexus 作为 Maven 私服 +# Nexus 运维 +> Nexus 是一个强大的 Maven 仓库管理器,可以用来搭建 Maven 私服。 +> > 关键词:maven, nexus > > 部署环境 @@ -10,17 +12,22 @@ -- [下载安装 Nexus](#下载安装-nexus) -- [启动停止 Nexus](#启动停止-nexus) -- [使用 Nexus](#使用-nexus) +- [安装 Nexus](#安装-nexus) +- [启动/停止 Nexus](#启动停止-nexus) +- [使用 Nexus 搭建 Maven 私服](#使用-nexus-搭建-maven-私服) + - [配置仓库](#配置仓库) - [配置 settings.xml](#配置-settingsxml) - [配置 pom.xml](#配置-pomxml) - [执行 maven 构建](#执行-maven-构建) +- [将 Nexus 设置为服务](#将-nexus-设置为服务) +- [Nexus 备份和迁移](#nexus-备份和迁移) + - [备份](#备份) + - [迁移](#迁移) - [参考资料](#参考资料) -## 下载安装 Nexus +## 安装 Nexus 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 @@ -40,7 +47,7 @@ tar -zxf nexus-unix.tar.gz - nexus-3.13.0-01 - 包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 - sonatype-work - 包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 -## 启动停止 Nexus +## 启动/停止 Nexus 进入 nexus-3.13.0-01/bin 目录,有一个可执行脚本 nexus。 @@ -52,7 +59,8 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} ``` - 启动 nexus - `./nexus start` -- 停止 nexus - +- 停止 nexus - `./nexus stop` +- 重启 nexus - `./nexus restart` 启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: @@ -60,23 +68,38 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} 点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。 -有必要提一下的是,在 Nexus 的 Repositories 管理页面,展示了可用的 maven 仓库,如下图所示: +## 使用 Nexus 搭建 Maven 私服 -
+### 配置仓库 -> 说明: -> -> - maven-central - maven 中央库(如果没有配置 mirror,默认就从这里下载 jar 包),从 https://repo1.maven.org/maven2/ 获取资源 -> - maven-releases - 存储私有仓库的发行版 jar 包 -> - maven-snapshots - 存储私有仓库的快照版(调试版本) jar 包 -> - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 +Nexus 中的仓库有以下类型: -## 使用 Nexus +- `hosted` - 宿主仓库。主要用于部署无法从公共仓库获取的构件(如 oracle 的 JDBC 驱动)以及自己或第三方的项目构件; +- `proxy` - 代理仓库。代理公共的远程仓库; +- `virtual` - 虚拟仓库。用于适配 Maven 1; +- `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -如果要使用 Nexus,还必须在 settings.xml 和 pom.xml 中配置认证信息。 +
+ +> **最佳实践** +> +> 建议配置如下: +> +> - hosted 仓库 +> - maven-releases - 存储私有仓库的发行版 jar 包 +> - maven-snapshots - 存储私有仓库的快照版(调试版本) jar 包 +> - proxy 仓库 +> - maven-central - maven 中央库(如果没有配置 mirror,默认就从这里下载 jar 包),从 https://repo1.maven.org/maven2/ 获取资源 +> - maven-aliyun - 国内 maven 仓库,提高访问速度。 +> - group 仓库 +> - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 + +
### 配置 settings.xml +如果要使用 Nexus,还必须在 settings.xml 和 pom.xml 中配置认证信息。 + 一份完整的 `settings.xml`: ```xml @@ -186,8 +209,59 @@ $ mvn clean package -Dmaven.skip.test=true -P zp $ mvn clean deploy -Dmaven.skip.test=true -P zp ``` +## 将 Nexus 设置为服务 + +将 Nexus 添加为服务,以便开机自启动。 + +在 `/lib/systemd/system` 目录下创建 `nexus.service` 文件,内容如下: + +```ini +[Unit] +Description=nexus +After=network.target + +[Service] +Type=forking +LimitNOFILE=65536 #警告处理 +Environment=RUN_AS_USER=root +ExecStart=/opt/maven/nexus-3.13.0-01/bin/nexus start +ExecReload=/opt/maven/nexus-3.13.0-01/bin/nexus restart +ExecStop=/opt/maven/nexus-3.13.0-01/bin/nexus stop +Restart=on-failure +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +保存后,可以使用以下命令应用 nexus 服务: + +- `systemctl enable nexus` - 启动 nexus 开机启动 +- `systemctl disable nexus` - 关闭 nexus 开机启动 +- `systemctl start nexus` - 启动 nexus 服务 +- `systemctl stop nexus` - 停止 nexus 服务 +- `systemctl restart nexus` - 重启 nexus 服务 + +## Nexus 备份和迁移 + +Nexus 三个重要目录: + +| 名称 | 目录名 | 重要配置文件 | +| :----------------- | :------------- | :------------------------------------------------ | +| nexus 主目录 | nexus-2.6.4-02 | conf/nexus.properties 里面有 sonatype-work 的地址 | +| sonatype-work 目录 | sonatype-work | nexus/conf/nexus.xml 里面有 storage 的地址 | +| storage 目录 | storage | 里面主要是各种程序的 jar 包等 | + +### 备份 + +Nexus 的数据都存储在 sonatype-work 目录,备份 Nexus 数据只需要将其打包即可。 + +### 迁移 + +将原 Nexus 服务器中的 sonatype-work 目录迁移到新 Nexus 服务器的 sonatype-work 目录下。 + ## 参考资料 -- https://www.cnblogs.com/hoobey/p/6102382.html -- https://blog.csdn.net/wzygis/article/details/49276779 -- https://blog.csdn.net/clj198606061111/article/details/52200928 +- [maven 私库 nexus3 安装及使用](https://blog.csdn.net/clj198606061111/article/details/52200928) +- [Nexus 安装 使用说明](https://www.cnblogs.com/jtlgb/p/7473837.html) +- [企业级开源仓库 nexus3 实战应用–使用 nexus3 配置 yum 私有仓库](http://www.eryajf.net/2002.html) diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md index 77dcb8b5..ec0b6a8c 100644 --- a/docs/linux/soft/redis-ops.md +++ b/docs/linux/soft/redis-ops.md @@ -1,14 +1,18 @@ # Redis 安装 +> **Redis** 是一个高性能的 key-value 数据库。 +> +> SET 操作每秒钟 110000 次;GET 操作每秒钟 81000 次。 + -- [安装](#安装) +- [安装 Redis](#安装-redis) - [启动](#启动) - [脚本](#脚本) -## 安装 +## 安装 Redis 安装步骤如下: @@ -56,3 +60,6 @@ cd /opt/redis/redis-4.0.8/src 以上两种安装方式,我都写了脚本去执行: | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | + +## 参考资料 + From fa9f0b7dcba761a4a88d924f010468fc7bd1a491 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 20 Aug 2019 18:42:40 +0800 Subject: [PATCH 03/64] update docs --- docs/docker/docker.md | 2 +- docs/git/git-flow.md | 4 ++-- docs/git/git-quickstart.md | 8 ++++---- docs/kubernetes/kubernetes.md | 2 +- ...256\345\275\225\347\256\241\347\220\206.md" | 4 ++-- ...277\347\224\250\350\257\246\350\247\243.md" | 2 +- docs/linux/ops/vim.md | 18 +++++++++--------- docs/linux/soft/elastic/elastic-kibana.md | 4 ++-- docs/linux/soft/elastic/elastic-quickstart.md | 2 +- docs/linux/soft/gitlab-install.md | 12 +++++------- docs/linux/soft/jdk-install.md | 4 ++-- docs/linux/soft/nexus-ops.md | 8 ++++---- docs/linux/soft/svn-ops.md | 2 +- docs/linux/soft/yapi-ops.md | 2 +- docs/package.json | 2 +- scripts/gitpush.sh | 2 +- 16 files changed, 38 insertions(+), 40 deletions(-) diff --git a/docs/docker/docker.md b/docs/docker/docker.md index 50d7e2ae..a33063f6 100644 --- a/docs/docker/docker.md +++ b/docs/docker/docker.md @@ -30,7 +30,7 @@ Docker 的主要用途,目前有三大类。 **启动慢** - 启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。 -

+

### Docker 平台 diff --git a/docs/git/git-flow.md b/docs/git/git-flow.md index 604daddc..62a07041 100644 --- a/docs/git/git-flow.md +++ b/docs/git/git-flow.md @@ -50,7 +50,7 @@ Git 是一个非常优秀的版本控制工具,但是在实际版本管理中 下面,来介绍一下 git-flow 模型。 -

+

`Gitflow`工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并`push`分支到要中央仓库中。 @@ -237,7 +237,7 @@ $ wget -q -O - --no-check-certificate https://github.com/nvie/gitflow/raw/develo - **开始一个Hotfix:** git flow hotfix start VERSION [BASENAME] - **发布一个Hotfix:** git flow hotfix finish VERSION -

+

### Source Tree diff --git a/docs/git/git-quickstart.md b/docs/git/git-quickstart.md index 1e19597d..9e7b0878 100644 --- a/docs/git/git-quickstart.md +++ b/docs/git/git-quickstart.md @@ -96,7 +96,7 @@ Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值 同理,如果你想同步别人的修改,你需要从远程仓库拉取更新。 -

+

## 安装 @@ -179,7 +179,7 @@ $ git config --global user.email johndoe@example.com 本节选择性介绍 git 中比较常用的命令行场景。 -

+

### 创建 @@ -447,7 +447,7 @@ HTTPS 这种方式要求你每次 push 时都要输入用户名、密码,有 而 SSH 要求你本地生成证书,然后在你的 Github 账户中注册。第一次配置麻烦是麻烦了点,但是以后就免去了每次 push 需要输入用户名、密码的繁琐。 -

+

以下介绍以下,如何生成证书,以及在 Github 中注册。 @@ -505,7 +505,7 @@ NrRFi9wrf+M7Q== schacon@mylaptop.local 最后,放一张我总结的脑图总结一下以上的知识点。 -

+

## 资料 diff --git a/docs/kubernetes/kubernetes.md b/docs/kubernetes/kubernetes.md index fdee32f1..303c459d 100644 --- a/docs/kubernetes/kubernetes.md +++ b/docs/kubernetes/kubernetes.md @@ -44,7 +44,7 @@ Kubernetes 包含若干抽象用来表示系统状态,包括:已部署的容 - Volume - Namespace -

+

高级对象 diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" index d2899e11..e26b114c 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" @@ -50,7 +50,7 @@ tags: linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -

+

### Linux 文件属性 @@ -77,7 +77,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot 每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 -

+

从左至右用 0-9 这些数字来表示。 diff --git "a/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" "b/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" index cab94cf6..f8ce05ca 100644 --- "a/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" +++ "b/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" @@ -181,7 +181,7 @@ Windows: 访问:`\\<你的ip>\<你的共享路径>` : -

+

Mac: diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 4b658e95..861c10a9 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -167,7 +167,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > > \> 如果你认为单词是由 blank 字符分隔符,那么你需要使用大写的 E 和 W。(注:程序语句) > -

+

下面,让我来说说最强的光标移动: @@ -216,7 +216,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。 > - `3fa` → 在当前行查找第三个出现的 a。 > - `F` 和 `T` → 和 `f` 和 `t` 一样,只不过是相反方向。 -

+

还有一个很有用的命令是 `dt"` → 删除所有的内容,直到遇到双引号—— `"。` @@ -238,7 +238,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `v2i)` → 会选择 `map (+) ("foo")` > - `v2a)` → 会选择 `(map (+) ("foo"))` -

+

#### 块操作: `` @@ -328,33 +328,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -

+

### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -

+

### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -

+

### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -

+

### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -

+

-

+

## 4. 资料 diff --git a/docs/linux/soft/elastic/elastic-kibana.md b/docs/linux/soft/elastic/elastic-kibana.md index cd85e162..66d4ffb9 100644 --- a/docs/linux/soft/elastic/elastic-kibana.md +++ b/docs/linux/soft/elastic/elastic-kibana.md @@ -279,7 +279,7 @@ Visualize工具使您能够以多种方式(如饼图、柱状图、曲线图 3. 在 `Field` 列表中选择 `level.keyword`。 4. 点击 ![images/apply-changes-button.png](https://www.elastic.co/guide/en/kibana/6.1/images/apply-changes-button.png) 按钮来更新图表。 -![image.png](https://upload-images.jianshu.io/upload_images/3101171-7fb2042dc6d59520.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![image.png](https://upload-images.jianshu.io/upload_images/3101171-7fb2042dc6d59520.png) 完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。 @@ -293,7 +293,7 @@ Visualize工具使您能够以多种方式(如饼图、柱状图、曲线图 4. 指定 X 轴所代表的字段 5. 点击 ![images/apply-changes-button.png](https://www.elastic.co/guide/en/kibana/6.1/images/apply-changes-button.png) 按钮来更新图表。 -![image.png](https://upload-images.jianshu.io/upload_images/3101171-5aa7627284c19a56.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![image.png](https://upload-images.jianshu.io/upload_images/3101171-5aa7627284c19a56.png) 完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。 diff --git a/docs/linux/soft/elastic/elastic-quickstart.md b/docs/linux/soft/elastic/elastic-quickstart.md index 31bafd42..35999496 100644 --- a/docs/linux/soft/elastic/elastic-quickstart.md +++ b/docs/linux/soft/elastic/elastic-quickstart.md @@ -274,7 +274,7 @@ output { 大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。 -

+

## 资料 diff --git a/docs/linux/soft/gitlab-install.md b/docs/linux/soft/gitlab-install.md index 6c8fb058..ef0ee848 100644 --- a/docs/linux/soft/gitlab-install.md +++ b/docs/linux/soft/gitlab-install.md @@ -25,7 +25,7 @@ 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 -

+

以 CentOS7 为例: @@ -87,7 +87,7 @@ docker run -d \ gitlab/gitlab-ce ``` -

+

## 安装 gitlab-ci-multi-runner @@ -138,7 +138,7 @@ sudo gitlab-runner register URL 和令牌信息在 Gitlab 的 Runner 管理页面获取: -

+

``` Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) @@ -322,11 +322,11 @@ sudo gitlab-ctl restart ### 创建项目 -

+

输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。 -

+

### 克隆项目到本地 @@ -334,8 +334,6 @@ sudo gitlab-ctl restart 拷贝项目地址,然后在本地执行 `git clone ` -

- ### 创建 Issue 依次点击 **Project’s Dashboard** > **Issues** > **New Issue** 可以新建 Issue diff --git a/docs/linux/soft/jdk-install.md b/docs/linux/soft/jdk-install.md index bd921cbe..b881d0f7 100644 --- a/docs/linux/soft/jdk-install.md +++ b/docs/linux/soft/jdk-install.md @@ -22,13 +22,13 @@ a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/java b. 选择需要的版本: -

+

c. 选择对应操作系统的安装包: Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。 -

+

(2)运行安装包,按提示逐步安装 diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index ea31a55e..598b4fc5 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -31,7 +31,7 @@ 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 -
+
本人希望将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 @@ -64,7 +64,7 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} 启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: -
+
点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。 @@ -79,7 +79,7 @@ Nexus 中的仓库有以下类型: - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -
+
> **最佳实践** > @@ -94,7 +94,7 @@ Nexus 中的仓库有以下类型: > - group 仓库 > - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 -
+
### 配置 settings.xml diff --git a/docs/linux/soft/svn-ops.md b/docs/linux/soft/svn-ops.md index fd83dbcb..726971b3 100644 --- a/docs/linux/soft/svn-ops.md +++ b/docs/linux/soft/svn-ops.md @@ -145,7 +145,7 @@ $ vi /etc/sysconfig/svnserve 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: -

+

## 2. 参考资料 diff --git a/docs/linux/soft/yapi-ops.md b/docs/linux/soft/yapi-ops.md index 4276c0e7..6da62055 100644 --- a/docs/linux/soft/yapi-ops.md +++ b/docs/linux/soft/yapi-ops.md @@ -4,7 +4,7 @@ > > 本文目的在于记录 svn 的安装、配置、使用。 -
+
diff --git a/docs/package.json b/docs/package.json index 317614bb..6a82d70f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,7 +2,7 @@ "name": "linux-tutorial", "version": "1.0.0", "scripts": { - "start": "docsify serve ./" + "start": "docsify serve ./ --port 4000" }, "dependencies": {}, "devDependencies": {} diff --git a/scripts/gitpush.sh b/scripts/gitpush.sh index ec14d5a9..6caf7c88 100644 --- a/scripts/gitpush.sh +++ b/scripts/gitpush.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash cd .. -git push github master +git push origin master git push gitee master From 15ab97fc817fe8346c39785f0640669531bfe6d0 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 21 Aug 2019 13:23:30 +0800 Subject: [PATCH 04/64] update images --- docs/coverpage.md | 2 +- docs/docker/docker.md | 2 +- docs/git/git-flow.md | 4 +- docs/git/git-quickstart.md | 4 +- docs/kubernetes/kubernetes.md | 2 +- ...56\345\275\225\347\256\241\347\220\206.md" | 2 +- docs/linux/ops/vim.md | 12 +- docs/linux/soft/nexus-ops.md | 2 +- docs/linux/soft/redis-ops.md | 103 ++++++++++++++---- 9 files changed, 99 insertions(+), 34 deletions(-) diff --git a/docs/coverpage.md b/docs/coverpage.md index 0de646b9..8f6b1e3c 100644 --- a/docs/coverpage.md +++ b/docs/coverpage.md @@ -1,4 +1,4 @@ -
+
# Linux Tutorial diff --git a/docs/docker/docker.md b/docs/docker/docker.md index a33063f6..36d5f53d 100644 --- a/docs/docker/docker.md +++ b/docs/docker/docker.md @@ -30,7 +30,7 @@ Docker 的主要用途,目前有三大类。 **启动慢** - 启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。 -

+

### Docker 平台 diff --git a/docs/git/git-flow.md b/docs/git/git-flow.md index 62a07041..d494ee22 100644 --- a/docs/git/git-flow.md +++ b/docs/git/git-flow.md @@ -50,7 +50,7 @@ Git 是一个非常优秀的版本控制工具,但是在实际版本管理中 下面,来介绍一下 git-flow 模型。 -

+

`Gitflow`工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并`push`分支到要中央仓库中。 @@ -237,7 +237,7 @@ $ wget -q -O - --no-check-certificate https://github.com/nvie/gitflow/raw/develo - **开始一个Hotfix:** git flow hotfix start VERSION [BASENAME] - **发布一个Hotfix:** git flow hotfix finish VERSION -

+

### Source Tree diff --git a/docs/git/git-quickstart.md b/docs/git/git-quickstart.md index 9e7b0878..f35e677f 100644 --- a/docs/git/git-quickstart.md +++ b/docs/git/git-quickstart.md @@ -96,7 +96,7 @@ Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值 同理,如果你想同步别人的修改,你需要从远程仓库拉取更新。 -

+

## 安装 @@ -179,7 +179,7 @@ $ git config --global user.email johndoe@example.com 本节选择性介绍 git 中比较常用的命令行场景。 -

+

### 创建 diff --git a/docs/kubernetes/kubernetes.md b/docs/kubernetes/kubernetes.md index 303c459d..c1191702 100644 --- a/docs/kubernetes/kubernetes.md +++ b/docs/kubernetes/kubernetes.md @@ -44,7 +44,7 @@ Kubernetes 包含若干抽象用来表示系统状态,包括:已部署的容 - Volume - Namespace -

+

高级对象 diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" index e26b114c..5431a4de 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" @@ -50,7 +50,7 @@ tags: linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -

+

### Linux 文件属性 diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 861c10a9..046fc472 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -328,33 +328,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -

+

### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -

+

### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -

+

### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -

+

### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -

+

-

+

## 4. 资料 diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 598b4fc5..466416da 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -79,7 +79,7 @@ Nexus 中的仓库有以下类型: - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -
+
> **最佳实践** > diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md index ec0b6a8c..c331ad59 100644 --- a/docs/linux/soft/redis-ops.md +++ b/docs/linux/soft/redis-ops.md @@ -6,40 +6,87 @@ -- [安装 Redis](#安装-redis) -- [启动](#启动) +- [安装](#安装) + - [Window 下安装](#window-下安装) + - [Linux 下安装](#linux-下安装) + - [Ubuntu 下安装](#ubuntu-下安装) + - [启动 Redis](#启动-redis) + - [查看 redis 是否启动?](#查看-redis-是否启动) - [脚本](#脚本) -## 安装 Redis +## 安装 -安装步骤如下: +### Window 下安装 -(1)下载并解压到本地 +**下载地址:**。 -进入官网下载地址:https://redis.io/download ,选择合适的版本下载。 +Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 **Redis-x64-xxx.zip**压缩包到 C 盘,解压后,将文件夹重新命名为 **redis**。 -我选择的是最新稳定版本 4.0.8:http://download.redis.io/releases/redis-4.0.8.tar.gz +打开一个 **cmd** 窗口 使用 cd 命令切换目录到 **C:\redis** 运行 **redis-server.exe redis.windows.conf** 。 -我个人喜欢存放在:`/opt/redis` +如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面: + +这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。 + +切换到 redis 目录下运行 **redis-cli.exe -h 127.0.0.1 -p 6379** 。 + +设置键值对 **set myKey abc** + +取出键值对 **get myKey** + +### Linux 下安装 + +**下载地址:** http://redis.io/download,下载最新文档版本。 + +本教程使用的最新文档版本为 2.8.17,下载并安装: + +``` +$ wget -O /opt/redis/redis-4.0.8.tar.gz http://download.redis.io/releases/redis-4.0.8.tar.gz +$ cd /opt/redis +$ tar zxvf redis-4.0.8.tar.gz +``` + +make 完后 redis-2.8.17 目录下会出现编译后的 redis 服务程序 redis-server,还有用于测试的客户端程序 redis-cli,两个程序位于安装目录 src 目录下: + +下面启动 redis 服务. + +``` +$ cd src +$ ./redis-server +``` + +注意这种方式启动 redis 使用的是默认配置。也可以通过启动参数告诉 redis 使用指定配置文件使用下面命令启动。 + +``` +$ cd src +$ ./redis-server redis.conf +``` + +redis.conf 是一个默认的配置文件。我们可以根据需要使用自己的配置文件。 + +启动 redis 服务进程后,就可以使用测试客户端程序 redis-cli 和 redis 服务交互了。 比如: ``` -wget -O /opt/redis/redis-4.0.8.tar.gz http://download.redis.io/releases/redis-4.0.8.tar.gz -cd /opt/redis -tar zxvf redis-4.0.8.tar.gz +$ cd src +$ ./redis-cli +redis> set foo bar +OK +redis> get foo +"bar" ``` -(2)编译安装 +### Ubuntu 下安装 -执行以下命令: +在 Ubuntu 系统安装 Redi 可以使用以下命令: ``` -cd /opt/redis/redis-4.0.8 -make +$sudo apt-get update +$sudo apt-get install redis-server ``` -## 启动 +### 启动 Redis **启动 redis 服务** @@ -55,11 +102,29 @@ cd /opt/redis/redis-4.0.8/src ./redis-cli ``` +### 查看 redis 是否启动? + +``` +$ redis-cli +``` + +以上命令将打开以下终端: + +``` +redis 127.0.0.1:6379> +``` + +127.0.0.1 是本机 IP ,6379 是 redis 服务端口。现在我们输入 PING 命令。 + +``` +redis 127.0.0.1:6379> ping +PONG +``` + +以上说明我们已经成功安装了 redis。 + ## 脚本 以上两种安装方式,我都写了脚本去执行: | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | - -## 参考资料 - From 4a135a175f6ee5ef8375cf1652353b07f3c9b370 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 21 Aug 2019 14:18:55 +0800 Subject: [PATCH 05/64] :fire: delete --- scripts/gitpush.sh | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 scripts/gitpush.sh diff --git a/scripts/gitpush.sh b/scripts/gitpush.sh deleted file mode 100644 index 6caf7c88..00000000 --- a/scripts/gitpush.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -cd .. -git push origin master -git push gitee master From d336b79af1674b0773ae813773669776161667d6 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 8 Oct 2019 21:28:44 +0800 Subject: [PATCH 06/64] update docs --- README.md | 7 +- docs/README.md | 7 +- docs/linux/ops/systemd.md | 966 +++++++++++++++++++++++++++++++++++ docs/linux/soft/mysql-ops.md | 2 +- docs/linux/soft/redis-ops.md | 196 +++++-- 5 files changed, 1137 insertions(+), 41 deletions(-) create mode 100644 docs/linux/ops/systemd.md diff --git a/README.md b/README.md index fd536cf8..e23af504 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ > :keyboard: 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > -> :book: [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) +> 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) | :wrench: | :shell: | :memo: | :books: | | :-------------------: | :-------------------: | :---------------: | :-------------------: | -| [软件运维](#软件运维) | [运维脚本](#运维脚本) | [知识点](#知识点) | [学习资源](#学习资源) | +| [软件运维](#软件运维) | [运维和脚本](#运维和脚本) | [知识点](#知识点) | [学习资源](#学习资源) | ## 软件运维 @@ -35,7 +35,7 @@ - [Mongodb 运维](docs/linux/soft/mongodb-ops.md) - [Redis 运维](docs/linux/soft/redis-ops.md) -## 运维脚本 +## 运维和脚本 - [系统运维脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) - [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) @@ -44,6 +44,7 @@ - [Zsh 应用指南](docs/linux/ops/zsh.md) - [Shell 教程](docs/linux/ops/shell.md) - [Python 教程](docs/linux/ops/python.md) +- [Systemd 入门教程](docs/linux/ops/systemd.md) > 提供一键式运维、配置软件脚本 diff --git a/docs/README.md b/docs/README.md index 88ae81fa..8b2f574a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,11 +2,11 @@ > :keyboard: 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > -> :book: [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) +> 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) | :wrench: | :shell: | :memo: | :books: | | :-------------------: | :-------------------: | :---------------: | :-------------------: | -| [软件运维](#软件运维) | [运维脚本](#运维脚本) | [知识点](#知识点) | [学习资源](#学习资源) | +| [软件运维](#软件运维) | [运维和脚本](#运维和脚本) | [知识点](#知识点) | [学习资源](#学习资源) | ## 软件运维 @@ -35,7 +35,7 @@ - [Mongodb 运维](linux/soft/mongodb-ops.md) - [Redis 运维](linux/soft/redis-ops.md) -## 运维脚本 +## 运维和脚本 - [系统运维脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) - [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) @@ -44,6 +44,7 @@ - [Zsh 应用指南](linux/ops/zsh.md) - [Shell 教程](linux/ops/shell.md) - [Python 教程](linux/ops/python.md) +- [Systemd 入门教程](linux/ops/systemd.md) > 提供一键式运维、配置软件脚本 diff --git a/docs/linux/ops/systemd.md b/docs/linux/ops/systemd.md new file mode 100644 index 00000000..2e05155b --- /dev/null +++ b/docs/linux/ops/systemd.md @@ -0,0 +1,966 @@ +# Systemd 入门教程 + +> 搬运自:[Systemd 入门教程:命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html)、[Systemd 入门教程:实战篇](hhttp://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) + +Systemd 是 Linux 系统工具,用来启动[守护进程](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html),已成为大多数发行版的标准配置。 + +本文介绍它的基本用法,分为上下两篇。今天介绍它的主要命令,[下一篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html)介绍如何用于实战。 + +## 由来 + +历史上,[Linux 的启动](http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html)一直采用[`init`](https://en.wikipedia.org/wiki/Init)进程。 + +下面的命令用来启动服务。 + +```bash +$ sudo /etc/init.d/apache2 start +# 或者 +$ service apache2 start +``` + +这种方法有两个缺点。 + +一是启动时间长。`init`进程是串行启动,只有前一个进程启动完,才会启动下一个进程。 + +二是启动脚本复杂。`init`进程只是执行启动脚本,不管其他事情。脚本需要自己处理各种 +情况,这往往使得脚本变得很长。 + +## Systemd 概述 + +Systemd 就是为了解决这些问题而诞生的。它的设计目标是,为系统的启动和管理提供一套 +完整的解决方案。 + +根据 Linux 惯例,字母`d`是守护进程(daemon)的缩写。 Systemd 这个名字的含义,就 +是它要守护整个系统。 + +使用了 Systemd,就不需要再用`init`了。Systemd 取代了`initd`,成为系统的第一个进 +程(PID 等于 1),其他进程都是它的子进程。 + +```bash +$ systemctl --version +``` + +上面的命令查看 Systemd 的版本。 + +Systemd 的优点是功能强大,使用方便,缺点是体系庞大,非常复杂。事实上,现在还有很 +多人反对使用 Systemd,理由就是它过于复杂,与操作系统的其他部分强耦合,违反"keep +simple, keep stupid" +的[Unix 哲学](http://www.ruanyifeng.com/blog/2009/06/unix_philosophy.html)。 + +![img](http://www.ruanyifeng.com/blogimg/asset/2016/bg2016030703.png) + +(上图为 Systemd 架构图) + +## 系统管理 + +Systemd 并不是一个命令,而是一组命令,涉及到系统管理的方方面面。 + +### systemctl + +`systemctl`是 Systemd 的主命令,用于管理系统。 + +```bash +# 重启系统 +$ sudo systemctl reboot + +# 关闭系统,切断电源 +$ sudo systemctl poweroff + +# CPU停止工作 +$ sudo systemctl halt + +# 暂停系统 +$ sudo systemctl suspend + +# 让系统进入冬眠状态 +$ sudo systemctl hibernate + +# 让系统进入交互式休眠状态 +$ sudo systemctl hybrid-sleep + +# 启动进入救援状态(单用户状态) +$ sudo systemctl rescue +``` + +### systemd-analyze + +`systemd-analyze`命令用于查看启动耗时。 + +```bash +# 查看启动耗时 +$ systemd-analyze + +# 查看每个服务的启动耗时 +$ systemd-analyze blame + +# 显示瀑布状的启动过程流 +$ systemd-analyze critical-chain + +# 显示指定服务的启动流 +$ systemd-analyze critical-chain atd.service +``` + +### hostnamectl + +`hostnamectl`命令用于查看当前主机的信息。 + +```bash +# 显示当前主机的信息 +$ hostnamectl + +# 设置主机名。 +$ sudo hostnamectl set-hostname rhel7 +``` + +### localectl + +`localectl`命令用于查看本地化设置。 + +```bash +# 查看本地化设置 +$ localectl + +# 设置本地化参数。 +$ sudo localectl set-locale LANG=en_GB.utf8 +$ sudo localectl set-keymap en_GB +``` + +### timedatectl + +`timedatectl`命令用于查看当前时区设置。 + +```bash +# 查看当前时区设置 +$ timedatectl + +# 显示所有可用的时区 +$ timedatectl list-timezones + +# 设置当前时区 +$ sudo timedatectl set-timezone America/New_York +$ sudo timedatectl set-time YYYY-MM-DD +$ sudo timedatectl set-time HH:MM:SS +``` + +### loginctl + +`loginctl`命令用于查看当前登录的用户。 + +```bash +# 列出当前session +$ loginctl list-sessions + +# 列出当前登录用户 +$ loginctl list-users + +# 列出显示指定用户的信息 +$ loginctl show-user ruanyf +``` + +## Unit + +### 含义 + +Systemd 可以管理所有系统资源。不同的资源统称为 Unit(单位)。 + +Unit 一共分成 12 种。 + +- Service unit:系统服务 +- Target unit:多个 Unit 构成的一个组 +- Device Unit:硬件设备 +- Mount Unit:文件系统的挂载点 +- Automount Unit:自动挂载点 +- Path Unit:文件或路径 +- Scope Unit:不是由 Systemd 启动的外部进程 +- Slice Unit:进程组 +- Snapshot Unit:Systemd 快照,可以切回某个快照 +- Socket Unit:进程间通信的 socket +- Swap Unit:swap 文件 +- Timer Unit:定时器 + +`systemctl list-units`命令可以查看当前系统的所有 Unit 。 + +```bash +# 列出正在运行的 Unit +$ systemctl list-units + +# 列出所有Unit,包括没有找到配置文件的或者启动失败的 +$ systemctl list-units --all + +# 列出所有没有运行的 Unit +$ systemctl list-units --all --state=inactive + +# 列出所有加载失败的 Unit +$ systemctl list-units --failed + +# 列出所有正在运行的、类型为 service 的 Unit +$ systemctl list-units --type=service +``` + +### Unit 的状态 + +`systemctl status`命令用于查看系统状态和单个 Unit 的状态。 + +```bash +# 显示系统状态 +$ systemctl status + +# 显示单个 Unit 的状态 +$ sysystemctl status bluetooth.service + +# 显示远程主机的某个 Unit 的状态 +$ systemctl -H root@rhel7.example.com status httpd.service +``` + +除了`status`命令,`systemctl`还提供了三个查询状态的简单方法,主要供脚本内部的判 +断语句使用。 + +```bash +# 显示某个 Unit 是否正在运行 +$ systemctl is-active application.service + +# 显示某个 Unit 是否处于启动失败状态 +$ systemctl is-failed application.service + +# 显示某个 Unit 服务是否建立了启动链接 +$ systemctl is-enabled application.service +``` + +### Unit 管理 + +对于用户来说,最常用的是下面这些命令,用于启动和停止 Unit(主要是 service)。 + +```bash +# 立即启动一个服务 +$ sudo systemctl start apache.service + +# 立即停止一个服务 +$ sudo systemctl stop apache.service + +# 重启一个服务 +$ sudo systemctl restart apache.service + +# 杀死一个服务的所有子进程 +$ sudo systemctl kill apache.service + +# 重新加载一个服务的配置文件 +$ sudo systemctl reload apache.service + +# 重载所有修改过的配置文件 +$ sudo systemctl daemon-reload + +# 显示某个 Unit 的所有底层参数 +$ systemctl show httpd.service + +# 显示某个 Unit 的指定属性的值 +$ systemctl show -p CPUShares httpd.service + +# 设置某个 Unit 的指定属性 +$ sudo systemctl set-property httpd.service CPUShares=500 +``` + +### 依赖关系 + +Unit 之间存在依赖关系:A 依赖于 B,就意味着 Systemd 在启动 A 的时候,同时会去启 +动 B。 + +`systemctl list-dependencies`命令列出一个 Unit 的所有依赖。 + +```bash +$ systemctl list-dependencies nginx.service +``` + +上面命令的输出结果之中,有些依赖是 Target 类型(详见下文),默认不会展开显示。如 +果要展开 Target,就需要使用`--all`参数。 + +```bash +$ systemctl list-dependencies --all nginx.service +``` + +## Unit 的配置文件 + +### 概述 + +每一个 Unit 都有一个配置文件,告诉 Systemd 怎么启动这个 Unit 。 + +Systemd 默认从目录`/etc/systemd/system/`读取配置文件。但是,里面存放的大部分文件 +都是符号链接,指向目录`/usr/lib/systemd/system/`,真正的配置文件存放在那个目录。 + +`systemctl enable`命令用于在上面两个目录之间,建立符号链接关系。 + +```bash +$ sudo systemctl enable clamd@scan.service +# 等同于 +$ sudo ln -s '/usr/lib/systemd/system/clamd@scan.service' '/etc/systemd/system/multi-user.target.wants/clamd@scan.service' +``` + +如果配置文件里面设置了开机启动,`systemctl enable`命令相当于激活开机启动。 + +与之对应的,`systemctl disable`命令用于在两个目录之间,撤销符号链接关系,相当于 +撤销开机启动。 + +```bash +$ sudo systemctl disable clamd@scan.service +``` + +配置文件的后缀名,就是该 Unit 的种类,比如`sshd.socket`。如果省略,Systemd 默认 +后缀名为`.service`,所以`sshd`会被理解成`sshd.service`。 + +### 配置文件的状态 + +`systemctl list-unit-files`命令用于列出所有配置文件。 + +```bash +# 列出所有配置文件 +$ systemctl list-unit-files + +# 列出指定类型的配置文件 +$ systemctl list-unit-files --type=service +``` + +这个命令会输出一个列表。 + +```bash +$ systemctl list-unit-files + +UNIT FILE STATE +chronyd.service enabled +clamd@.service static +clamd@scan.service disabled +``` + +这个列表显示每个配置文件的状态,一共有四种。 + +- enabled:已建立启动链接 +- disabled:没建立启动链接 +- static:该配置文件没有`[Install]`部分(无法执行),只能作为其他配置文件的依赖 +- masked:该配置文件被禁止建立启动链接 + +注意,从配置文件的状态无法看出,该 Unit 是否正在运行。这必须执行前面提到 +的`systemctl status`命令。 + +```bash +$ systemctl status bluetooth.service +``` + +一旦修改配置文件,就要让 SystemD 重新加载配置文件,然后重新启动,否则修改不会生 +效。 + +```bash +$ sudo systemctl daemon-reload +$ sudo systemctl restart httpd.service +``` + +### 配置文件的格式 + +配置文件就是普通的文本文件,可以用文本编辑器打开。 + +`systemctl cat`命令可以查看配置文件的内容。 + +```bash +$ systemctl cat atd.service + +[Unit] +Description=ATD daemon + +[Service] +Type=forking +ExecStart=/usr/bin/atd + +[Install] +WantedBy=multi-user.target +``` + +从上面的输出可以看到,配置文件分成几个区块。每个区块的第一行,是用方括号表示的区 +别名,比如`[Unit]`。注意,配置文件的区块名和字段名,都是大小写敏感的。 + +每个区块内部是一些等号连接的键值对。 + +```bash +[Section] +Directive1=value +Directive2=value + +. . . +``` + +注意,键值对的等号两侧不能有空格。 + +### 配置文件的区块 + +`[Unit]`区块通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 +Unit 的关系。它的主要字段如下。 + +- `Description`:简短描述 +- `Documentation`:文档地址 +- `Requires`:当前 Unit 依赖的其他 Unit,如果它们没有运行,当前 Unit 会启动失败 +- `Wants`:与当前 Unit 配合的其他 Unit,如果它们没有运行,当前 Unit 不会启动失败 +- `BindsTo`:与`Requires`类似,它指定的 Unit 如果退出,会导致当前 Unit 停止运行 +- `Before`:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之后启动 +- `After`:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动 +- `Conflicts`:这里指定的 Unit 不能与当前 Unit 同时运行 +- `Condition...`:当前 Unit 运行必须满足的条件,否则不会运行 +- `Assert...`:当前 Unit 运行必须满足的条件,否则会报启动失败 + +`[Install]`通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。它 +的主要字段如下。 + +- `WantedBy`:它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放 + 入`/etc/systemd/system`目录下面以 Target 名 + `.wants`后缀构成的子目录中 +- `RequiredBy`:它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放 + 入`/etc/systemd/system`目录下面以 Target 名 + `.required`后缀构成的子目录中 +- `Alias`:当前 Unit 可用于启动的别名 +- `Also`:当前 Unit 激活(enable)时,会被同时激活的其他 Unit + +`[Service]`区块用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。它的 +主要字段如下。 + +- `Type`:定义启动时的进程行为。它有以下几种值。 +- `Type=simple`:默认值,执行`ExecStart`指定的命令,启动主进程 +- `Type=forking`:以 fork 方式从父进程创建子进程,创建后父进程会立即退出 +- `Type=oneshot`:一次性进程,Systemd 会等当前服务退出,再继续往下执行 +- `Type=dbus`:当前服务通过 D-Bus 启动 +- `Type=notify`:当前服务启动完毕,会通知`Systemd`,再继续往下执行 +- `Type=idle`:若有其他任务执行完毕,当前服务才会运行 +- `ExecStart`:启动当前服务的命令 +- `ExecStartPre`:启动当前服务之前执行的命令 +- `ExecStartPost`:启动当前服务之后执行的命令 +- `ExecReload`:重启当前服务时执行的命令 +- `ExecStop`:停止当前服务时执行的命令 +- `ExecStopPost`:停止当其服务之后执行的命令 +- `RestartSec`:自动重启当前服务间隔的秒数 +- `Restart`:定义何种情况 Systemd 会自动重启当前服务,可能的值包括`always`(总是 + 重启)、`on-success`、`on-failure`、`on-abnormal`、`on-abort`、`on-watchdog` +- `TimeoutSec`:定义 Systemd 停止当前服务之前等待的秒数 +- `Environment`:指定环境变量 + +Unit 配置文件的完整字段清单,请参 +考[官方文档](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)。 + +## Target + +启动计算机的时候,需要启动大量的 Unit。如果每一次启动,都要一一写明本次启动需要 +哪些 Unit,显然非常不方便。Systemd 的解决方案就是 Target。 + +简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候 +,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于"状态点 +",启动某个 Target 就好比启动到某种状态。 + +传统的`init`启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是 +,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动 +。 + +```bash +# 查看当前系统的所有 Target +$ systemctl list-unit-files --type=target + +# 查看一个 Target 包含的所有 Unit +$ systemctl list-dependencies multi-user.target + +# 查看启动时的默认 Target +$ systemctl get-default + +# 设置启动时的默认 Target +$ sudo systemctl set-default multi-user.target + +# 切换 Target 时,默认不关闭前一个 Target 启动的进程, +# systemctl isolate 命令改变这种行为, +# 关闭前一个 Target 里面所有不属于后一个 Target 的进程 +$ sudo systemctl isolate multi-user.target +``` + +Target 与 传统 RunLevel 的对应关系如下。 + +```bash +Traditional runlevel New target name Symbolically linked to... + +Runlevel 0 | runlevel0.target -> poweroff.target +Runlevel 1 | runlevel1.target -> rescue.target +Runlevel 2 | runlevel2.target -> multi-user.target +Runlevel 3 | runlevel3.target -> multi-user.target +Runlevel 4 | runlevel4.target -> multi-user.target +Runlevel 5 | runlevel5.target -> graphical.target +Runlevel 6 | runlevel6.target -> reboot.target +``` + +它与`init`进程的主要差别如下。 + +**(1)默认的 RunLevel**(在`/etc/inittab`文件设置)现在被默认的 Target 取代, +位置是`/etc/systemd/system/default.target`,通常符号链接到`graphical.target`( +图形界面)或者`multi-user.target`(多用户命令行)。 + +**(2)启动脚本的位置**,以前是`/etc/init.d`目录,符号链接到不同的 RunLevel 目 +录 (比如`/etc/rc3.d`、`/etc/rc5.d`等),现在则存放 +在`/lib/systemd/system`和`/etc/systemd/system`目录。 + +**(3)配置文件的位置**,以前`init`进程的配置文件是`/etc/inittab`,各种服务的 +配置文件存放在`/etc/sysconfig`目录。现在的配置文件主要存放在`/lib/systemd`目录 +,在`/etc/systemd`目录里面的修改可以覆盖原始设置。 + +## 日志管理 + +Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用`journalctl`一个命 +令,查看所有日志(内核日志和应用日志)。日志的配置文件 +是`/etc/systemd/journald.conf`。 + +`journalctl`功能强大,用法非常多。 + +```bash +# 查看所有日志(默认情况下 ,只保存本次启动的日志) +$ sudo journalctl + +# 查看内核日志(不显示应用日志) +$ sudo journalctl -k + +# 查看系统本次启动的日志 +$ sudo journalctl -b +$ sudo journalctl -b -0 + +# 查看上一次启动的日志(需更改设置) +$ sudo journalctl -b -1 + +# 查看指定时间的日志 +$ sudo journalctl --since="2012-10-30 18:17:16" +$ sudo journalctl --since "20 min ago" +$ sudo journalctl --since yesterday +$ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00" +$ sudo journalctl --since 09:00 --until "1 hour ago" + +# 显示尾部的最新10行日志 +$ sudo journalctl -n + +# 显示尾部指定行数的日志 +$ sudo journalctl -n 20 + +# 实时滚动显示最新日志 +$ sudo journalctl -f + +# 查看指定服务的日志 +$ sudo journalctl /usr/lib/systemd/systemd + +# 查看指定进程的日志 +$ sudo journalctl _PID=1 + +# 查看某个路径的脚本的日志 +$ sudo journalctl /usr/bin/bash + +# 查看指定用户的日志 +$ sudo journalctl _UID=33 --since today + +# 查看某个 Unit 的日志 +$ sudo journalctl -u nginx.service +$ sudo journalctl -u nginx.service --since today + +# 实时滚动显示某个 Unit 的最新日志 +$ sudo journalctl -u nginx.service -f + +# 合并显示多个 Unit 的日志 +$ journalctl -u nginx.service -u php-fpm.service --since today + +# 查看指定优先级(及其以上级别)的日志,共有8级 +# 0: emerg +# 1: alert +# 2: crit +# 3: err +# 4: warning +# 5: notice +# 6: info +# 7: debug +$ sudo journalctl -p err -b + +# 日志默认分页输出,--no-pager 改为正常的标准输出 +$ sudo journalctl --no-pager + +# 以 JSON 格式(单行)输出 +$ sudo journalctl -b -u nginx.service -o json + +# 以 JSON 格式(多行)输出,可读性更好 +$ sudo journalctl -b -u nginx.serviceqq + -o json-pretty + +# 显示日志占据的硬盘空间 +$ sudo journalctl --disk-usage + +# 指定日志文件占据的最大空间 +$ sudo journalctl --vacuum-size=1G + +# 指定日志文件保存多久 +$ sudo journalctl --vacuum-time=1years +``` + +## 实战 + +### 开机启动 + +对于那些支持 Systemd 的软件,安装的时候,会自动在`/usr/lib/systemd/system`目录添 +加一个配置文件。 + +如果你想让该软件开机启动,就执行下面的命令(以`httpd.service`为例)。 + +``` +$ sudo systemctl enable httpd +``` + +上面的命令相当于在`/etc/systemd/system`目录添加一个符号链接,指 +向`/usr/lib/systemd/system`里面的`httpd.service`文件。 + +这是因为开机时,`Systemd`只执行`/etc/systemd/system`目录里面的配置文件。这也意味 +着,如果把修改后的配置文件放在该目录,就可以达到覆盖原始配置的效果。 + +### 启动服务 + +设置开机启动以后,软件并不会立即启动,必须等到下一次开机。如果想现在就运行该软件 +,那么要执行`systemctl start`命令。 + +``` +$ sudo systemctl start httpd +``` + +执行上面的命令以后,有可能启动失败,因此要用`systemctl status`命令查看一下该服务 +的状态。 + +``` +$ sudo systemctl status httpd + +httpd.service - The Apache HTTP Server +Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled) +Active: active (running) since 金 2014-12-05 12:18:22 JST; 7min ago +Main PID: 4349 (httpd) +Status: "Total requests: 1; Current requests/sec: 0; Current traffic: 0 B/sec" +CGroup: /system.slice/httpd.service + ├─4349 /usr/sbin/httpd -DFOREGROUND + ├─4350 /usr/sbin/httpd -DFOREGROUND + ├─4351 /usr/sbin/httpd -DFOREGROUND + ├─4352 /usr/sbin/httpd -DFOREGROUND + ├─4353 /usr/sbin/httpd -DFOREGROUND + └─4354 /usr/sbin/httpd -DFOREGROUND + +12月 05 12:18:22 localhost.localdomain systemd[1]: Starting The Apache HTTP Server... +12月 05 12:18:22 localhost.localdomain systemd[1]: Started The Apache HTTP Server. +12月 05 12:22:40 localhost.localdomain systemd[1]: Started The Apache HTTP Server. +``` + +上面的输出结果含义如下。 + +- `Loaded`行:配置文件的位置,是否设为开机启动 +- `Active`行:表示正在运行 +- `Main PID`行:主进程 ID +- `Status`行:由应用本身(这里是 httpd )提供的软件当前状态 +- `CGroup`块:应用的所有子进程 +- 日志块:应用的日志 + +### 停止服务 + +终止正在运行的服务,需要执行`systemctl stop`命令。 + +``` +$ sudo systemctl stop httpd.service +``` + +有时候,该命令可能没有响应,服务停不下来。这时候就不得不"杀进程"了,向正在运行的 +进程发出`kill`信号。 + +``` +$ sudo systemctl kill httpd.service +``` + +此外,重启服务要执行`systemctl restart`命令。 + +``` +$ sudo systemctl restart httpd.service +``` + +### 读懂配置文件 + +一个服务怎么启动,完全由它的配置文件决定。下面就来看,配置文件有些什么内容。 + +前面说过,配置文件主要放在`/usr/lib/systemd/system`目录,也可能 +在`/etc/systemd/system`目录。找到配置文件以后,使用文本编辑器打开即可。 + +`systemctl cat`命令可以用来查看配置文件,下面以`sshd.service`文件为例,它的作用 +是启动一个 SSH 服务器,供其他用户以 SSH 方式登录。 + +``` +$ systemctl cat sshd.service + +[Unit] +Description=OpenSSH server daemon +Documentation=man:sshd(8) man:sshd_config(5) +After=network.target sshd-keygen.service +Wants=sshd-keygen.service + +[Service] +EnvironmentFile=/etc/sysconfig/sshd +ExecStart=/usr/sbin/sshd -D $OPTIONS +ExecReload=/bin/kill -HUP $MAINPID +Type=simple +KillMode=process +Restart=on-failure +RestartSec=42s + +[Install] +WantedBy=multi-user.target +``` + +可以看到,配置文件分成几个区块,每个区块包含若干条键值对。 + +下面依次解释每个区块的内容。 + +### [Unit] 区块:启动顺序与依赖关系。 + +`Unit`区块的`Description`字段给出当前服务的简单描述,`Documentation`字段给出文档 +位置。 + +接下来的设置是启动顺序和依赖关系,这个比较重要。 + +> `After`字段:表示如果`network.target`或`sshd-keygen.service`需要启动,那 +> 么`sshd.service`应该在它们之后启动。 + +相应地,还有一个`Before`字段,定义`sshd.service`应该在哪些服务之前启动。 + +注意,`After`和`Before`字段只涉及启动顺序,不涉及依赖关系。 + +举例来说,某 Web 应用需要 postgresql 数据库储存数据。在配置文件中,它只定义要在 +postgresql 之后启动,而没有定义依赖 postgresql 。上线后,由于某种原因 +,postgresql 需要重新启动,在停止服务期间,该 Web 应用就会无法建立数据库连接。 + +设置依赖关系,需要使用`Wants`字段和`Requires`字段。 + +> `Wants`字段:表示`sshd.service`与`sshd-keygen.service`之间存在"弱依赖"关系,即 +> 如果"sshd-keygen.service"启动失败或停止运行,不影响`sshd.service`继续执行。 + +`Requires`字段则表示"强依赖"关系,即如果该服务启动失败或异常退出,那 +么`sshd.service`也必须退出。 + +注意,`Wants`字段与`Requires`字段只涉及依赖关系,与启动顺序无关,默认情况下是同 +时启动的。 + +### [Service] 区块:启动行为 + +`Service`区块定义如何启动当前服务。 + +#### 启动命令 + +许多软件都有自己的环境参数文件,该文件可以用`EnvironmentFile`字段读取。 + +> `EnvironmentFile`字段:指定当前服务的环境参数文件。该文件内部的`key=value`键值 +> 对,可以用`$key`的形式,在当前配置文件中获取。 + +上面的例子中,sshd 的环境参数文件是`/etc/sysconfig/sshd`。 + +配置文件里面最重要的字段是`ExecStart`。 + +> `ExecStart`字段:定义启动进程时执行的命令。 + +上面的例子中,启动`sshd`,执行的命令是`/usr/sbin/sshd -D $OPTIONS`,其中的变 +量`$OPTIONS`就来自`EnvironmentFile`字段指定的环境参数文件。 + +与之作用相似的,还有如下这些字段。 + +- `ExecReload`字段:重启服务时执行的命令 +- `ExecStop`字段:停止服务时执行的命令 +- `ExecStartPre`字段:启动服务之前执行的命令 +- `ExecStartPost`字段:启动服务之后执行的命令 +- `ExecStopPost`字段:停止服务之后执行的命令 + +请看下面的例子。 + +``` +[Service] +ExecStart=/bin/echo execstart1 +ExecStart= +ExecStart=/bin/echo execstart2 +ExecStartPost=/bin/echo post1 +ExecStartPost=/bin/echo post2 +``` + +上面这个配置文件,第二行`ExecStart`设为空值,等于取消了第一行的设置,运行结果如 +下。 + +``` +execstart2 +post1 +post2 +``` + +所有的启动设置之前,都可以加上一个连词号(`-`),表示"抑制错误",即发生错误的时 +候,不影响其他命令的执行。比如,`EnvironmentFile=-/etc/sysconfig/sshd`(注意等号 +后面的那个连词号),就表示即使`/etc/sysconfig/sshd`文件不存在,也不会抛出错误。 + +#### 启动类型 + +`Type`字段定义启动类型。它可以设置的值如下。 + +- simple(默认值):`ExecStart`字段启动的进程为主进程 +- forking:`ExecStart`字段将以`fork()`方式启动,此时父进程将会退出,子进程将成 +为主进程 +- oneshot:类似于`simple`,但只执行一次,Systemd 会等它执行完,才启动其他服务 +- dbus:类似于`simple`,但会等待 D-Bus 信号后启动 +- notify:类似于`simple`,启动结束后会发出通知信号,然后 Systemd 再启动其他服 +务 +- idle:类似于`simple`,但是要等到其他任务都执行完,才会启动该服务。一种使用场 +合是为让该服务的输出,不与其他服务的输出相混合 + +下面是一个`oneshot`的例子,笔记本电脑启动时,要把触摸板关掉,配置文件可以这样写 +。 + +``` +[Unit] +Description=Switch-off Touchpad + +[Service] +Type=oneshot +ExecStart=/usr/bin/touchpad-off + +[Install] +WantedBy=multi-user.target +``` + +上面的配置文件,启动类型设为`oneshot`,就表明这个服务只要运行一次就够了,不需要 +长期运行。 + +如果关闭以后,将来某个时候还想打开,配置文件修改如下。 + +``` +[Unit] +Description=Switch-off Touchpad + +[Service] +Type=oneshot +ExecStart=/usr/bin/touchpad-off start +ExecStop=/usr/bin/touchpad-off stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +``` + +上面配置文件中,`RemainAfterExit`字段设为`yes`,表示进程退出以后,服务仍然保持执 +行。这样的话,一旦使用`systemctl stop`命令停止服务,`ExecStop`指定的命令就会执行 +,从而重新开启触摸板。 + +#### 重启行为 + +`Service`区块有一些字段,定义了重启行为。 + +> `KillMode`字段:定义 Systemd 如何停止 sshd 服务。 + +上面这个例子中,将`KillMode`设为`process`,表示只停止主进程,不停止任何 sshd 子 +进程,即子进程打开的 SSH session 仍然保持连接。这个设置不太常见,但对 sshd 很重 +要,否则你停止服务的时候,会连自己打开的 SSH session 一起杀掉。 + +`KillMode`字段可以设置的值如下。 + +- control-group(默认值):当前控制组里面的所有子进程,都会被杀掉 +- process:只杀主进程 +- mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号 +- none:没有进程会被杀掉,只是执行服务的 stop 命令。 + +接下来是`Restart`字段。 + +> `Restart`字段:定义了 sshd 退出后,Systemd 的重启方式。 + +上面的例子中,`Restart`设为`on-failure`,表示任何意外的失败,就将重启 sshd。如果 +sshd 正常停止(比如执行`systemctl stop`命令),它就不会重启。 + +`Restart`字段可以设置的值如下。 + +- no(默认值):退出后不会重启 +- on-success:只有正常退出时(退出状态码为 0),才会重启 +- on-failure:非正常退出时(退出状态码非 0),包括被信号终止和超时,才会重启 +- on-abnormal:只有被信号终止和超时,才会重启 +- on-abort:只有在收到没有捕捉到的信号终止时,才会重启 +- on-watchdog:超时退出,才会重启 +- always:不管是什么退出原因,总是重启 + +对于守护进程,推荐设为`on-failure`。对于那些允许发生错误退出的服务,可以设 +为`on-abnormal`。 + +最后是`RestartSec`字段。 + +> `RestartSec`字段:表示 Systemd 重启服务之前,需要等待的秒数。上面的例子设为等 +> 待 42 秒。 + +### [Install] 区块 + +`Install`区块,定义如何安装这个配置文件,即怎样做到开机启动。 + +`WantedBy`字段:表示该服务所在的 Target。 + +`Target`的含义是服务组,表示一组服务。`WantedBy=multi-user.target`指的是,sshd +所在的 Target 是`multi-user.target`。 + +这个设置非常重要,因为执行`systemctl enable sshd.service`命令时 +,`sshd.service`的一个符号链接,就会放在`/etc/systemd/system`目录下面 +的`multi-user.target.wants`子目录之中。 + +Systemd 有默认的启动 Target。 + +``` +$ systemctl get-default +multi-user.target +``` + +上面的结果表示,默认的启动 Target 是`multi-user.target`。在这个组里的所有服务, +都将开机启动。这就是为什么`systemctl enable`命令能设置开机启动的原因。 + +使用 Target 的时候,`systemctl list-dependencies`命令和`systemctl isolate`命令也 +很有用。 + +``` +# 查看 multi-user.target 包含的所有服务 +$ systemctl list-dependencies multi-user.target + +# 切换到另一个 target +# shutdown.target 就是关机状态 +$ sudo systemctl isolate shutdown.target +``` + +一般来说,常用的 Target 有两个:一个是`multi-user.target`,表示多用户命令行状态 +;另一个是`graphical.target`,表示图形用户状态,它依赖于`multi-user.target`。官 +方文档有一张非常清晰的 +[Target 依赖关系图](https://www.freedesktop.org/software/systemd/man/bootup.html#System%20Manager%20Bootup)。 + +### Target 的配置文件 + +Target 也有自己的配置文件。 + +``` +$ systemctl cat multi-user.target + +[Unit] +Description=Multi-User System +Documentation=man:systemd.special(7) +Requires=basic.target +Conflicts=rescue.service rescue.target +After=basic.target rescue.service rescue.target +AllowIsolate=yes +``` + +注意,Target 配置文件里面没有启动命令。 + +上面输出结果中,主要字段含义如下。 + +- `Requires`字段:要求`basic.target`一起运行。 +- `Conflicts`字段:冲突字段。如果`rescue.service`或`rescue.target`正在运行 + ,`multi-user.target`就不能运行,反之亦然。 +- `After`:表示`multi-user.target`在`basic.target` 、 `rescue.service`、 + `rescue.target`之后启动,如果它们有启动的话。 +- `AllowIsolate`:允许使用`systemctl isolate`命令切换到`multi-user.target`。 + +### 修改配置文件后重启 + +修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。 + +``` +# 重新加载配置文件 +$ sudo systemctl daemon-reload + +# 重启相关服务 +$ sudo systemctl restart foobar +``` + +## 参考资料 + +- [Systemd 入门教程:命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html) +- [Systemd 入门教程:实战篇](hhttp://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) diff --git a/docs/linux/soft/mysql-ops.md b/docs/linux/soft/mysql-ops.md index f4548369..d4d48213 100644 --- a/docs/linux/soft/mysql-ops.md +++ b/docs/linux/soft/mysql-ops.md @@ -490,4 +490,4 @@ Query OK, 0 rows affected (0.00 sec) ## :door: 传送门 -| [技术文档归档](https://github.com/dunwu/blog) | [数据库教程系列](https://github.com/dunwu/db-tutorial/codes) | +| [我的 Github 博客](https://github.com/dunwu/blog) | [db-tutorial 首页](https://github.com/dunwu/db-tutorial) | diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md index c331ad59..dec2e90d 100644 --- a/docs/linux/soft/redis-ops.md +++ b/docs/linux/soft/redis-ops.md @@ -1,18 +1,31 @@ -# Redis 安装 +# Redis 运维 > **Redis** 是一个高性能的 key-value 数据库。 > > SET 操作每秒钟 110000 次;GET 操作每秒钟 81000 次。 - + - [安装](#安装) - [Window 下安装](#window-下安装) - [Linux 下安装](#linux-下安装) - [Ubuntu 下安装](#ubuntu-下安装) - - [启动 Redis](#启动-redis) - - [查看 redis 是否启动?](#查看-redis-是否启动) + - [开机启动](#开机启动) + - [开放防火墙端口](#开放防火墙端口) +- [Redis 使用和配置](#redis-使用和配置) + - [启动](#启动) + - [常见配置](#常见配置) + - [设为守护进程](#设为守护进程) + - [远程访问](#远程访问) + - [设置密码](#设置密码) +- [Redis 集群使用和配置](#redis-集群使用和配置) + - [集群规划](#集群规划) + - [部署](#部署) +- [Redis 命令](#redis-命令) +- [压力测试](#压力测试) +- [客户端](#客户端) - [脚本](#脚本) +- [参考资料](#参考资料) @@ -26,51 +39,37 @@ Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况 打开一个 **cmd** 窗口 使用 cd 命令切换目录到 **C:\redis** 运行 **redis-server.exe redis.windows.conf** 。 -如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面: +如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。 这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。 切换到 redis 目录下运行 **redis-cli.exe -h 127.0.0.1 -p 6379** 。 -设置键值对 **set myKey abc** - -取出键值对 **get myKey** - ### Linux 下安装 **下载地址:** http://redis.io/download,下载最新文档版本。 -本教程使用的最新文档版本为 2.8.17,下载并安装: +下载、解压、编译 Redis ``` -$ wget -O /opt/redis/redis-4.0.8.tar.gz http://download.redis.io/releases/redis-4.0.8.tar.gz -$ cd /opt/redis -$ tar zxvf redis-4.0.8.tar.gz +$ wget http://download.redis.io/releases/redis-5.0.4.tar.gz +$ tar xzf redis-5.0.4.tar.gz +$ cd redis-5.0.4 +$ make ``` -make 完后 redis-2.8.17 目录下会出现编译后的 redis 服务程序 redis-server,还有用于测试的客户端程序 redis-cli,两个程序位于安装目录 src 目录下: +为了编译 Redis 源码,你需要 gcc-c++和 tcl。如果你的系统是 CentOS,可以直接执行命令:`yum install -y gcc-c++ tcl` 来安装。 -下面启动 redis 服务. +进入到解压后的 `src` 目录,通过如下命令启动 Redis: ``` -$ cd src -$ ./redis-server +$ src/redis-server ``` -注意这种方式启动 redis 使用的是默认配置。也可以通过启动参数告诉 redis 使用指定配置文件使用下面命令启动。 +您可以使用内置的客户端与 Redis 进行交互: ``` -$ cd src -$ ./redis-server redis.conf -``` - -redis.conf 是一个默认的配置文件。我们可以根据需要使用自己的配置文件。 - -启动 redis 服务进程后,就可以使用测试客户端程序 redis-cli 和 redis 服务交互了。 比如: - -``` -$ cd src -$ ./redis-cli +$ src/redis-cli redis> set foo bar OK redis> get foo @@ -86,23 +85,35 @@ $sudo apt-get update $sudo apt-get install redis-server ``` -### 启动 Redis +### 开机启动 + +- 开机启动配置:`echo "/usr/local/bin/redis-server /etc/redis.conf" >> /etc/rc.local` + +### 开放防火墙端口 + +- 添加规则:`iptables -I INPUT -p tcp -m tcp --dport 6379 -j ACCEPT` +- 保存规则:`service iptables save` +- 重启 iptables:`service iptables restart` + +## Redis 使用和配置 + +### 启动 **启动 redis 服务** ``` -cd /opt/redis/redis-4.0.8/src +cd /opt/redis/redis-5.0.4/src ./redis-server ``` **启动 redis 客户端** ``` -cd /opt/redis/redis-4.0.8/src +cd /opt/redis/redis-5.0.4/src ./redis-cli ``` -### 查看 redis 是否启动? +**查看 redis 是否启动** ``` $ redis-cli @@ -121,10 +132,127 @@ redis 127.0.0.1:6379> ping PONG ``` -以上说明我们已经成功安装了 redis。 +以上说明我们已经成功启动了 redis。 + +### 常见配置 + +> Redis 默认的配置文件是根目录下的 `redis.conf` 文件。 +> +> 如果需要指定特定文件作为配置文件,需要使用命令: `./redis-server -c xxx.conf` +> +> 每次修改配置后,需要重启才能生效。 +> +> Redis 官方默认配置: +> +> - 自描述文档 [redis.conf for Redis 2.8](https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf) +> - 自描述文档 [redis.conf for Redis 2.6](https://raw.githubusercontent.com/antirez/redis/2.6/redis.conf). +> - 自描述文档 [redis.conf for Redis 2.4](https://raw.githubusercontent.com/antirez/redis/2.4/redis.conf). +> +> 自 Redis2.6 起就可以直接通过命令行传递 Redis 配置参数。这种方法可以用于测试。自 Redis2.6 起就可以直接通过命令行传递 Redis 配置参数。这种方法可以用于测试。 + +### 设为守护进程 + +Redis 默认以非守护进程方式启动,而通常我们会将 Redis 设为守护进程启动方式,配置:`daemonize yes` + +#### 远程访问 + +Redis 默认绑定 127.0.0.1,这样就只能本机才能访问,若要 Redis 允许远程访问,需要配置:`bind 0.0.0.0` + +#### 设置密码 + +Redis 默认访问不需要密码,如果需要设置密码,需要如下配置: + +- `protected-mode yes` +- `requirepass <密码>` + +## Redis 集群使用和配置 + +Redis 3.0 后支持集群模式。 + +### 集群规划 + +`Redis` 集群一般由 **多个节点** 组成,节点数量至少为 `6` 个,才能保证组成 **完整高可用** 的集群。 + +
+ +理想情况当然是所有节点各自在不同的机器上,首先于资源,本人在部署 Redis 集群时,只得到 3 台服务器。所以,我的基本规划是满足两个条件: + +- 每台服务器上部署一个主节点、一个从节点。 +- 每个主节点所对应的从节点,必须在另外一台服务器上。 + +
+ +> 为集群内 **所有节点** 统一目录,一般划分三个目录:`conf`、`data`、`log`,分别存放 **配置**、**数据** 和 **日志** 相关文件。把 `6` 个节点配置统一放在 `conf` 目录下。 + +### 部署 + +Redis 集群节点的安装与单节点服务相同,差异仅在于部署方式。 + +假设三台服务器地址如下: + +- 服务 A:127.0.0.1 +- 服务 B:127.0.0.2 +- 服务 C:127.0.0.3 + +分配如下: + +| 服务器 | 127.0.0.1 | 127.0.0.2 | 127.0.0.3 | +| ------ | -------------- | -------------- | -------------- | +| 主节点 | 127.0.0.1:6380 | 127.0.0.2:6381 | 127.0.0.3:6382 | +| 从节点 | 127.0.0.1:6382 | 127.0.0.2:6380 | 127.0.0.3:6381 | + +## Redis 命令 + +> 命令详细用法,请参考 [**Redis 命令官方文档**](https://redis.io/commands) +> +> 搬迁两张 cheat sheet 图,原址:https://www.cheatography.com/tasjaevan/cheat-sheets/redis/ + +
+ +
+ +## 压力测试 + +> 参考官方文档:[How fast is Redis?](https://redis.io/topics/benchmarks) + +Redis 自带了一个性能测试工具:`redis-benchmark` + +**(1)基本测试** + +``` +$ redis-benchmark -q -n 100000 +``` + +- `-q` 表示静默(quiet)执行 +- `-n 100000` 请求 10 万次 + +**(2)测试指定读写指令** + +``` +$ redis-benchmark -t set,lpush -n 100000 -q +SET: 74239.05 requests per second +LPUSH: 79239.30 requests per second +``` + +**(3)测试 pipeline 模式下指定读写指令** + +``` +redis-benchmark -n 1000000 -t set,get -P 16 -q +SET: 403063.28 requests per second +GET: 508388.41 requests per second +``` + +## 客户端 + +推荐使用 [**RedisDesktopManager**](https://github.com/uglide/RedisDesktopManager) ## 脚本 以上两种安装方式,我都写了脚本去执行: | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | + +## 参考资料 + +- [Redis 官方文档](https://redis.io) +- [深入剖析 Redis 系列(三) - Redis 集群模式搭建与原理详解](https://juejin.im/post/5b8fc5536fb9a05d2d01fb11) From 0c58f678fe6794ae5fc1efb4b2f94c914feb2fd6 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 10 Oct 2019 08:56:31 +0800 Subject: [PATCH 07/64] format codes --- .editorconfig | 8 +- codes/linux/build/helper.sh | 48 ++-- codes/linux/build/java-app-boot.sh | 98 +++---- codes/linux/build/java-app-release.sh | 71 ++--- codes/linux/build/java-app-run.sh | 71 ++--- codes/linux/build/js-app-release.sh | 75 ++--- codes/linux/build/main.sh | 231 +++++++-------- codes/linux/build/update-code.sh | 110 ++++---- codes/linux/download.sh | 14 +- codes/linux/dunwu-ops.sh | 83 +++--- codes/linux/dunwu-soft.sh | 53 ++-- codes/linux/dunwu-sys.sh | 62 ++-- codes/linux/soft/docker-install.sh | 20 +- codes/linux/soft/elk/boot-elk.sh | 80 +++--- codes/linux/soft/elk/install-elk.sh | 89 +++--- codes/linux/soft/elk/install_elasticserch.sh | 177 ++++++------ codes/linux/soft/elk/install_filebeat.sh | 101 +++---- codes/linux/soft/elk/install_kibana.sh | 103 +++---- codes/linux/soft/elk/install_logstash.sh | 103 +++---- codes/linux/soft/fastdfs-install.sh | 28 +- codes/linux/soft/install_grafana.sh | 77 ++--- codes/linux/soft/jdk8-install.sh | 3 +- codes/linux/soft/kafka-install.sh | 19 +- codes/linux/soft/maven-install.sh | 17 +- codes/linux/soft/mongodb-install.sh | 14 +- codes/linux/soft/mysql-install.sh | 9 +- codes/linux/soft/nacos-install.sh | 12 +- codes/linux/soft/nginx-install.sh | 17 +- codes/linux/soft/nodejs-install.sh | 12 +- codes/linux/soft/redis-install.sh | 15 +- codes/linux/soft/rocketmq-install.sh | 14 +- codes/linux/soft/tomcat8-install.sh | 14 +- codes/linux/soft/zookeeper-install.sh | 14 +- codes/linux/soft/zsh-install.sh | 6 +- codes/linux/sys/change-yum-repo.sh | 20 +- codes/linux/sys/set-dns.sh | 14 +- codes/linux/sys/sys-settings.sh | 117 ++++---- codes/linux/tool/Autoinstall_ELK_V1.3.sh | 167 +++++------ codes/linux/tool/Cpu_Limit.sh | 39 +-- codes/linux/tool/Custom_Rm.sh | 3 +- codes/linux/tool/Daily_Archive.sh | 59 ++-- codes/linux/tool/Hourly_Archive.sh | 59 ++-- ...50\346\210\267\350\204\232\346\234\254.sh" | 265 +++++++++--------- ...77\347\224\250\346\203\205\345\206\265.sh" | 14 +- codes/shell/action/oper/input.sh | 16 +- codes/shell/action/system/dir.sh | 3 +- codes/shell/demos/array-demo.sh | 8 +- codes/shell/demos/comment-demo.sh | 2 +- codes/shell/demos/debug-demo.sh | 8 +- codes/shell/demos/function/function-demo.sh | 55 ++-- codes/shell/demos/function/function-demo2.sh | 19 +- codes/shell/demos/function/function-demo3.sh | 29 +- codes/shell/demos/helloworld.sh | 1 + codes/shell/demos/operator/operator-demo.sh | 8 +- codes/shell/demos/operator/operator-demo2.sh | 28 +- codes/shell/demos/operator/operator-demo3.sh | 20 +- codes/shell/demos/operator/operator-demo4.sh | 12 +- codes/shell/demos/operator/operator-demo5.sh | 24 +- codes/shell/demos/operator/operator-demo6.sh | 28 +- codes/shell/demos/statement/break-demo.sh | 10 +- codes/shell/demos/statement/case-demo.sh | 44 +-- codes/shell/demos/statement/continue-demo.sh | 8 +- codes/shell/demos/statement/for-demo.sh | 8 +- codes/shell/demos/statement/if-demo.sh | 16 +- codes/shell/demos/statement/select-demo.sh | 8 +- codes/shell/demos/statement/until-demo.sh | 4 +- codes/shell/demos/statement/while-demo.sh | 4 +- codes/shell/demos/string-demo.sh | 6 +- codes/shell/demos/variable-demo.sh | 6 +- ...47\350\241\214\350\204\232\346\234\254.sh" | 4 +- ...22\345\205\245\346\225\260\346\215\256.sh" | 23 +- ...23\345\207\272\346\225\260\346\215\256.sh" | 2 +- ...21\351\200\201\345\221\275\344\273\244.sh" | 5 +- ...72\346\234\254\344\275\277\347\224\250.sh" | 6 +- .../exit\345\221\275\344\273\244.sh" | 3 +- ...60\345\255\246\350\277\220\347\256\227.sh" | 1 + ...27\350\241\250\350\276\276\345\274\217.sh" | 2 +- ...60\345\255\246\350\277\220\347\256\227.sh" | 2 +- ...67\347\232\204\344\275\277\347\224\250.sh" | 1 + ...\344\270\255\344\275\277\347\224\250bc.sh" | 1 + ...14\347\231\273\345\275\225\350\200\205.sh" | 1 + ...54\344\271\211\345\255\227\347\254\246.sh" | 3 +- ...00\346\226\207\344\273\266\345\220\215.sh" | 1 + ...62\346\210\220\346\225\260\347\273\204.sh" | 4 +- ...72\346\234\254\344\275\277\347\224\250.sh" | 6 +- ...25\346\215\211\344\277\241\345\217\267.sh" | 8 +- ...54\347\232\204\351\200\200\345\207\272.sh" | 8 +- ...73\351\231\244\346\215\225\346\215\211.sh" | 8 +- ...72\346\234\254\344\275\277\347\224\250.sh" | 8 +- .../gawk/gawk.sh" | 2 +- ...77\347\224\250\345\217\230\351\207\217.sh" | 1 + ...04\345\214\226\345\221\275\344\273\244.sh" | 3 +- ...32\344\271\211\345\207\275\346\225\260.sh" | 1 + .../gawk/\350\276\223\345\207\272.sh" | 2 +- ...07\344\273\266\350\256\241\346\225\260.sh" | 16 +- ...21\345\231\250\345\237\272\347\241\200.sh" | 1 + ...344\270\255\344\275\277\347\224\250sed.sh" | 1 + ...60\347\232\204\346\225\260\346\215\256.sh" | 1 + ...345\220\221sed\350\276\223\345\207\272.sh" | 4 +- ...24\345\233\236\346\225\260\347\273\204.sh" | 28 +- ...\224\250return\345\221\275\344\273\244.sh" | 6 +- ...45\347\232\204\351\227\256\351\242\230.sh" | 8 +- ...75\346\225\260\350\276\223\345\207\272.sh" | 5 +- ...22\347\232\204\345\217\202\346\225\260.sh" | 10 +- ...00\351\203\250\345\217\230\351\207\217.sh" | 8 +- ...50\345\261\200\345\217\230\351\207\217.sh" | 2 +- ...75\346\225\260\351\200\222\345\275\222.sh" | 16 +- ...77\347\224\250\345\217\202\346\225\260.sh" | 18 +- ...32\346\234\254\345\207\275\346\225\260.sh" | 6 +- ...60\347\273\204\346\225\260\346\215\256.sh" | 18 +- ...04\344\270\255\347\232\204\345\200\274.sh" | 18 +- .../\350\204\232\346\234\254\345\272\223.sh" | 20 +- ...72\347\212\266\346\200\201\347\240\201.sh" | 8 +- ...\224\250select\345\221\275\344\273\244.sh" | 39 +-- ...32\346\234\254\350\217\234\345\215\225.sh" | 62 ++-- ...\224\250dialog\345\221\275\344\273\244.sh" | 52 ++-- ...66\351\207\215\345\256\232\345\220\221.sh" | 1 + ...73\345\217\226\346\225\260\346\215\256.sh" | 5 +- .../\344\275\277\347\224\250getopts.sh" | 13 +- ...71\345\222\214\345\217\202\346\225\260.sh" | 19 +- ...\224\250getopt\345\221\275\344\273\244.sh" | 27 +- ...7\224\250shift\345\221\275\344\273\244.sh" | 7 +- ...60\345\222\214\351\200\211\351\241\271.sh" | 22 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 6 +- ...64\346\227\266\346\226\207\344\273\266.sh" | 4 +- ...46\344\270\255\346\201\242\345\244\215.sh" | 2 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 5 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 1 + ...66\346\217\217\350\277\260\347\254\246.sh" | 11 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 2 +- ...02\346\225\260\350\256\241\346\225\260.sh" | 1 + ...64\346\227\266\346\226\207\344\273\266.sh" | 2 +- ...32\345\220\221\350\276\223\345\205\245.sh" | 5 +- ...74\347\232\204\351\200\211\351\241\271.sh" | 28 +- ...00\345\215\225\351\200\211\351\241\271.sh" | 15 +- ...00\346\234\211\346\225\260\346\215\256.sh" | 9 +- ...05\351\207\215\345\256\232\345\220\221.sh" | 5 +- ...50\346\210\267\350\276\223\345\205\245.sh" | 4 +- ...73\345\217\226\345\217\202\346\225\260.sh" | 4 +- ...26\347\250\213\345\272\217\345\220\215.sh" | 1 + ...23\345\205\245\350\256\241\346\225\260.sh" | 29 +- ...73\345\217\226\346\225\260\346\215\256.sh" | 3 +- ...25\346\215\211\350\204\232\346\234\254.sh" | 1 + ...77\347\224\250\346\210\267\346\225\260.sh" | 1 + ...25\346\215\211\350\204\232\346\234\254.sh" | 3 +- ...53\347\205\247\346\212\245\345\221\212.sh" | 19 +- .../Update_Problem.sh" | 68 ++--- ...45\346\211\276\351\227\256\351\242\230.sh" | 27 +- ...60\345\275\225\351\227\256\351\242\230.sh" | 21 +- ...06\346\265\256\347\202\271\346\225\260.sh" | 5 +- ...32\345\261\202\345\276\252\347\216\257.sh" | 23 +- .../if-then-else\350\257\255\345\217\245.sh" | 7 +- .../if-then\350\257\255\345\217\245.sh" | 9 +- ...345\245\227for\345\276\252\347\216\257.sh" | 14 +- ...14\345\245\227\345\276\252\347\216\257.sh" | 13 +- ...73\345\217\226\345\210\227\350\241\250.sh" | 3 +- ...44\350\257\273\345\217\226\345\200\274.sh" | 3 +- ...347\232\204for\345\221\275\344\273\244.sh" | 5 +- ...26\351\203\250\345\276\252\347\216\257.sh" | 21 +- ...47\224\250case\350\257\255\345\217\245.sh" | 18 +- ...24\250continue\345\221\275\344\273\244.sh" | 12 +- .../\344\275\277\347\224\250elif.sh" | 14 +- ...60\345\200\274\346\257\224\350\276\203.sh" | 7 +- ...7\224\250until\345\221\275\344\273\244.sh" | 16 +- .../\344\275\277\347\224\250while.sh" | 10 +- ...13\350\257\225\345\221\275\344\273\244.sh" | 8 +- ...14\345\234\206\346\213\254\345\217\267.sh" | 9 +- ...04\345\255\227\347\254\246\344\270\262.sh" | 3 +- ...14\346\226\271\346\213\254\345\217\267.sh" | 7 +- ...345\271\266\344\277\256\346\224\271IFS.sh" | 14 +- ...41\344\273\266\346\265\213\350\257\225.sh" | 5 +- ...26\351\207\215\345\256\232\345\220\221.sh" | 14 +- ...04\347\220\206\347\233\256\345\275\225.sh" | 15 +- ...57\345\220\246\344\270\272\351\233\266.sh" | 19 +- ...32\344\270\252\345\217\230\351\207\217.sh" | 5 +- ...32\346\235\241\345\221\275\344\273\244.sh" | 5 +- ...46\344\270\262\346\257\224\350\276\203.sh" | 3 +- ...57\345\220\246\345\255\230\345\234\250.sh" | 27 +- ...00\346\237\245\347\233\256\345\275\225.sh" | 9 +- ...16\345\260\217\344\272\216\345\217\267.sh" | 7 +- ...45\222\214sort\344\270\215\345\220\214.sh" | 5 +- ...04\345\244\215\346\235\202\345\200\274.sh" | 4 +- ...50\344\270\255\347\232\204\345\200\274.sh" | 2 +- ...15\346\235\202\347\232\204\345\200\274.sh" | 4 +- ...16\345\260\217\344\272\216\345\217\267.sh" | 5 +- docs/linux/soft/redis-ops.md | 36 +++ 186 files changed, 2183 insertions(+), 2027 deletions(-) diff --git a/.editorconfig b/.editorconfig index d9c3abd5..3b69c906 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ # EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # http://editorconfig.org -# 所有文件换行使用 Unix like 风格(LF),bat 文件使用 win 风格(CRLF) +# 所有文件换行以 Unix like 风格(LF),win 格式特定的除外(bat) # 缩进 java 4 个空格,其他所有文件 2 个空格 root = true @@ -11,9 +11,8 @@ root = true end_of_line = lf # Change these settings to your own preference -indent_size = 2 indent_style = space -max_line_length = 120 +indent_size = 2 # We recommend you to keep these unchanged charset = utf-8 @@ -23,9 +22,8 @@ insert_final_newline = true [*.bat] end_of_line = crlf -[*.java] +[*.{java, sh}] indent_size = 4 [*.md] -max_line_length = 0 trim_trailing_whitespace = false diff --git a/codes/linux/build/helper.sh b/codes/linux/build/helper.sh index c3e120e1..2466cd6c 100644 --- a/codes/linux/build/helper.sh +++ b/codes/linux/build/helper.sh @@ -2,7 +2,7 @@ # 打印UI页头信息 function printHeadInfo() { -cat << EOF + cat << EOF *********************************************************************************** * 欢迎使用项目引导式发布脚本。 * 输入任意键进入脚本操作。 @@ -12,7 +12,7 @@ EOF # 打印UI页尾信息 function printFootInfo() { -cat << EOF + cat << EOF *********************************************************************************** @@ -25,18 +25,18 @@ EOF # 检查文件是否存在,不存在则退出脚本 function checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 1 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 1 + fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { - if [ ! -d "$1" ];then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 记录发布的版本信息 @@ -45,18 +45,18 @@ function createFolderIfNotExist() { # 第三个参数为代码分支 # 第四个参数为运行环境 function saveVersionInfo() { - if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then - echo "缺少参数,退出" - exit 1 - fi - - VERSION_LOG_FILE=$1/$2-version.log - rm -rf ${VERSION_LOG_FILE} - touch ${VERSION_LOG_FILE} - chmod 777 ${VERSION_LOG_FILE} - - echo -e "\n=================== $2 ===================" >> ${VERSION_LOG_FILE} - echo "Branch is: $3" >> ${VERSION_LOG_FILE} - echo "Profile is: $4" >> ${VERSION_LOG_FILE} - echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} + if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then + echo "缺少参数,退出" + exit 1 + fi + + VERSION_LOG_FILE=$1/$2-version.log + rm -rf ${VERSION_LOG_FILE} + touch ${VERSION_LOG_FILE} + chmod 777 ${VERSION_LOG_FILE} + + echo -e "\n=================== $2 ===================" >> ${VERSION_LOG_FILE} + echo "Branch is: $3" >> ${VERSION_LOG_FILE} + echo "Profile is: $4" >> ${VERSION_LOG_FILE} + echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} } diff --git a/codes/linux/build/java-app-boot.sh b/codes/linux/build/java-app-boot.sh index 4879c08c..85db9110 100644 --- a/codes/linux/build/java-app-boot.sh +++ b/codes/linux/build/java-app-boot.sh @@ -7,22 +7,22 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { - if [ "${app}" == "" ] || [ "${oper}" == "" ] || [ "${javaArgs}" == "" ] || [ "${classpathArgs}" == "" ] || [ "${bootstrapClass}" == "" ]; then - echo "请输入脚本参数:app oper javaArgs classpathArgs bootstrapClass" - echo " app: 应用名。" - echo " oper: 运行环境(必填)。可选值:start|stop|restart" - echo " javaArgs: JVM 参数(必填)。" - echo " classpathArgs: classpath参数(必填)。" - echo " bootstrapClass: 启动类(必填)。" - exit 0 - fi + if [ "${app}" == "" ] || [ "${oper}" == "" ] || [ "${javaArgs}" == "" ] || [ "${classpathArgs}" == "" ] || [ "${bootstrapClass}" == "" ]; then + echo "请输入脚本参数:app oper javaArgs classpathArgs bootstrapClass" + echo " app: 应用名。" + echo " oper: 运行环境(必填)。可选值:start|stop|restart" + echo " javaArgs: JVM 参数(必填)。" + echo " classpathArgs: classpath参数(必填)。" + echo " bootstrapClass: 启动类(必填)。" + exit 0 + fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { - if [ ! -d "$1" ];then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 检查服务是否已经启动 @@ -37,46 +37,46 @@ function checkStarted() { } function main() { - case "${oper}" in - start ) - echo -n "starting server: " - # 检查服务是否已经启动 - if checkStarted ;then - echo "ERROR: server already started!" - echo "PID: ${pids}" - exit 1 - fi + case "${oper}" in + start) + echo -n "starting server: " + # 检查服务是否已经启动 + if checkStarted; then + echo "ERROR: server already started!" + echo "PID: ${pids}" + exit 1 + fi - args="${javaArgs} -classpath ${classpathArgs} ${bootstrapClass}" - echo -e "statup params:\n ${args}" + args="${javaArgs} -classpath ${classpathArgs} ${bootstrapClass}" + echo -e "statup params:\n ${args}" - #启动服务 - touch ${LOG_DIR}/${app}-startup.log - nohup java ${args} > ${LOG_DIR}/${app}-startup.log 2>&1 & - # echo -e "执行参数:\n${args}" - echo -e "\nthe server is started..." - ;; - stop ) - echo -n "stopping server: " - #dubbo提供优雅停机, 不能使用kill -9 - if checkStarted ;then - kill ${pids} - echo -e "\nthe server is stopped..." - else - echo -e "\nno server to be stopped..." - fi - ;; - restart ) - $0 ${app} stop "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" - sleep 5 - $0 ${app} start "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" - ;; - * ) - echo "Invalid oper: ${oper}." - exit 1 - esac + #启动服务 + touch ${LOG_DIR}/${app}-startup.log + nohup java ${args} > ${LOG_DIR}/${app}-startup.log 2>&1 & + # echo -e "执行参数:\n${args}" + echo -e "\nthe server is started..." + ;; + stop) + echo -n "stopping server: " + #dubbo提供优雅停机, 不能使用kill -9 + if checkStarted; then + kill ${pids} + echo -e "\nthe server is stopped..." + else + echo -e "\nno server to be stopped..." + fi + ;; + restart) + $0 ${app} stop "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" + sleep 5 + $0 ${app} start "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" + ;; + *) + echo "Invalid oper: ${oper}." + exit 1 + esac - exit 0 + exit 0 } ######################################## MAIN ######################################## diff --git a/codes/linux/build/java-app-release.sh b/codes/linux/build/java-app-release.sh index 45d916c3..02b77c0f 100644 --- a/codes/linux/build/java-app-release.sh +++ b/codes/linux/build/java-app-release.sh @@ -7,41 +7,41 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 checkInput() { - if [ "${branch}" == "" ] || [ "${profile}" == "" ]; then - echo "请输入脚本参数:branch profile" - echo " branch: git分支(必填)。如 feature/1.1.16, master" - echo " profile: 运行环境(必填)。可选值:development | test" - echo "例:./java-app-release.sh feature/1.1.16 test" - exit 0 - fi + if [ "${branch}" == "" ] || [ "${profile}" == "" ]; then + echo "请输入脚本参数:branch profile" + echo " branch: git分支(必填)。如 feature/1.1.16, master" + echo " profile: 运行环境(必填)。可选值:development | test" + echo "例:./java-app-release.sh feature/1.1.16 test" + exit 0 + fi } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } # 检查文件夹是否存在,不存在则创建 createFolderIfNotExist() { - if [ ! -d "$1" ];then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 记录发布的版本信息 saveVersionInfo() { - rm -rf ${VERSION_LOG_FILE} - touch ${VERSION_LOG_FILE} - chmod 777 ${VERSION_LOG_FILE} - - echo -e "\n=================== Version Info ===================" >> ${VERSION_LOG_FILE} - echo "Branch is: ${branch}" >> ${VERSION_LOG_FILE} - echo "Profile is: ${profile}" >> ${VERSION_LOG_FILE} - echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} + rm -rf ${VERSION_LOG_FILE} + touch ${VERSION_LOG_FILE} + chmod 777 ${VERSION_LOG_FILE} + + echo -e "\n=================== Version Info ===================" >> ${VERSION_LOG_FILE} + echo "Branch is: ${branch}" >> ${VERSION_LOG_FILE} + echo "Profile is: ${profile}" >> ${VERSION_LOG_FILE} + echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} } ######################################## MAIN ######################################## @@ -50,7 +50,8 @@ export LANG="zh_CN.UTF-8" # 设置全局常量 LOG_DIR=/home/zp/log/ -SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) +SCRIPT_DIR=$(cd "$(dirname "$0")"; +pwd) SOURCE_DIR=/home/zp/source/ APP_NAME=XXX RESOURCES_DIR=/home/zp/source/${APP_NAME}/src/main/resources @@ -73,10 +74,10 @@ echo ">>>>>>>>>>>>>> 2. 更新代码" ${UPDATE_CODE_SCRIPT_FILE} ${APP_NAME} ${branch} ${SOURCE_DIR} execode=$? if [ "${execode}" == "0" ]; then - echo "更新代码成功" + echo "更新代码成功" else - echo "更新代码失败" - exit 1 + echo "更新代码失败" + exit 1 fi echo ">>>>>>>>>>>>>> 3. 替换配置" @@ -87,12 +88,12 @@ cd ${SOURCE_DIR}/ck-lion mvn clean package -e -Dmaven.test.skip=true | tee ${MAVEN_LOG_FILE} eexecode=$? if [ "${execode}" == "0" ]; then - echo "构建编译成功" - echo "编译详情见:${MAVEN_LOG_FILE}" + echo "构建编译成功" + echo "编译详情见:${MAVEN_LOG_FILE}" else - echo "构建编译失败" - echo "编译详情见:${MAVEN_LOG_FILE}" - exit 1 + echo "构建编译失败" + echo "编译详情见:${MAVEN_LOG_FILE}" + exit 1 fi echo ">>>>>>>>>>>>>> 5. 启动应用" @@ -101,10 +102,10 @@ echo 3 > /proc/sys/vm/drop_caches ${SCRIPT_DIR}/java-app-run.sh ${profile} start execode=$? if [ "${execode}" == "0" ]; then - echo "启动应用成功" + echo "启动应用成功" else - echo "启动应用失败" - exit 1 + echo "启动应用失败" + exit 1 fi echo ">>>>>>>>>>>>>> 6. 记录发布的版本信息" diff --git a/codes/linux/build/java-app-run.sh b/codes/linux/build/java-app-run.sh index 88dfc4e7..db81b243 100644 --- a/codes/linux/build/java-app-run.sh +++ b/codes/linux/build/java-app-run.sh @@ -7,51 +7,51 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { - if [ "${profile}" == "" ] || [ "${oper}" == "" ]; then - echo "请输入脚本参数:profile oper [debug]" - echo " profile: 运行环境(必填)。可选值:development|test" - echo " oper: 运行环境(必填)。可选值:start|stop|restart" - echo " debug: debug启动开关。默认不填为不启动。" - exit 0 - fi + if [ "${profile}" == "" ] || [ "${oper}" == "" ]; then + echo "请输入脚本参数:profile oper [debug]" + echo " profile: 运行环境(必填)。可选值:development|test" + echo " oper: 运行环境(必填)。可选值:start|stop|restart" + echo " debug: debug启动开关。默认不填为不启动。" + exit 0 + fi } #检查文件是否存在,不存在则退出脚本 function checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } # 封装启动参数,调用启动脚本 -function main(){ - APP_NAME=ck-lion +function main() { + APP_NAME=ck-lion - # JVM 参数 - JAVA_OPTS=" -Djava.awt.headless=true -Dfile.encoding=UTF8 -Djava.net.preferIPv4Stack=true -Ddubbo.shutdown.hook=true -Dspring.profiles.active=${profile} -Djava.security.egd=file:/dev/./urandom -Xms1024m -Xmx1024m -Xss2m " - JAVA_DEBUG_OPTS="" - if [ "$2" == "debug" ]; then - JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=2236,server=y,suspend=n " - shift - fi - javaArgs=" ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} " + # JVM 参数 + JAVA_OPTS=" -Djava.awt.headless=true -Dfile.encoding=UTF8 -Djava.net.preferIPv4Stack=true -Ddubbo.shutdown.hook=true -Dspring.profiles.active=${profile} -Djava.security.egd=file:/dev/./urandom -Xms1024m -Xmx1024m -Xss2m " + JAVA_DEBUG_OPTS="" + if [ "$2" == "debug" ]; then + JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=2236,server=y,suspend=n " + shift + fi + javaArgs=" ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} " - # classpath 参数 - classpathArgs="${SERVER_ROOT}/WEB-INF/classes:${SERVER_ROOT}/WEB-INF/lib/*" + # classpath 参数 + classpathArgs="${SERVER_ROOT}/WEB-INF/classes:${SERVER_ROOT}/WEB-INF/lib/*" - # 启动类 - bootstrapClass="com.alibaba.dubbo.container.Main" + # 启动类 + bootstrapClass="com.alibaba.dubbo.container.Main" - ${SCRIPT_DIR}/java-app-boot.sh ${APP_NAME} ${oper} "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" - execode=$? - if [ "${execode}" == "0" ]; then - echo "执行操作成功" - else - echo "执行操作失败" - exit 1 - fi + ${SCRIPT_DIR}/java-app-boot.sh ${APP_NAME} ${oper} "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" + execode=$? + if [ "${execode}" == "0" ]; then + echo "执行操作成功" + else + echo "执行操作失败" + exit 1 + fi } ######################################## MAIN ######################################## @@ -59,7 +59,8 @@ function main(){ export LANG="zh_CN.UTF-8" # 设置全局常量 -SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) +SCRIPT_DIR=$(cd "$(dirname "$0")"; +pwd) SOURCE_DIR=/home/zp/source/ APP_NAME=XXX SERVER_ROOT=/home/zp/source/${APP_NAME}/target/ diff --git a/codes/linux/build/js-app-release.sh b/codes/linux/build/js-app-release.sh index ec5aab49..617d9ae3 100644 --- a/codes/linux/build/js-app-release.sh +++ b/codes/linux/build/js-app-release.sh @@ -5,50 +5,50 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { - if [ "${branch}" == "" ]; then - echo "请输入脚本参数:branch" - echo " branch: git分支。如 feature/1.1.16, master" - exit 1 - fi + if [ "${branch}" == "" ]; then + echo "请输入脚本参数:branch" + echo " branch: git分支。如 feature/1.1.16, master" + exit 1 + fi } # 脚本主方法 function main() { - echo ">>>>>>>>>>>>>> 1. 更新代码" - ${SCRIPT_DIR}/update-code.sh ${APP} ${branch} ${SOURCE_DIR} - execode=$? - if [ "${execode}" == "0" ]; then - echo "更新代码成功" - else - echo "更新代码失败" - exit 1 - fi + echo ">>>>>>>>>>>>>> 1. 更新代码" + ${SCRIPT_DIR}/update-code.sh ${APP} ${branch} ${SOURCE_DIR} + execode=$? + if [ "${execode}" == "0" ]; then + echo "更新代码成功" + else + echo "更新代码失败" + exit 1 + fi - echo ">>>>>>>>>>>>>> 2. 替换配置" - # 有的应用此处可能需要替换配置 + echo ">>>>>>>>>>>>>> 2. 替换配置" + # 有的应用此处可能需要替换配置 - echo ">>>>>>>>>>>>>> 3. 构建编译" - cd ${SOURCE_DIR}/${APP} - source "${HOME}/.nvm/nvm.sh" - nvm use 8.9 - npm install - if [ "${profile}" == "develop" ] || [ "${profile}" == "test" ]; then - npm start - elif [ "${profile}" == "preview" ] || [ "${profile}" == "product" ]; then - npm run build - fi - execode=$? - if [ "${execode}" == "0" ]; then - echo "构建编译成功" - else - echo "构建编译失败" - exit 1 - fi + echo ">>>>>>>>>>>>>> 3. 构建编译" + cd ${SOURCE_DIR}/${APP} + source "${HOME}/.nvm/nvm.sh" + nvm use 8.9 + npm install + if [ "${profile}" == "develop" ] || [ "${profile}" == "test" ]; then + npm start + elif [ "${profile}" == "preview" ] || [ "${profile}" == "product" ]; then + npm run build + fi + execode=$? + if [ "${execode}" == "0" ]; then + echo "构建编译成功" + else + echo "构建编译失败" + exit 1 + fi - echo ">>>>>>>>>>>>>> 4. 记录发布的版本信息" - saveVersionInfo ${LOG_DIR} ${APP} ${branch} ${profile} + echo ">>>>>>>>>>>>>> 4. 记录发布的版本信息" + saveVersionInfo ${LOG_DIR} ${APP} ${branch} ${profile} - echo ">>>>>>>>>>>>>> 发布应用结束" + echo ">>>>>>>>>>>>>> 发布应用结束" } ######################################## MAIN ######################################## @@ -59,7 +59,8 @@ export LANG="zh_CN.UTF-8" APP=blog LOG_DIR=/home/zp/log SOURCE_DIR=/home/zp/source -SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) +SCRIPT_DIR=$(cd "$(dirname "$0")"; +pwd) # 装载函数库 . ${SCRIPT_DIR}/helper.sh diff --git a/codes/linux/build/main.sh b/codes/linux/build/main.sh index a74f73a1..26d92fea 100644 --- a/codes/linux/build/main.sh +++ b/codes/linux/build/main.sh @@ -7,7 +7,7 @@ # 选择应用 function chooseAppName() { -cat << EOF + cat << EOF 请选择应用名(数字或关键字均可)。 可选值如下: [0] all (所有应用) @@ -15,28 +15,28 @@ cat << EOF [2] APP2 EOF -while read app -do - case ${app} in - 0 ) - app=all - break ;; - 1 ) - app=js-app - break ;; - 2 ) - app=APP2 - break ;; - all | js-app | APP2 ) - break ;; - * ) echo "无法识别 ${app}" ;; - esac -done + while read app + do + case ${app} in + 0) + app=all + break ;; + 1) + app=js-app + break ;; + 2) + app=APP2 + break ;; + all | js-app | APP2) + break ;; + *) echo "无法识别 ${app}" ;; + esac + done } # 选择操作 function chooseOper() { -cat << EOF + cat << EOF 请选择想要执行的操作(数字或关键字均可)。 可选值如下: [1] start @@ -44,44 +44,44 @@ cat << EOF [3] stop EOF -while read oper -do - case ${oper} in - 1 ) - oper=start - break ;; - 2 ) - oper=restart - break ;; - 3 ) - oper=stop - break ;; - start | restart | stop ) - break ;; - * ) echo "无法识别 ${oper}" ;; - esac -done + while read oper + do + case ${oper} in + 1) + oper=start + break ;; + 2) + oper=restart + break ;; + 3) + oper=stop + break ;; + start | restart | stop) + break ;; + *) echo "无法识别 ${oper}" ;; + esac + done } # 选择代码分支 function chooseBranch() { -cat << EOF + cat << EOF 请输入 git 分支。 如:develop、master、feature/xxx EOF -read branch -if [[ "${branch}" =~ ^(feature/)([^ \f\n\r\t\v]+) ]] || [ "${branch}" == "develop" ] || [ "${branch}" == "master" ]; then - echo "输入了 ${branch}" -else - echo "无法识别 ${branch}" - chooseBranch -fi + read branch + if [[ "${branch}" =~ ^ ( feature/ ) ( [^ \f\n\r\t\v]+ ) ]] || [ "${branch}" == "develop" ] || [ "${branch}" == "master" ]; then + echo "输入了 ${branch}" + else + echo "无法识别 ${branch}" + chooseBranch + fi } # 选择运行环境 function chooseProfile() { -cat << EOF + cat << EOF 请选择运行环境(数字或关键字均可)。 可选值: [1] develop (开发环境) @@ -90,32 +90,32 @@ cat << EOF [4] product (生产环境) EOF -while read profile -do - case ${profile} in - 1 ) - profile=develop - break ;; - 2 ) - profile=test - break ;; - 3 ) - profile=preview - break ;; - 4 ) - profile=product - break ;; - develop | test | preview | product ) - break ;; - * ) echo "无法识别 ${profile}" ;; - esac -done + while read profile + do + case ${profile} in + 1) + profile=develop + break ;; + 2) + profile=test + break ;; + 3) + profile=preview + break ;; + 4) + profile=product + break ;; + develop | test | preview | product) + break ;; + *) echo "无法识别 ${profile}" ;; + esac + done } # 确认选择 function confirmChoice() { -cat << EOF + cat << EOF =================================================== 请确认您的选择:Y/N app: ${app} @@ -125,66 +125,66 @@ cat << EOF =================================================== EOF - while read confirm - do - case ${confirm} in - y | Y ) - echo -e "\n\n>>>>>>>>>>>>>> 开始发布应用" - break ;; - n | N ) - echo -e "重新输入发布参数\n" - inputParams ;; - * ) - echo "无法识别 ${confirm}" ;; - esac - done + while read confirm + do + case ${confirm} in + y | Y) + echo -e "\n\n>>>>>>>>>>>>>> 开始发布应用" + break ;; + n | N) + echo -e "重新输入发布参数\n" + inputParams ;; + *) + echo "无法识别 ${confirm}" ;; + esac + done } # 引导式发布应用 function releaseApp() { - # 输入执行参数 - app="" - branch="" - profile="" - chooseAppName - chooseOper - if [ "${oper}" == "stop" ]; then - confirmChoice - if [ "${app}" == "all" ]; then - ${SCRIPT_DIR}/${app}-run.sh stop ${profile} - else - ${SCRIPT_DIR}/${app}-run.sh stop ${profile} - fi - else - chooseBranch - chooseProfile - confirmChoice - if [ "${app}" == "all" ]; then - ${SCRIPT_DIR}/js-app-release.sh ${branch} ${profile} + # 输入执行参数 + app="" + branch="" + profile="" + chooseAppName + chooseOper + if [ "${oper}" == "stop" ]; then + confirmChoice + if [ "${app}" == "all" ]; then + ${SCRIPT_DIR}/${app}-run.sh stop ${profile} + else + ${SCRIPT_DIR}/${app}-run.sh stop ${profile} + fi else - ${SCRIPT_DIR}/${app}-release.sh ${branch} ${profile} + chooseBranch + chooseProfile + confirmChoice + if [ "${app}" == "all" ]; then + ${SCRIPT_DIR}/js-app-release.sh ${branch} ${profile} + else + ${SCRIPT_DIR}/${app}-release.sh ${branch} ${profile} + fi fi - fi } # 脚本主方法 function main() { -printHeadInfo -while read sign -do - case ${sign} in - exit) - echo "主动退出脚本" - exit 0 ;; - * ) - releaseApp ;; - esac - - # 装载函数库 - printFootInfo -done + printHeadInfo + while read sign + do + case ${sign} in + exit) + echo "主动退出脚本" + exit 0 ;; + *) + releaseApp ;; + esac + + # 装载函数库 + printFootInfo + done } ######################################## MAIN ######################################## @@ -192,7 +192,8 @@ done export LANG="zh_CN.UTF-8" # 设置全局常量 -SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) +SCRIPT_DIR=$(cd "$(dirname "$0")"; +pwd) SOURCE_DIR=/home/zp/source/ # 装载函数库 diff --git a/codes/linux/build/update-code.sh b/codes/linux/build/update-code.sh index 5a3a0df6..cadaa7e7 100644 --- a/codes/linux/build/update-code.sh +++ b/codes/linux/build/update-code.sh @@ -8,78 +8,78 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 checkInput() { - if [ "${repository}" == "" ] || [ "${branch}" == "" ]; then - echo "请输入脚本参数:repository branch [source] [target]" - echo " repository: git 仓储(必填)。" - echo " branch: git 分支(必填)。如 master/develop" - echo " source: 代码存放目录。默认为/home/zp/source。" - echo " target: 代码存放目录。默认为脚本所在目录。" - exit 1 - fi + if [ "${repository}" == "" ] || [ "${branch}" == "" ]; then + echo "请输入脚本参数:repository branch [source] [target]" + echo " repository: git 仓储(必填)。" + echo " branch: git 分支(必填)。如 master/develop" + echo " source: 代码存放目录。默认为/home/zp/source。" + echo " target: 代码存放目录。默认为脚本所在目录。" + exit 1 + fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { - if [ ! -d "$1" ];then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 判断 git 版本库是否存在。根据实际结果修改 ${gitok} 值。 gitok=false function isGitExist() { - cd ${SOURCE_DIR} - if [ -d "${SOURCE_DIR}/${repository}/${target}" ]; then - cd ${SOURCE_DIR}/${repository}/${target} - #(1)删除git状态零时文件 - if [ -f "gitstatus.tmp" ]; then - rm -rf gitstatus.tmp - fi - - #(2) 判断是否存在.git目录 - if [ -d "./.git" ]; then - #(3) 判断git是否可用 - git status &> gitstatus.tmp - grep -iwq 'not a git repository' gitstatus.tmp && gitok=false || gitok=true - fi - - #返回到主目录 cd ${SOURCE_DIR} - fi + if [ -d "${SOURCE_DIR}/${repository}/${target}" ]; then + cd ${SOURCE_DIR}/${repository}/${target} + #(1)删除git状态零时文件 + if [ -f "gitstatus.tmp" ]; then + rm -rf gitstatus.tmp + fi + + #(2) 判断是否存在.git目录 + if [ -d "./.git" ]; then + #(3) 判断git是否可用 + git status &> gitstatus.tmp + grep -iwq 'not a git repository' gitstatus.tmp && gitok=false || gitok=true + fi + + #返回到主目录 + cd ${SOURCE_DIR} + fi } # 如果 git 版本库存在(根据 ${gitok} 值),执行 fetch 操作;反之,执行 clone 操作。 function doFetchOrClone() { - if ${gitok}; then - cd ${SOURCE_DIR}/${repository}/${target} - git reset --hard - git clean -ffdx - git fetch - echo "git fetch ${repository} remote repository 到本地成功" - else - #删除所有内容,便于重新进行git clone - rm -rf ${repository} - git clone --no-checkout git@github.com:${GITHUB_ACCOUNT}/${repository}.git ${SOURCE_DIR}/${repository}/${target} - echo "git clone ${repository} remote repository 到本地成功" - cd ${SOURCE_DIR}/${repository}/${target} - fi + if ${gitok}; then + cd ${SOURCE_DIR}/${repository}/${target} + git reset --hard + git clean -ffdx + git fetch + echo "git fetch ${repository} remote repository 到本地成功" + else + #删除所有内容,便于重新进行git clone + rm -rf ${repository} + git clone --no-checkout git@github.com:${GITHUB_ACCOUNT}/${repository}.git ${SOURCE_DIR}/${repository}/${target} + echo "git clone ${repository} remote repository 到本地成功" + cd ${SOURCE_DIR}/${repository}/${target} + fi } # 切换到 ${branch} 分支 function doCheckout() { - echo "检出 ${repository} ${branch} 分支代码" - isRemoteBranch=false - gitRemoteBranch=`git branch -r` - echo -e "$gitRemoteBranch" | grep -iwq ${branch} && isRemoteBranch=true || isRemoteBranch=false - if ${isRemoteBranch}; then - echo "找到 ${branch} 分支。" - git checkout -f 'origin/'${branch} - else - echo "未找到 ${branch} 分支!" - exit 2 - fi - echo "更新子模块代码" - git submodule update --init --recursive --force + echo "检出 ${repository} ${branch} 分支代码" + isRemoteBranch=false + gitRemoteBranch=`git branch -r` + echo -e "$gitRemoteBranch" | grep -iwq ${branch} && isRemoteBranch=true || isRemoteBranch=false + if ${isRemoteBranch}; then + echo "找到 ${branch} 分支。" + git checkout -f 'origin/'${branch} + else + echo "未找到 ${branch} 分支!" + exit 2 + fi + echo "更新子模块代码" + git submodule update --init --recursive --force } ######################################## MAIN ######################################## @@ -96,7 +96,7 @@ checkInput GITHUB_ACCOUNT=dunwu SOURCE_DIR=/home/xyz/source if [ "${source}" != "" ]; then - SOURCE_DIR=${source} + SOURCE_DIR=${source} fi createFolderIfNotExist ${SOURCE_DIR} diff --git a/codes/linux/download.sh b/codes/linux/download.sh index ee6d323c..5300e3a0 100644 --- a/codes/linux/download.sh +++ b/codes/linux/download.sh @@ -29,15 +29,17 @@ printf "${RESET}" path=/home/scripts/linux-tutorial printf "\n${GREEN}>>>>>>>> Download linux-tutorial to ${path} begin.${RESET}\n" -command -v yum >/dev/null 2>&1 || { printf "${RED}Not detected yum.${RESET}"; exit 1; } -command -v git >/dev/null 2>&1 || { printf "${YELLOW}Not detected git. Install git.${RESET}\n"; yum -y install git; } +command -v yum > /dev/null 2>&1 || { printf "${RED}Not detected yum.${RESET}"; + exit 1; } +command -v git > /dev/null 2>&1 || { printf "${YELLOW}Not detected git. Install git.${RESET}\n"; + yum -y install git; } if [[ -d ${path} ]]; then - cd ${path} - git pull + cd ${path} + git pull else - mkdir -p ${path} - git clone https://gitee.com/turnon/linux-tutorial.git ${path} + mkdir -p ${path} + git clone https://gitee.com/turnon/linux-tutorial.git ${path} fi chmod +x -R ${path} printf "\n${GREEN}<<<<<<<< Download linux-tutorial to ${path} end.${RESET}\n" diff --git a/codes/linux/dunwu-ops.sh b/codes/linux/dunwu-ops.sh index 161554ef..0422d6db 100644 --- a/codes/linux/dunwu-ops.sh +++ b/codes/linux/dunwu-ops.sh @@ -14,8 +14,8 @@ RESET="$(tput sgr0)" # 打印头部信息 printHeadInfo() { -printf "${BLUE}" -cat << EOF + printf "${BLUE}" + cat << EOF *********************************************************************************** * 欢迎使用 Linux CentOS 环境运维脚本 @@ -23,67 +23,68 @@ cat << EOF *********************************************************************************** EOF -printf "${RESET}" + printf "${RESET}" } # 打印尾部信息 printFootInfo() { -printf "${BLUE}" -cat << EOF + printf "${BLUE}" + cat << EOF *********************************************************************************** * 脚本执行结束,感谢使用! *********************************************************************************** EOF -printf "${RESET}" + printf "${RESET}" } # 检查操作系统环境 -checkOsVersion(){ - if(($1 == 1)); then - echo -e "检查操作系统环境是否兼容本套脚本" +checkOsVersion() { + if (($1 == 1)); then + echo -e "检查操作系统环境是否兼容本套脚本" - platform=`uname -i` - if [[ ${platform} != "x86_64" ]]; then - echo "脚本仅支持 64 位操作系统!" - exit 1 - fi + platform=`uname -i` + if [[ ${platform} != "x86_64" ]]; then + echo "脚本仅支持 64 位操作系统!" + exit 1 + fi - version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` - if [[ ${version} != 7 ]]; then - echo "脚本仅支持 CentOS 7!" - exit 1 - fi + version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` + if [[ ${version} != 7 ]]; then + echo "脚本仅支持 CentOS 7!" + exit 1 + fi - echo -e "脚本可以在本环境运行!" - fi + echo -e "脚本可以在本环境运行!" + fi } -menus=("配置系统" "安装软件" "退出") +menus=( "配置系统" "安装软件" "退出" ) main() { -PS3="请输入命令编号:" -select item in ${menus[@]} -do -case ${item} in - "配置系统") - ./dunwu-sys.sh - main ;; - "安装软件") - ./dunwu-soft.sh - main ;; - "退出") - exit 0 ;; - *) - printf "输入项不支持!\n" - main ;; -esac -break -done + PS3="请输入命令编号:" + select item in ${menus[@]} + do + case ${item} in + "配置系统") + ./dunwu-sys.sh + main ;; + "安装软件") + ./dunwu-soft.sh + main ;; + "退出") + exit 0 ;; + *) + printf "输入项不支持!\n" + main ;; + esac + break + done } ######################################## MAIN ######################################## -path=$(cd "$(dirname "$0")"; pwd) +path=$(cd "$(dirname "$0")"; +pwd) printHeadInfo checkOsVersion 0 main diff --git a/codes/linux/dunwu-soft.sh b/codes/linux/dunwu-soft.sh index fdb933fc..049acbf4 100644 --- a/codes/linux/dunwu-soft.sh +++ b/codes/linux/dunwu-soft.sh @@ -24,37 +24,38 @@ EOF # print menu printf "${PURPLE}" -menus=(docker fastdfs gitlab jdk8 jenkins kafka maven mongodb mysql nacos nexus nginx nodejs redis rocketmq tomcat8 -zookeeper zsh exit) +menus=( docker fastdfs gitlab jdk8 jenkins kafka maven mongodb mysql nacos nexus nginx nodejs redis rocketmq tomcat8 +zookeeper zsh exit ) for i in "${!menus[@]}"; do - index=`expr ${i} + 1` - val=`expr ${index} % 2` - printf "[%02d] %-20s" "${index}" "${menus[$i]}" - if [[ ${val} -eq 0 ]]; then - printf "\n" - fi + index=`expr ${i} + 1` + val=`expr ${index} % 2` + printf "[%02d] %-20s" "${index}" "${menus[$i]}" + if [[ ${val} -eq 0 ]]; then + printf "\n" + fi done printf "\n${RESET}请输入需要安装的软件编号:\n" # exec shell to install soft doInstall() { -read -t 30 index -if [[ -n ${index} ]]; then - no=`expr ${index} - 1` - len=${#menus[*]} - if [[ ${index} -gt ${len} ]]; then - echo "输入项不支持!" - exit -1 - fi - key=${menus[$no]} - if [[ key == 'exit' ]]; then - exit 0 - fi - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/${key}-install.sh | bash - doInstall -else - echo "输入项不支持!" - exit -1 -fi + read -t 30 index + if [[ -n ${index} ]]; then + no=`expr ${index} - 1` + len=${#menus[*]} + if [[ ${index} -gt ${len} ]]; then + echo "输入项不支持!" + exit -1 + fi + key=${menus[$no]} + if [[ key == 'exit' ]]; then + exit 0 + fi + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/${key}-install.sh | bash + doInstall + else + echo "输入项不支持!" + exit -1 + fi } + doInstall diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index f1d8747f..e493bde4 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -9,38 +9,38 @@ cat << EOF EOF -menus=("替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出") +menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { -PS3="请输入命令编号:" -select item in ${menus[@]} -do -case ${item} in - "替换yum镜像") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/change-yum-repo.sh | bash - main ;; - "安装基本的命令工具") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash - main ;; - "安装常用libs") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash - main ;; - "系统配置") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sys-settings.sh | bash - main ;; - "全部执行") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/change-yum-repo.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sys-settings.sh | bash - printf "执行完毕,退出。\n" ;; - "退出") - exit 0 ;; - *) - printf "输入项不支持!\n" - main ;; -esac -break -done + PS3="请输入命令编号:" + select item in ${menus[@]} + do + case ${item} in + "替换yum镜像") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/change-yum-repo.sh | bash + main ;; + "安装基本的命令工具") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash + main ;; + "安装常用libs") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash + main ;; + "系统配置") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sys-settings.sh | bash + main ;; + "全部执行") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/change-yum-repo.sh | bash + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools | bash + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sys-settings.sh | bash + printf "执行完毕,退出。\n" ;; + "退出") + exit 0 ;; + *) + printf "输入项不支持!\n" + main ;; + esac + break + done } ######################################## MAIN ######################################## diff --git a/codes/linux/soft/docker-install.sh b/codes/linux/soft/docker-install.sh index 93056976..549406ae 100644 --- a/codes/linux/soft/docker-install.sh +++ b/codes/linux/soft/docker-install.sh @@ -26,16 +26,16 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install docker begin.${RESET}\n" # uninstall old version docker -sudo yum remove docker \ - docker-client \ - docker-client-latest \ - docker-common \ - docker-latest \ - docker-latest-logrotate \ - docker-logrotate \ - docker-selinux \ - docker-engine-selinux \ - docker-engine +sudo yum remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-selinux \ + docker-engine-selinux \ + docker-engine # install required libs sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # add docker yum repo diff --git a/codes/linux/soft/elk/boot-elk.sh b/codes/linux/soft/elk/boot-elk.sh index 31ee08fa..501e36e7 100644 --- a/codes/linux/soft/elk/boot-elk.sh +++ b/codes/linux/soft/elk/boot-elk.sh @@ -2,50 +2,50 @@ # 检查脚本输入参数 checkInput() { - if [ "${app}" == "" ] || [ "${oper}" == "" ]; then - echo "请输入脚本参数:name" - echo " app: 要启动的进程关键字(必填)。可选值:elasticsearch|logstash|kibana|filebeat" - echo " oper: 执行操作(必填)。可选值:start|stop" - echo "例:./boot-elk.sh logstash start" - exit 0 - fi + if [ "${app}" == "" ] || [ "${oper}" == "" ]; then + echo "请输入脚本参数:name" + echo " app: 要启动的进程关键字(必填)。可选值:elasticsearch|logstash|kibana|filebeat" + echo " oper: 执行操作(必填)。可选值:start|stop" + echo "例:./boot-elk.sh logstash start" + exit 0 + fi - if [ "${app}" != "elasticsearch" ] && [ "${app}" != "logstash" ] && [ "${app}" != "kibana" ] && [ "${app}" != "filebeat" ]; then - echo "name 输入错误" - echo "可选值:elasticsearch|logstash|kibana|filebeat" - exit 0 - fi + if [ "${app}" != "elasticsearch" ] && [ "${app}" != "logstash" ] && [ "${app}" != "kibana" ] && [ "${app}" != "filebeat" ]; then + echo "name 输入错误" + echo "可选值:elasticsearch|logstash|kibana|filebeat" + exit 0 + fi } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } startup() { - if [ "${app}" == "elasticsearch" ]; then - checkFileExist ${ELASTICSEARCH_BIN_PATH}/elasticsearch - nohup sh ${ELASTICSEARCH_BIN_PATH}/elasticsearch >>${ELASTICSEARCH_BIN_PATH}/nohup.out 2>&1 & - elif [ "${app}" == "logstash" ]; then - checkFileExist ${LOGSTASH_BIN_PATH}/logstash - nohup sh ${LOGSTASH_BIN_PATH}/logstash -f ${LOGSTASH_BIN_PATH}/logstash.conf >>${LOGSTASH_BIN_PATH}/nohup.out 2>&1 & - elif [ "${app}" == "kibana" ]; then - checkFileExist ${KIBANA_BIN_PATH}/kibana - nohup sh ${KIBANA_BIN_PATH}/kibana >> ${KIBANA_BIN_PATH}/nohup.out 2>&1 & - elif [ "${app}" == "filebeat" ]; then - checkFileExist ${FILEBEAT_PATH}/filebeat - touch ${FILEBEAT_PATH}/nohup.out - nohup ${FILEBEAT_PATH}/filebeat -e -c ${FILEBEAT_PATH}/filebeat.yml -d "publish" >> ${FILEBEAT_PATH}/nohup.out 2>&1 & - fi + if [ "${app}" == "elasticsearch" ]; then + checkFileExist ${ELASTICSEARCH_BIN_PATH}/elasticsearch + nohup sh ${ELASTICSEARCH_BIN_PATH}/elasticsearch >> ${ELASTICSEARCH_BIN_PATH}/nohup.out 2>&1 & + elif [ "${app}" == "logstash" ]; then + checkFileExist ${LOGSTASH_BIN_PATH}/logstash + nohup sh ${LOGSTASH_BIN_PATH}/logstash -f ${LOGSTASH_BIN_PATH}/logstash.conf >> ${LOGSTASH_BIN_PATH}/nohup.out 2>&1 & + elif [ "${app}" == "kibana" ]; then + checkFileExist ${KIBANA_BIN_PATH}/kibana + nohup sh ${KIBANA_BIN_PATH}/kibana >> ${KIBANA_BIN_PATH}/nohup.out 2>&1 & + elif [ "${app}" == "filebeat" ]; then + checkFileExist ${FILEBEAT_PATH}/filebeat + touch ${FILEBEAT_PATH}/nohup.out + nohup ${FILEBEAT_PATH}/filebeat -e -c ${FILEBEAT_PATH}/filebeat.yml -d "publish" >> ${FILEBEAT_PATH}/nohup.out 2>&1 & + fi } shutdown() { - pid=`ps -ef | grep java | grep ${app} | awk '{print $2}'` - kill -9 ${pid} + pid=`ps -ef | grep java | grep ${app} | awk '{print $2}'` + kill -9 ${pid} } ##############################__MAIN__######################################## @@ -60,14 +60,14 @@ FILEBEAT_PATH=/opt/elastic/filebeat-${version}-linux-x86_64 checkInput case ${oper} in - start) - echo "启动 ${app}" - startup + start) + echo "启动 ${app}" + startup ;; - stop) - echo "终止 ${app}" - shutdown + stop) + echo "终止 ${app}" + shutdown ;; - * ) echo "${oper} is invalid oper";; + *) echo "${oper} is invalid oper" ;; esac diff --git a/codes/linux/soft/elk/install-elk.sh b/codes/linux/soft/elk/install-elk.sh index 87519603..9b39fec9 100644 --- a/codes/linux/soft/elk/install-elk.sh +++ b/codes/linux/soft/elk/install-elk.sh @@ -9,98 +9,99 @@ # 获取当前设备IP ipaddr='127.0.0.1' function getDeviceIp() { - ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') + ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } init() { - mkdir -p ${ELASTIC_SOFTWARE_PATH} - getDeviceIp + mkdir -p ${ELASTIC_SOFTWARE_PATH} + getDeviceIp } # 安装 elasticsearch installElasticsearch() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${version}.tar.gz - tar -xzf elasticsearch-${version}.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${version}.tar.gz + tar -xzf elasticsearch-${version}.tar.gz } installRuby() { - cd ${RUBY_SOFTWARE_PATH} - wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz - tar -xzf ruby-2.5.0.tar.gz - cd ruby-2.5.0 - ./configure - make & make install + cd ${RUBY_SOFTWARE_PATH} + wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz + tar -xzf ruby-2.5.0.tar.gz + cd ruby-2.5.0 + ./configure + make & make install } # 安装 logstash installLogstash() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/logstash/logstash-${version}.tar.gz - tar -xzf logstash-${version}.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/logstash/logstash-${version}.tar.gz + tar -xzf logstash-${version}.tar.gz } # 安装 kibana installKibana() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/kibana/kibana-${version}-linux-x86_64.tar.gz - tar -xzf kibana-${version}-linux-x86_64.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/kibana/kibana-${version}-linux-x86_64.tar.gz + tar -xzf kibana-${version}-linux-x86_64.tar.gz } # 安装 filebeat installFilebeat() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${version}-linux-x86_64.tar.gz - tar -zxf filebeat-${version}-linux-x86_64.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${version}-linux-x86_64.tar.gz + tar -zxf filebeat-${version}-linux-x86_64.tar.gz } # 替换 Elasticsearch 配置 # 1. 替换 192.168.0.1 为本机 IP replaceElasticsearchConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml.bak - sed -i "s/#network.host: 192.168.0.1/network.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml - touch ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/bin/nohup.out + cp ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml.bak + sed -i "s/#network.host: 192.168.0.1/network.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml + touch ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/bin/nohup.out } replaceLogstashConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml.bak - sed -i "s/# http.host: \"127.0.0.1\"/ http.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml - touch ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin/nohup.out - cd ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin - wget "https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/elk/config/logstash.conf" + cp ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml.bak + sed -i "s/# http.host: \"127.0.0.1\"/ http.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml + touch ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin/nohup.out + cd ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin + wget "https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/elk/config/logstash.conf" } # 替换 Kibana 配置 # 1. 替换 localhost 为本机 IP replaceKibanaConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml.bak - sed -i "s/#server.host: \"localhost\"/server.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml - sed -i "s/#elasticsearch.url: \"http://localhost:9200\"/#elasticsearch.url: \"${IP}\"/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml - touch ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/bin/nohup.out + cp ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml.bak + sed -i "s/#server.host: \"localhost\"/server.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml + sed -i "s/#elasticsearch.url: \"http://localhost:9200\"/#elasticsearch.url: \"${IP}\"/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml + touch ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/bin/nohup.out } # 替换 Filebeat 配置 replaceFilebeatConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml.bak - cd ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64 - wget https://github.com/dunwu/OS/blob/master/codes/deploy/tool/elk/config/filebeat.yml - sed -i 's/127.0.0.1/'"${IP}"'/g' ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml + cp ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml.bak + cd ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64 + wget https://github.com/dunwu/OS/blob/master/codes/deploy/tool/elk/config/filebeat.yml + sed -i 's/127.0.0.1/'"${IP}"'/g' ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml } # 为 elk.elk 用户设置权限 setPrivilegeForUser() { - chown -R elk.elk ${ELASTIC_SOFTWARE_PATH} - chown -R elk.elk /var/log/ + chown -R elk.elk ${ELASTIC_SOFTWARE_PATH} + chown -R elk.elk /var/log/ } + ######################################## MAIN ######################################## echo -e "\n>>>>>>>>> install elk" diff --git a/codes/linux/soft/elk/install_elasticserch.sh b/codes/linux/soft/elk/install_elasticserch.sh index 95113905..479b3e3a 100644 --- a/codes/linux/soft/elk/install_elasticserch.sh +++ b/codes/linux/soft/elk/install_elasticserch.sh @@ -1,4 +1,5 @@ #!/bin/bash + # auth:kaliarch # version:v1.0 # func:elasticsearch 5.4.1/6.0.1/6.3.1安装 @@ -12,9 +13,9 @@ env_file=/etc/profile.d/elasticsearch.sh install_log_path=/var/log/appinstall/ install_path=/usr/local/ software_config_file=${install_path}elasticsearch/config/elasticsearch.yml -sysversion=$(rpm -q centos-release|cut -d- -f3) +sysversion=$(rpm -q centos-release | cut -d- -f3) jvm_conf="/usr/local/elasticsearch/config/jvm.options" -sys_mem=`free -m|grep Mem:|awk '{print $2}'|awk '{sum+=$1} END {print sum/1024}'|cut -d. -f1` +sys_mem=`free -m | grep Mem: | awk '{print $2}' | awk '{sum+=$1} END {print sum/1024}' | cut -d. -f1` hostname=elk-server clear @@ -29,23 +30,23 @@ echo "3: Install elasticsearch 6.3.1" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion -if [ "${softversion}" == "1" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-5.4.1.tar.gz" -elif [ "${softversion}" == "2" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.0.1.tar.gz" -elif [ "${softversion}" == "3" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.3.1.tar.gz" -elif [ "${softversion}" == "4" ];then - echo "you choce channel!" - exit 1; +if [ "${softversion}" == "1" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-5.4.1.tar.gz" +elif [ "${softversion}" == "2" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.0.1.tar.gz" +elif [ "${softversion}" == "3" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.3.1.tar.gz" +elif [ "${softversion}" == "4" ]; then + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4|5}" - exit 0; + echo "input Error! Place input{1|2|3|4|5}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*;do + for msg in $*; do action $msg /bin/true done } @@ -53,21 +54,21 @@ output_msg() { # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 >/dev/null 2>&1 - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` check command $1 ">>${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 >/dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" - for dirname in $*;do - [ -d $dirname ] || mkdir -p $dirname >/dev/null 2>&1 + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } @@ -75,13 +76,13 @@ check_dir() { # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" - mkdir -p $download_path - for file in $*;do + mkdir -p $download_path + for file in $*; do wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` $file download success!">>${install_log_path}${install_log_name} + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else - echo "`date +%F' '%H:%M:%s` $file download fail!">>${install_log_path}${install_log_name} && exit 1 + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } @@ -89,54 +90,54 @@ download_file() { # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*;do - if [ "${file##*.}" == "gz" ];then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ];then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!">>${install_log_path}${install_log_name} && exit 1 - fi + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" - - echo "export PATH=\$PATH:$1" >${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!">> ${install_log_path}${install_log_name} + + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 配置主机名,第一个为主机名 config_hostname() { -if [ ${sysversion} -eq 6 ];then - hostname $1 -elif [ ${sysversion} -eq 7 ];then - hostnamectl set-hostname $1 -else - echo "`date +%F' '%H:%M:%S` hostname $1 config fail">> ${install_log_path}${install_log_name} -fi + if [ ${sysversion} -eq 6 ]; then + hostname $1 + elif [ ${sysversion} -eq 7 ]; then + hostnamectl set-hostname $1 + else + echo "`date +%F' '%H:%M:%S` hostname $1 config fail" >> ${install_log_path}${install_log_name} + fi } config_limits() { output_msg "配置limits" - cat >>/etc/security/limits.conf<> /etc/security/limits.conf << EOF +* soft nofile 65536 +* hard nofile 65536 +* soft nproc 65536 * hard nproc 65536 EOF -echo "vm.max_map_count = 655360" >>/etc/sysctl.conf -sysctl -p >/dev/null 2>&1 + echo "vm.max_map_count = 655360" >> /etc/sysctl.conf + sysctl -p > /dev/null 2>&1 } # 添加配置文件 add_config() { -cat > $1 << EOF + cat > $1 << EOF cluster.name: my-application node.name: ${hostname} path.data: /usr/local/elasticsearch/data @@ -148,46 +149,46 @@ EOF } config_user() { -useradd $1 >/dev/null 2>&1 -if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` $1 user add success">> ${install_log_path}${install_log_name} -else - echo "`date +%F' '%H:%M:%S` $1 user add fail">> ${install_log_path}${install_log_name} && exit 1 -fi -chown ${1}.${1} ${install_path}elasticsearch/ -R + useradd $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $1 user add success" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $1 user add fail" >> ${install_log_path}${install_log_name} && exit 1 + fi + chown ${1}.${1} ${install_path}elasticsearch/ -R } config_jvm() { -if [ ${sys_mem} -eq 0 ];then - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} -else - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} -fi + if [ ${sys_mem} -eq 0 ]; then + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} + else + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} + fi } main() { -check_dir $install_log_path $install_path -check_yum_command wget wget -download_file $URL -config_hostname $hostname - -software_name=$(echo $URL|awk -F'/' '{print $NF}'|awk -F'.tar.gz' '{print $1}') -for filename in `ls $download_path`;do - extract_file ${download_path}$filename -done - -rm -fr ${download_path} -ln -s $install_path$software_name ${install_path}elasticsearch -add_config $software_config_file -check_dir ${install_path}elasticsearch/{data,logs} -config_user elasticsearch -config_env ${install_path}elasticsearch/bin -config_limits -config_jvm -echo "请使用一下命令启动服务:'su - elasticsearch -c 'nohup /usr/local/elasticsearch/bin/elasticsearch &'" + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + config_hostname $hostname + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + + rm -fr ${download_path} + ln -s $install_path$software_name ${install_path}elasticsearch + add_config $software_config_file + check_dir ${install_path}elasticsearch/{data,logs} + config_user elasticsearch + config_env ${install_path}elasticsearch/bin + config_limits + config_jvm + echo "请使用一下命令启动服务:'su - elasticsearch -c 'nohup /usr/local/elasticsearch/bin/elasticsearch &'" } diff --git a/codes/linux/soft/elk/install_filebeat.sh b/codes/linux/soft/elk/install_filebeat.sh index 6c09aec2..bd8265ee 100644 --- a/codes/linux/soft/elk/install_filebeat.sh +++ b/codes/linux/soft/elk/install_filebeat.sh @@ -1,4 +1,5 @@ #!/bin/bash + # auth:kaliarch # version:v1.0 # func:filebeat 5.6.1/6.1.3/6.3.2 安装 @@ -26,23 +27,23 @@ echo "3: Install filebeat 6.3.2" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion -if [ "${softversion}" == "1" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-5.6.1-linux-x86_64.tar.gz" -elif [ "${softversion}" == "2" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.1.3-linux-x86_64.tar.gz" -elif [ "${softversion}" == "3" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.3.2-linux-x86_64.tar.gz" -elif [ "${softversion}" == "4" ];then - echo "you choce channel!" - exit 1; +if [ "${softversion}" == "1" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-5.6.1-linux-x86_64.tar.gz" +elif [ "${softversion}" == "2" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.1.3-linux-x86_64.tar.gz" +elif [ "${softversion}" == "3" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.3.2-linux-x86_64.tar.gz" +elif [ "${softversion}" == "4" ]; then + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*;do + for msg in $*; do action $msg /bin/true done } @@ -50,21 +51,21 @@ output_msg() { # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 >/dev/null 2>&1 - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` check command $1 ">>${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 >/dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" - for dirname in $*;do - [ -d $dirname ] || mkdir -p $dirname >/dev/null 2>&1 + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } @@ -72,13 +73,13 @@ check_dir() { # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" - mkdir -p $download_path - for file in $*;do + mkdir -p $download_path + for file in $*; do wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` $file download success!">>${install_log_path}${install_log_name} + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else - echo "`date +%F' '%H:%M:%s` $file download fail!">>${install_log_path}${install_log_name} && exit 1 + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } @@ -86,29 +87,29 @@ download_file() { # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*;do - if [ "${file##*.}" == "gz" ];then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ];then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!">>${install_log_path}${install_log_name} && exit 1 - fi + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" >${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!">> ${install_log_path}${install_log_name} + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { -cat> $1 < $1 << EOF filebeat.prospectors: - input_type: log paths: @@ -119,17 +120,17 @@ EOF } main() { -check_dir $install_log_path $install_path -check_yum_command wget wget -download_file $URL - -software_name=$(echo $URL|awk -F'/' '{print $NF}'|awk -F'.tar.gz' '{print $1}') -for filename in `ls $download_path`;do - extract_file ${download_path}$filename -done -rm -fr ${download_path} -ln -s $install_path$software_name ${install_path}filebeat -add_config ${software_config_file} + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + rm -fr ${download_path} + ln -s $install_path$software_name ${install_path}filebeat + add_config ${software_config_file} } main diff --git a/codes/linux/soft/elk/install_kibana.sh b/codes/linux/soft/elk/install_kibana.sh index 55325317..7359d2d7 100644 --- a/codes/linux/soft/elk/install_kibana.sh +++ b/codes/linux/soft/elk/install_kibana.sh @@ -1,4 +1,5 @@ #!/bin/bash + # auth:kaliarch # version:v1.0 # func:kibana 6.0.1/6.2.4/6.3.1 安装 @@ -26,23 +27,23 @@ echo "3: Install kibana 6.3.1" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion -if [ "${softversion}" == "1" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.0.1-linux-x86_64.tar.gz" -elif [ "${softversion}" == "2" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.2.4-linux-x86_64.tar.gz" -elif [ "${softversion}" == "3" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.3.1-linux-x86_64.tar.gz" -elif [ "${softversion}" == "4" ];then - echo "you choce channel!" - exit 1; +if [ "${softversion}" == "1" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.0.1-linux-x86_64.tar.gz" +elif [ "${softversion}" == "2" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.2.4-linux-x86_64.tar.gz" +elif [ "${softversion}" == "3" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.3.1-linux-x86_64.tar.gz" +elif [ "${softversion}" == "4" ]; then + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*;do + for msg in $*; do action $msg /bin/true done } @@ -50,21 +51,21 @@ output_msg() { # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 >/dev/null 2>&1 - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` check command $1 ">>${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 >/dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" - for dirname in $*;do - [ -d $dirname ] || mkdir -p $dirname >/dev/null 2>&1 + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } @@ -72,13 +73,13 @@ check_dir() { # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" - mkdir -p $download_path - for file in $*;do + mkdir -p $download_path + for file in $*; do wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` $file download success!">>${install_log_path}${install_log_name} + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else - echo "`date +%F' '%H:%M:%s` $file download fail!">>${install_log_path}${install_log_name} && exit 1 + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } @@ -86,29 +87,29 @@ download_file() { # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*;do - if [ "${file##*.}" == "gz" ];then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ];then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!">>${install_log_path}${install_log_name} && exit 1 - fi + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" >${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!">> ${install_log_path}${install_log_name} + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { -cat> $1 < $1 << EOF server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "http://127.0.0.1:9200" @@ -116,18 +117,18 @@ EOF } main() { -check_dir $install_log_path $install_path -check_yum_command wget wget -download_file $URL - -software_name=$(echo $URL|awk -F'/' '{print $NF}'|awk -F'.tar.gz' '{print $1}') -for filename in `ls $download_path`;do - extract_file ${download_path}$filename -done -rm -fr ${download_path} -ln -s ${install_path}$software_name ${install_path}kibana -add_config ${software_config_file} -config_env ${install_path}kibana/bin + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + rm -fr ${download_path} + ln -s ${install_path}$software_name ${install_path}kibana + add_config ${software_config_file} + config_env ${install_path}kibana/bin } main diff --git a/codes/linux/soft/elk/install_logstash.sh b/codes/linux/soft/elk/install_logstash.sh index 673e52de..7300bff3 100644 --- a/codes/linux/soft/elk/install_logstash.sh +++ b/codes/linux/soft/elk/install_logstash.sh @@ -1,4 +1,5 @@ #!/bin/bash + # auth:kaliarch # version:v1.0 # func:logstash 5.4/6.1/6.3 安装 @@ -26,23 +27,23 @@ echo "3: Install logstash-6.3" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion -if [ "${softversion}" == "1" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-5.4.1.tar.gz" -elif [ "${softversion}" == "2" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.1.3.tar.gz" -elif [ "${softversion}" == "3" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.3.2.tar.gz" -elif [ "${softversion}" == "4" ];then - echo "you choce channel!" - exit 1; +if [ "${softversion}" == "1" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-5.4.1.tar.gz" +elif [ "${softversion}" == "2" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.1.3.tar.gz" +elif [ "${softversion}" == "3" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.3.2.tar.gz" +elif [ "${softversion}" == "4" ]; then + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*;do + for msg in $*; do action $msg /bin/true done } @@ -50,21 +51,21 @@ output_msg() { # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 >/dev/null 2>&1 - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` check command $1 ">>${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 >/dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" - for dirname in $*;do - [ -d $dirname ] || mkdir -p $dirname >/dev/null 2>&1 + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } @@ -72,13 +73,13 @@ check_dir() { # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" - mkdir -p $download_path - for file in $*;do + mkdir -p $download_path + for file in $*; do wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` $file download success!">>${install_log_path}${install_log_name} + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else - echo "`date +%F' '%H:%M:%s` $file download fail!">>${install_log_path}${install_log_name} && exit 1 + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } @@ -86,29 +87,29 @@ download_file() { # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*;do - if [ "${file##*.}" == "gz" ];then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ];then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path">>${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!">>${install_log_path}${install_log_name} && exit 1 - fi + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" >${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!">> ${install_log_path}${install_log_name} + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { -cat> $1 < $1 << EOF input { beats { port => "5044" @@ -124,18 +125,18 @@ EOF } main() { -check_dir $install_log_path $install_path -check_yum_command wget wget -download_file $URL - -software_name=$(echo $URL|awk -F'/' '{print $NF}'|awk -F'.tar.gz' '{print $1}') -for filename in `ls $download_path`;do - extract_file ${download_path}$filename -done -rm -fr ${download_path} -ln -s $install_path$software_name ${install_path}logstash -add_config ${software_config_file} -config_env ${install_path}logstash/bin + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + rm -fr ${download_path} + ln -s $install_path$software_name ${install_path}logstash + add_config ${software_config_file} + config_env ${install_path}logstash/bin } main diff --git a/codes/linux/soft/fastdfs-install.sh b/codes/linux/soft/fastdfs-install.sh index 0ad79b42..65617f69 100644 --- a/codes/linux/soft/fastdfs-install.sh +++ b/codes/linux/soft/fastdfs-install.sh @@ -28,19 +28,21 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install fastdfs begin.${RESET}\n" -command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } -command -v git >/dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; exit 1; } - -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh fastdfs-install.sh [path]\n" - printf "\t Example: sh fastdfs-install.sh /opt/fastdfs\n" - printf "${RESET}\n" +command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; } +command -v git > /dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; + exit 1; } + +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh fastdfs-install.sh [path]\n" + printf "\t Example: sh fastdfs-install.sh /opt/fastdfs\n" + printf "${RESET}\n" fi path=/opt/fdfs if [[ -n $1 ]]; then - path=$1 + path=$1 fi nginx_version=1.16.0 @@ -55,8 +57,8 @@ path=/opt/fdfs mkdir -p ${path}/libfastcommon curl -o ${path}/libfastcommon.zip http://dunwu.test.upcdn.net/soft/fdfs/libfastcommon.zip if [[ ! -f ${path}/libfastcommon.zip ]]; then - printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" - exit 1 + printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" + exit 1 fi unzip -o ${path}/libfastcommon.zip -d ${path} @@ -69,7 +71,7 @@ printf "${GREEN}>>>>>>>>> install fastdfs${RESET}" mkdir -p ${path}/fastdfs curl -o ${path}/fastdfs.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs.zip if [[ ! -f ${path}/fastdfs.zip ]]; then - printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" + printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" fi unzip -o ${path}/fastdfs.zip -d ${path} cd ${path}/fastdfs @@ -80,7 +82,7 @@ printf "${GREEN}>>>>>>>>> install fastdfs-nginx-module${RESET}\n" mkdir -p ${path}/fastdfs-nginx-module curl -o ${path}/fastdfs-nginx-module.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs-nginx-module.zip if [[ ! -f ${path}/fastdfs-nginx-module.zip ]]; then - printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" + printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" fi unzip -o ${path}/fastdfs-nginx-module.zip -d ${path} diff --git a/codes/linux/soft/install_grafana.sh b/codes/linux/soft/install_grafana.sh index 36ddc399..1a235db5 100644 --- a/codes/linux/soft/install_grafana.sh +++ b/codes/linux/soft/install_grafana.sh @@ -1,4 +1,5 @@ #!/bin/bash + # auth:kaliarch # version:v1.0 # func:grafana 5.1.0/5.1.5/5.2.2 安装 @@ -25,23 +26,23 @@ echo "3: Install grafana 5.2.2" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion -if [ "${softversion}" == "1" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.0-1.x86_64.rpm" -elif [ "${softversion}" == "2" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.5-1.x86_64.rpm" -elif [ "${softversion}" == "3" ];then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.2.2-1.x86_64.rpm" -elif [ "${softversion}" == "4" ];then - echo "you choce channel!" - exit 1; +if [ "${softversion}" == "1" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.0-1.x86_64.rpm" +elif [ "${softversion}" == "2" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.5-1.x86_64.rpm" +elif [ "${softversion}" == "3" ]; then + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.2.2-1.x86_64.rpm" +elif [ "${softversion}" == "4" ]; then + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*;do + for msg in $*; do action $msg /bin/true done } @@ -49,21 +50,21 @@ output_msg() { # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 >/dev/null 2>&1 - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` check command $1 ">>${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 >/dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" - for dirname in $*;do - [ -d $dirname ] || mkdir -p $dirname >/dev/null 2>&1 + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } @@ -71,13 +72,13 @@ check_dir() { # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" - mkdir -p $download_path - for file in $*;do + mkdir -p $download_path + for file in $*; do wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` $file download success!">>${install_log_path}${install_log_name} + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else - echo "`date +%F' '%H:%M:%s` $file download fail!">>${install_log_path}${install_log_name} && exit 1 + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } @@ -86,24 +87,24 @@ download_file() { install_grafana_plugins() { output_msg "grafana插件安装" check_yum_command grafana-cli - grafana-cli plugins install $* >/dev/null - if [ $? -eq 0 ];then - echo "`date +%F' '%H:%M:%S` grafana plugins $* install success!">>${install_log_path}${install_log_name} + grafana-cli plugins install $* > /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} else - echo "`date +%F' '%H:%M:%s` grafana plugins $* install success!">>${install_log_path}${install_log_name} && exit 1 + echo "`date +%F' '%H:%M:%s` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} && exit 1 fi } main() { -check_dir $install_log_path $install_path -check_yum_command wget wget -download_file $URL -for filename in `ls $download_path`;do - yum -y install $download_path$filename >/dev/null 2>&1 -done -install_grafana_plugins alexanderzobnin-zabbix-app + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + for filename in `ls $download_path`; do + yum -y install $download_path$filename > /dev/null 2>&1 + done + install_grafana_plugins alexanderzobnin-zabbix-app } main diff --git a/codes/linux/soft/jdk8-install.sh b/codes/linux/soft/jdk8-install.sh index 24618255..9ac84ea2 100644 --- a/codes/linux/soft/jdk8-install.sh +++ b/codes/linux/soft/jdk8-install.sh @@ -27,7 +27,8 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install jdk8 begin.${RESET}\n" -command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } +command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; } yum -y install java-1.8.0-openjdk-devel.x86_64 java -version diff --git a/codes/linux/soft/kafka-install.sh b/codes/linux/soft/kafka-install.sh index d7282d3c..6a31a87c 100644 --- a/codes/linux/soft/kafka-install.sh +++ b/codes/linux/soft/kafka-install.sh @@ -26,23 +26,24 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install kafka begin.${RESET}\n" -command -v java >/dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; exit 1; } - -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh kafka-install.sh [version] [path]\n" - printf "\t Example: sh kafka-install.sh 2.2.0 /opt/kafka\n" - printf "${RESET}\n" +command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; + exit 1; } + +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh kafka-install.sh [version] [path]\n" + printf "\t Example: sh kafka-install.sh 2.2.0 /opt/kafka\n" + printf "${RESET}\n" fi version=2.2.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/kafka if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/maven-install.sh b/codes/linux/soft/maven-install.sh index f6983520..399e51cd 100644 --- a/codes/linux/soft/maven-install.sh +++ b/codes/linux/soft/maven-install.sh @@ -28,23 +28,24 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install maven begin.${RESET}\n" -command -v java >/dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; exit 1; } +command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; + exit 1; } -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh maven-install.sh [version] [path]\n" - printf "\t Example: sh maven-install.sh 3.6.0 /opt/maven\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh maven-install.sh [version] [path]\n" + printf "\t Example: sh maven-install.sh 3.6.0 /opt/maven\n" + printf "${RESET}\n" fi version=3.6.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/maven if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/mongodb-install.sh b/codes/linux/soft/mongodb-install.sh index ad17a63c..f92d02e0 100644 --- a/codes/linux/soft/mongodb-install.sh +++ b/codes/linux/soft/mongodb-install.sh @@ -26,21 +26,21 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install mongodb begin.${RESET}\n" -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh mongodb-install.sh [version] [path]\n" - printf "\t Example: sh mongodb-install.sh 4.0.9 /opt/mongodb\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh mongodb-install.sh [version] [path]\n" + printf "\t Example: sh mongodb-install.sh 4.0.9 /opt/mongodb\n" + printf "${RESET}\n" fi version=4.0.9 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/mongodb if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/mysql-install.sh b/codes/linux/soft/mysql-install.sh index 066e4edd..0b3971b5 100644 --- a/codes/linux/soft/mysql-install.sh +++ b/codes/linux/soft/mysql-install.sh @@ -26,9 +26,12 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install mysql begin.${RESET}\n" -command -v wget >/dev/null 2>&1 || { printf "${RED}Require wget but it's not installed.${RESET}\n"; exit 1; } -command -v rpm >/dev/null 2>&1 || { printf "${RED}Require rpm but it's not installed.${RESET}\n"; exit 1; } -command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } +command -v wget > /dev/null 2>&1 || { printf "${RED}Require wget but it's not installed.${RESET}\n"; + exit 1; } +command -v rpm > /dev/null 2>&1 || { printf "${RED}Require rpm but it's not installed.${RESET}\n"; + exit 1; } +command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; } # 使用 rpm 安装 mysql wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm diff --git a/codes/linux/soft/nacos-install.sh b/codes/linux/soft/nacos-install.sh index 68bf8346..41828591 100644 --- a/codes/linux/soft/nacos-install.sh +++ b/codes/linux/soft/nacos-install.sh @@ -11,22 +11,24 @@ cat << EOF EOF -command -v java >/dev/null 2>&1 || { echo >&2 "Require java but it's not installed."; exit 1; } -command -v mvn >/dev/null 2>&1 || { echo >&2 "Require mvn but it's not installed."; exit 1; } +command -v java > /dev/null 2>&1 || { echo >&2 "Require java but it's not installed."; + exit 1; } +command -v mvn > /dev/null 2>&1 || { echo >&2 "Require mvn but it's not installed."; + exit 1; } -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then echo "Usage: sh nacos-install.sh [version] [path]" printf "Example: sh nacos-install.sh 1.0.0 /opt/nacos\n" fi version=1.0.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi root=/opt/nacos if [[ -n $2 ]]; then - root=$2 + root=$2 fi echo "Current execution: install nacos ${version} to ${root}" diff --git a/codes/linux/soft/nginx-install.sh b/codes/linux/soft/nginx-install.sh index fd147904..a44726c6 100644 --- a/codes/linux/soft/nginx-install.sh +++ b/codes/linux/soft/nginx-install.sh @@ -27,23 +27,24 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install maven begin.${RESET}\n" -command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } +command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; } -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh nginx-install.sh [version] [path]\n" - printf "\t Example: sh nginx-install.sh 1.16.0 /opt/nginx\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh nginx-install.sh [version] [path]\n" + printf "\t Example: sh nginx-install.sh 1.16.0 /opt/nginx\n" + printf "${RESET}\n" fi version=1.16.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/nginx if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/nodejs-install.sh b/codes/linux/soft/nodejs-install.sh index 9a84b9b0..ca11b597 100644 --- a/codes/linux/soft/nodejs-install.sh +++ b/codes/linux/soft/nodejs-install.sh @@ -27,16 +27,16 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install nodejs begin.${RESET}\n" -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh nodejs-install.sh [version]\n" - printf "\t Example: sh nodejs-install.sh 10.15.2\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh nodejs-install.sh [version]\n" + printf "\t Example: sh nodejs-install.sh 10.15.2\n" + printf "${RESET}\n" fi version=10.15.2 if [[ -n $1 ]]; then - version=$1 + version=$1 fi # install info diff --git a/codes/linux/soft/redis-install.sh b/codes/linux/soft/redis-install.sh index f1033250..ee942bfc 100644 --- a/codes/linux/soft/redis-install.sh +++ b/codes/linux/soft/redis-install.sh @@ -24,31 +24,32 @@ cat << EOF EOF printf "${RESET}" -command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } +command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; } -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]] || [[ $# -lt 3 ]] || [[ $# -lt 4 ]];then +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]] || [[ $# -lt 3 ]] || [[ $# -lt 4 ]]; then echo "Usage: sh redis-install.sh [version] [path] [port] [password]" echo -e "Example: sh redis-install.sh 5.0.4 /opt/redis 6379 123456\n" fi version=5.0.4 if [[ -n $1 ]]; then - version=$1 + version=$1 fi root=/opt/redis if [[ -n $2 ]]; then - root=$2 + root=$2 fi port=6379 if [[ -n $3 ]]; then - port=$3 + port=$3 fi password= if [[ -n $4 ]]; then - password=$4 + password=$4 fi printf "${GREEN}>>>>>>>> install redis begin.${RESET}\n" @@ -72,7 +73,7 @@ mkdir -p /etc/redis cp ${path}/redis.conf /etc/redis/${port}.conf sed -i "s/^port 6379/port ${port}/g" /etc/redis/${port}.conf if [[ -n ${password} ]]; then - sed -i "s/^# requirepass/requirepass ${password}/g" /etc/redis/${port}.conf + sed -i "s/^# requirepass/requirepass ${password}/g" /etc/redis/${port}.conf fi printf "\n${CYAN}>>>>>>>>> add firewall port${RESET}\n" diff --git a/codes/linux/soft/rocketmq-install.sh b/codes/linux/soft/rocketmq-install.sh index c7fe7710..cf4fff85 100644 --- a/codes/linux/soft/rocketmq-install.sh +++ b/codes/linux/soft/rocketmq-install.sh @@ -26,21 +26,21 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install tomcat begin.${RESET}\n" -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh rocketmq-install.sh [version] [path]\n" - printf "\t Example: sh rocketmq-install.sh 4.5.0 /opt/rocketmq\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh rocketmq-install.sh [version] [path]\n" + printf "\t Example: sh rocketmq-install.sh 4.5.0 /opt/rocketmq\n" + printf "${RESET}\n" fi version=4.5.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/rocketmq if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/tomcat8-install.sh b/codes/linux/soft/tomcat8-install.sh index 0980b153..f967bd0f 100644 --- a/codes/linux/soft/tomcat8-install.sh +++ b/codes/linux/soft/tomcat8-install.sh @@ -26,21 +26,21 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install tomcat begin.${RESET}\n" -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh tomcat8-install.sh [version] [path]\n" - printf "\t Example: sh tomcat8-install.sh 8.5.28 /opt/tomcat8\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh tomcat8-install.sh [version] [path]\n" + printf "\t Example: sh tomcat8-install.sh 8.5.28 /opt/tomcat8\n" + printf "${RESET}\n" fi version=8.5.28 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/tomcat if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/zookeeper-install.sh b/codes/linux/soft/zookeeper-install.sh index 2a8fe8fe..68cacef7 100644 --- a/codes/linux/soft/zookeeper-install.sh +++ b/codes/linux/soft/zookeeper-install.sh @@ -26,21 +26,21 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install zookeeper begin.${RESET}\n" -if [[ $# -lt 1 ]] || [[ $# -lt 2 ]];then - printf "${PURPLE}[Hint]\n" - printf "\t sh zookeeper-install.sh [version] [path]\n" - printf "\t Example: sh zookeeper-install.sh 3.4.12 /opt/zookeeper\n" - printf "${RESET}\n" +if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then + printf "${PURPLE}[Hint]\n" + printf "\t sh zookeeper-install.sh [version] [path]\n" + printf "\t Example: sh zookeeper-install.sh 3.4.12 /opt/zookeeper\n" + printf "${RESET}\n" fi version=3.4.12 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/zookeeper if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/zsh-install.sh b/codes/linux/soft/zsh-install.sh index 0831750e..ce7a0ecc 100644 --- a/codes/linux/soft/zsh-install.sh +++ b/codes/linux/soft/zsh-install.sh @@ -27,8 +27,10 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install zsh begin.${RESET}\n" -command -v yum >/dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } -command -v git >/dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; exit 1; } +command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; } +command -v git > /dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; + exit 1; } # install zsh yum install -y zsh diff --git a/codes/linux/sys/change-yum-repo.sh b/codes/linux/sys/change-yum-repo.sh index 345c0c3d..9ab7cb1e 100644 --- a/codes/linux/sys/change-yum-repo.sh +++ b/codes/linux/sys/change-yum-repo.sh @@ -22,21 +22,21 @@ cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # 根据发型版本选择相应 yum 镜像 if [[ ${version} == 5 ]]; then - # Cento5 已废弃,只能使用 http://vault.CentOS.org/ 替换,但由于是国外镜像,速度较慢 - wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-5.repo -O /etc/yum.repos.d/CentOS-Base.repo + # Cento5 已废弃,只能使用 http://vault.CentOS.org/ 替换,但由于是国外镜像,速度较慢 + wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-5.repo -O /etc/yum.repos.d/CentOS-Base.repo - # 根据实际发型版本情况替换 - detailVersion=`lsb_release -r | awk '{print substr($2,1,3)}'` - sed -i 's/$releasever/'"${detailVersion}"'/g' /etc/yum.repos.d/CentOS-Base.repo + # 根据实际发型版本情况替换 + detailVersion=`lsb_release -r | awk '{print substr($2,1,3)}'` + sed -i 's/$releasever/'"${detailVersion}"'/g' /etc/yum.repos.d/CentOS-Base.repo - # 不替换下面的开关,可能会出现错误:Could not open/read repomd.xml - sed -i 's/enabled=1/enabled=0/g' /etc/yum.repos.d/CentOS-Media.repo + # 不替换下面的开关,可能会出现错误:Could not open/read repomd.xml + sed -i 's/enabled=1/enabled=0/g' /etc/yum.repos.d/CentOS-Media.repo elif [[ ${version} == 6 ]]; then - wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-6.repo -O /etc/yum.repos.d/CentOS-Base.repo + wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-6.repo -O /etc/yum.repos.d/CentOS-Base.repo elif [[ ${version} == 7 ]]; then - wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-7.repo -O /etc/yum.repos.d/CentOS-Base.repo + wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-7.repo -O /etc/yum.repos.d/CentOS-Base.repo else - echo -e "版本不支持,替换 yum repo 失败" + echo -e "版本不支持,替换 yum repo 失败" fi # 更新缓存 diff --git a/codes/linux/sys/set-dns.sh b/codes/linux/sys/set-dns.sh index ff3c389e..fe07f014 100644 --- a/codes/linux/sys/set-dns.sh +++ b/codes/linux/sys/set-dns.sh @@ -7,22 +7,22 @@ ################################################################################### ip='127.0.0.1' function getDeviceIp() { - ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') + ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') } function setDNSServer() { -echo -e "设置DNS服务器" -cat >> /etc/resolv.conf << EOF + echo -e "设置DNS服务器" + cat >> /etc/resolv.conf << EOF nameserver 114.114.114.114 nameserver 8.8.8.8 EOF } function setHosts() { -getDeviceIp -host=`hostname` -cat >> /etc/hosts << EOF -${ip} ${host} + getDeviceIp + host=`hostname` + cat >> /etc/hosts << EOF + ${ip} ${host} EOF } diff --git a/codes/linux/sys/sys-settings.sh b/codes/linux/sys/sys-settings.sh index e08c7d0f..dcd5828b 100644 --- a/codes/linux/sys/sys-settings.sh +++ b/codes/linux/sys/sys-settings.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash printHeadInfo() { -cat << EOF + cat << EOF ################################################################################### # Linux Centos7 系统配置脚本(根据需要选择) # @author: Zhang Peng @@ -11,47 +11,47 @@ EOF } setLimit() { -cat >> /etc/security/limits.conf << EOF + cat >> /etc/security/limits.conf << EOF * - nofile 65535 * - nproc 65535 EOF } setLang() { -cat > /etc/sysconfig/i18n << EOF + cat > /etc/sysconfig/i18n << EOF LANG="zh_CN.UTF-8" EOF } closeShutdownShortkey() { - echo "关闭 Ctrl+Alt+Del 快捷键防止重新启动" - sed -i 's#exec /sbin/shutdown -r now#\#exec /sbin/shutdown -r now#' /etc/init/control-alt-delete.conf + echo "关闭 Ctrl+Alt+Del 快捷键防止重新启动" + sed -i 's#exec /sbin/shutdown -r now#\#exec /sbin/shutdown -r now#' /etc/init/control-alt-delete.conf } closeSelinux() { - echo "关闭 selinux" + echo "关闭 selinux" - # see http://blog.51cto.com/13570193/2093299 - sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config + # see http://blog.51cto.com/13570193/2093299 + sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config } setBootMode() { - # 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) - # 2. 单用户模式,就像 Win9X 下的安全模式 - # 3. 多用户,但是没有 NFS - # 4. 完全多用户模式,准则的运行级 - # 5. 通常不用,在一些特殊情况下可以用它来做一些事情 - # 6. X11,即进到 X-Window 系统 - # 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) - echo "设置 Linux 启动模式" - sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab + # 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) + # 2. 单用户模式,就像 Win9X 下的安全模式 + # 3. 多用户,但是没有 NFS + # 4. 完全多用户模式,准则的运行级 + # 5. 通常不用,在一些特殊情况下可以用它来做一些事情 + # 6. X11,即进到 X-Window 系统 + # 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) + echo "设置 Linux 启动模式" + sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab } # 配置 IPv4 -configIpv4(){ -echo "配置 ipv4" +configIpv4() { + echo "配置 ipv4" -cat >> /etc/sysctl.conf << EOF + cat >> /etc/sysctl.conf << EOF net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 2 @@ -78,56 +78,57 @@ EOF # 关闭 IPv6 closeIpv6() { -echo "关闭 ipv6" + echo "关闭 ipv6" -cat > /etc/modprobe.d/ipv6.conf << EOF + cat > /etc/modprobe.d/ipv6.conf << EOF alias net-pf-10 off options ipv6 disable=1 EOF -echo "NETWORKING_IPV6=off" >> /etc/sysconfig/network + echo "NETWORKING_IPV6=off" >> /etc/sysconfig/network } # 入口函数 main() { -PS3="请选择要执行的操作:" -select ITEM in "设置 DNS" "设置 NTP" "关闭防火墙" "配置 IPv4" "关闭 IPv6" "全部执行" -do - -case ${ITEM} in - "设置 DNS") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash - ;; - "设置 NTP") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash - ;; - "关闭防火墙") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash - ;; - "配置 IPv4") - configIpv4 - ;; - "关闭 IPv6") - closeIpv6 - ;; - "全部执行") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash - configIpv4 - closeIpv6 - ;; - *) - echo -e "输入项不支持!" - main - ;; -esac -break -done + PS3="请选择要执行的操作:" + select ITEM in "设置 DNS" "设置 NTP" "关闭防火墙" "配置 IPv4" "关闭 IPv6" "全部执行" + do + + case ${ITEM} in + "设置 DNS") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash + ;; + "设置 NTP") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash + ;; + "关闭防火墙") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash + ;; + "配置 IPv4") + configIpv4 + ;; + "关闭 IPv6") + closeIpv6 + ;; + "全部执行") + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash + curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash + configIpv4 + closeIpv6 + ;; + *) + echo -e "输入项不支持!" + main + ;; + esac + break + done } ######################################## MAIN ######################################## -filepath=$(cd "$(dirname "$0")"; pwd) +filepath=$(cd "$(dirname "$0")"; +pwd) printHeadInfo main diff --git a/codes/linux/tool/Autoinstall_ELK_V1.3.sh b/codes/linux/tool/Autoinstall_ELK_V1.3.sh index 4d1f583a..87557ddf 100644 --- a/codes/linux/tool/Autoinstall_ELK_V1.3.sh +++ b/codes/linux/tool/Autoinstall_ELK_V1.3.sh @@ -1,4 +1,5 @@ #!/bin/bash + #mail:xuel@anchnet.com #data:2017/9/7 #AutoInstall ELK scripts @@ -9,65 +10,66 @@ echo "# Auto Install ELK. ##" echo "# Press Ctrl + C to cancel ##" echo "# Any key to continue ##" echo "##########################################" -read -p +read -p software_dir="/usr/local/software" elasticsearch_url="https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.1.tar.gz" kibana_url="https://artifacts.elastic.co/downloads/kibana/kibana-5.4.1-linux-x86_64.tar.gz" logstash_url="https://artifacts.elastic.co/downloads/logstash/logstash-5.4.1.tar.gz" filebeat_url="https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.4.1-linux-x86_64.tar.gz" -sys_version=`cat /etc/redhat-release |awk '{print $4}'|cut -d. -f1` -IP=`ip addr|grep "inet "|grep -v 127.0.0.1|awk '{print $2}'|cut -d/ -f1` +sys_version=`cat /etc/redhat-release | awk '{print $4}' | cut -d. -f1` +IP=`ip addr | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1` jvm_conf="/usr/local/elasticsearch/config/jvm.options" -sys_mem=`free -m|grep Mem:|awk '{print $2}'|awk '{sum+=$1} END {print sum/1024}'|cut -d. -f1` +sys_mem=`free -m | grep Mem: | awk '{print $2}' | awk '{sum+=$1} END {print sum/1024}' | cut -d. -f1` #wget software wget_fun() { -if [ ! -d ${software_dir} ];then - mkdir -p ${software_dir} && cd ${software_dir} -else - cd ${software_dir} -fi -for software in $elasticsearch_url $kibana_url $logstash_url $filebeat_url -do - wget -c $software -done -clear + if [ ! -d ${software_dir} ]; then + mkdir -p ${software_dir} && cd ${software_dir} + else + cd ${software_dir} + fi + for software in $elasticsearch_url $kibana_url $logstash_url $filebeat_url + do + wget -c $software + done + clear } + #initial system:install java wget;set hostname;disable firewalld init_sys() { -[ -f /etc/init.d/functions ] && . /etc/init.d/functions -[ "${sys_version}" != "7" ] && echo "Error:This Scripts Support Centos7.xx" && exit 1 -[ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 -sed -i "s/SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config -setenforce 0 -yum install -y java-1.8.0-openjdk wget net-tools -hostnamectl set-hostname elk-server -systemctl stop firewalld -cat >>/etc/security/limits.conf<> /etc/security/limits.conf << EOF +* soft nofile 65536 +* hard nofile 65536 +* soft nGproc 65536 * hard nproc 65536 EOF } #install elasticsearch install_elasticsearch() { -cd $software_dir -tar zxf elasticsearch-5.4.1.tar.gz -mv elasticsearch-5.4.1 /usr/local/elasticsearch -mkdir -p /usr/local/elasticsearch/data /usr/local/elasticsearch/logs -useradd elasticsearch -chown -R elasticsearch:elasticsearch /usr/local/elasticsearch -echo "vm.max_map_count = 655360" >>/etc/sysctl.conf && sysctl -p -if [ ${sys_mem} -eq 0 ];then - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} -else - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} -fi -cat >>/usr/local/elasticsearch/config/elasticsearch.yml<> /etc/sysctl.conf && sysctl -p + if [ ${sys_mem} -eq 0 ]; then + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} + else + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} + fi + cat >> /usr/local/elasticsearch/config/elasticsearch.yml << EOF cluster.name: my-application node.name: elk-server path.data: /usr/local/elasticsearch/data @@ -76,15 +78,15 @@ network.host: 127.0.0.1 http.port: 9200 discovery.zen.ping.unicast.hosts: ["elk-server"] EOF -su - elasticsearch -c "nohup /usr/local/elasticsearch/bin/elasticsearch &" + su - elasticsearch -c "nohup /usr/local/elasticsearch/bin/elasticsearch &" } #install logstash install_logstash() { -cd $software_dir -tar -zxf logstash-5.4.1.tar.gz -mv logstash-5.4.1 /usr/local/logstash -cat>/usr/local/logstash/config/01-syslog.conf< /usr/local/logstash/config/01-syslog.conf << EOF input { beats { port => "5044" @@ -97,15 +99,15 @@ output { stdout { codec => rubydebug } } EOF -nohup /usr/local/logstash/bin/logstash -f /usr/local/logstash/config/01-syslog.conf & >/dev/null + nohup /usr/local/logstash/bin/logstash -f /usr/local/logstash/config/01-syslog.conf & > /dev/null } #install filebeat install_filebeat() { -cd $software_dir -tar -zxf filebeat-5.4.1-linux-x86_64.tar.gz -mv filebeat-5.4.1-linux-x86_64 /usr/local/filebeat -cat >/usr/local/filebeat/filebeat.yml< /usr/local/filebeat/filebeat.yml << EOF filebeat.prospectors: - input_type: log paths: @@ -113,52 +115,53 @@ filebeat.prospectors: output.logstash: hosts: ["127.0.0.1:5044"] EOF -cd /usr/local/filebeat/ -nohup /usr/local/filebeat/filebeat & >/dev/null + cd /usr/local/filebeat/ + nohup /usr/local/filebeat/filebeat & > /dev/null } #install kibana install_kibana() { -cd $software_dir -tar -zxf kibana-5.4.1-linux-x86_64.tar.gz -mv kibana-5.4.1-linux-x86_64 /usr/local/kibana -cat >> /usr/local/kibana/config/kibana.yml <> /usr/local/kibana/config/kibana.yml << EOF server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "http://127.0.0.1:9200" EOF -nohup /usr/local/kibana/bin/kibana & >/dev/null + nohup /usr/local/kibana/bin/kibana & > /dev/null } check() { -port=$1 -program=$2 -check_port=`netstat -lntup|grep ${port}|wc -l` -check_program=`ps -ef|grep ${program}|grep -v grep|wc -l` -if [ $check_port -gt 0 ] && [ $check_program -gt 0 ];then + port=$1 + program=$2 + check_port=`netstat -lntup | grep ${port} | wc -l` + check_program=`ps -ef | grep ${program} | grep -v grep | wc -l` + if [ $check_port -gt 0 ] && [ $check_program -gt 0 ]; then action "${program} run is ok!" /bin/true -else + else action "${program} run is error!" /bin/false -fi + fi } main() { -init_sys -wget_fun -install_elasticsearch -install_filebeat -install_logstash -install_kibana -echo -e "\033[32m Checking Elasticsearch...\033[0m" -sleep 20 -check :9200 "elasticsearch" -echo -e "\033[32m Checking Logstash...\033[0m" -sleep 2 -check ":9600" "logstash" -echo -e "\033[32m Checking Kibana...\033[0m" -sleep 2 -check ":5601" "kibana" -action "ELK install is success!" /bin/true -echo "url:http://$IP:5601" + init_sys + wget_fun + install_elasticsearch + install_filebeat + install_logstash + install_kibana + echo -e "\033[32m Checking Elasticsearch...\033[0m" + sleep 20 + check :9200 "elasticsearch" + echo -e "\033[32m Checking Logstash...\033[0m" + sleep 2 + check ":9600" "logstash" + echo -e "\033[32m Checking Kibana...\033[0m" + sleep 2 + check ":5601" "kibana" + action "ELK install is success!" /bin/true + echo "url:http://$IP:5601" } + main diff --git a/codes/linux/tool/Cpu_Limit.sh b/codes/linux/tool/Cpu_Limit.sh index 1d959da0..5e082e0b 100644 --- a/codes/linux/tool/Cpu_Limit.sh +++ b/codes/linux/tool/Cpu_Limit.sh @@ -1,4 +1,5 @@ #!/bin/bash + # auth:kaliarch # func:sys info check # version:v1.0 @@ -16,38 +17,38 @@ LIMIT_CPU=85 LOG_DIR=/var/log/cpulimit/ # 超过阀值进程pid -PIDARG=$(ps -aux |awk -v CPU=${PEC_CPU} '{if($3 > CPU) print $2}') +PIDARG=$(ps -aux | awk -v CPU=${PEC_CPU} '{if($3 > CPU) print $2}') CPULIMITCMD=$(which cpulimit) install_cpulimit() { - [ ! -d /tmp ] && mkdir /tmp || cd /tmp - wget -c https://github.com/opsengine/cpulimit/archive/v0.2.tar.gz - tar -zxf v0.2.tar.gz - cd cpulimit-0.2 && make - [ $? -eq 0 ] && cp src/cpulimit /usr/bin/ + [ ! -d /tmp ] && mkdir /tmp || cd /tmp + wget -c https://github.com/opsengine/cpulimit/archive/v0.2.tar.gz + tar -zxf v0.2.tar.gz + cd cpulimit-0.2 && make + [ $? -eq 0 ] && cp src/cpulimit /usr/bin/ } do_cpulimit() { -[ ! -d ${LOG_DIR} ] && mkdir -p ${LOG_DIR} -for i in ${PIDARG}; -do - MSG=$(ps -aux |awk -v pid=$i '{if($2 == pid) print $0}') + [ ! -d ${LOG_DIR} ] && mkdir -p ${LOG_DIR} + for i in ${PIDARG}; + do + MSG=$(ps -aux | awk -v pid=$i '{if($2 == pid) print $0}') echo ${MSG} - [ ! -d /tmp ] && mkdir /tmp || cd /tmp - nohup ${CPULIMITCMD} -p $i -l ${LIMIT_CPU} & + [ ! -d /tmp ] && mkdir /tmp || cd /tmp + nohup ${CPULIMITCMD} -p $i -l ${LIMIT_CPU} & echo "$(date) -- ${MSG}" >> ${LOG_DIR}$(date +%F).log -done + done } main() { - hash cpulimit - if [ $? -eq 0 ];then - do_cpulimit - else - install_cpulimit && do_cpulimit - fi + hash cpulimit + if [ $? -eq 0 ]; then + do_cpulimit + else + install_cpulimit && do_cpulimit + fi } main diff --git a/codes/linux/tool/Custom_Rm.sh b/codes/linux/tool/Custom_Rm.sh index 75a06d3c..8b948ee7 100644 --- a/codes/linux/tool/Custom_Rm.sh +++ b/codes/linux/tool/Custom_Rm.sh @@ -1,4 +1,5 @@ #!/bin/bash + # function:自定义rm命令,每天晚上定时清理 CMD_SCRIPTS=$HOME/.rm_scripts.sh @@ -7,7 +8,7 @@ CRON_FILE=/var/spool/cron/root BASHRC=$HOME/.bashrc [ ! -d ${TRASH_DIR} ] && mkdir -p ${TRASH_DIR} -cat > $CMD_SCRIPTS < $CMD_SCRIPTS << EOF PARA_CNT=\$# TRASH_DIR=$TRASH_DIR for i in \$*; do diff --git a/codes/linux/tool/Daily_Archive.sh b/codes/linux/tool/Daily_Archive.sh index 7e614b5e..9fbaafe4 100644 --- a/codes/linux/tool/Daily_Archive.sh +++ b/codes/linux/tool/Daily_Archive.sh @@ -1,4 +1,5 @@ #!/bin/bash + # # Daily_Archive - Archive designated files & directories ###################################################### @@ -12,7 +13,7 @@ DATE=`date +%y%m%d` FILE=archive$DATE.tar.gz # # Set Configuration and Destination File -# +# CONFIG_FILE=/home/tiandi/archive/Files_To_Backup DESTINATION=/home/tiandi/archive/$FILE # @@ -20,46 +21,46 @@ DESTINATION=/home/tiandi/archive/$FILE # # Check Backup Config file exists # -if [ -f $CONFIG_FILE ] #Make sure the config file still exists +if [ -f $CONFIG_FILE ] #Make sure the config file still exists then - echo + echo else - echo - echo "$CONFIG_FILE does not exist." - echo "Backup not completed due to missing Configuration file" - echo - exit + echo + echo "$CONFIG_FILE does not exist." + echo "Backup not completed due to missing Configuration file" + echo + exit fi # # Build the names of all the files to backup # -FILE_NO=1 # Start on Line 1 of Config file. -exec < $CONFIG_FILE # Redirect Std Input to name of Config File +FILE_NO=1 # Start on Line 1 of Config file. +exec < $CONFIG_FILE # Redirect Std Input to name of Config File # -read FILE_NAME # Read 1st record +read FILE_NAME # Read 1st record # while [ $? -eq 0 ] do - # Make sure the file or directory exists. - if [ -f $FILE_NAME -o -d $FILE_NAME ] - then - # If file exists, add its name to the lists - FILE_LIST="$FILE_LIST $FILE_NAME" - else - # If file doesn't exist, issue warning - echo - echo "$FILE_NAME, does not exist." - echo "Obviously, I will not include it in this archive." - echo "It is listed on line $FILE_NO of the config file." - echo "Continuing to build archive file." - echo - fi -# - FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one - read FILE_NAME # Read next record. + # Make sure the file or directory exists. + if [ -f $FILE_NAME -o -d $FILE_NAME ] + then + # If file exists, add its name to the lists + FILE_LIST="$FILE_LIST $FILE_NAME" + else + # If file doesn't exist, issue warning + echo + echo "$FILE_NAME, does not exist." + echo "Obviously, I will not include it in this archive." + echo "It is listed on line $FILE_NO of the config file." + echo "Continuing to build archive file." + echo + fi + # + FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one + read FILE_NAME # Read next record. done ########################################################### -# +# # Backup the files and Compress Archive # tar -czf $DESTINATION $FILE_LIST 2> /dev/null diff --git a/codes/linux/tool/Hourly_Archive.sh b/codes/linux/tool/Hourly_Archive.sh index e0396f4d..d29a08eb 100644 --- a/codes/linux/tool/Hourly_Archive.sh +++ b/codes/linux/tool/Hourly_Archive.sh @@ -1,10 +1,11 @@ #!/bin/bash + # # Hourly_Archive - Every hour create an archive ###################################################### # # Set Configuration and Destination File -# +# CONFIG_FILE=/home/tiandi/archive/Files_To_Backup # # Gather Current Date,Month & Time @@ -27,46 +28,46 @@ DESTINATION=$BASEDEST/$MONTH/$DAY/archive$TIME.tar.gz # # Check Backup Config file exists # -if [ -f $CONFIG_FILE ] #Make sure the config file still exists +if [ -f $CONFIG_FILE ] #Make sure the config file still exists then - echo + echo else - echo - echo "$CONFIG_FILE does not exist." - echo "Backup not completed due to missing Configuration file" - echo - exit + echo + echo "$CONFIG_FILE does not exist." + echo "Backup not completed due to missing Configuration file" + echo + exit fi # # Build the names of all the files to backup # -FILE_NO=1 # Start on Line 1 of Config file. -exec < $CONFIG_FILE # Redirect Std Input to name of Config File +FILE_NO=1 # Start on Line 1 of Config file. +exec < $CONFIG_FILE # Redirect Std Input to name of Config File # -read FILE_NAME # Read 1st record +read FILE_NAME # Read 1st record # while [ $? -eq 0 ] do - # Make sure the file or directory exists. - if [ -f $FILE_NAME -o -d $FILE_NAME ] - then - # If file exists, add its name to the lists - FILE_LIST="$FILE_LIST $FILE_NAME" - else - # If file doesn't exist, issue warning - echo - echo "$FILE_NAME, does not exist." - echo "Obviously, I will not include it in this archive." - echo "It is listed on line $FILE_NO of the config file." - echo "Continuing to build archive file." - echo - fi -# - FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one - read FILE_NAME # Read next record. + # Make sure the file or directory exists. + if [ -f $FILE_NAME -o -d $FILE_NAME ] + then + # If file exists, add its name to the lists + FILE_LIST="$FILE_LIST $FILE_NAME" + else + # If file doesn't exist, issue warning + echo + echo "$FILE_NAME, does not exist." + echo "Obviously, I will not include it in this archive." + echo "It is listed on line $FILE_NO of the config file." + echo "Continuing to build archive file." + echo + fi + # + FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one + read FILE_NAME # Read next record. done ########################################################### -# +# # Backup the files and Compress Archive # tar -czf $DESTINATION $FILE_LIST 2> /dev/null diff --git "a/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" "b/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" index 724dfc0d..2da94f74 100644 --- "a/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" +++ "b/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" @@ -1,85 +1,92 @@ #!/bin/bash + # # Delete_User - Automates the 4 steps to remove an account # ################################################################# -# +# # Define Functions # ################################################################# function get_answer { -# - unset ANSWER - ASK_COUNT=0 -# - while [ -z "$ANSWER" ] # while no answer is given, keep asking - do - ASK_COUNT=$[ $ASK_COUNT + 1 ] -# - case $ASK_COUNT in # If user gives no answer in time allowed - 2) - echo - echo "Please answer the question." - echo - ;; - 3) - echo - echo "One last try... please answer the question." - echo - ;; - 4) - echo - echo "Since you refuse to answer the question..." - echo "exiting program." - echo - # - exit - ;; - esac -# - echo -# - if [ -n "$LINE2" ] - then - echo $LINE1 # Print 2 lines - echo -e $LINE2" \c" - else # Print 1 line - echo -e $LINE1" \c" - fi -# -# Allow 60 seconds to answer before time-out - read -t 60 ANSWER - done -# -# Do a little variable clean-up -# - unset LINE1 - unset LINE2 -# -} #end of get_answer function + # + unset ANSWER + ASK_COUNT=0 + # + while [ -z "$ANSWER" ] # while no answer is given, keep asking + do + ASK_COUNT=$[ $ASK_COUNT + 1 ] + # + case $ASK_COUNT in + # If user gives no answer in time allowed + 2) + echo + echo "Please answer the question." + echo + ;; + 3) + echo + echo "One last try... please answer the question." + echo + ;; + 4) + echo + echo "Since you refuse to answer the question..." + echo "exiting program." + echo + # + exit + ;; + esac + # + echo + # + if [ -n "$LINE2" ] + then + echo $LINE1 # Print 2 lines + echo -e $LINE2" \c" + else + # Print 1 line + echo -e $LINE1" \c" + fi + # + # Allow 60 seconds to answer before time-out + read -t 60 ANSWER + done + # + # Do a little variable clean-up + # + unset LINE1 + unset LINE2 + # +} + +#end of get_answer function # ################################################################# function process_answer { -# - case $ANSWER in - y|Y|YES|yes|yEs|yeS|YEs|yES) - # If user answers "yes".do nothing. - ;; - *) - # If user answers anything but "yes", exit script - echo - echo $EXIT_LINE1 - echo $EXIT_LINE2 - echo - exit - ;; - esac - # - # Do a little variable clean-up - unset EXIT_LINE1 - unset EXIT_LINE2 -# -} #End of process_answer function + # + case $ANSWER in + y | Y | YES | yes | yEs | yeS | YEs | yES) + # If user answers "yes".do nothing. + ;; + *) + # If user answers anything but "yes", exit script + echo + echo $EXIT_LINE1 + echo $EXIT_LINE2 + echo + exit + ;; + esac + # + # Do a little variable clean-up + unset EXIT_LINE1 + unset EXIT_LINE2 + # +} + +#End of process_answer function # ################################################################ # @@ -108,13 +115,13 @@ get_answer # USER_ACCOUNT_RECORD=$(cat /etc/passwd | grep -w $USER_ACCOUNT) # -if [ $? -eq 1 ] # If the account is not found, exit script +if [ $? -eq 1 ] # If the account is not found, exit script then - echo - echo "Account, $USER_ACCOUNT, not found." - echo "Leaving the script..." - echo - exit + echo + echo "Account, $USER_ACCOUNT, not found." + echo "Leaving the script..." + echo + exit fi # echo @@ -143,59 +150,59 @@ echo echo "$USER_ACCOUNT has the following processes running: " echo # -ps -u $USER_ACCOUNT #List the processes running +ps -u $USER_ACCOUNT #List the processes running # case $? in -1) # No processes running for this User Account - # - echo "There are no processes for this account currently running." - echo -;; -0) # Processes running for this User Account. - # Ask Script User if wants us to kill the processes. - # - unset ANSWER # I think this line is not needed - LINE1="Would you like me to kill the process(es)? [y/n]:" - get_answer - # - case $ANSWER in - y|Y|YES|yes|Yes|yEs|yeS|YEs|yES) # if user answer "yes", - #kill User Account processes - # - echo - # - # Clean-up temp file upon signals - # - trap "rm $USER_ACCOUNT_Running_Process.rpt" SIGTERM SIGINT SIGQUIT - # - # List user processes running - ps -u $USER_ACCOUNT > $USER_ACCOUNT_Running_Process.rpt - # - exec < $USER_ACCOUNT_Running_Process.rpt # Make report Std Input - # - read USER_PROCESS_REC # First record will be blank - read USER_PROCESS_REC - # - while [ $? -eq 0 ] - do - # obtain PID - USER_PID=$(echo $USER_PROCESS_REC | cut -d " " -f1 ) - kill -9 $USER_PID - echo "Killed process $USER_PID" - read USER_PROCESS_REC - done - # - echo - # - rm $USER_ACCOUNT_Running_Process.rpt # Remove temp report - ;; - *) # If user answers anything but "yes", do not kill. - echo - echo "Will not kill the process(es)." - echo - ;; - esac -;; + 1) # No processes running for this User Account + # + echo "There are no processes for this account currently running." + echo + ;; + 0) # Processes running for this User Account. + # Ask Script User if wants us to kill the processes. + # + unset ANSWER # I think this line is not needed + LINE1="Would you like me to kill the process(es)? [y/n]:" + get_answer + # + case $ANSWER in + y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) # if user answer "yes", + #kill User Account processes + # + echo + # + # Clean-up temp file upon signals + # + trap "rm$USER_ACCOUNT_Running_Process.rpt" SIGTERM SIGINT SIGQUIT + # + # List user processes running + ps -u $USER_ACCOUNT > $USER_ACCOUNT_Running_Process.rpt + # + exec < $USER_ACCOUNT_Running_Process.rpt # Make report Std Input + # + read USER_PROCESS_REC # First record will be blank + read USER_PROCESS_REC + # + while [ $? -eq 0 ] + do + # obtain PID + USER_PID=$(echo $USER_PROCESS_REC | cut -d " " -f1) + kill -9 $USER_PID + echo "Killed process $USER_PID" + read USER_PROCESS_REC + done + # + echo + # + rm $USER_ACCOUNT_Running_Process.rpt # Remove temp report + ;; + *) # If user answers anything but "yes", do not kill. + echo + echo "Will not kill the process(es)." + echo + ;; + esac + ;; esac ################################################################################### # @@ -216,7 +223,7 @@ echo "Please wait. This may take a while..." REPORT_DATE=`date +%y%m%d` REPORT_FILE=$USER_ACCOUNT"_Files_"$REPORT_DATE".rpt" # -find / -user $USER_ACCOUNT > $REPORT_FILE 2>/dev/null +find / -user $USER_ACCOUNT > $REPORT_FILE 2> /dev/null # echo echo "Report is complete." @@ -240,7 +247,7 @@ EXIT_LINE1="Since you do not wish to remove the user account." EXIT_LINE2="$USER_ACCOUNT at this time, exiting the script..." process_answer # -userdel $USER_ACCOUNT # delete user account +userdel $USER_ACCOUNT # delete user account echo echo "User account, $USER_ACCOUNT, has been removed" echo diff --git "a/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" "b/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" index 4639c449..d3c46ea4 100644 --- "a/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" +++ "b/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" @@ -9,11 +9,11 @@ echo "Top Ten Disk Space Usage" echo "for ${DIRS} Directories" for DIR in ${DIRS} do - echo "" - echo "The ${DIR} Directory:" - du -S ${DIR} 2>/dev/null | - sort -rn | - sed '{11,$D; =}' | - sed 'N; s/\n/ /' | - gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}' + echo "" + echo "The ${DIR} Directory:" + du -S ${DIR} 2> /dev/null | + sort -rn | + sed '{11,$D; =}' | + sed 'N; s/\n/ /' | + gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}' done diff --git a/codes/shell/action/oper/input.sh b/codes/shell/action/oper/input.sh index 29ad9182..037aa914 100644 --- a/codes/shell/action/oper/input.sh +++ b/codes/shell/action/oper/input.sh @@ -7,19 +7,19 @@ ################### 读取脚本输入参数并校验 ################### declare -a serial -serial=(start stop restart) +serial=( start stop restart ) echo -n "请选择操作(可选值:start|stop|restart):" -read oper +read oper if ! echo ${serial[@]} | grep -q ${oper}; then - echo "请选择正确操作(可选值:start|stop|restart)" - exit 1 + echo "请选择正确操作(可选值:start|stop|restart)" + exit 1 fi declare -a serial2 -serial2=(dev test prod) +serial2=( dev test prod ) echo -n "请选择运行环境(可选值:dev|test|prod):" -read profile +read profile if ! echo ${serial2[@]} | grep -q ${profile}; then - echo "请选择正确运行环境(可选值:dev|test|prod)" - exit 1 + echo "请选择正确运行环境(可选值:dev|test|prod)" + exit 1 fi diff --git a/codes/shell/action/system/dir.sh b/codes/shell/action/system/dir.sh index b9499d03..8a3b2b75 100644 --- a/codes/shell/action/system/dir.sh +++ b/codes/shell/action/system/dir.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -current_dir=$(cd `dirname $0`; pwd) +current_dir=$(cd `dirname $0`; +pwd) echo "当前目录是:${current_dir}" parent_dir=$(dirname $(pwd)) diff --git a/codes/shell/demos/array-demo.sh b/codes/shell/demos/array-demo.sh index 65ff2ef7..5c412e2c 100644 --- a/codes/shell/demos/array-demo.sh +++ b/codes/shell/demos/array-demo.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # 创建数组 -nums=([2]=2 [0]=0 [1]=1) -colors=(red yellow "dark blue") +nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 ) +colors=( red yellow "dark blue" ) # 访问数组的单个元素 echo ${nums[1]} @@ -43,13 +43,13 @@ echo ${#nums[*]} # 3 # 向数组中添加元素 -colors=(white "${colors[@]}" green black) +colors=( white "${colors[@]}" green black ) echo ${colors[@]} # Output: # white red yellow dark blue green black # 从数组中删除元素 -unset nums[0] +unset nums[ 0 ] echo ${nums[@]} # Output: # 1 2 diff --git a/codes/shell/demos/comment-demo.sh b/codes/shell/demos/comment-demo.sh index 0a5e4213..098e1bc1 100644 --- a/codes/shell/demos/comment-demo.sh +++ b/codes/shell/demos/comment-demo.sh @@ -9,7 +9,7 @@ ########## 这是分割线 ########## -:< ${y} ]]; then - echo "${x} > ${y}" + echo "${x} > ${y}" elif [[ ${x} < ${y} ]]; then - echo "${x} < ${y}" + echo "${x} < ${y}" else - echo "${x} = ${y}" + echo "${x} = ${y}" fi # Output: 10 < 20 diff --git a/codes/shell/demos/statement/select-demo.sh b/codes/shell/demos/statement/select-demo.sh index 956ac8f3..4e59b5a3 100644 --- a/codes/shell/demos/statement/select-demo.sh +++ b/codes/shell/demos/statement/select-demo.sh @@ -5,10 +5,10 @@ select ITEM in bower npm gem pip do echo -n "Enter the package name: " && read PACKAGE case ${ITEM} in - bower) bower install ${PACKAGE} ;; - npm) npm install ${PACKAGE} ;; - gem) gem install ${PACKAGE} ;; - pip) pip install ${PACKAGE} ;; + bower) bower install ${PACKAGE} ;; + npm) npm install ${PACKAGE} ;; + gem) gem install ${PACKAGE} ;; + pip) pip install ${PACKAGE} ;; esac break # 避免无限循环 done diff --git a/codes/shell/demos/statement/until-demo.sh b/codes/shell/demos/statement/until-demo.sh index 320b5909..58a0fce5 100644 --- a/codes/shell/demos/statement/until-demo.sh +++ b/codes/shell/demos/statement/until-demo.sh @@ -2,8 +2,8 @@ x=0 until [[ ${x} -ge 5 ]]; do - echo ${x} - x=`expr ${x} + 1` + echo ${x} + x=`expr ${x} + 1` done # Output: # 0 diff --git a/codes/shell/demos/statement/while-demo.sh b/codes/shell/demos/statement/while-demo.sh index 375bed9e..9492b0a8 100644 --- a/codes/shell/demos/statement/while-demo.sh +++ b/codes/shell/demos/statement/while-demo.sh @@ -4,8 +4,8 @@ x=0 ### x小于10 while [[ ${x} -lt 10 ]]; do - echo $((x * x)) - x=$((x + 1)) + echo $((x * x)) + x=$((x + 1)) done # Output: # 0 diff --git a/codes/shell/demos/string-demo.sh b/codes/shell/demos/string-demo.sh index 652c57ae..0594f6bb 100644 --- a/codes/shell/demos/string-demo.sh +++ b/codes/shell/demos/string-demo.sh @@ -47,8 +47,8 @@ echo "key is ${key}" ################### 判断字符串中是否包含子字符串 ################### result=$(echo "${str}" | grep "feature/") -if [[ "$result" != "" ]] ; then - echo "feature/ 是 ${str} 的子字符串" +if [[ "$result" != "" ]]; then + echo "feature/ 是 ${str} 的子字符串" else - echo "feature/ 不是 ${str} 的子字符串" + echo "feature/ 不是 ${str} 的子字符串" fi diff --git a/codes/shell/demos/variable-demo.sh b/codes/shell/demos/variable-demo.sh index 3cb6fdf7..3305ce65 100644 --- a/codes/shell/demos/variable-demo.sh +++ b/codes/shell/demos/variable-demo.sh @@ -13,10 +13,10 @@ readonly rword # rword="bye" # 如果放开注释,执行时会报错 ################### 删除变量 ################### -dword="hello" # 声明变量 -echo ${dword} # 输出变量值 +dword="hello" # 声明变量 +echo ${dword} # 输出变量值 # Output: hello -unset dword # 删除变量 +unset dword # 删除变量 echo ${dword} # Output: (空) diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" index e74299d2..39d40f47 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" @@ -6,7 +6,7 @@ database='test' for f in `ls */*.sql` do -echo ${f}; -mysql -u${user} -p${password} -f ${database} -e "source $f"; + echo ${f}; + mysql -u${user} -p${password} -f ${database} -e "source $f"; done echo 'OK!' diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" index c2eaef4c..73c73d13 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" @@ -1,21 +1,22 @@ #!/bin/bash + # send data to the the table in the MYSQL database MYSQL=`which mysql` if [ $# -ne 2 ] then - echo "Usage:mtest2 emplid lastname firstname salary" + echo "Usage:mtest2 emplid lastname firstname salary" else - #脚本变量一定要用双引号,字符串变量使用单引号 - statement=" insert into em_admin values(NULL, '$1', $2)" - $MYSQL emwjs -u test < $temp - dialog --textbox $temp 20 60 + df -k > $temp + dialog --textbox $temp 20 60 } function whoseon { - who > $temp - dialog --textbox $temp 20 50 + who > $temp + dialog --textbox $temp 20 50 } function menusage { - cat /proc/meminfo > $temp - dialog --textbox $temp 20 50 + cat /proc/meminfo > $temp + dialog --textbox $temp 20 50 } while [ 1 ] do - dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2 - if [ $? -eq 1 ] - then - break - fi - - selection=`cat $temp2` - - case $selection in - 1) - diskspace;; - 2) - whoseon;; - 3) - menusage;; - 0) - break;; - *) - dialog --msgbox "Sorry,invalid selection" 10 30 - esac + dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2 + if [ $? -eq 1 ] + then + break + fi + + selection=`cat $temp2` + + case $selection in + 1) + diskspace ;; + 2) + whoseon ;; + 3) + menusage ;; + 0) + break ;; + *) + dialog --msgbox "Sorry,invalid selection" 10 30 + esac done rm -f $temp 2> /dev/null rm -f $temp2 2> /dev/null diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" index 4681abe6..fce0459e 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # testing STDERR messages echo "This is an error " >&2 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" index 015c852f..2115cc78 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" @@ -1,11 +1,12 @@ #!/bin/bash + # reading data from a file count=1 cat test | while read line do - echo "Line $count: $line" - count=$[ $count + 1 ] + echo "Line $count: $line" + count=$[ $count + 1 ] done echo "Finished processing the file" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" index 63389393..1a486718 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" @@ -1,12 +1,13 @@ #!/bin/bash + # simple demonstration of the getopts command while getopts :ab:c opt do - case "$opt" in - a) echo "Found the -a option";; - b) echo "Found the -b option, with value $OPTARG";; - c) echo "Found the -c option";; - *) echo "Unknown option:$opt";; - esac + case "$opt" in + a) echo "Found the -a option" ;; + b) echo "Found the -b option, with value $OPTARG" ;; + c) echo "Found the -c option" ;; + *) echo "Unknown option:$opt" ;; + esac done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" index 732ac69e..8bcb5831 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" @@ -1,20 +1,21 @@ #!/bin/bash + # processing options and parameters with getopts while getopts :ab:cd opt do - case "$opt" in - a) echo "Found the -a option";; - b) echo "Found the -b option,with value $OPTARG";; - c) echo "Found the -c option";; - d) echo "Found the -d option";; - *) echo "Unknown option: $opt";; - esac + case "$opt" in + a) echo "Found the -a option" ;; + b) echo "Found the -b option,with value $OPTARG" ;; + c) echo "Found the -c option" ;; + d) echo "Found the -d option" ;; + *) echo "Unknown option: $opt" ;; + esac done shift $[ $OPTIND - 1 ] count=1 for param in "$@" do - echo "Parameter $count: $param" - count=$[ $count + 1 ] + echo "Parameter $count: $param" + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" index 55c98677..999b235d 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" @@ -1,25 +1,26 @@ #!/bin/bash + #extracting command line options and values with getopt # getopt command is not goot at dealing with space,we can use getopts set -- `getopt -q ab:c "$@"` while [ -n "$1" ] do - case "$1" in - -a) echo "Found the -a option";; - -b) param="$2" - echo "Found the -b option,with parameter value $param" - shift;; - -c) echo "Found the -c option";; - --) shift - break;; - *) echo "$1 is not an option";; - esac - shift + case "$1" in + -a) echo "Found the -a option" ;; + -b) param="$2" + echo "Found the -b option,with parameter value $param" + shift ;; + -c) echo "Found the -c option" ;; + --) shift + break ;; + *) echo "$1 is not an option" ;; + esac + shift done count=1 for param in "$@" do - echo "Parameter #$count: $param" - count=$[ $count+1 ] + echo "Parameter #$count: $param" + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" index 11877cff..0f249e45 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" @@ -1,12 +1,13 @@ #!/bin/bash + # shift n 移动变量 count=1 while [ -n "$1" ] do - echo "Parameter #$count = $1" - count=$[ $count+1 ] - shift + echo "Parameter #$count = $1" + count=$[ $count + 1 ] + shift done echo -e "\n" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" index ee3da94c..1e20cf7a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" @@ -4,20 +4,20 @@ while [ -n "$1" ] do - case "$1" in - -a) echo "Found the -a option";; - -b) echo "Found the -b option";; - -c) echo "Found the -c option";; - --) shift - break;; - *) echo "$1 is not an option";; - esac - shift + case "$1" in + -a) echo "Found the -a option" ;; + -b) echo "Found the -b option" ;; + -c) echo "Found the -c option" ;; + --) shift + break ;; + *) echo "$1 is not an option" ;; + esac + shift done count=1 for param in $@ do - echo "Parameter #$count: $param" - count=$[ $count + 1 ] + echo "Parameter #$count: $param" + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index 417bfa41..7df0bfd0 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -2,9 +2,9 @@ # testing lsof with file descriptors -exec 3>test -exec 6>test -exec 7 test +exec 6> test +exec 7< test lsof -a -p $$ -d0,1,2,3,6,7 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" index 6d15a9de..8659114b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" @@ -4,7 +4,7 @@ tempfile=`mktemp test.XXXXXX` -exec 3>$tempfile +exec 3> $tempfile echo "This script writes to temp file $tempfile" @@ -18,5 +18,5 @@ echo "Done creating temp file. The contents are:" cat $tempfile -rm -f $tempfile 2>/dev/null +rm -f $tempfile 2> /dev/null diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" index 0face232..ba8cf187 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" @@ -3,7 +3,7 @@ #storing STDOUT, then coming back to it exec 3>&1 -exec 1>test +exec 1> test echo "This should store in output file" echo "along with this line" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index 154e2bf7..4d091cf4 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,7 +1,8 @@ #!/bin/bash + # testing closing file descriptors -exec 3>test +exec 3> test echo "This is a test line of data" >&3 # closing file descriptor @@ -12,5 +13,5 @@ echo "This won't work" >&3 cat test #覆盖前一个test文件 -exec 3>test +exec 3> test echo "This'll be bad" >&3 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index 53b76454..c8e25d1d 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # testing inpiut/output file descriptor exec 3<> test diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index c47be1b1..df248e95 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,22 +1,23 @@ #!/bin/bash + # redirecting input file descriptors exec 3>&1 echo "This is the 3 file descriptor" >&3 exec 6>&0 -exec 0test +exec 3> test echo "This should display on the monitor" echo "and this should be stored in the file" >&3 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" index 65673e87..1ed6071b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # getting the number of parameters echo There were $# parameters supplied diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" index a4497a18..90ebad0e 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" @@ -7,6 +7,6 @@ tempfile=`mktemp -t tmp.XXXXXX` echo "This is a test file" > $tempfile echo "This is the second line of the test" >> $tempfile -echo ”The temp is locate at : $tempfile“ +echo ” The temp is locate at : $tempfile “ cat $tempfile rm -f $tempfile diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" index c3105fe0..d95a3688 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # redirecting the inpiut # 从test中读取数据,而不是从STDIN中读取数据 @@ -6,7 +7,7 @@ exec 0< test count=1 while read line do - echo "Line #$count : $line " - count=$[ $count +1 ] + echo "Line #$count : $line " + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" index b8cc6315..63163ed7 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" @@ -3,23 +3,23 @@ # extracting command line options and values while [ -n "$1" ] -do - case "$1" in - -a) echo "Found the -a option";; - -b) param="$2" - echo "Found the -b option, with parameter value $param" - shift;; - -c) echo "Found the -c option";; - --) shift - break;; - *) echo "$1 is not an option";; - esac - shift +do + case "$1" in + -a) echo "Found the -a option" ;; + -b) param="$2" + echo "Found the -b option, with parameter value $param" + shift ;; + -c) echo "Found the -c option" ;; + --) shift + break ;; + *) echo "$1 is not an option" ;; + esac + shift done count=1 for param in "$@" do - echo "Parameter #$count : $param" - count=$[ $count + 1 ] + echo "Parameter #$count : $param" + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" index 60dd4040..9e679959 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" @@ -1,13 +1,14 @@ #!/bin/bash + # extracting command line options as parameters while [ -n "$1" ] do - case "$1" in - -a) echo "Found the -a option";; - -b) echo "Found the -b optins";; - -c) echo "Found the -c optins";; - *) echo "$1 is not a valid options";; - esac - shift + case "$1" in + -a) echo "Found the -a option" ;; + -b) echo "Found the -b optins" ;; + -c) echo "Found the -c optins" ;; + *) echo "$1 is not a valid options" ;; + esac + shift done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" index a93ccf06..38075263 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" @@ -1,16 +1,17 @@ #!/bin/bash + # testing $* and $@ count=1 for param in "$*" do - echo "\$* Parameter #$count = $param" - count=$[ $count+1 ] + echo "\$* Parameter #$count = $param" + count=$[ $count + 1 ] done count=1 for param in "$@" do - echo "\$@ Paramenter #$count = $param" - count=$[ $count+1 ] + echo "\$@ Paramenter #$count = $param" + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" index d03a3bd7..ee7a5a06 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" @@ -1,14 +1,15 @@ #!/bin/bash + # testing STDERR messages # redirecting all to a file # 脚本执行期间,用exec命令告诉shell重定向某个特定文件描述符 -exec 2>test +exec 2> test ls badtest echo "This is test of redirecting all output" echo "from a script to another file" -exec 1>test1 +exec 1> test1 echo "This is the end of the script" echo "but this should go to the testerror file" >&2 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" index 4e361863..c8e33c21 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" @@ -17,9 +17,9 @@ echo "Checking data for $last. $first..." #如果不指定变量,read命令就会把它收到的任何数据都放到特殊环境变量REPLY中 read -p "Enter a number:" factorial=1 -for (( count=1; count<=$REPLY; count++)) +for (( count = 1; count <= $REPLY; count ++ )) do - factorial=$[ $factorial * $count ] + factorial=$[ $factorial * $count ] done echo "The factorial of $REPLY is $factorial" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" index 1a37a7cf..c5f89f5a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" @@ -3,8 +3,8 @@ # using one command line parameter factorial=1 -for (( number = 1; number <= $1; number++)) +for (( number = 1; number <= $1; number ++ )) do - factorial=$[ $factorial * $number ] + factorial=$[ $factorial * $number ] done echo The factor of $1 is $factorial diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" index f148da03..0637a453 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # testing the $0 parameter echo The command entered is $0 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" index 7dc83875..5879db36 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" @@ -1,22 +1,23 @@ #!/bin/bash + # timing the data entry if read -t 5 -p "Please enter your name:" name then - echo "Hello, $name, welcome to my script" + echo "Hello, $name, welcome to my script" else - #起到换行的作用 - echo - #输入计数 -n1 - read -n1 -p "Do you want to continue [Y/N]?" answer - case $answer in - Y | y) echo - echo "Fine, continue on...";; - N | n) echo - echo "OK,goodbye";; - *) echo - echo "OK, wrong, goodbye" - esac - echo "Sorry, this is the end of the script" + #起到换行的作用 + echo + #输入计数 -n1 + read -n1 -p "Do you want to continue [Y/N]?" answer + case $answer in + Y | y) echo + echo "Fine, continue on..." ;; + N | n) echo + echo "OK,goodbye" ;; + *) echo + echo "OK, wrong, goodbye" + esac + echo "Sorry, this is the end of the script" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" index 1299be24..ba9b2c5d 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" @@ -1,8 +1,9 @@ #!/bin/bash + # hiding input data from monitor read -s -p "Please enter your password: " pass #添加了-s选项之后,不会自动换行,不添加-s 会自动换行 -echo +echo echo "Is your password really $pass?" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" index 85059456..dcd43a10 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # # Capture_Stats - Gather System Performance Statistics # diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" index 07ba5ba9..ebc0eab2 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" @@ -1,3 +1,4 @@ #!/bin/bash + # uptime | sed 's/user.*$//' | gawk '{print $NF}' diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" index 35fce817..7b3d6462 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" @@ -1,9 +1,10 @@ #!/bin/bash + # # Report_Stats - Generates Rpt from Captured Perf Stats # ############################################################ -# +# # Set Script Variables # REPORT_FILE=/home/tiandi/Documents/capstats.csv diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" index 8614a1c7..5fa363d7 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # # Snapshot_Stats - produces a report for system stats # @@ -16,9 +17,9 @@ REPORT=/home/tiandi/Documents/Snapshot_Stats_$DATE.rpt # # Create Report File # -exec 3>&1 # Save file descriptor +exec 3>&1 # Save file descriptor # -exec 1> $REPORT # direct output to rpt file +exec 1> $REPORT # direct output to rpt file # ################################################### # @@ -44,10 +45,10 @@ uptime | sed -n '/,/s/,/ /gp' | gawk '{if($4 == "days" || $4 == "day") {print $2 #2) Gather Disk Usage Statistics # echo -for DISK in $DISK_TO_MONITOR # loop to check disk space +for DISK in $DISK_TO_MONITOR # loop to check disk space do - echo -e "$DISK usage: \c" - df -h $DISK | sed -n '/% \//p' | gawk '{ print $5 }' + echo -e "$DISK usage: \c" + df -h $DISK | sed -n '/% \//p' | gawk '{ print $5 }' done # ################################################################## @@ -68,10 +69,10 @@ ZOMBIE_CHECK=`ps -al | gawk '{print $2,$4}' | grep Z` # if [ "$ZOMBIE_CHECK" = "" ] then - echo "No Zombie Process on System at this Time" + echo "No Zombie Process on System at this Time" else - echo "Current System Zombie Processes" - ps -al | gawk '{print $2,$4}' | grep Z + echo "Current System Zombie Processes" + ps -al | gawk '{print $2,$4}' | grep Z fi echo # @@ -79,7 +80,7 @@ echo # # Restore File Descriptor & Mail Report # -exec 1>&3 # Restore output to STDOUT +exec 1>&3 # Restore output to STDOUT # #$MAIL -a $REPORT -s "System Sstatistics Report for $DATE" #-- $MAIL_TO < /dev/null diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" index a935ad10..99196b33 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # # Update_Problem - updates problem record in database # @@ -12,28 +13,29 @@ MYSQL=`which mysql`" Problem_Trek -u root" # # Obtain Record Id # -if [ $# -eq 0 ] # Check if id number was passed -then # If not passed ask for it -# -# Check if any unfinished records exist. - RECORDS_EXIST=`$MYSQL -Bse 'SELECT id_number FROM problem_logger where fixed_date="0000-00-00" OR prob_solutions=""'` -# - if [ "$RECORDS_EXIST" != "" ] - then - echo - echo "The following record(s) need updating..." - $MYSQL < 90)) -then - (( var2 = $var1 ** 2)) - echo "The square of $var1 if $var2" +if (($var1 ** 2 > 90)) +then + ((var2 = $var1 ** 2)) + echo "The square of $var1 if $var2" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" index a965817e..4f2b2932 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" @@ -1,7 +1,8 @@ #!/bin/bash + # another example of how not to use the for command for test in Newada "New Hampshire" do - echo "Now going to $test" + echo "Now going to $test" done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" index 4d76ecaa..3f73c78f 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" @@ -1,9 +1,10 @@ #!/bin/bash + # using pattern matching if [[ $USER == r* ]] -then - echo "Hello $USER" +then + echo "Hello $USER" else - echo "Sorry, I do not know you" + echo "Sorry, I do not know you" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" index 5887c99b..fae9a1f5 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" @@ -5,11 +5,11 @@ IFS.OLD=$IFS IFS=$'\n' for entry in `cat /etc/passwd` -do - echo "Values in $entry -" - IFS=: - for value in $entry - do - echo " $value" - done +do + echo "Values in $entry -" + IFS=: + for value in $entry + do + echo " $value" + done done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" index 89f7a8fa..c8634b45 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" @@ -1,9 +1,10 @@ #!/bin/bash + #testing compound comparisons if [ -d $HOME ] && [ -w $HOME/testing ] then - echo "The file exists and you can write to it" + echo "The file exists and you can write to it" else - echo "I cannot write to it" + echo "I cannot write to it" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" index 7ec74a40..9e201002 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" @@ -3,18 +3,18 @@ # redirecting the for output to a file for file in /home/tiandi/* do - if [ -d "$file" ] - then - echo "$file is a directory" - else - echo "$file is a file" - fi + if [ -d "$file" ] + then + echo "$file is a directory" + else + echo "$file is a file" + fi done > output.txt # piping a loop to another command for state in "North Dakota" Connecticut do - echo "$state is next place to go" + echo "$state is next place to go" done | sort echo "This completes our travels" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" index cc45dad4..c872f2ed 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" @@ -1,12 +1,13 @@ #!/bin/bash + #iterate through all the files in a directory for file in /home/tiandi/test/* do - if [ -d "$file" ] - then - echo "$file is a directory" - elif [ -f "$file" ] - then - echo "$file is a file" - fi + if [ -d "$file" ] + then + echo "$file is a directory" + elif [ -f "$file" ] + then + echo "$file is a file" + fi done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" index 13114ccf..829594a4 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # testing string length #-n 判断长度是否非零 @@ -8,22 +9,22 @@ val1=testing val2='' if [ -n "$val1" ] -then - echo "The string $val1 is not empty" +then + echo "The string $val1 is not empty" else - echo "The string $val1 is empty" + echo "The string $val1 is empty" fi if [ -z "$val2" ] -then - echo "The string $val2 is empty" +then + echo "The string $val2 is empty" else - echo "The string $val2 is not empty" + echo "The string $val2 is not empty" fi if [ -z "$val3" ] -then - echo "The string $val3 is empty" +then + echo "The string $val3 is empty" else - echo "The string $val3 is not empty" + echo "The string $val3 is not empty" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" index 48d4118f..a1420a1f 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" @@ -1,7 +1,8 @@ #!/bin/bash + # multiple variables -for (( a=1, b=10; a<=10; a++,b-- )) +for (( a = 1 , b = 10; a <= 10; a ++ , b -- )) do - echo "$a - $b" + echo "$a - $b" done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" index a4d439c8..3392e338 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" @@ -1,8 +1,9 @@ #!/bin/bash + #testing multiple commands in the then section testuser=tiandi if grep $testuser /etc/passwd then - echo The bash files from user $testuser are: - ls -a /home/$testuser/.b* + echo The bash files from user $testuser are: + ls -a /home/$testuser/.b* fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" index 03827617..c48c9943 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" @@ -1,9 +1,10 @@ #!/bin/bash + #testing string equality testuser=tiandi if [ $USER = $testuser ] then - echo "Welcome $testuser" + echo "Welcome $testuser" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" index f0380e83..0e029818 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" @@ -1,20 +1,21 @@ #!/bin/bash + # checking if a directory or a file exists if [ -e $HOME ] then - echo "OK on the directory.now to check the file" - #checking if a file exists - if [ -e $HOME/testing ] - then - #the file exists,append data to it - echo "Appending date to existing file" - date >> $HOME/testing - else - #the file is not exists,create a new file - echo "Creating a new file" - date > $HOME/testing - fi + echo "OK on the directory.now to check the file" + #checking if a file exists + if [ -e $HOME/testing ] + then + #the file exists,append data to it + echo "Appending date to existing file" + date >> $HOME/testing + else + #the file is not exists,create a new file + echo "Creating a new file" + date > $HOME/testing + fi else - echo 'Sorry. you do not have a $HOME directory' + echo 'Sorry. you do not have a $HOME directory' fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" index 7a1fe79b..4bb2cf77 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" @@ -1,11 +1,12 @@ #!/bin/bash + # look before you leap if [ -d $HOME ] then - echo "Your home directory exists" - cd $HOME - ls -a + echo "Your home directory exists" + cd $HOME + ls -a else - echo "There is a problem with your HOME direcotry" + echo "There is a problem with your HOME direcotry" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" index 3cc68768..eecac339 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" @@ -1,12 +1,13 @@ #!/bin/bash + #正确使用大于小于号 val1=baseball val2=hocky if [ $val1 \> $val2 ] -then - echo "$val1 is greater than $val2" +then + echo "$val1 is greater than $val2" else - echo "$val1 is less than $val2" + echo "$val1 is less than $val2" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" index 96123e40..2ea2de5e 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" @@ -1,4 +1,5 @@ #!/bin/bash + #test命令中,大小字母会被当成小于小写字符,而在sort中,小写字母会先出现,test使用标准的ASCII排序,sort使用本地化语言设置进行排序,对于英语,本地化设置制定了排序顺序中小写字母出现在大写字母之前 var1=Testing @@ -6,7 +7,7 @@ var2=testing if [ $val1 \> $val2 ] then - echo '$val1 is greater than $val2' + echo '$val1 is greater than $val2' else - echo '$val1 is less than $val2' + echo '$val1 is less than $val2' fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" index e5e442db..45b8c74c 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" @@ -1,6 +1,6 @@ #!/bin/bash for test in I don\'t know if "this'll" work -do - echo "word:$test" +do +echo "word:$test" done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" index 7063175b..1c1d292a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" @@ -3,5 +3,5 @@ # basic for command for test in Alabama Alaska Arizona do - echo The next state is $test + echo The next state is $test done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" index 52034e74..d8723d7c 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" @@ -3,6 +3,6 @@ # another example of how not to use the for command for test in I don't know if this'll work -do - echo "word:$test" +do + echo "word:$test" done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" index 7bc876b8..25170b07 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" @@ -1,4 +1,5 @@ #!/bin/bash + # 大于小于号必须转义,否则shell会将它们当做重定向符号而把字符串值当做文件名处理 # 大于小于号顺序和sort命令所采用的有所不同 # mis-using string comparisons @@ -8,7 +9,7 @@ val2=hockey if [ $val1 > $val2 ] then - echo "$val1 is greater than $val2" + echo "$val1 is greater than $val2" else - echo "$val1 is less than $val2" + echo "$val1 is less than $val2" fi diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md index dec2e90d..72cdeb74 100644 --- a/docs/linux/soft/redis-ops.md +++ b/docs/linux/soft/redis-ops.md @@ -18,6 +18,7 @@ - [设为守护进程](#设为守护进程) - [远程访问](#远程访问) - [设置密码](#设置密码) + - [配置参数表](#配置参数表) - [Redis 集群使用和配置](#redis-集群使用和配置) - [集群规划](#集群规划) - [部署](#部署) @@ -165,6 +166,41 @@ Redis 默认访问不需要密码,如果需要设置密码,需要如下配 - `protected-mode yes` - `requirepass <密码>` +#### 配置参数表 + +| 配置项 | 说明 | +| :-- | :-- | +| `daemonize no` | Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no ) | +| `pidfile /var/run/redis.pid` | 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定 | +| `port 6379` | 指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上 MERZ 对应的号码,而 MERZ 取自意大利歌女 Alessia Merz 的名字 | +| `bind 127.0.0.1` | 绑定的主机地址 | +| `timeout 300` | 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能 | +| `loglevel notice` | 指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice | +| `logfile stdout` | 日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null | +| `databases 16` | 设置数据库的数量,默认数据库为 0,可以使用 SELECT 命令在连接上指定数据库 id | +| `save ` Redis 默认配置文件中提供了三个条件:**save 900 1**、**save 300 10**、**save 60 10000** 分别表示 900 秒(15 分钟)内有 1 个更改,300 秒(5 分钟)内有 10 个更改以及 60 秒内有 10000 个更改。 | 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合 | +| `rdbcompression yes` | 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大 | +| `dbfilename dump.rdb` | 指定本地数据库文件名,默认值为 dump.rdb | +| `dir ./` | 指定本地数据库存放目录 | +| `slaveof ` | 设置当本机为 slav 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步 | +| `masterauth ` | 当 master 服务设置了密码保护时,slav 服务连接 master 的密码 | +| `requirepass foobared` | 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH 命令提供密码,默认关闭 | +| `maxclients 128` | 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息 | +| `maxmemory ` | 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区 | +| `appendonly no` | 指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为 no | +| `appendfilename appendonly.aof` | 指定更新日志文件名,默认为 appendonly.aof | +| `appendfsync everysec` | 指定更新日志条件,共有 3 个可选值:**no**:表示等操作系统进行数据缓存同步到磁盘(快)**always**:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)**everysec**:表示每秒同步一次(折中,默认值) | +| `vm-enabled no` | 指定是否启用虚拟内存机制,默认值为 no,简单的介绍一下,VM 机制将数据分页存放,由 Redis 将访问量较少的页即冷数据 swap 到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析 Redis 的 VM 机制) | +| `vm-swap-file /tmp/redis.swap` | 虚拟内存文件路径,默认值为 /tmp/redis.swap,不可多个 Redis 实例共享 | +| `vm-max-memory 0` | 将所有大于 vm-max-memory 的数据存入虚拟内存,无论 vm-max-memory 设置多小,所有索引数据都是内存存储的(Redis 的索引数据 就是 keys),也就是说,当 vm-max-memory 设置为 0 的时候,其实是所有 value 都存在于磁盘。默认值为 0 | +| `vm-page-size 32` | Redis swap 文件分成了很多的 page,一个对象可以保存在多个 page 上面,但一个 page 上不能被多个对象共享,vm-page-size 是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为 32 或者 64bytes;如果存储很大大对象,则可以使用更大的 page,如果不确定,就使用默认值 | +| `vm-pages 134217728` | 设置 swap 文件中的 page 数量,由于页表(一种表示页面空闲或使用的 bitmap)是在放在内存中的,,在磁盘上每 8 个 pages 将消耗 1byte 的内存。 | +| `vm-max-threads 4` | 设置访问 swap 文件的线程数,最好不要超过机器的核数,如果设置为 0,那么所有对 swap 文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为 4 | +| `glueoutputbuf yes` | 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启 | +| `hash-max-zipmap-entries 64 hash-max-zipmap-value 512` | 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法 | +| `activerehashing yes` | 指定是否激活重置哈希,默认为开启(后面在介绍 Redis 的哈希算法时具体介绍) | +| `include /path/to/local.conf` | 指定包含其它的配置文件,可以在同一主机上多个 Redis 实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件 | + ## Redis 集群使用和配置 Redis 3.0 后支持集群模式。 From 0aafd7e706610597c85755dbcfc0696b87fac279 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 10 Oct 2019 14:16:45 +0800 Subject: [PATCH 08/64] update scripts --- codes/shell/action/README.md | 3 -- codes/shell/action/system/dir.sh | 8 ----- .../README.md" | 3 ++ ...50\346\210\267\347\256\241\347\220\206.sh" | 5 ++++ ...56\345\275\225\346\223\215\344\275\234.sh" | 29 +++++++++++++++++++ ...11\346\213\251\345\217\202\346\225\260.sh" | 0 6 files changed, 37 insertions(+), 11 deletions(-) delete mode 100644 codes/shell/action/README.md delete mode 100644 codes/shell/action/system/dir.sh create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" rename codes/shell/action/system/useradd.sh => "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" (54%) create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" rename codes/shell/action/oper/input.sh => "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" (100%) diff --git a/codes/shell/action/README.md b/codes/shell/action/README.md deleted file mode 100644 index 56076832..00000000 --- a/codes/shell/action/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Shell 脚本实战 - -> 本目录的代码是本人在学习、开发中总结的一些面向实战的 Shell 代码。 diff --git a/codes/shell/action/system/dir.sh b/codes/shell/action/system/dir.sh deleted file mode 100644 index 8a3b2b75..00000000 --- a/codes/shell/action/system/dir.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -current_dir=$(cd `dirname $0`; -pwd) -echo "当前目录是:${current_dir}" - -parent_dir=$(dirname $(pwd)) -echo "父目录是:${parent_dir}" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" new file mode 100644 index 00000000..88386601 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" @@ -0,0 +1,3 @@ +# Shell 脚本实战 + +> 本目录的代码是本人在学习、开发中收集、总结的一些面向实战的 Shell 脚本代码。 diff --git a/codes/shell/action/system/useradd.sh "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" similarity index 54% rename from codes/shell/action/system/useradd.sh rename to "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" index aefd2b7f..84111a11 100644 --- a/codes/shell/action/system/useradd.sh +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" @@ -1,5 +1,10 @@ #!/usr/bin/env bash +################################################################################### +# Linux 系统用户管理 +# @author: Zhang Peng +################################################################################### + # 创建用户组 groupadd elk # 创建新用户,-g elk 设置其用户组为 elk,-p elk 设置其密码为 elk diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" new file mode 100644 index 00000000..81689be6 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +################################################################################### +# 目录操作示例 +# @author: Zhang Peng +################################################################################### + +# 创建目录(整个文件路径中的目录如果不存在,都会一一创建,如果目录已存在,则什么也不做) +mkdir -p /home/linux-tutorial/temp + +# 进入目录 +cd /home/linux-tutorial/temp + +# 查看目录路径 +current_dir=$(pwd) +echo "当前目录是:${current_dir}" + +# 查看目录上一级路径 +parent_dir=$(dirname $(pwd)) +echo "父目录是:${parent_dir}" + +# 复制目录(复制 temp 目录所有内容,并命名新文件夹叫 temp2) +cp -rf /home/linux-tutorial/temp /home/linux-tutorial/temp2 + +# 移动目录(将 temp2 移到 temp 目录下) +mv /home/linux-tutorial/temp2 /home/linux-tutorial/temp/temp2 + +# 删除目录 +rm -rf /home/linux-tutorial diff --git a/codes/shell/action/oper/input.sh "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" similarity index 100% rename from codes/shell/action/oper/input.sh rename to "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" From 254eb941f22f872f32c49772961c85658a20d147 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 10 Oct 2019 18:19:50 +0800 Subject: [PATCH 09/64] update docs --- docs/linux/soft/redis-ops.md | 192 +++++++++++++++++++++++++++++++---- 1 file changed, 174 insertions(+), 18 deletions(-) diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md index 72cdeb74..7840e871 100644 --- a/docs/linux/soft/redis-ops.md +++ b/docs/linux/soft/redis-ops.md @@ -209,16 +209,9 @@ Redis 3.0 后支持集群模式。 `Redis` 集群一般由 **多个节点** 组成,节点数量至少为 `6` 个,才能保证组成 **完整高可用** 的集群。 -
+
-理想情况当然是所有节点各自在不同的机器上,首先于资源,本人在部署 Redis 集群时,只得到 3 台服务器。所以,我的基本规划是满足两个条件: - -- 每台服务器上部署一个主节点、一个从节点。 -- 每个主节点所对应的从节点,必须在另外一台服务器上。 - -
- -> 为集群内 **所有节点** 统一目录,一般划分三个目录:`conf`、`data`、`log`,分别存放 **配置**、**数据** 和 **日志** 相关文件。把 `6` 个节点配置统一放在 `conf` 目录下。 +理想情况当然是所有节点各自在不同的机器上,首先于资源,本人在部署 Redis 集群时,只得到 3 台服务器。所以,我计划每台服务器部署 2 个 Redis 节点。 ### 部署 @@ -232,10 +225,175 @@ Redis 集群节点的安装与单节点服务相同,差异仅在于部署方 分配如下: -| 服务器 | 127.0.0.1 | 127.0.0.2 | 127.0.0.3 | -| ------ | -------------- | -------------- | -------------- | -| 主节点 | 127.0.0.1:6380 | 127.0.0.2:6381 | 127.0.0.3:6382 | -| 从节点 | 127.0.0.1:6382 | 127.0.0.2:6380 | 127.0.0.3:6381 | +| 127.0.0.1 | 127.0.0.2 | 127.0.0.3 | +| -------------- | -------------- | -------------- | +| 127.0.0.1:6381 | 127.0.0.2:6383 | 127.0.0.3:6385 | +| 127.0.0.1:6382 | 127.0.0.2:6384 | 127.0.0.3:6386 | + +#### (1)创建节点目录 + +我个人偏好将软件放在 `/opt` 目录下,在我的机器中,Redis 都安装在 `/opt/redis/redis-5.0.4` 目录下。所以,下面的命令和配置都假设 Redis 安装目录为 `/opt/redis/redis-5.0.4` 。 + +确保机器上已经安装了 Redis 后,执行以下命令,创建 Redis 集群节点实例目录: + +- 127.0.0.1 + +```bash +sudo mkdir -p /opt/redis/redis-5.0.4/cluster/6381 +sudo mkdir -p /opt/redis/redis-5.0.4/cluster/6382 +``` + +- 127.0.0.2 + +```bash +sudo mkdir -p /opt/redis/redis-5.0.4/cluster/6383 +sudo mkdir -p /opt/redis/redis-5.0.4/cluster/6384 +``` + +- 127.0.0.3 + +```bash +sudo mkdir -p /opt/redis/redis-5.0.4/cluster/6385 +sudo mkdir -p /opt/redis/redis-5.0.4/cluster/6386 +``` + + +#### (2)集群节点实例配置 + +每个实例目录下,新建 `redis.conf` 配置文件。 + +实例配置模板以 6381 节点为例(其他节点,完全替换配置中的端口号 6381 即可),如下: + +``` +# 端口号 +port 6381 +# 绑定的主机端口(0.0.0.0 表示允许远程访问) +bind 0.0.0.0 +# 以守护进程方式启动 +daemonize yes + +# 开启集群模式 +cluster-enabled yes +# 集群的配置,配置文件首次启动自动生成 +cluster-config-file /opt/redis/redis-5.0.4/cluster/6381/6381.conf +# 请求超时时间,设置 10 秒 +cluster-node-timeout 10000 + +# 开启 AOF 持久化 +appendonly yes +# 数据存放目录 +dir /opt/redis/redis-5.0.4/cluster/6381 +# 进程文件 +pidfile /var/run/redis-cluster/redis-6381.pid +# 日志文件 +logfile /opt/redis/redis-5.0.4/cluster/6381/6381.log +``` + +#### (3)启动 Redis 节点 + +Redis 的 utils/create-cluster 目录下自带了一个名为 create-cluster 的脚本工具,可以利用它来新建、启动、停止、重启 Redis 节点。 + +脚本中有几个关键参数: + +- `PORT`=30000 - 初始端口号 +- `TIMEOUT`=2000 - 超时时间 +- `NODES`=6 - 节点数 +- `REPLICAS`=1 - 备份数 + +脚本中的每个命令项会根据初始端口号,以及设置的节点数,遍历的去执行操作。 + +由于前面的规划中,节点端口是从 6381 ~ 6386,所以需要将 PORT 变量设为 6380。 + +脚本中启动每个 Redis 节点是通过指定命令行参数来配置属性。所以,我们需要改一下: + +```bash +if [ "$1" == "start" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + echo "Starting $PORT" + /opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/${PORT}/redis.conf + done + exit 0 +fi +``` + +好了,在每台服务器上,都执行 `./create-cluster start` 来启动节点。 + +然后,通过 ps 命令来确认 Redis 进程是否已经工作: + +``` +$ ps -ef | grep redis +root 12036 1 12 16:26 ? 00:08:28 /opt/redis/redis-5.0.4/src/redis-server 0.0.0.0:6381 [cluster] +root 12038 1 0 16:26 ? 00:00:03 /opt/redis/redis-5.0.4/src/redis-server 0.0.0.0:6382 [cluster] +``` + +#### (4)启动集群 + +通过 `redis-cli --cluster create` 命令可以自动配置集群,如下: + +```bash +$ /opt/redis/redis-5.0.4/src/redis-cli --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.2:6383 127.0.0.2:6384 127.0.0.3:6385 127.0.0.3:6386 --cluster-replicas 1 +``` + +如果启动成功,可以看到如下信息: + +``` +>>> Performing hash slots allocation on 6 nodes... +Master[0] -> Slots 0 - 5460 +Master[1] -> Slots 5461 - 10922 +Master[2] -> Slots 10923 - 16383 +Adding replica 127.0.0.2:6384 to 127.0.0.1:6381 +Adding replica 127.0.0.3:6386 to 127.0.0.2:6383 +Adding replica 127.0.0.1:6382 to 127.0.0.3:6385 +M: 75527b790e46530ea271a5b78f9e0fd9030f68e0 127.0.0.1:6381 + slots:[0-5460] (5461 slots) master +S: 031dd0fd5ad90fa26fcf45d49ad906d063611a6d 127.0.0.1:6382 + replicates 53012ebdd25005840da9ecbe07d937296a264206 +M: 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 127.0.0.2:6383 + slots:[5461-10922] (5462 slots) master +S: 016ae9624202891cc6f2b480ff0634de478197fb 127.0.0.2:6384 + replicates 75527b790e46530ea271a5b78f9e0fd9030f68e0 +M: 53012ebdd25005840da9ecbe07d937296a264206 127.0.0.3:6385 + slots:[10923-16383] (5461 slots) master +S: b6d70f2ed78922b1dcb7967ebe1d05ad9157fca8 127.0.0.3:6386 + replicates 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 +Can I set the above configuration? (type 'yes' to accept): yes +>>> Nodes configuration updated +>>> Assign a different config epoch to each node +>>> Sending CLUSTER MEET messages to join the cluster +Waiting for the cluster to join +.... +>>> Performing Cluster Check (using node 127.0.0.1:6381) +M: 75527b790e46530ea271a5b78f9e0fd9030f68e0 127.0.0.1:6381 + slots:[0-5460] (5461 slots) master + 1 additional replica(s) +M: 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 127.0.0.2:6383 + slots:[5461-10922] (5462 slots) master + 1 additional replica(s) +S: 016ae9624202891cc6f2b480ff0634de478197fb 127.0.0.2:6384 + slots: (0 slots) slave + replicates 75527b790e46530ea271a5b78f9e0fd9030f68e0 +M: 53012ebdd25005840da9ecbe07d937296a264206 127.0.0.3:6385 + slots:[10923-16383] (5461 slots) master + 1 additional replica(s) +S: 031dd0fd5ad90fa26fcf45d49ad906d063611a6d 127.0.0.1:6382 + slots: (0 slots) slave + replicates 53012ebdd25005840da9ecbe07d937296a264206 +S: b6d70f2ed78922b1dcb7967ebe1d05ad9157fca8 127.0.0.3:6386 + slots: (0 slots) slave + replicates 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 +[OK] All nodes agree about slots configuration. +>>> Check for open slots... +>>> Check slots coverage... +[OK] All 16384 slots covered. +``` + +#### (5)日常维护操作 + +- 关闭集群 - `./create-cluster stop` +- 检查集群是否健康(指定任意节点即可):`./redis-cli --cluster check ` +- 尝试修复集群节点:`./redis-cli --cluster fix ` ## Redis 命令 @@ -243,9 +401,9 @@ Redis 集群节点的安装与单节点服务相同,差异仅在于部署方 > > 搬迁两张 cheat sheet 图,原址:https://www.cheatography.com/tasjaevan/cheat-sheets/redis/ -
+
-
+
## 压力测试 @@ -284,9 +442,7 @@ GET: 508388.41 requests per second ## 脚本 -以上两种安装方式,我都写了脚本去执行: - -| [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | +如果想傻瓜式安装一个 Redis 单节点服务,可以使用我的 [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft#redis-%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE) ## 参考资料 From 1072e77e7538c798764931c8294856cd1cfeafec Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Sat, 12 Oct 2019 17:53:49 +0800 Subject: [PATCH 10/64] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmaven=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E6=97=A0=E6=B3=95=E5=AE=89=E8=A3=85=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- codes/linux/soft/maven-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/linux/soft/maven-install.sh b/codes/linux/soft/maven-install.sh index 399e51cd..e562f71e 100644 --- a/codes/linux/soft/maven-install.sh +++ b/codes/linux/soft/maven-install.sh @@ -38,7 +38,7 @@ if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${RESET}\n" fi -version=3.6.0 +version=3.6.2 if [[ -n $1 ]]; then version=$1 fi From 7d1ce36b6e160255b776b7a2310e6f318e038dd0 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 15 Oct 2019 14:17:17 +0800 Subject: [PATCH 11/64] update scripts --- .editorconfig | 1 + codes/shell/demos/string-demo.sh | 13 ++- codes/shell/demos/variable-demo.sh | 2 +- ...72\346\234\254\344\275\277\347\224\250.sh" | 22 ---- ...74\347\273\231\345\217\230\351\207\217.sh" | 4 - ...73\347\273\237\345\217\230\351\207\217.sh" | 5 - ...32\344\271\211\345\217\230\351\207\217.sh" | 8 -- ...77\347\224\250\347\244\272\344\276\213.sh" | 56 +++++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 107 ++++++++++++++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 14 +-- ...62\346\210\220\346\225\260\347\273\204.sh" | 19 ---- ...72\346\234\254\344\275\277\347\224\250.sh" | 55 --------- ...44\346\226\255\347\244\272\344\276\213.sh" | 52 +++++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 49 ++++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 59 ++++++++++ ...7\347\224\250\347\244\272\344\276\2132.sh" | 45 ++++++++ ...7\347\224\250\347\244\272\344\276\2133.sh" | 33 ++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 55 +++++++++ ...47\350\241\214\346\214\207\344\273\244.sh" | 29 +++++ .../\350\204\232\346\234\254\345\272\223.sh" | 7 +- ...77\347\224\250\347\244\272\344\276\213.sh" | 13 +++ ...77\347\224\250\347\244\272\344\276\213.sh" | 41 +++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 30 +++++ ...32\345\261\202\345\276\252\347\216\257.sh" | 17 --- ...77\347\224\250\347\244\272\344\276\213.sh" | 68 +++++++++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 35 ++++++ .../if-then-else\350\257\255\345\217\245.sh" | 11 -- .../if-then\350\257\255\345\217\245.sh" | 13 --- .../select-demo.sh" | 14 +++ ...77\347\224\250\347\244\272\344\276\213.sh" | 27 +++++ ...77\347\224\250\347\244\272\344\276\213.sh" | 51 +++++++++ ...345\245\227for\345\276\252\347\216\257.sh" | 16 --- ...14\345\245\227\345\276\252\347\216\257.sh" | 12 -- ...73\345\217\226\345\210\227\350\241\250.sh" | 12 -- ...347\232\204for\345\221\275\344\273\244.sh" | 8 -- ...26\351\203\250\345\276\252\347\216\257.sh" | 17 --- ...47\224\250case\350\257\255\345\217\245.sh" | 15 --- ...24\250continue\345\221\275\344\273\244.sh" | 12 -- .../\344\275\277\347\224\250elif.sh" | 19 ---- ...60\345\200\274\346\257\224\350\276\203.sh" | 16 --- ...7\224\250until\345\221\275\344\273\244.sh" | 21 ---- .../\344\275\277\347\224\250while.sh" | 10 -- ...13\350\257\225\345\221\275\344\273\244.sh" | 11 -- ...04\345\255\227\347\254\246\344\270\262.sh" | 8 -- ...16\345\260\217\344\272\216\345\217\267.sh" | 11 +- ...41\344\273\266\346\265\213\350\257\225.sh" | 10 -- ...57\345\220\246\344\270\272\351\233\266.sh" | 30 ----- ...32\344\270\252\345\217\230\351\207\217.sh" | 8 -- ...32\346\235\241\345\221\275\344\273\244.sh" | 9 -- ...46\344\270\262\346\257\224\350\276\203.sh" | 10 -- .../\345\265\214\345\245\227if.sh" | 2 - ...57\345\220\246\345\255\230\345\234\250.sh" | 21 ---- ...00\346\237\245\347\233\256\345\275\225.sh" | 12 -- ...16\345\260\217\344\272\216\345\217\267.sh" | 13 --- ...04\345\244\215\346\235\202\345\200\274.sh" | 6 - ...50\344\270\255\347\232\204\345\200\274.sh" | 7 -- 56 files changed, 792 insertions(+), 479 deletions(-) delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" => "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" (60%) delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\351\200\200\345\207\272\345\244\232\345\261\202\345\276\252\347\216\257.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then-else\350\257\255\345\217\245.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then\350\257\255\345\217\245.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" create mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\351\207\214\351\235\242\345\265\214\345\245\227for\345\276\252\347\216\257.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\270\244\344\270\252for\345\265\214\345\245\227\345\276\252\347\216\257.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\217\230\351\207\217\350\257\273\345\217\226\345\210\227\350\241\250.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250C\350\257\255\350\250\200\351\243\216\346\240\274\347\232\204for\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250break\350\267\263\345\207\272\345\244\226\351\203\250\345\276\252\347\216\257.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250case\350\257\255\345\217\245.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250continue\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250elif.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250test\350\277\233\350\241\214\346\225\260\345\200\274\346\257\224\350\276\203.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250until\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while\345\244\232\344\270\252\346\265\213\350\257\225\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" => "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" (55%) delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\265\214\345\245\227if.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" diff --git a/.editorconfig b/.editorconfig index 3b69c906..610df05d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,6 +23,7 @@ insert_final_newline = true end_of_line = crlf [*.{java, sh}] +indent_style = tab indent_size = 4 [*.md] diff --git a/codes/shell/demos/string-demo.sh b/codes/shell/demos/string-demo.sh index 0594f6bb..d3030fab 100644 --- a/codes/shell/demos/string-demo.sh +++ b/codes/shell/demos/string-demo.sh @@ -20,9 +20,9 @@ echo ${str3}_${str4} ################### 获取字符串长度 ################### text="12345" -echo ${#text} +echo "${text} length is: ${#text}" # Output: -# 5 +# 12345 length is: 5 ################### 获取字符串长度 ################### text="12345" @@ -37,13 +37,14 @@ echo `expr index "${text}" ll` # 3 ################### 截取关键字左边内容 ################### -str="feature/1.0.0" -branch=`echo ${str#feature/}` +full_branch="feature/1.0.0" +branch=`echo ${full_branch#feature/}` echo "branch is ${branch}" ################### 截取关键字右边内容 ################### -key=`echo ${str%/1.0.0}` -echo "key is ${key}" +full_version="0.0.1-SNAPSHOT" +version=`echo ${full_version%-SNAPSHOT}` +echo "version is ${version}" ################### 判断字符串中是否包含子字符串 ################### result=$(echo "${str}" | grep "feature/") diff --git a/codes/shell/demos/variable-demo.sh b/codes/shell/demos/variable-demo.sh index 3305ce65..db59ca7a 100644 --- a/codes/shell/demos/variable-demo.sh +++ b/codes/shell/demos/variable-demo.sh @@ -3,7 +3,7 @@ ################### 声明变量 ################### name="world" echo "hello ${name}" -# Output: hello +# Output: hello world ################### 只读变量 ################### rword="hello" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" deleted file mode 100644 index 644a93c5..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\217\230\351\207\217\345\237\272\346\234\254\344\275\277\347\224\250.sh" +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -################### 声明变量 ################### -name="world" -echo "hello ${name}" -# Output: hello world - -################### 只读变量 ################### -readonly_var="hello" -echo ${readonly_var} -# Output: hello -readonly readonly_var -# rword="bye" # 如果放开注释,执行时会报错 - -################### 删除变量 ################### -dword="hello" # 声明变量 -echo ${dword} # 输出变量值 -# Output: hello - -unset dword # 删除变量 -echo ${dword} -# Output: (空) diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" deleted file mode 100644 index b9358593..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\345\260\206\350\276\223\345\207\272\347\273\223\346\236\234\350\265\213\345\200\274\347\273\231\345\217\230\351\207\217.sh" +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -folder=$(pwd) -echo "current path: ${folder}" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" deleted file mode 100644 index 3c9bb1c2..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\347\263\273\347\273\237\345\217\230\351\207\217.sh" +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -echo "User info fro userId:$USER" -echo UID:$UID -echo HOME:$HOME diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" deleted file mode 100644 index 854b16de..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\217\230\351\207\217/\350\207\252\345\256\232\344\271\211\345\217\230\351\207\217.sh" +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -days=10 -guest="Katie" -echo "$guest logged in $days days age" -guest="Katie2" -days=5 -echo "$guest logged in $days days age" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..3faa3b55 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +################### 声明变量 ################### +name="world" +echo "hello ${name}" +# Output: hello world + +################### 输出变量 ################### +folder=$(pwd) +echo "current path: ${folder}" + +################### 只读变量 ################### +rword="hello" +echo ${rword} +# Output: hello +readonly rword +# rword="bye" # 如果放开注释,执行时会报错 + +################### 删除变量 ################### +dword="hello" # 声明变量 +echo ${dword} # 输出变量值 +# Output: hello + +unset dword # 删除变量 +echo ${dword} +# Output: (空) + +################### 系统变量 ################### +echo "UID:$UID" +echo LOGNAME:$LOGNAME +echo User:$USER +echo HOME:$HOME +echo PATH:$PATH +echo HOSTNAME:$HOSTNAME +echo SHELL:$SHELL +echo LANG:$LANG + +################### 自定义变量 ################### +days=10 +user="admin" +echo "$user logged in $days days age" +days=5 +user="root" +echo "$user logged in $days days age" +# Output: +# admin logged in 10 days age +# root logged in 5 days age + +################### 从变量读取列表 ################### +colors="Red Yellow Blue" +colors=$colors" White Black" + +for color in $colors +do + echo " $color" +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..3cb19105 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +################### 使用单引号拼接字符串 ################### +name1='white' +str1='hello, '${name1}'' +str2='hello, ${name1}' +echo ${str1}_${str2} +# Output: +# hello, white_hello, ${name1} + +################### 使用双引号拼接字符串 ################### +name2="black" +str3="hello, "${name2}"" +str4="hello, ${name2}" +echo ${str3}_${str4} +# Output: +# hello, black_hello, black + +################### 获取字符串长度 ################### +text="12345" +echo "${text} length is: ${#text}" +# Output: +# 12345 length is: 5 + +# 获取子字符串 +text="12345" +echo ${text:2:2} +# Output: +# 34 + +################### 查找子字符串 ################### +text="hello" +echo `expr index "${text}" ll` +# Output: +# 3 + +################### 判断字符串中是否包含子字符串 ################### +result=$(echo "${str}" | grep "feature/") +if [[ "$result" != "" ]]; then + echo "feature/ 是 ${str} 的子字符串" +else + echo "feature/ 不是 ${str} 的子字符串" +fi + +################### 截取关键字左边内容 ################### +full_branch="feature/1.0.0" +branch=`echo ${full_branch#feature/}` +echo "branch is ${branch}" + +################### 截取关键字右边内容 ################### +full_version="0.0.1-SNAPSHOT" +version=`echo ${full_version%-SNAPSHOT}` +echo "version is ${version}" + +################### 字符串分割成数组 ################### +str="0.0.0.1" +OLD_IFS="$IFS" +IFS="." +array=( ${str} ) +IFS="$OLD_IFS" +size=${#array[*]} +lastIndex=`expr ${size} - 1` +echo "数组长度:${size}" +echo "最后一个数组元素:${array[${lastIndex}]}" +for item in ${array[@]} +do + echo "$item" +done + +################### 判断字符串是否为空 ################### +#-n 判断长度是否非零 +#-z 判断长度是否为零 + +str=testing +str2='' +if [[ -n "$str" ]] +then + echo "The string $str is not empty" +else + echo "The string $str is empty" +fi + +if [[ -n "$str2" ]] +then + echo "The string $str2 is not empty" +else + echo "The string $str2 is empty" +fi + +# Output: +# The string testing is not empty +# The string is empty + +################### 字符串比较 ################### +str=hello +str2=world +if [[ $str = "hello" ]]; then + echo "str equals hello" +else + echo "str not equals hello" +fi + +if [[ $str2 = "hello" ]]; then + echo "str2 equals hello" +else + echo "str2 not equals hello" +fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" similarity index 60% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" rename to "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" index 5c412e2c..7fbecc0f 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\225\260\347\273\204/\346\225\260\347\273\204\345\237\272\346\234\254\344\275\277\347\224\250.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -1,14 +1,14 @@ #!/usr/bin/env bash -# 创建数组 +################### 创建数组 ################### nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 ) colors=( red yellow "dark blue" ) -# 访问数组的单个元素 +################### 访问数组的单个元素 ################### echo ${nums[1]} # Output: 1 -# 访问数组的所有元素 +################### 访问数组的所有元素 ################### echo ${colors[*]} # Output: red yellow dark blue @@ -32,23 +32,23 @@ printf "+ %s\n" "${colors[@]}" # + yellow # + dark blue -# 访问数组的部分元素 +################### 访问数组的部分元素 ################### echo ${nums[@]:0:2} # Output: # 0 1 -# 访问数组长度 +################### 获取数组长度 ################### echo ${#nums[*]} # Output: # 3 -# 向数组中添加元素 +################### 向数组中添加元素 ################### colors=( white "${colors[@]}" green black ) echo ${colors[@]} # Output: # white red yellow dark blue green black -# 从数组中删除元素 +################### 从数组中删除元素 ################### unset nums[ 0 ] echo ${nums[@]} # Output: diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" deleted file mode 100644 index 58b05563..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\210\206\345\211\262\346\210\220\346\225\260\347\273\204.sh" +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# ---------------------------------------------------------------------------------- -# 根据特定字符将一个字符串分割成数组 -# ---------------------------------------------------------------------------------- - -str="0.0.0.1" -OLD_IFS="$IFS" -IFS="." -array=( ${str} ) -IFS="$OLD_IFS" -size=${#array[*]} -lastIndex=`expr ${size} - 1` -echo "数组长度:${size}" -echo "最后一个数组元素:${array[${lastIndex}]}" -for item in ${array[@]} -do - echo "$item" -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" deleted file mode 100644 index d3030fab..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262/\345\255\227\347\254\246\344\270\262\345\237\272\346\234\254\344\275\277\347\224\250.sh" +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -################### 单引号和双引号 ################### -################### 拼接字符串 ################### -# 使用单引号拼接 -name1='white' -str1='hello, '${name1}'' -str2='hello, ${name1}' -echo ${str1}_${str2} -# Output: -# hello, white_hello, ${name1} - -# 使用双引号拼接 -name2="black" -str3="hello, "${name2}"" -str4="hello, ${name2}" -echo ${str3}_${str4} -# Output: -# hello, black_hello, black - -################### 获取字符串长度 ################### -text="12345" -echo "${text} length is: ${#text}" -# Output: -# 12345 length is: 5 - -################### 获取字符串长度 ################### -text="12345" -echo ${text:2:2} -# Output: -# 34 - -################### 查找子字符串 ################### -text="hello" -echo `expr index "${text}" ll` -# Output: -# 3 - -################### 截取关键字左边内容 ################### -full_branch="feature/1.0.0" -branch=`echo ${full_branch#feature/}` -echo "branch is ${branch}" - -################### 截取关键字右边内容 ################### -full_version="0.0.1-SNAPSHOT" -version=`echo ${full_version%-SNAPSHOT}` -echo "version is ${version}" - -################### 判断字符串中是否包含子字符串 ################### -result=$(echo "${str}" | grep "feature/") -if [[ "$result" != "" ]]; then - echo "feature/ 是 ${str} 的子字符串" -else - echo "feature/ 不是 ${str} 的子字符串" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..0182f1db --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +x="abc" +if [[ -n $1 ]]; then + x=$1 +fi + +y="xyz" +if [[ -n $2 ]]; then + y=$2 +fi + +echo "x=${x}, y=${y}" + +if [[ ${x} = ${y} ]]; then + echo "${x} = ${y} : x 等于 y" +else + echo "${x} = ${y}: x 不等于 y" +fi + +if [[ ${x} != ${y} ]]; then + echo "${x} != ${y} : x 不等于 y" +else + echo "${x} != ${y}: x 等于 y" +fi + +if [[ -z ${x} ]]; then + echo "-z ${x} : 字符串长度为 0" +else + echo "-z ${x} : 字符串长度不为 0" +fi + +if [[ -n "${x}" ]]; then + echo "-n ${x} : 字符串长度不为 0" +else + echo "-n ${x} : 字符串长度为 0" +fi + +if [[ ${x} ]]; then + echo "${x} : 字符串不为空" +else + echo "${x} : 字符串为空" +fi + +# Execute: ./operator-demo5.sh +# Output: +# x=abc, y=xyz +# abc = xyz: x 不等于 y +# abc != xyz : x 不等于 y +# -z abc : 字符串长度不为 0 +# -n abc : 字符串长度不为 0 +# abc : 字符串不为空 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..b0a99b41 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +file="/etc/hosts" + +if [[ -r ${file} ]]; then + echo "${file} 文件可读" +else + echo "${file} 文件不可读" +fi +if [[ -w ${file} ]]; then + echo "${file} 文件可写" +else + echo "${file} 文件不可写" +fi +if [[ -x ${file} ]]; then + echo "${file} 文件可执行" +else + echo "${file} 文件不可执行" +fi +if [[ -f ${file} ]]; then + echo "${file} 文件为普通文件" +else + echo "${file} 文件为特殊文件" +fi +if [[ -d ${file} ]]; then + echo "${file} 文件是个目录" +else + echo "${file} 文件不是个目录" +fi +if [[ -s ${file} ]]; then + echo "${file} 文件不为空" +else + echo "${file} 文件为空" +fi +if [[ -e ${file} ]]; then + echo "${file} 文件存在" +else + echo "${file} 文件不存在" +fi + +# Execute: ./operator-demo6.sh +# Output:(根据文件的实际情况,输出结果可能不同) +# /etc/hosts 文件可读 +# /etc/hosts 文件可写 +# /etc/hosts 文件不可执行 +# /etc/hosts 文件为普通文件 +# /etc/hosts 文件不是个目录 +# /etc/hosts 文件不为空 +# /etc/hosts 文件存在 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..166c539b --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +x=10 +if [[ -n $1 ]]; then + x=$1 +fi + +y=20 +if [[ -n $2 ]]; then + y=$2 +fi + +echo "x=${x}, y=${y}" + +if [[ ${x} -eq ${y} ]]; then + echo "${x} -eq ${y} : x 等于 y" +else + echo "${x} -eq ${y}: x 不等于 y" +fi + +if [[ ${x} -ne ${y} ]]; then + echo "${x} -ne ${y}: x 不等于 y" +else + echo "${x} -ne ${y}: x 等于 y" +fi + +if [[ ${x} -gt ${y} ]]; then + echo "${x} -gt ${y}: x 大于 y" +else + echo "${x} -gt ${y}: x 不大于 y" +fi + +if [[ ${x} -lt ${y} ]]; then + echo "${x} -lt ${y}: x 小于 y" +else + echo "${x} -lt ${y}: x 不小于 y" +fi + +if [[ ${x} -ge ${y} ]]; then + echo "${x} -ge ${y}: x 大于或等于 y" +else + echo "${x} -ge ${y}: x 小于 y" +fi + +if [[ ${x} -le ${y} ]]; then + echo "${x} -le ${y}: x 小于或等于 y" +else + echo "${x} -le ${y}: x 大于 y" +fi + +# Execute: ./operator-demo2.sh +# Output: +# x=10, y=20 +# 10 -eq 20: x 不等于 y +# 10 -ne 20: x 不等于 y +# 10 -gt 20: x 不大于 y +# 10 -lt 20: x 小于 y +# 10 -ge 20: x 小于 y +# 10 -le 20: x 小于或等于 y diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" new file mode 100644 index 00000000..e644253b --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +x=10 +if [[ -n $1 ]]; then + x=$1 +fi + +y=20 +if [[ -n $2 ]]; then + y=$2 +fi + +echo "x=${x}, y=${y}" + +if [[ ${x} != ${y} ]]; then + echo "${x} != ${y} : x 不等于 y" +else + echo "${x} != ${y}: x 等于 y" +fi + +if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then + echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" +else + echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" +fi + +if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then + echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" +else + echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" +fi + +if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then + echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" +else + echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" +fi + +# Execute: ./operator-demo3.sh +# Output: +# x=10, y=20 +# 10 != 20 : x 不等于 y +# 10 小于 100 且 20 大于 15 : 返回 true +# 10 小于 100 或 20 大于 100 : 返回 true +# 10 小于 5 或 20 大于 100 : 返回 false diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" new file mode 100644 index 00000000..e0ac1c4a --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +x=10 +if [[ -n $1 ]]; then + x=$1 +fi + +y=20 +if [[ -n $2 ]]; then + y=$2 +fi + +echo "x=${x}, y=${y}" + +if [[ ${x} -lt 100 && ${y} -gt 100 ]] +then + echo "${x} -lt 100 && ${y} -gt 100 返回 true" +else + echo "${x} -lt 100 && ${y} -gt 100 返回 false" +fi + +if [[ ${x} -lt 100 || ${y} -gt 100 ]] +then + echo "${x} -lt 100 || ${y} -gt 100 返回 true" +else + echo "${x} -lt 100 || ${y} -gt 100 返回 false" +fi + +# Execute: ./operator-demo4.sh +# Output: +# x=10, y=20 +# 10 -lt 100 && 20 -gt 100 返回 false +# 10 -lt 100 || 20 -gt 100 返回 true diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..8e62f92c --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +x=10 +if [[ -n $1 ]]; then + x=$1 +fi + +y=20 +if [[ -n $2 ]]; then + y=$2 +fi + +echo "x=${x}, y=${y}" + +val=`expr ${x} + ${y}` +echo "${x} + ${y} = $val" + +val=`expr ${x} - ${y}` +echo "${x} - ${y} = $val" + +val=`expr ${x} \* ${y}` +echo "${x} * ${y} = $val" + +val=`expr ${y} / ${x}` +echo "${y} / ${x} = $val" + +val=`expr ${y} % ${x}` +echo "${y} % ${x} = $val" + +if [[ ${x} == ${y} ]]; then + echo "${x} = ${y}" +fi +if [[ ${x} != ${y} ]]; then + echo "${x} != ${y}" +fi + +# Execute: ./operator-demo.sh +# Output: +# x=10, y=20 +# 10 + 20 = 30 +# 10 - 20 = -10 +# 10 * 20 = 200 +# 20 / 10 = 2 +# 20 % 10 = 0 +# 10 != 20 + +# Execute: ./operator-demo.sh 10 30 +# Output: +# x=10, y=30 +# 10 + 30 = 40 +# 10 - 30 = -20 +# 10 * 30 = 300 +# 30 / 10 = 3 +# 30 % 10 = 0 +# 10 不等于 30 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" new file mode 100644 index 00000000..758232c4 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" @@ -0,0 +1,29 @@ +#!/bin/bashost="127.0.0.2" +user="root" +password="root" + +/usr/bin/expect << EOF +set timeout 5 +spawn ssh -o "StrictHostKeyChecking no" ${user}@${host} +expect { +"yes/no)?" { send "yes\r"; exp_continue } +"password:" { send "${password}\r" } +} + +expect "root*" +send "ssh-keygen -t rsa\r" +expect "Enter file in which to save the key*" +send "\r" + +expect { +"(y/n)?" { send "n\r"; exp_continue } +"Enter passphrase*" { send "\r"; exp_continue } +"Enter same passphrase again:" { send "\r" } +} + +expect "root*" +send "df -h\r" +expect "root*" +send "exit\r" + +EOF diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" index 5fb13277..8dd479bd 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" @@ -10,11 +10,8 @@ function multem { echo $[ $1 * $2 ] } -function divem - -{ -if [ $2 -ne 0] -then +function divem { +if [ $2 -ne 0]; then echo $[ $1 / $2 ] else echo -1 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..a155eae5 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +################### 使用break跳出外部循环 ################### +# 查找 10 以内第一个能整除 2 和 3 的正整数 +i=1 +while [[ ${i} -lt 10 ]]; do + if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then + echo ${i} + break; + fi + i=`expr ${i} + 1` +done +# Output: 6 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..1f29b951 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +echo "input param: " $1 $2 $3 + +x=0 +if [[ -n $1 ]]; then + x=$1 +fi + +oper="" +if [[ -n $2 ]]; then + oper=$2 +fi + +y=0 +if [[ -n $3 ]]; then + y=$3 +fi + +exec +case ${oper} in + + | add) + val=`expr ${x} + ${y}` + echo "${x} + ${y} = ${val}" + ;; + - | sub) + val=`expr ${x} - ${y}` + echo "${x} - ${y} = ${val}" + ;; + * | mul) + val=`expr ${x} \* ${y}` + echo "${x} * ${y} = ${val}" + ;; + / | div) + val=`expr ${x} / ${y}` + echo "${x} / ${y} = ${val}" + ;; + *) + echo "Unknown oper!" + ;; +esac diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..15d3927e --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# 打印10以内的奇数 +for (( i = 0; i < 10; i ++ )); do + if [[ $((i % 2)) -eq 0 ]]; then + continue + fi + echo ${i} +done +# Output: +# 1 +# 3 +# 5 +# 7 +# 9 + +# 多重循环中的 continue 用法 +for (( a = 1; a <= 5; a ++ )) +do + echo "Iteration $a:" + for (( b = 1; b < 3; b ++ )) + do + if [[ $a -gt 2 ]] && [[ $a -lt 4 ]] + then + continue 2 + fi + var3=$[ $a * $b ] + echo " The result of $a * $b is $var3" + done +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\351\200\200\345\207\272\345\244\232\345\261\202\345\276\252\347\216\257.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\351\200\200\345\207\272\345\244\232\345\261\202\345\276\252\347\216\257.sh" deleted file mode 100644 index 2c59b315..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\351\200\200\345\207\272\345\244\232\345\261\202\345\276\252\347\216\257.sh" +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -#continuing an outer loop - -for (( a = 1; a <= 5; a ++ )) -do - echo "Iteration $a:" - for (( b = 1; b < 3; b ++ )) - do - if [ $a -gt 2 ] && [ $a -lt 4 ] - then - continue 2 - fi - var3=$[ $a * $b ] - echo " The result of $a * $b is $var3" - done -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..b4cb00be --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +################### for 语句 ################### +echo "print 0 to 9" +for (( j = 0; j < 10; j ++ )); do + echo ${j} +done +# Output: +# print 0 to 9 +# 0 +# 1 +# 2 +# 3 +# 4 +# 5 +# 6 +# 7 +# 8 +# 9 + +################### for in 语句 ################### +echo "print 1 to 5" +for i in {1..5}; do + echo ${i}; +done +# Output: +# print 1 to 5 +# 1 +# 2 +# 3 +# 4 +# 5 + +################### for in 语句遍历文件 ################### +DIR=/home/zp +for FILE in ${DIR}/*.sh; do + mv "$FILE" "${DIR}/scripts" +done +# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts + +################### 在 for 语句中使用多个变量 ################### +for (( x = 1 , y = 10; x <= y; x ++ , y -- )) +do + echo "$y - $x = $(($y - $x))" +done + +################### 嵌套 for 循环 ################### +for (( x = 1; x <= 3; x ++ )) +do + echo "Starting loop $x:" + for (( y = 1; y <= 3; y ++ )) + do + echo "Inside loog: $y:" + done +done +#Output +#Starting loop 1: +#Inside loog: 1: +#Inside loog: 2: +#Inside loog: 3: +#Starting loop 2: +#Inside loog: 1: +#Inside loog: 2: +#Inside loog: 3: +#Starting loop 3: +#Inside loog: 1: +#Inside loog: 2: +#Inside loog: 3: diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..408e32a2 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +################### if 语句 ################### +# 写成一行 +if [[ 1 -eq 1 ]]; then + echo "1 -eq 1 result is: true"; +fi +# Output: 1 -eq 1 result is: true + +# 写成多行 +if [[ "abc" -eq "abc" ]] +then + echo ""abc" -eq "abc" result is: true" +fi +# Output: abc -eq abc result is: true + +################### if else 语句 ################### +if [[ 2 -ne 1 ]]; then + echo "true" +else + echo "false" +fi +# Output: true + +################### if elif else 语句 ################### +x=10 +y=20 +if [[ ${x} > ${y} ]]; then + echo "${x} > ${y}" +elif [[ ${x} < ${y} ]]; then + echo "${x} < ${y}" +else + echo "${x} = ${y}" +fi +# Output: 10 < 20 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then-else\350\257\255\345\217\245.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then-else\350\257\255\345\217\245.sh" deleted file mode 100644 index 95f56165..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then-else\350\257\255\345\217\245.sh" +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -#testing the else section -testuser=badtest -if grep $testuser /etc/passwd -then - echo The files for user $testuser are: - ls -a /home/.b* -else - echo "The user name $testuser does not exist on this system" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then\350\257\255\345\217\245.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then\350\257\255\345\217\245.sh" deleted file mode 100644 index 2e76a6ce..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-then\350\257\255\345\217\245.sh" +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -#testing the if statement -if date -then - echo "it worked" -fi -echo -e '\n' -if asd -then - echo "it not worked" -fi -echo 'We are outside the if statement' diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" new file mode 100644 index 00000000..4e59b5a3 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +PS3="Choose the package manager: " +select ITEM in bower npm gem pip +do +echo -n "Enter the package name: " && read PACKAGE +case ${ITEM} in + bower) bower install ${PACKAGE} ;; + npm) npm install ${PACKAGE} ;; + gem) gem install ${PACKAGE} ;; + pip) pip install ${PACKAGE} ;; +esac +break # 避免无限循环 +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..97070802 --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +x=0 +until [[ ${x} -ge 5 ]]; do + echo ${x} + x=`expr ${x} + 1` +done +# Output: +# 0 +# 1 +# 2 +# 3 +# 4 + +x=0 +until echo $x +[[ $x -ge 5 ]] +do + x=$[ $x + 1 ] +done +# Output: +# 0 +# 1 +# 2 +# 3 +# 4 +# 5 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..30e73f0c --- /dev/null +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +################### while 循环输出 0 ~ 9 的平方数 ################### +x=0 +while [[ ${x} -lt 10 ]]; do + echo $((x * x)) + x=$((x + 1)) +done +# Output: +# 0 +# 1 +# 4 +# 9 +# 16 +# 25 +# 36 +# 49 +# 64 +# 81 + +################### while 循环输出 0 ~ 9 ################### +x=0 +while echo ${x} +[[ ${x} -lt 9 ]] +do + x=$((x + 1)) +done +# Output: +# 0 +# 1 +# 2 +# 3 +# 4 +# 5 +# 6 +# 7 +# 8 +# 9 + +################### while 循环嵌套 for 循环 ################### +x=5 +while [[ $x -ge 0 ]] +do + echo "Outer loop: $x" + for (( y = 1; $y < 3; y ++ )) + do + z=$[ $x * $y ] + echo "Inner loop: $x * $y = $z" + done + x=$[ $x - 1 ] +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\351\207\214\351\235\242\345\265\214\345\245\227for\345\276\252\347\216\257.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\351\207\214\351\235\242\345\265\214\345\245\227for\345\276\252\347\216\257.sh" deleted file mode 100644 index 54718a6e..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\351\207\214\351\235\242\345\265\214\345\245\227for\345\276\252\347\216\257.sh" +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# placing a for loop inside a while loop - -var1=5 - -while [ $var1 -ge 0 ] -do - echo "Outer loop: $var1" - for (( var2 = 1; $var2 < 3; var2 ++ )) - do - var3=$[ $var1 * $var2 ] - echo "Inner loop: $var1 * $var2 = $var3" - done - var1=$[ $var1 - 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\270\244\344\270\252for\345\265\214\345\245\227\345\276\252\347\216\257.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\270\244\344\270\252for\345\265\214\345\245\227\345\276\252\347\216\257.sh" deleted file mode 100644 index 87d41216..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\270\244\344\270\252for\345\265\214\345\245\227\345\276\252\347\216\257.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -#nesting for loops - -for (( a = 1; a <= 3; a ++ )) -do - echo "Starting loop $a:" - for (( b = 1; b <= 3; b ++ )) - do - echo "Inside loog: $b:" - done -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\217\230\351\207\217\350\257\273\345\217\226\345\210\227\350\241\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\217\230\351\207\217\350\257\273\345\217\226\345\210\227\350\241\250.sh" deleted file mode 100644 index 89905c66..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\217\230\351\207\217\350\257\273\345\217\226\345\210\227\350\241\250.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# using a variable to hold the list - -list="Alabama Alaska Arizona" -list=$list" Connecticut" - -for state in $list -do - echo "Have you ever visited $state" -done - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250C\350\257\255\350\250\200\351\243\216\346\240\274\347\232\204for\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250C\350\257\255\350\250\200\351\243\216\346\240\274\347\232\204for\345\221\275\344\273\244.sh" deleted file mode 100644 index 187b4ff3..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250C\350\257\255\350\250\200\351\243\216\346\240\274\347\232\204for\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -#testing the C-style for loop - -for (( i = 1; i <= 10; i ++ )) -do - echo "The next number is $i" -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250break\350\267\263\345\207\272\345\244\226\351\203\250\345\276\252\347\216\257.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250break\350\267\263\345\207\272\345\244\226\351\203\250\345\276\252\347\216\257.sh" deleted file mode 100644 index 2fdfec4f..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250break\350\267\263\345\207\272\345\244\226\351\203\250\345\276\252\347\216\257.sh" +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# break n,默认为1 - -for (( a = 1; a <= 3; a ++ )) -do - echo "Outer loop : $a" - for (( b = 1; b < 100; b ++ )) - do - if [ $b -gt 4 ] - then - break 2 - fi - echo " Inner loop:$b" - done -done - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250case\350\257\255\345\217\245.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250case\350\257\255\345\217\245.sh" deleted file mode 100644 index 557899cf..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250case\350\257\255\345\217\245.sh" +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -#using the case command - -case $USER in - tiandi | barbar) - echo "Welcome, $USER" - echo "Pleas enjoy your visit" ;; - testing) - echo "Special testing account" ;; - jessica) - echo "Do not forget to logout when you are out" ;; - *) - echo "Sorry, you are not allowed here" ;; -esac diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250continue\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250continue\345\221\275\344\273\244.sh" deleted file mode 100644 index 967b1e71..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250continue\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -#using the continue command - -for (( var1 = 1; var1 < 15; var1 ++ )) -do - if [ $var1 -gt 5 ] && [ $var1 -lt 10 ] - then - continue - fi - echo "Iteration number:$var1" -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250elif.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250elif.sh" deleted file mode 100644 index 50471239..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250elif.sh" +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# looking for a possible value - -if [ $USER = "tiandi" ] -then - echo "Welcome $USER" - echo "Please enjoy your visit" -elif [ $USER = testing ] -then - echo "Welcome $USER" - echo "Please enjoy your visit" -elif [ $USER = barbar ] -then - echo "Do not forget to logout when you're done" -else - echo "Sorry, you are not allowed here" -fi - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250test\350\277\233\350\241\214\346\225\260\345\200\274\346\257\224\350\276\203.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250test\350\277\233\350\241\214\346\225\260\345\200\274\346\257\224\350\276\203.sh" deleted file mode 100644 index cb052b0a..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250test\350\277\233\350\241\214\346\225\260\345\200\274\346\257\224\350\276\203.sh" +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -var1=10 -var2=5 -if [ $var1 -gt 5 ] -then - echo "The test value $var1 is greater than 5" -fi -if [ $var1 -eq $var2 ] -then - echo "The values is equal" -else - echo "The values are different" -fi - - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250until\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250until\345\221\275\344\273\244.sh" deleted file mode 100644 index 859d038d..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250until\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -#using the until command - -var1=100 -until [ $var1 -eq 0 ] -do - echo $var1 - var1=$[ $var1 - 25 ] -done - -var1=100 -until echo $var1 -[ $var1 -eq 0 ] -do - echo Inside the loop: $var1 - var1=$[ $var1 - 25 ] -done - - - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while.sh" deleted file mode 100644 index 6b931d5b..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while.sh" +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# while command test -var1=10 -while [ $var1 -gt 0 ] -do - echo $var1 - var1=$[ $var1 - 1 ] -done - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while\345\244\232\344\270\252\346\265\213\350\257\225\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while\345\244\232\344\270\252\346\265\213\350\257\225\345\221\275\344\273\244.sh" deleted file mode 100644 index 2a3a0dd6..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250while\345\244\232\344\270\252\346\265\213\350\257\225\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -#testing a multicommand while loop - -var1=10 -while echo $var1 -[ $var1 -ge 0 ] -do - echo 'This is inside the loop' - var1=$[ $var1 - 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" deleted file mode 100644 index 4f2b2932..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\274\225\345\217\267\345\234\210\350\265\267\347\224\250\347\251\272\346\240\274\345\210\206\351\232\224\347\232\204\345\255\227\347\254\246\344\270\262.sh" +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# another example of how not to use the for command - -for test in Newada "New Hampshire" -do - echo "Now going to $test" -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" similarity index 55% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" rename to "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" index 25170b07..728832c7 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\351\224\231\350\257\257\347\232\204\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" +++ "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" @@ -7,9 +7,18 @@ val1=baseball val2=hockey -if [ $val1 > $val2 ] +################### 错误使用大于小于号 ################## +if [[ $val1 > $val2 ]] then echo "$val1 is greater than $val2" else echo "$val1 is less than $val2" fi + +################### 正确使用大于小于号 ################### +if [[ $val1 \> $val2 ]] +then + echo "$val1 is greater than $val2" +else + echo "$val1 is less than $val2" +fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" deleted file mode 100644 index c8634b45..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\254\246\345\220\210\346\235\241\344\273\266\346\265\213\350\257\225.sh" +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -#testing compound comparisons - -if [ -d $HOME ] && [ -w $HOME/testing ] -then - echo "The file exists and you can write to it" -else - echo "I cannot write to it" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" deleted file mode 100644 index 829594a4..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\210\244\346\226\255\345\255\227\347\254\246\344\270\262\351\225\277\345\272\246\346\230\257\345\220\246\344\270\272\351\233\266.sh" +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# testing string length - -#-n 判断长度是否非零 -#-z 判断长度是否为零 - -val1=testing -val2='' - -if [ -n "$val1" ] -then - echo "The string $val1 is not empty" -else - echo "The string $val1 is empty" -fi - -if [ -z "$val2" ] -then - echo "The string $val2 is empty" -else - echo "The string $val2 is not empty" -fi -if [ -z "$val3" ] -then - echo "The string $val3 is empty" -else - echo "The string $val3 is not empty" -fi - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" deleted file mode 100644 index a1420a1f..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250for\350\257\255\345\217\245\344\270\255\344\275\277\347\224\250\345\244\232\344\270\252\345\217\230\351\207\217.sh" +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# multiple variables - -for (( a = 1 , b = 10; a <= 10; a ++ , b -- )) -do - echo "$a - $b" -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" deleted file mode 100644 index 3392e338..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\234\250then\345\235\227\344\270\255\344\275\277\347\224\250\345\244\232\346\235\241\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -#testing multiple commands in the then section -testuser=tiandi -if grep $testuser /etc/passwd -then - echo The bash files from user $testuser are: - ls -a /home/$testuser/.b* -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" deleted file mode 100644 index c48c9943..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203.sh" +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -#testing string equality - -testuser=tiandi - -if [ $USER = $testuser ] -then - echo "Welcome $testuser" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\265\214\345\245\227if.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\265\214\345\245\227if.sh" deleted file mode 100644 index 05a7907c..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\345\265\214\345\245\227if.sh" +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" deleted file mode 100644 index 0e029818..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\346\226\207\344\273\266\346\210\226\347\233\256\345\275\225\346\230\257\345\220\246\345\255\230\345\234\250.sh" +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# checking if a directory or a file exists - -if [ -e $HOME ] -then - echo "OK on the directory.now to check the file" - #checking if a file exists - if [ -e $HOME/testing ] - then - #the file exists,append data to it - echo "Appending date to existing file" - date >> $HOME/testing - else - #the file is not exists,create a new file - echo "Creating a new file" - date > $HOME/testing - fi -else - echo 'Sorry. you do not have a $HOME directory' -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" deleted file mode 100644 index 4bb2cf77..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\243\200\346\237\245\347\233\256\345\275\225.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# look before you leap - -if [ -d $HOME ] -then - echo "Your home directory exists" - cd $HOME - ls -a -else - echo "There is a problem with your HOME direcotry" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" deleted file mode 100644 index eecac339..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\255\243\347\241\256\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -#正确使用大于小于号 - -val1=baseball -val2=hocky - -if [ $val1 \> $val2 ] -then - echo "$val1 is greater than $val2" -else - echo "$val1 is less than $val2" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" deleted file mode 100644 index 45b8c74c..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\247\243\345\206\263\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\244\215\346\235\202\345\200\274.sh" +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -for test in I don\'t know if "this'll" work -do -echo "word:$test" -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" deleted file mode 100644 index 1c1d292a..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\345\210\227\350\241\250\344\270\255\347\232\204\345\200\274.sh" +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# basic for command -for test in Alabama Alaska Arizona -do - echo The next state is $test -done From 68e2da182071b3bdc922961bf49860275fc77c51 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 17 Oct 2019 17:32:13 +0800 Subject: [PATCH 12/64] =?UTF-8?q?redis=20=E9=9B=86=E7=BE=A4=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../soft/config/redis-cluster/6381/redis.conf | 12 +++ .../soft/config/redis-cluster/6382/redis.conf | 12 +++ .../soft/config/redis-cluster/6383/redis.conf | 12 +++ .../soft/config/redis-cluster/6384/redis.conf | 12 +++ .../soft/config/redis-cluster/6385/redis.conf | 12 +++ .../soft/config/redis-cluster/6386/redis.conf | 12 +++ codes/linux/soft/config/redis-cluster/boot.sh | 7 ++ .../soft/config/redis-cluster/create-cluster | 102 ++++++++++++++++++ docs/coverpage.md | 2 +- docs/index.html | 4 +- 10 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 codes/linux/soft/config/redis-cluster/6381/redis.conf create mode 100644 codes/linux/soft/config/redis-cluster/6382/redis.conf create mode 100644 codes/linux/soft/config/redis-cluster/6383/redis.conf create mode 100644 codes/linux/soft/config/redis-cluster/6384/redis.conf create mode 100644 codes/linux/soft/config/redis-cluster/6385/redis.conf create mode 100644 codes/linux/soft/config/redis-cluster/6386/redis.conf create mode 100644 codes/linux/soft/config/redis-cluster/boot.sh create mode 100644 codes/linux/soft/config/redis-cluster/create-cluster diff --git a/codes/linux/soft/config/redis-cluster/6381/redis.conf b/codes/linux/soft/config/redis-cluster/6381/redis.conf new file mode 100644 index 00000000..f0315bec --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/6381/redis.conf @@ -0,0 +1,12 @@ +port 6381 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /opt/redis/redis-5.0.4/cluster/6381/6381.conf +cluster-node-timeout 10000 + +appendonly yes +dir /opt/redis/redis-5.0.4/cluster/6381 +pidfile /var/run/redis-cluster/redis-6381.pid +logfile /opt/redis/redis-5.0.4/cluster/6381/6381.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis-cluster/6382/redis.conf b/codes/linux/soft/config/redis-cluster/6382/redis.conf new file mode 100644 index 00000000..6ca21eaa --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/6382/redis.conf @@ -0,0 +1,12 @@ +port 6382 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /opt/redis/redis-5.0.4/cluster/6382/6382.conf +cluster-node-timeout 10000 + +appendonly yes +dir /opt/redis/redis-5.0.4/cluster/6382 +pidfile /var/run/redis-cluster/redis-6382.pid +logfile /opt/redis/redis-5.0.4/cluster/6382/6382.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis-cluster/6383/redis.conf b/codes/linux/soft/config/redis-cluster/6383/redis.conf new file mode 100644 index 00000000..509f9d38 --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/6383/redis.conf @@ -0,0 +1,12 @@ +port 6383 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /opt/redis/redis-5.0.4/cluster/6383/6383.conf +cluster-node-timeout 10000 + +appendonly yes +dir /opt/redis/redis-5.0.4/cluster/6383 +pidfile /var/run/redis-cluster/redis-6383.pid +logfile /opt/redis/redis-5.0.4/cluster/6383/6383.log diff --git a/codes/linux/soft/config/redis-cluster/6384/redis.conf b/codes/linux/soft/config/redis-cluster/6384/redis.conf new file mode 100644 index 00000000..10a64db2 --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/6384/redis.conf @@ -0,0 +1,12 @@ +port 6384 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /opt/redis/redis-5.0.4/cluster/6384/6384.conf +cluster-node-timeout 10000 + +appendonly yes +dir /opt/redis/redis-5.0.4/cluster/6384 +pidfile /var/run/redis-cluster/redis-6384.pid +logfile /opt/redis/redis-5.0.4/cluster/6384/6384.log diff --git a/codes/linux/soft/config/redis-cluster/6385/redis.conf b/codes/linux/soft/config/redis-cluster/6385/redis.conf new file mode 100644 index 00000000..33b66a68 --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/6385/redis.conf @@ -0,0 +1,12 @@ +port 6385 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /opt/redis/redis-5.0.4/cluster/6385/6385.conf +cluster-node-timeout 10000 + +appendonly yes +dir /opt/redis/redis-5.0.4/cluster/6385 +pidfile /var/run/redis-cluster/redis-6385.pid +logfile /opt/redis/redis-5.0.4/cluster/6385/6385.log diff --git a/codes/linux/soft/config/redis-cluster/6386/redis.conf b/codes/linux/soft/config/redis-cluster/6386/redis.conf new file mode 100644 index 00000000..21af8615 --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/6386/redis.conf @@ -0,0 +1,12 @@ +port 6386 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /opt/redis/redis-5.0.4/cluster/6386/6386.conf +cluster-node-timeout 10000 + +appendonly yes +dir /opt/redis/redis-5.0.4/cluster/6386 +pidfile /var/run/redis-cluster/redis-6386.pid +logfile /opt/redis/redis-5.0.4/cluster/6386/6386.log diff --git a/codes/linux/soft/config/redis-cluster/boot.sh b/codes/linux/soft/config/redis-cluster/boot.sh new file mode 100644 index 00000000..731bd463 --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/boot.sh @@ -0,0 +1,7 @@ + +/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6381/redis.conf +/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6382/redis.conf +/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6383/redis.conf +/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6384/redis.conf +/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6385/redis.conf +/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6386/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/create-cluster b/codes/linux/soft/config/redis-cluster/create-cluster new file mode 100644 index 00000000..3de81525 --- /dev/null +++ b/codes/linux/soft/config/redis-cluster/create-cluster @@ -0,0 +1,102 @@ +#!/bin/bash + +# Settings +PORT=6380 +TIMEOUT=2000 +NODES=6 +REPLICAS=1 + +# You may want to put the above config parameters into config.sh in order to +# override the defaults without modifying this script. + +if [ -a config.sh ] +then + source "config.sh" +fi + +# Computed vars +ENDPORT=$((PORT+NODES)) + +if [ "$1" == "start" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + echo "Starting $PORT" + /opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/${PORT}/redis.conf + done + exit 0 +fi + +if [ "$1" == "create" ] +then + HOSTS="" + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + HOSTS="$HOSTS 127.0.0.1:$PORT" + done + /opt/redis/redis-5.0.4/src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS + exit 0 +fi + +if [ "$1" == "stop" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + echo "Stopping $PORT" + /opt/redis/redis-5.0.4/src/redis-cli -p $PORT shutdown nosave + done + exit 0 +fi + +if [ "$1" == "watch" ] +then + PORT=$((PORT+1)) + while [ 1 ]; do + clear + date + /opt/redis/redis-5.0.4/src/redis-cli -p $PORT cluster nodes | head -30 + sleep 1 + done + exit 0 +fi + +if [ "$1" == "tail" ] +then + INSTANCE=$2 + PORT=$((PORT+INSTANCE)) + tail -f ${PORT}.log + exit 0 +fi + +if [ "$1" == "call" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + /opt/redis/redis-5.0.4/src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 + done + exit 0 +fi + +if [ "$1" == "clean" ] +then + rm -rf *.log + rm -rf appendonly*.aof + rm -rf dump*.rdb + rm -rf nodes*.conf + exit 0 +fi + +if [ "$1" == "clean-logs" ] +then + rm -rf *.log + exit 0 +fi + +echo "Usage: $0 [start|create|stop|watch|tail|clean]" +echo "start -- Launch Redis Cluster instances." +echo "create -- Create a cluster using redis-cli --cluster create." +echo "stop -- Stop Redis Cluster instances." +echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." +echo "tail -- Run tail -f of instance at base port + ID." +echo "clean -- Remove all instances data, logs, configs." +echo "clean-logs -- Remove just instances logs." diff --git a/docs/coverpage.md b/docs/coverpage.md index 8f6b1e3c..881c7086 100644 --- a/docs/coverpage.md +++ b/docs/coverpage.md @@ -1,4 +1,4 @@ -
+
# Linux Tutorial diff --git a/docs/index.html b/docs/index.html index b72a9db2..c50503e0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -9,7 +9,7 @@ name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> - + - - - - - -
正在加载...
- - - - - - - - - - - - + + + Linux Tutorial + + + + + + + + + + + +
正在加载...
+ + + + + + + + + + + + + diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" index 5431a4de..6e062ad8 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" @@ -250,7 +250,7 @@ touch ex2 > ln 命令用来为文件创建连接,连接类型分为硬连接和符号连接两种,默认的连接类型是硬连接。如果要创建符号连接必须使用"-s"选项。 > -> 注意:符号链接文件不是一个独立的文件,它的许多属性依赖于源文件,所以给符号链接文件设置存取权限是没有意义的。 +> 🔔 注意:符号链接文件不是一个独立的文件,它的许多属性依赖于源文件,所以给符号链接文件设置存取权限是没有意义的。 > > 参考:http://man.linuxde.net/ln diff --git "a/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" index ad250ce3..7292493c 100644 --- "a/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" @@ -150,7 +150,7 @@ yum 的默认源是国外的,下载速度比较慢,所以最好替换为一 | | Centos6:http://mirrors.aliyun.com/repo/Centos-6.repo
Centos7:http://mirrors.aliyun.com/repo/Centos-7.repo | | | Centos6:http://mirrors.163.com/.help/CentOS6-Base-163.repo
Centos7:http://mirrors.163.com/.help/CentOS7-Base-163.repo | -> 注意:Cento5 已废弃,只能使用 http://vault.centos.org/ 替换,但由于是国外镜像,速度较慢。 +> 🔔 注意:Cento5 已废弃,只能使用 http://vault.centos.org/ 替换,但由于是国外镜像,速度较慢。 替换方法,以 aliyun CentOS7 为例: diff --git "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" "b/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" index 1e5bd3ba..f8ed21f4 100644 --- "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" +++ "b/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" @@ -5,7 +5,7 @@ date: 2019-03-06 # Linux 典型运维应用 -> :bulb: 如果没有特殊说明,本文的案例都是针对 Centos 发行版本。 +> 💡 如果没有特殊说明,本文的案例都是针对 Centos 发行版本。 ## 网络操作 diff --git a/docs/linux/ops/shell.md b/docs/linux/ops/shell.md index 43cfd6c3..b8b5c986 100644 --- a/docs/linux/ops/shell.md +++ b/docs/linux/ops/shell.md @@ -5,7 +5,7 @@ > 本文主要介绍 bash 的语法,对于 linux 指令不做任何介绍。 > > :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog)」 -> :keyboard: 本文的源码已归档到 [linux-tutorial](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos) +> 🔁 本文的源码已归档到 [linux-tutorial](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos) ``` ███████╗██╗ ██╗███████╗██╗ ██╗ @@ -173,7 +173,7 @@ chmod +x /path/to/script.sh #使脚本具有执行权限 这种方式要求脚本文件的第一行必须指明运行该脚本的程序,比如: -**:keyboard: 『示例源码』** [helloworld.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos/helloworld.sh) +**🔁 『示例源码』** [helloworld.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos/helloworld.sh) ```bash #!/usr/bin/env bash @@ -207,7 +207,7 @@ shell 语法中,注释是特殊的语句,会被 shell 解释器忽略。 - 单行注释 - 以 `#` 开头,到行尾结束。 - 多行注释 - 以 `:< > `continue`语句用来跳过某次迭代。 -**:keyboard: 『示例源码』** [break-demo.sh](https://github.com/dunwu/linux-tutorial/blob/master/codes/shell/demos/statement/break-demo.sh) +**🔁 『示例源码』** [break-demo.sh](https://github.com/dunwu/linux-tutorial/blob/master/codes/shell/demos/statement/break-demo.sh) ```bash # 查找 10 以内第一个能整除 2 和 3 的正整数 @@ -1244,7 +1244,7 @@ done # Output: 6 ``` -**:keyboard: 『示例源码』** [continue-demo.sh](https://github.com/dunwu/linux-tutorial/blob/master/codes/shell/demos/statement/continue-demo.sh) +**🔁 『示例源码』** [continue-demo.sh](https://github.com/dunwu/linux-tutorial/blob/master/codes/shell/demos/statement/continue-demo.sh) ```bash # 打印10以内的奇数 @@ -1273,14 +1273,14 @@ bash 函数定义语法如下: } ``` -> :bulb: 说明: +> 💡 说明: > > 1. 函数定义时,`function` 关键字可有可无。 > 2. 函数返回值 - return 返回函数返回值,返回值类型只能为整数(0-255)。如果不加 return 语句,shell 默认将以最后一条命令的运行结果,作为函数返回值。 > 3. 函数返回值在调用该函数后通过 `$?` 来获得。 > 4. 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。 -**:keyboard: 『示例源码』** [function-demo.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos//function/function-demo.sh) +**🔁 『示例源码』** [function-demo.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos//function/function-demo.sh) ```bash #!/usr/bin/env bash @@ -1346,7 +1346,7 @@ the result is: 100 | `$#` | 不包括`$0`在内的位置参数的个数 | | `$FUNCNAME` | 函数名称(仅在函数内部有值) | -**:keyboard: 『示例源码』** [function-demo2.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos//function/function-demo2.sh) +**🔁 『示例源码』** [function-demo2.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos//function/function-demo2.sh) ```bash #!/usr/bin/env bash @@ -1406,7 +1406,7 @@ $ ./function-demo2.sh 10 20 | `$-` | 返回 Shell 使用的当前选项,与 set 命令功能相同。 | | `$?` | 函数返回值 | -**:keyboard: 『示例源码』** [function-demo3.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos//function/function-demo3.sh) +**🔁 『示例源码』** [function-demo3.sh](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos//function/function-demo3.sh) ```bash runner() { @@ -1644,7 +1644,7 @@ $ ./my_script 有时我们值需要 debug 脚本的一部分。这种情况下,使用`set`命令会很方便。这个命令可以启用或禁用选项。使用`-`启用选项,`+`禁用选项: -**:keyboard: 『示例源码』** [debug-demo.sh](https://github.com/dunwu/linux-tutorial/blob/master/codes/shell/demos/debug-demo.sh) +**🔁 『示例源码』** [debug-demo.sh](https://github.com/dunwu/linux-tutorial/blob/master/codes/shell/demos/debug-demo.sh) ```bash # 开启 debug diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 466416da..fa65f4fe 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -190,7 +190,7 @@ Nexus 中的仓库有以下类型: ``` -> 注意: +> 🔔 注意: > > - `` 和 `` 的 id 必须和 `settings.xml` 配置文件中的 `` 标签中的 id 匹配。 > - `` 标签的地址需要和 maven 私服的地址匹配。 diff --git a/docs/mac.md b/docs/mac.md index 0637f620..146d4751 100644 --- a/docs/mac.md +++ b/docs/mac.md @@ -314,7 +314,7 @@ Cmd+C、Cmd+V、Cmd+X、Cmd+A、Cmd+Z。 打开 CheatSheet 后,长按 command 键,会弹出当前应用程序的所有快捷键。我们还可以对这些快捷键进行保存。 -## :books: 学习资源 +## 📚 学习资源 - [Awesome Mac](https://github.com/jaywcjlove/awesome-mac) - [awesome-macos-command-line](https://github.com/herrbischoff/awesome-macos-command-line) From 90908bb3785812c5f2b75bd33e3f940fb3e04145 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 24 Oct 2019 18:15:59 +0800 Subject: [PATCH 14/64] update docs --- codes/linux/build/spring-boot-run.sh | 152 ++++++++++++++++++++ codes/linux/dunwu-ops.sh | 2 +- codes/linux/dunwu-sys.sh | 2 +- codes/linux/soft/arthas-install.sh | 35 +++++ codes/linux/soft/config/settings-aliyun.xml | 7 +- codes/linux/soft/elk/config/filebeat.yml | 98 ++++++------- codes/linux/soft/elk/config/logback.xml | 8 +- 7 files changed, 246 insertions(+), 58 deletions(-) create mode 100644 codes/linux/build/spring-boot-run.sh create mode 100644 codes/linux/soft/arthas-install.sh diff --git a/codes/linux/build/spring-boot-run.sh b/codes/linux/build/spring-boot-run.sh new file mode 100644 index 00000000..d41a048a --- /dev/null +++ b/codes/linux/build/spring-boot-run.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +############################################################################## +# console color +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +############################################################################## + +JAVA_OPTS="" +APP_OPTS="" +packageJavaOpts() { + + # GC OPTS + JAVA_OPTS="${JAVA_OPTS} -server -Xms8g -Xmx16g -Xss512k" + JAVA_OPTS="${JAVA_OPTS} -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=150" + JAVA_OPTS="${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom" + + # DEBUG OPTS + if [[ ${debug} == "on" ]]; then + + # Remote Debug + JAVA_OPTS="${JAVA_OPTS} -Xdebug -Xnoagent -Djava.compiler=NONE" + JAVA_OPTS="${JAVA_OPTS} -Xrunjdwp:transport=dt_socket,address=28889,server=y,suspend=n" + + # GC LOG + JAVA_OPTS="${JAVA_OPTS} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" + JAVA_OPTS="${JAVA_OPTS} -verbose:gc -Xloggc:${LOG_PATH}/${APP_NAME}.gc.log" + JAVA_OPTS="${JAVA_OPTS} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" + + # Heap Dump + JAVA_OPTS="${JAVA_OPTS} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" + JAVA_OPTS="${JAVA_OPTS} -XX:HeapDumpPath=${LOG_PATH}/${APP_NAME}.heapdump.hprof" + + # JMX OPTS + IP=`ip addr|grep "inet "|grep -v 127.0.0.1|awk '{print $2}'|cut -d/ -f1` + JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote=true" + JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" + JAVA_OPTS="${JAVA_OPTS} -Djava.rmi.server.hostname=${IP} -Dcom.sun.management.jmxremote.port=18889" + fi + + # APP OPTS + JAVA_OPTS="${JAVA_OPTS} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true" + JAVA_OPTS="${JAVA_OPTS} -Dspring.profiles.active=${profile} -Dfile.encoding=UTF-8" + + # CLASSPATH + APP_OPTS=" -classpath lib/* -Dlogging.config=file:./config/logback.dev.xml --spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/" +} + +# 检查服务是否已经启动 +pid="" +checkStarted() { + pid=`ps -ef | grep java | grep ${APP_NAME} | awk '{print $2}'` + if [[ -n "${pid}" ]]; then + return 0 + else + return 1 + fi +} + +main() { + case "${oper}" in + start ) + startServer + ;; + stop ) + stopServer + ;; + restart ) + stopServer + sleep 5 + startServer + ;; + * ) + echo "Invalid oper: ${oper}." + exit 1 + esac + + exit 0 +} + +stopServer() { + echo -n "stopping server: " + if checkStarted ;then + kill -9 ${pid} + printf "${GREEN}\n${APP_NAME} is stopped.${RESET}\n" + else + printf "${RED}\n${APP_NAME} fail to stop.${RESET}\n" + fi +} + +startServer() { + printf "${BLUE}starting ${APP_NAME}...${RESET}\n" + if checkStarted ;then + printf "${YELLOW}[WARN] ${APP_NAME} already started!${RESET}\n" + printf "PID: ${pid}\n" + exit 1 + fi + + packageJavaOpts + printf "${CYAN}JVM OPTS:\n ${JAVA_OPTS}${RESET}\n" + if [[ ! -f "${LOG_PATH}/start.out" ]]; then + touch "${LOG_PATH}/start.out" + fi + nohup java ${JAVA_OPTS} -jar ${ROOT_DIR}/../spring-boot-app.jar ${APP_OPTS} >> ${LOG_PATH}/start.out 2>&1 & + printf "${GREEN}\n${APP_NAME} is started.${RESET}\n" +} + +######################################## MAIN ######################################## +# 设置环境变量 +export LANG="zh_CN.UTF-8" +ROOT_DIR=$(pwd) + +APP_NAME=spring-boot-app +LOG_PATH=${ROOT_DIR}/../logs +mkdir -p ${LOG_PATH} + +declare -a serial +serial=(start stop restart) +echo -n "请选择操作(可选值:start|stop|restart):" +read oper +if ! echo "${serial[@]}" | grep -q ${oper}; then + echo "请选择正确操作(可选值:start|stop|restart)" + exit 1 +fi + +if [[ ${oper} == "start" ]] || [[ "${oper}" == "restart" ]]; then + declare -a serial2 + serial2=(prod dev test) + echo -n "选择 profile(可选值:prod|dev|test):" + read profile + if ! echo "${serial2[@]}" | grep -q ${profile}; then + echo "请选择正确 profile(可选值:prod|dev|test)" + exit 1 + fi + + declare -a serial3 + serial3=(on off) + echo -n "是否启动 debug 模式(可选值:on|off):" + read debug + if ! echo "${serial3[@]}" | grep -q ${debug}; then + echo "是否启动 debug 模式(可选值:on|off)" + exit 1 + fi +fi + +main diff --git a/codes/linux/dunwu-ops.sh b/codes/linux/dunwu-ops.sh index 0422d6db..80f9204a 100644 --- a/codes/linux/dunwu-ops.sh +++ b/codes/linux/dunwu-ops.sh @@ -63,7 +63,7 @@ checkOsVersion() { menus=( "配置系统" "安装软件" "退出" ) main() { PS3="请输入命令编号:" - select item in ${menus[@]} + select item in "${menus[@]}" do case ${item} in "配置系统") diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index e493bde4..0002424d 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -12,7 +12,7 @@ EOF menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { PS3="请输入命令编号:" - select item in ${menus[@]} + select item in "${menus[@]}" do case ${item} in "替换yum镜像") diff --git a/codes/linux/soft/arthas-install.sh b/codes/linux/soft/arthas-install.sh new file mode 100644 index 00000000..3266f88f --- /dev/null +++ b/codes/linux/soft/arthas-install.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# ---------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# ---------------------------------------------------------------------------------- + +printf "${PURPLE}" +cat << EOF +# ---------------------------------------------------------------------------------- +# Arthas 安装脚本 +# @author: Zhang Peng +# ---------------------------------------------------------------------------------- +EOF +printf "${RESET}" + +printf "${BLUE}>>>>>>>> begin.\n${RESET}" + +root=/opt/arthas +if [[ -n $1 ]]; then + root=$1 +fi + +mkdir -p ${root} +curl -o ${root}/arthas-boot.jar https://alibaba.github.io/arthas/arthas-boot.jar + +printf "${GREEN}[OK]\n${RESET}" +printf "${BLUE}<<<<<<<< end.\n${RESET}" diff --git a/codes/linux/soft/config/settings-aliyun.xml b/codes/linux/soft/config/settings-aliyun.xml index a996086e..a71a89c8 100644 --- a/codes/linux/soft/config/settings-aliyun.xml +++ b/codes/linux/soft/config/settings-aliyun.xml @@ -1,7 +1,8 @@ - + @@ -18,4 +19,4 @@ central - \ No newline at end of file + diff --git a/codes/linux/soft/elk/config/filebeat.yml b/codes/linux/soft/elk/config/filebeat.yml index c11956c1..d51ef33f 100644 --- a/codes/linux/soft/elk/config/filebeat.yml +++ b/codes/linux/soft/elk/config/filebeat.yml @@ -14,54 +14,54 @@ filebeat.prospectors: -# Each - is a prospector. Most options can be set at the prospector level, so -# you can use different prospectors for various configurations. -# Below are the prospector specific configurations. + # Each - is a prospector. Most options can be set at the prospector level, so + # you can use different prospectors for various configurations. + # Below are the prospector specific configurations. -- type: log + - type: log - # Change to true to enable this prospector configuration. - enabled: true + # Change to true to enable this prospector configuration. + enabled: true - # Paths that should be crawled and fetched. Glob based paths. - paths: - #- /var/log/*.log - #- c:\programdata\elasticsearch\logs\* - - /home/zp/log/*.log + # Paths that should be crawled and fetched. Glob based paths. + paths: + #- /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + - /home/zp/log/*.log - # Exclude lines. A list of regular expressions to match. It drops the lines that are - # matching any regular expression from the list. - #exclude_lines: ['^DBG'] + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] - # Include lines. A list of regular expressions to match. It exports the lines that are - # matching any regular expression from the list. - #include_lines: ['^ERR', '^WARN'] + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] - # Exclude files. A list of regular expressions to match. Filebeat drops the files that - # are matching any regular expression from the list. By default, no files are dropped. - #exclude_files: ['.gz$'] + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #exclude_files: ['.gz$'] - # Optional additional fields. These fields can be freely picked - # to add additional information to the crawled log files for filtering - #fields: - # level: debug - # review: 1 + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 - ### Multiline options + ### Multiline options - # Mutiline can be used for log messages spanning multiple lines. This is common - # for Java Stack Traces or C-Line Continuation + # Mutiline can be used for log messages spanning multiple lines. This is common + # for Java Stack Traces or C-Line Continuation - # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ - #multiline.pattern: ^\[ + # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ + #multiline.pattern: ^\[ - # Defines if the pattern set under pattern should be negated or not. Default is false. - #multiline.negate: false + # Defines if the pattern set under pattern should be negated or not. Default is false. + #multiline.negate: false - # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern - # that was (not) matched before or after or as long as a pattern is not matched based on negate. - # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash - #multiline.match: after + # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern + # that was (not) matched before or after or as long as a pattern is not matched based on negate. + # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash + #multiline.match: after #============================= Filebeat modules =============================== @@ -123,25 +123,25 @@ setup.kibana: # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 host: "192.168.28.11:5601" -#============================= Elastic Cloud ================================== + #============================= Elastic Cloud ================================== -# These settings simplify using filebeat with the Elastic Cloud (https://cloud.elastic.co/). + # These settings simplify using filebeat with the Elastic Cloud (https://cloud.elastic.co/). -# The cloud.id setting overwrites the `output.elasticsearch.hosts` and -# `setup.kibana.host` options. -# You can find the `cloud.id` in the Elastic Cloud web UI. -#cloud.id: + # The cloud.id setting overwrites the `output.elasticsearch.hosts` and + # `setup.kibana.host` options. + # You can find the `cloud.id` in the Elastic Cloud web UI. + #cloud.id: -# The cloud.auth setting overwrites the `output.elasticsearch.username` and -# `output.elasticsearch.password` settings. The format is `:`. -#cloud.auth: + # The cloud.auth setting overwrites the `output.elasticsearch.username` and + # `output.elasticsearch.password` settings. The format is `:`. + #cloud.auth: -#================================ Outputs ===================================== + #================================ Outputs ===================================== -# Configure what output to use when sending the data collected by the beat. + # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ -#output.elasticsearch: + #-------------------------- Elasticsearch output ------------------------------ + #output.elasticsearch: # Array of hosts to connect to. #hosts: ["192.168.28.11:9200"] diff --git a/codes/linux/soft/elk/config/logback.xml b/codes/linux/soft/elk/config/logback.xml index c86d34a2..a174cf94 100644 --- a/codes/linux/soft/elk/config/logback.xml +++ b/codes/linux/soft/elk/config/logback.xml @@ -3,7 +3,7 @@ - + @@ -45,12 +45,12 @@ - - + + - + From 2b73cbc127acaa1a1e87985658753641915b15fa Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 24 Oct 2019 22:00:00 +0800 Subject: [PATCH 15/64] update scripts --- codes/linux/README.md | 2 +- codes/linux/download.sh | 39 ++--- codes/linux/dunwu-ops.sh | 78 +++++----- codes/linux/dunwu-soft.sh | 59 ++++---- codes/linux/dunwu-sys.sh | 66 ++++---- codes/linux/soft/config/settings-aliyun.xml | 34 ++--- codes/linux/soft/elk/config/filebeat.yml | 158 ++++++++++---------- codes/linux/soft/elk/config/logback.xml | 100 ++++++------- codes/linux/sys/change-yum-repo.sh | 23 ++- codes/linux/sys/install-libs.sh | 40 +++-- codes/linux/sys/install-tools.sh | 77 +++++----- codes/linux/sys/set-dns.sh | 44 +++--- codes/linux/sys/set-ntp.sh | 26 +++- codes/linux/sys/stop-firewall.sh | 15 +- codes/linux/sys/sys-settings.sh | 105 +++++++------ 15 files changed, 478 insertions(+), 388 deletions(-) diff --git a/codes/linux/README.md b/codes/linux/README.md index 6e95ca7d..acd0e771 100644 --- a/codes/linux/README.md +++ b/codes/linux/README.md @@ -1,4 +1,4 @@ -# Linux 傻瓜式运维脚本 +# Dunwu Shell 运维脚本 > **本项目脚本代码用于在 [CentOS](https://www.centos.org/) 机器上安装常用命令工具或开发软件。** diff --git a/codes/linux/download.sh b/codes/linux/download.sh index 5300e3a0..96ae8815 100644 --- a/codes/linux/download.sh +++ b/codes/linux/download.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -################################################################################### +# --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" @@ -10,11 +10,10 @@ BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" -################################################################################### +# --------------------------------------------------------------------------------- -printf "${BLUE}" +printf "${BLUE}\n" cat << EOF - ################################################################################### # linux-tutorial 运维脚本工具集下载脚本 # 下载 https://github.com/dunwu/linux-tutorial 中的所有脚本到当前服务器的 @@ -23,23 +22,27 @@ cat << EOF # @author: Zhang Peng # See: https://github.com/dunwu/linux-tutorial ################################################################################### - EOF -printf "${RESET}" +printf "${RESET}\n" + +root=/home/scripts/linux-tutorial +printf "\n${GREEN}>>>>>>>> Download linux-tutorial to ${root} begin.${RESET}\n" +command -v yum > /dev/null 2>&1 || { + printf "\n${RED}Not detected yum.${RESET}"; + exit 1; +} -path=/home/scripts/linux-tutorial -printf "\n${GREEN}>>>>>>>> Download linux-tutorial to ${path} begin.${RESET}\n" -command -v yum > /dev/null 2>&1 || { printf "${RED}Not detected yum.${RESET}"; - exit 1; } -command -v git > /dev/null 2>&1 || { printf "${YELLOW}Not detected git. Install git.${RESET}\n"; - yum -y install git; } +command -v git > /dev/null 2>&1 || { + printf "\n${YELLOW}Not detected git. Install git.${RESET}\n"; + yum install -y git; +} -if [[ -d ${path} ]]; then - cd ${path} +if [[ -d ${root} ]]; then + cd ${root} git pull else - mkdir -p ${path} - git clone https://gitee.com/turnon/linux-tutorial.git ${path} + mkdir -p ${root} + git clone https://gitee.com/turnon/linux-tutorial.git ${root} fi -chmod +x -R ${path} -printf "\n${GREEN}<<<<<<<< Download linux-tutorial to ${path} end.${RESET}\n" +chmod +x -R ${root} +printf "\n${GREEN}<<<<<<<< Download linux-tutorial to ${root} end.${RESET}\n" diff --git a/codes/linux/dunwu-ops.sh b/codes/linux/dunwu-ops.sh index 80f9204a..100bd72e 100644 --- a/codes/linux/dunwu-ops.sh +++ b/codes/linux/dunwu-ops.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -################################################################################### +# --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" @@ -10,82 +10,74 @@ BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" -################################################################################### +# --------------------------------------------------------------------------------- # 打印头部信息 printHeadInfo() { - printf "${BLUE}" - cat << EOF - -*********************************************************************************** -* 欢迎使用 Linux CentOS 环境运维脚本 -* @author: Zhang Peng -*********************************************************************************** - +printf "${BLUE}\n" +cat << EOF +################################################################################### +# 欢迎使用 Dunwu Shell 运维脚本 +# 适用于 Linux CentOS 环境 +# @author: Zhang Peng +################################################################################### EOF - printf "${RESET}" +printf "${RESET}\n" } # 打印尾部信息 printFootInfo() { - printf "${BLUE}" - cat << EOF - -*********************************************************************************** -* 脚本执行结束,感谢使用! -*********************************************************************************** - +printf "${BLUE}\n" +cat << EOF +################################################################################### +# 脚本执行结束,感谢使用! +################################################################################### EOF - printf "${RESET}" +printf "${RESET}\n" } # 检查操作系统环境 checkOsVersion() { if (($1 == 1)); then - echo -e "检查操作系统环境是否兼容本套脚本" - platform=`uname -i` if [[ ${platform} != "x86_64" ]]; then - echo "脚本仅支持 64 位操作系统!" + printf "\n${RED}脚本仅支持 64 位操作系统!${RESET}\n" exit 1 fi version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` if [[ ${version} != 7 ]]; then - echo "脚本仅支持 CentOS 7!" + printf "\n${RED}脚本仅支持 CentOS 7!${RESET}\n" exit 1 fi - - echo -e "脚本可以在本环境运行!" fi } menus=( "配置系统" "安装软件" "退出" ) -main() { +selectAndExecTask() { + printHeadInfo PS3="请输入命令编号:" select item in "${menus[@]}" do case ${item} in - "配置系统") - ./dunwu-sys.sh - main ;; - "安装软件") - ./dunwu-soft.sh - main ;; - "退出") - exit 0 ;; - *) - printf "输入项不支持!\n" - main ;; + "配置系统") + ./dunwu-sys.sh + selectAndExecTask ;; + "安装软件") + ./dunwu-soft.sh + selectAndExecTask ;; + "退出") + printFootInfo + exit 0 ;; + *) + printf "\n${RED}输入项不支持!${RESET}\n" + selectAndExecTask ;; esac break done } + ######################################## MAIN ######################################## -path=$(cd "$(dirname "$0")"; -pwd) -printHeadInfo -checkOsVersion 0 -main -printFootInfo +checkOsVersion 1 +selectAndExecTask diff --git a/codes/linux/dunwu-soft.sh b/codes/linux/dunwu-soft.sh index 049acbf4..deb166eb 100644 --- a/codes/linux/dunwu-soft.sh +++ b/codes/linux/dunwu-soft.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -################################################################################### +# --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" @@ -10,52 +10,57 @@ BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" -################################################################################### +# --------------------------------------------------------------------------------- printf "${BLUE}\n" cat << EOF - -*********************************************************************************** -* 欢迎使用 Linux CentOS 软件安装配置脚本 -* @author: Zhang Peng -*********************************************************************************** - +################################################################################### +# 欢迎使用 Dunwu Shell 软件安装脚本 +# 适用于 Linux CentOS 环境 +# @author: Zhang Peng +################################################################################### EOF +printf "${RESET}\n" # print menu -printf "${PURPLE}" -menus=( docker fastdfs gitlab jdk8 jenkins kafka maven mongodb mysql nacos nexus nginx nodejs redis rocketmq tomcat8 -zookeeper zsh exit ) -for i in "${!menus[@]}"; do - index=`expr ${i} + 1` - val=`expr ${index} % 2` - printf "[%02d] %-20s" "${index}" "${menus[$i]}" - if [[ ${val} -eq 0 ]]; then - printf "\n" - fi -done -printf "\n${RESET}请输入需要安装的软件编号:\n" +printMenu() { + printf "${PURPLE}" + menus=( docker fastdfs gitlab jdk8 jenkins kafka maven mongodb mysql nacos nexus nginx nodejs redis rocketmq tomcat8 zookeeper zsh exit ) + for i in "${!menus[@]}"; do + index=`expr ${i} + 1` + val=`expr ${index} % 2` + printf "[%02d] %-20s" "${index}" "${menus[$i]}" + if [[ ${val} -eq 0 ]]; then + printf "\n" + fi + done + + printf "\n\n${BLUE}请选择需要安装的软件:${RESET}" +} # exec shell to install soft -doInstall() { +main() { + printMenu read -t 30 index if [[ -n ${index} ]]; then no=`expr ${index} - 1` len=${#menus[*]} if [[ ${index} -gt ${len} ]]; then - echo "输入项不支持!" + printf "${RED}输入项不支持!\n${RESET}" exit -1 fi key=${menus[$no]} - if [[ key == 'exit' ]]; then + if [[ ${key} == 'exit' ]]; then + printf "${GREEN}退出 Dunwu 软件安装脚本。\n${RESET}" exit 0 fi - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/${key}-install.sh | bash - doInstall + sh soft/${key}-install.sh + printf "\n" + main else - echo "输入项不支持!" + printf "${RED}输入项不支持!\n${RESET}" exit -1 fi } -doInstall +main diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index 0002424d..c40725d1 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -1,13 +1,26 @@ #!/usr/bin/env bash -cat << EOF +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- +printf "${BLUE}\n" +cat << EOF ################################################################################### -# Linux CentOS 环境初始化脚本(设置环境配置、安装基本的命令工具) +# 欢迎使用 Dunwu Shell 环境初始化脚本(设置环境配置、安装基本的命令工具) +# 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### - EOF +printf "${RESET}\n" menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { @@ -15,33 +28,34 @@ main() { select item in "${menus[@]}" do case ${item} in - "替换yum镜像") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/change-yum-repo.sh | bash - main ;; - "安装基本的命令工具") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash - main ;; - "安装常用libs") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash - main ;; - "系统配置") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sys-settings.sh | bash - main ;; - "全部执行") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/change-yum-repo.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sys-settings.sh | bash - printf "执行完毕,退出。\n" ;; - "退出") - exit 0 ;; - *) - printf "输入项不支持!\n" - main ;; + "替换yum镜像") + sh ${root}/sys/change-yum-repo.sh + main ;; + "安装基本的命令工具") + sh ${root}/sys/install-tools.sh + main ;; + "安装常用libs") + sh ${root}/sys/install-libs.sh + main ;; + "系统配置") + sh ${root}/sys/sys-settings.sh ${root}/sys + main ;; + "全部执行") + sh ${root}/sys/change-yum-repo.sh + sh ${root}/sys/install-tools.sh + sh ${root}/sys/install-libs.sh + sh ${root}/sys/sys-settings.sh ${root}/sys + printf "${GREEN}执行完毕,退出。${RESET}\n" ;; + "退出") + exit 0 ;; + *) + printf "${RED}输入项不支持!${RESET}\n" + main ;; esac break done } ######################################## MAIN ######################################## +root=$(pwd) main diff --git a/codes/linux/soft/config/settings-aliyun.xml b/codes/linux/soft/config/settings-aliyun.xml index a71a89c8..482173f2 100644 --- a/codes/linux/soft/config/settings-aliyun.xml +++ b/codes/linux/soft/config/settings-aliyun.xml @@ -1,22 +1,22 @@ + xmlns="http://maven.apache.org/SETTINGS/1.0.0" + xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> - - - - aliyun - Aliyun - http://maven.aliyun.com/nexus/content/groups/public - central - - - repo2 - Mirror from Maven Repo2 - http://repo2.maven.org/maven2/ - central - - + + + + aliyun + Aliyun + http://maven.aliyun.com/nexus/content/groups/public + central + + + repo2 + Mirror from Maven Repo2 + http://repo2.maven.org/maven2/ + central + + diff --git a/codes/linux/soft/elk/config/filebeat.yml b/codes/linux/soft/elk/config/filebeat.yml index d51ef33f..ce1df792 100644 --- a/codes/linux/soft/elk/config/filebeat.yml +++ b/codes/linux/soft/elk/config/filebeat.yml @@ -14,74 +14,74 @@ filebeat.prospectors: - # Each - is a prospector. Most options can be set at the prospector level, so - # you can use different prospectors for various configurations. - # Below are the prospector specific configurations. + # Each - is a prospector. Most options can be set at the prospector level, so + # you can use different prospectors for various configurations. + # Below are the prospector specific configurations. - - type: log + - type: log - # Change to true to enable this prospector configuration. - enabled: true + # Change to true to enable this prospector configuration. + enabled: true - # Paths that should be crawled and fetched. Glob based paths. - paths: - #- /var/log/*.log - #- c:\programdata\elasticsearch\logs\* - - /home/zp/log/*.log + # Paths that should be crawled and fetched. Glob based paths. + paths: + #- /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + - /home/zp/log/*.log - # Exclude lines. A list of regular expressions to match. It drops the lines that are - # matching any regular expression from the list. - #exclude_lines: ['^DBG'] + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] - # Include lines. A list of regular expressions to match. It exports the lines that are - # matching any regular expression from the list. - #include_lines: ['^ERR', '^WARN'] + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] - # Exclude files. A list of regular expressions to match. Filebeat drops the files that - # are matching any regular expression from the list. By default, no files are dropped. - #exclude_files: ['.gz$'] + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #exclude_files: ['.gz$'] - # Optional additional fields. These fields can be freely picked - # to add additional information to the crawled log files for filtering - #fields: - # level: debug - # review: 1 + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 - ### Multiline options + ### Multiline options - # Mutiline can be used for log messages spanning multiple lines. This is common - # for Java Stack Traces or C-Line Continuation + # Mutiline can be used for log messages spanning multiple lines. This is common + # for Java Stack Traces or C-Line Continuation - # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ - #multiline.pattern: ^\[ + # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ + #multiline.pattern: ^\[ - # Defines if the pattern set under pattern should be negated or not. Default is false. - #multiline.negate: false + # Defines if the pattern set under pattern should be negated or not. Default is false. + #multiline.negate: false - # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern - # that was (not) matched before or after or as long as a pattern is not matched based on negate. - # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash - #multiline.match: after + # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern + # that was (not) matched before or after or as long as a pattern is not matched based on negate. + # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash + #multiline.match: after #============================= Filebeat modules =============================== filebeat.config.modules: - # Glob pattern for configuration loading - path: ${path.config}/modules.d/*.yml + # Glob pattern for configuration loading + path: ${path.config}/modules.d/*.yml - # Set to true to enable config reloading - reload.enabled: true + # Set to true to enable config reloading + reload.enabled: true - # Period on which files under path should be checked for changes - #reload.period: 10s + # Period on which files under path should be checked for changes + #reload.period: 10s #==================== Elasticsearch template setting ========================== setup.template.settings: - index.number_of_shards: 3 - #index.codec: best_compression - #_source.enabled: false + index.number_of_shards: 3 + #index.codec: best_compression + #_source.enabled: false #================================ General ===================================== @@ -96,7 +96,7 @@ name: 127.0.0.1 # Optional fields that you can specify to add additional information to the # output. fields: - profile: development + profile: development #============================== Dashboards ===================================== @@ -117,53 +117,53 @@ setup.dashboards.enabled: true # This requires a Kibana endpoint configuration. setup.kibana: - # Kibana Host - # Scheme and port can be left out and will be set to the default (http and 5601) - # In case you specify and additional path, the scheme is required: http://localhost:5601/path - # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 - host: "192.168.28.11:5601" + # Kibana Host + # Scheme and port can be left out and will be set to the default (http and 5601) + # In case you specify and additional path, the scheme is required: http://localhost:5601/path + # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 + host: "192.168.28.11:5601" - #============================= Elastic Cloud ================================== + #============================= Elastic Cloud ================================== - # These settings simplify using filebeat with the Elastic Cloud (https://cloud.elastic.co/). + # These settings simplify using filebeat with the Elastic Cloud (https://cloud.elastic.co/). - # The cloud.id setting overwrites the `output.elasticsearch.hosts` and - # `setup.kibana.host` options. - # You can find the `cloud.id` in the Elastic Cloud web UI. - #cloud.id: + # The cloud.id setting overwrites the `output.elasticsearch.hosts` and + # `setup.kibana.host` options. + # You can find the `cloud.id` in the Elastic Cloud web UI. + #cloud.id: - # The cloud.auth setting overwrites the `output.elasticsearch.username` and - # `output.elasticsearch.password` settings. The format is `:`. - #cloud.auth: + # The cloud.auth setting overwrites the `output.elasticsearch.username` and + # `output.elasticsearch.password` settings. The format is `:`. + #cloud.auth: - #================================ Outputs ===================================== + #================================ Outputs ===================================== - # Configure what output to use when sending the data collected by the beat. + # Configure what output to use when sending the data collected by the beat. - #-------------------------- Elasticsearch output ------------------------------ - #output.elasticsearch: - # Array of hosts to connect to. - #hosts: ["192.168.28.11:9200"] + #-------------------------- Elasticsearch output ------------------------------ + #output.elasticsearch: + # Array of hosts to connect to. + #hosts: ["192.168.28.11:9200"] - # Optional protocol and basic auth credentials. - protocol: "http" - #username: "elastic" - #password: "changeme" + # Optional protocol and basic auth credentials. + protocol: "http" + #username: "elastic" + #password: "changeme" #----------------------------- Logstash output -------------------------------- output.logstash: - # The Logstash hosts - hosts: ["192.168.28.32:5044"] + # The Logstash hosts + hosts: ["192.168.28.32:5044"] - # Optional SSL. By default is off. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + # Optional SSL. By default is off. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" - # Client Certificate Key - #ssl.key: "/etc/pki/client/cert.key" + # Client Certificate Key + #ssl.key: "/etc/pki/client/cert.key" #================================ Logging ===================================== diff --git a/codes/linux/soft/elk/config/logback.xml b/codes/linux/soft/elk/config/logback.xml index a174cf94..bb45d085 100644 --- a/codes/linux/soft/elk/config/logback.xml +++ b/codes/linux/soft/elk/config/logback.xml @@ -3,55 +3,55 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n - - - - - - - - ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log - 30 - - - - - 30MB - - - - %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n - - - - - 192.168.28.32:9251 - - {"appname":"javatool"} - - - - - - - - - - - - - - - - + + + + + + %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n + + + + + + + + ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log + 30 + + + + + 30MB + + + + %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n + + + + + 192.168.28.32:9251 + + {"appname":"javatool"} + + + + + + + + + + + + + + + + diff --git a/codes/linux/sys/change-yum-repo.sh b/codes/linux/sys/change-yum-repo.sh index 9ab7cb1e..9027a18f 100644 --- a/codes/linux/sys/change-yum-repo.sh +++ b/codes/linux/sys/change-yum-repo.sh @@ -1,15 +1,28 @@ #!/usr/bin/env bash +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- + +printf "${BLUE}\n" cat << EOF ################################################################################### # 本脚本用于替换 yum repo,使用国内 yum 仓库,加速下载 # 要求:仅适用于 Linux CentOS 发行版本,并且环境必须已支持 yum 、lsb_release 命令 # @author: Zhang Peng ################################################################################### - EOF +printf "${RESET}\n" -echo -e "\n>>>>>>>>> 替换 yum repo 源" +printf "\n${GREEN}>>>>>>>>> 替换 yum repo 源开始${RESET}\n" # 备份 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak @@ -18,8 +31,6 @@ cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # version=`lsb_release -r | awk '{print substr($2,1,1)}'` # 很多机器没有 lsb_release 命令 version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` -cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak - # 根据发型版本选择相应 yum 镜像 if [[ ${version} == 5 ]]; then # Cento5 已废弃,只能使用 http://vault.CentOS.org/ 替换,但由于是国外镜像,速度较慢 @@ -36,11 +47,11 @@ elif [[ ${version} == 6 ]]; then elif [[ ${version} == 7 ]]; then wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-7.repo -O /etc/yum.repos.d/CentOS-Base.repo else - echo -e "版本不支持,替换 yum repo 失败" + printf "\n${RED}版本不支持,替换 yum repo 失败${RESET}\n" fi # 更新缓存 yum clean all yum makecache -echo -e "\n>>>>>>>>> 替换 yum repo 源成功" +printf "\n${GREEN}<<<<<<<< 替换 yum repo 源结束${RESET}\n" diff --git a/codes/linux/sys/install-libs.sh b/codes/linux/sys/install-libs.sh index 02cec37b..8fc7d240 100644 --- a/codes/linux/sys/install-libs.sh +++ b/codes/linux/sys/install-libs.sh @@ -1,29 +1,45 @@ #!/usr/bin/env bash -################################################################################### -# 安装常见 lib -# @author: Zhang Peng -# -# 如果不知道某个命令工具是由哪个包提供的,使用 yum provides xxx -# 或 yum whatprovides xxx 来查找 -################################################################################### +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- +printf "${BLUE}\n" +cat << EOF ################################################################################### -# 执行本脚本后支持的 lib 清单: +# 安装常见 lib +# 如果不知道命令在哪个 lib,可以使用 yum search xxx 来查找 +# lib 清单如下: # gcc gcc-c++ kernel-devel libtool # openssl openssl-devel # zlib zlib-devel # pcre +# +# @author: Zhang Peng ################################################################################### +EOF +printf "${RESET}\n" -echo -e "\n>>>>>>>>> install gcc gcc-c++ kernel-devel libtool" +printf "\n${GREEN}>>>>>>>>> 安装常见 lib 开始${RESET}\n" + +printf "\n${CYAN}>>>> install gcc gcc-c++ kernel-devel libtool${RESET}\n" yum -y install make gcc gcc-c++ kernel-devel libtool -echo -e "\n>>>>>>>>> install openssl openssl-devel" +printf "\n${CYAN}>>>> install openssl openssl-devel${RESET}\n" yum -y install make openssl openssl-devel -echo -e "\n>>>>>>>>> install zlib zlib-devel" +printf "\n${CYAN}>>>> install zlib zlib-devel${RESET}\n" yum -y install make zlib zlib-devel -echo -e "\n>>>>>>>>> install pcre" +printf "\n${CYAN}>>>> install pcre${RESET}\n" yum -y install pcre + +printf "\n${GREEN}<<<<<<<< 安装常见 lib 结束${RESET}\n" diff --git a/codes/linux/sys/install-tools.sh b/codes/linux/sys/install-tools.sh index 01f3d772..999898c0 100644 --- a/codes/linux/sys/install-tools.sh +++ b/codes/linux/sys/install-tools.sh @@ -1,19 +1,22 @@ #!/usr/bin/env bash -cat << EOF -################################################################################### -# 安装基本的命令工具 -# @author: Zhang Peng -# -# 如果不知道某个命令工具是由哪个包提供的,使用 yum provides xxx -# 或 yum whatprovides xxx 来查找 -################################################################################### - -EOF +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- +printf "${BLUE}\n" cat << EOF ################################################################################### -# 执行本脚本后支持的命令工具清单: +# 安装常用命令工具 +# 命令工具清单如下: # 核心工具:df、du、chkconfig # 网络工具:ifconfig、netstat、route、iptables # IP工具:ip、ss、ping、tracepath、traceroute @@ -25,72 +28,78 @@ cat << EOF # 抓包工具:tcpdump # 压缩工具:unzip、zip # 版本控制工具:git、subversion +# +# @author: Zhang Peng ################################################################################### - EOF +printf "${RESET}\n" + +printf "\n${GREEN}>>>>>>>>> 安装常用命令工具开始${RESET}\n" # 核心工具 -echo -e "\n>>>>>>>>> install coreutils(df、du)" +printf "\n${CYAN}>>>> install coreutils(df、du)${RESET}\n" yum install -y coreutils -echo -e "\n>>>>>>>>> install chkconfig" +printf "\n${CYAN}>>>> install chkconfig${RESET}\n" yum install -y chkconfig # 网络工具 -echo -e "\n>>>>>>>>> install net-tools(ifconfig、netstat、route)" +printf "\n${CYAN}>>>> install net-tools(ifconfig、netstat、route)${RESET}\n" yum install -y net-tools -echo -e "\n>>>>>>>>> install iptables" +printf "\n${CYAN}>>>> install iptables${RESET}\n" yum install -y iptables # IP工具 -echo -e "\n>>>>>>>>> install iputils(ping、tracepath)" +printf "\n${CYAN}>>>> install iputils(ping、tracepath)${RESET}\n" yum install -y iputils -echo -e "\n>>>>>>>>> install traceroute" +printf "\n${CYAN}>>>> install traceroute${RESET}\n" yum install -y traceroute -echo -e "\n>>>>>>>>> install iproute(ip、ss)" +printf "\n${CYAN}>>>> install iproute(ip、ss)${RESET}\n" yum install -y iproute # 端口工具 -echo -e "\n>>>>>>>>> install lsof" +printf "\n${CYAN}>>>> install lsof${RESET}\n" yum install -y lsof -echo -e "\n>>>>>>>>> install nc" +printf "\n${CYAN}>>>> install nc${RESET}\n" yum install -y nc -echo -e "\n>>>>>>>>> install netstat" +printf "\n${CYAN}>>>> install netstat${RESET}\n" yum install -y netstat # DNS工具 -echo -e "\n>>>>>>>>> install bind-utils(dig、host、nslookup)" +printf "\n${CYAN}>>>> install bind-utils(dig、host、nslookup)${RESET}\n" yum install -y bind-utils -echo -e "\n>>>>>>>>> install whois" +printf "\n${CYAN}>>>> install whois${RESET}\n" yum install -y whois # 下载工具 -echo -e "\n>>>>>>>>> install curl" +printf "\n${CYAN}>>>> install curl${RESET}\n" yum install -y curl -echo -e "\n>>>>>>>>> install wget" +printf "\n${CYAN}>>>> install wget${RESET}\n" yum install -y wget # 编辑工具 -echo -e "\n>>>>>>>>> install emacs" +printf "\n${CYAN}>>>> install emacs${RESET}\n" yum install -y emacs -echo -e "\n>>>>>>>>> install vim" +printf "\n${CYAN}>>>> install vim${RESET}\n" yum install -y vim # 流量工具 -echo -e "\n>>>>>>>>> install iftop" +printf "\n${CYAN}>>>> install iftop${RESET}\n" yum install -y iftop -echo -e "\n>>>>>>>>> install nethogs" +printf "\n${CYAN}>>>> install nethogs${RESET}\n" yum install -y nethogs # 抓包工具 -echo -e "\n>>>>>>>>> install tcpdump" +printf "\n${CYAN}>>>> install tcpdump${RESET}\n" yum install -y tcpdump # 压缩工具 -echo -e "\n>>>>>>>>> install unzip" +printf "\n${CYAN}>>>> install unzip${RESET}\n" yum install -y unzip # 版本控制工具 -echo -e "\n>>>>>>>>> install git" +printf "\n${CYAN}>>>> install git${RESET}\n" yum install -y git -echo -e "\n>>>>>>>>> install subversion" +printf "\n${CYAN}>>>> install subversion${RESET}\n" yum install -y subversion + +printf "\n${GREEN}<<<<<<<< 安装常用命令工具结束${RESET}\n" diff --git a/codes/linux/sys/set-dns.sh b/codes/linux/sys/set-dns.sh index fe07f014..c69a7a9b 100644 --- a/codes/linux/sys/set-dns.sh +++ b/codes/linux/sys/set-dns.sh @@ -1,32 +1,36 @@ #!/usr/bin/env bash +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- + ################################################################################### -# 在 /etc/resolv.conf 中设置 DNS 服务器 -# 在 /etc/hosts 中设置本机域名 +# 在 /etc/resolv.conf 中配置 DNS 服务器 +# 在 /etc/hosts 中配置本机域名 # @author: Zhang Peng ################################################################################### -ip='127.0.0.1' -function getDeviceIp() { - ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') -} -function setDNSServer() { - echo -e "设置DNS服务器" - cat >> /etc/resolv.conf << EOF +printf "\n${GREEN}>>>>>>>>> 配置 DNS 开始${RESET}\n" + +printf "\n${CYAN}>>>> 配置 DNS 解析服务器${RESET}\n" +cat >> /etc/resolv.conf << EOF nameserver 114.114.114.114 nameserver 8.8.8.8 EOF -} -function setHosts() { - getDeviceIp - host=`hostname` - cat >> /etc/hosts << EOF - ${ip} ${host} +printf "\n${CYAN}>>>> 配置本机域名和IP映射${RESET}\n" +ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') +host=`hostname` +cat >> /etc/hosts << EOF +${ip} ${host} EOF -} -######################################## MAIN ######################################## -echo -e "\n>>>>>>>>> 配置系统环境" -setDNSServer -setHosts +printf "\n${GREEN}<<<<<<<< 配置 DNS 结束${RESET}\n" diff --git a/codes/linux/sys/set-ntp.sh b/codes/linux/sys/set-ntp.sh index 29945fd3..a95b2d2f 100644 --- a/codes/linux/sys/set-ntp.sh +++ b/codes/linux/sys/set-ntp.sh @@ -1,28 +1,42 @@ #!/usr/bin/env bash +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- + ################################################################################### # 使用 NTP 进行时间同步 # 参考:https://www.cnblogs.com/quchunhui/p/7658853.html # @author: Zhang Peng ################################################################################### -echo -e "\n>>>>>>>>> 设置 ntp" +printf "\n${GREEN}>>>>>>>>> 设置 NTP 开始${RESET}\n" -echo -e "先安装时钟同步工具 ntp" +printf "\n${CYAN}>>>> 安装 NTP 服务${RESET}\n" yum -y install ntp ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') /sbin/iptables -A INPUT -p UDP -i eth0 -s ${ip}/24 --dport 123 -j ACCEPT -echo -e "启动 NTP 服务" +printf "\n${CYAN}>>>> 启动 NTP 服务${RESET}\n" systemctl start ntpd.service -echo -e "立即执行时间同步" +printf "\n${CYAN}>>>> 立即执行时间同步${RESET}\n" /usr/sbin/ntpdate ntp.sjtu.edu.cn -echo -e "自动定时同步时间" +printf "\n${CYAN}>>>> 自动定时同步时间${RESET}\n" echo "* 3 * * * /usr/sbin/ntpdate ntp.sjtu.edu.cn" >> /etc/crontab systemctl restart crond.service -echo -e "同步后系统时间:" +printf "\n${CYAN}>>>> 同步结束,当前系统时间:${RESET}\n" date + +printf "\n${GREEN}<<<<<<<< 设置 NTP 结束${RESET}\n" diff --git a/codes/linux/sys/stop-firewall.sh b/codes/linux/sys/stop-firewall.sh index 09824276..7214e297 100644 --- a/codes/linux/sys/stop-firewall.sh +++ b/codes/linux/sys/stop-firewall.sh @@ -1,10 +1,23 @@ #!/usr/bin/env bash +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- + ################################################################################### -# 彻底关闭防火墙 +# 关闭防火墙 # 参考:https://www.cnblogs.com/moxiaoan/p/5683743.html # @author: Zhang Peng ################################################################################### systemctl stop firewalld systemctl disable firewalld +printf "\n${GREEN}<<<<<<<< 已关闭防火墙${RESET}\n" diff --git a/codes/linux/sys/sys-settings.sh b/codes/linux/sys/sys-settings.sh index dcd5828b..96adb9b5 100644 --- a/codes/linux/sys/sys-settings.sh +++ b/codes/linux/sys/sys-settings.sh @@ -1,37 +1,49 @@ #!/usr/bin/env bash +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- + printHeadInfo() { - cat << EOF +printf "${BLUE}\n" +cat << EOF ################################################################################### # Linux Centos7 系统配置脚本(根据需要选择) # @author: Zhang Peng ################################################################################### - EOF +printf "${RESET}\n" } setLimit() { - cat >> /etc/security/limits.conf << EOF +cat >> /etc/security/limits.conf << EOF * - nofile 65535 * - nproc 65535 EOF } setLang() { - cat > /etc/sysconfig/i18n << EOF +cat > /etc/sysconfig/i18n << EOF LANG="zh_CN.UTF-8" EOF } closeShutdownShortkey() { - echo "关闭 Ctrl+Alt+Del 快捷键防止重新启动" + printf "\n${CYAN}>>>> 关闭 Ctrl+Alt+Del 快捷键防止重新启动${RESET}\n" sed -i 's#exec /sbin/shutdown -r now#\#exec /sbin/shutdown -r now#' /etc/init/control-alt-delete.conf } closeSelinux() { - echo "关闭 selinux" - # see http://blog.51cto.com/13570193/2093299 + printf "\n${CYAN}>>>> 关闭 selinux${RESET}\n" sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config } @@ -43,15 +55,15 @@ setBootMode() { # 5. 通常不用,在一些特殊情况下可以用它来做一些事情 # 6. X11,即进到 X-Window 系统 # 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) - echo "设置 Linux 启动模式" + printf "\n${CYAN}>>>> 配置 Linux 启动模式${RESET}\n" sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab } # 配置 IPv4 configIpv4() { - echo "配置 ipv4" + printf "\n${CYAN}>>>> 配置 IPv4${RESET}\n" - cat >> /etc/sysctl.conf << EOF +cat >> /etc/sysctl.conf << EOF net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 2 @@ -78,9 +90,9 @@ EOF # 关闭 IPv6 closeIpv6() { - echo "关闭 ipv6" + printf "\n${CYAN}>>>> 关闭 IPv6${RESET}\n" - cat > /etc/modprobe.d/ipv6.conf << EOF +cat > /etc/modprobe.d/ipv6.conf << EOF alias net-pf-10 off options ipv6 disable=1 EOF @@ -90,45 +102,42 @@ EOF # 入口函数 main() { - PS3="请选择要执行的操作:" - select ITEM in "设置 DNS" "设置 NTP" "关闭防火墙" "配置 IPv4" "关闭 IPv6" "全部执行" - do - - case ${ITEM} in - "设置 DNS") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash - ;; - "设置 NTP") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash - ;; - "关闭防火墙") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash - ;; - "配置 IPv4") - configIpv4 - ;; - "关闭 IPv6") - closeIpv6 - ;; - "全部执行") - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash - curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash - configIpv4 - closeIpv6 - ;; - *) - echo -e "输入项不支持!" - main - ;; - esac - break - done + PS3="请选择要执行的操作:" + select ITEM in "配置 DNS" "配置 NTP" "关闭防火墙" "配置 IPv4" "关闭 IPv6" "全部执行" + do + + case ${ITEM} in + "配置 DNS") + sh ${root}/set-dns.sh ;; + "配置 NTP") + sh ${root}/set-ntp.sh ;; + "关闭防火墙") + sh ${root}/stop-firewall.sh ;; + "配置 IPv4") + configIpv4 ;; + "关闭 IPv6") + closeIpv6 ;; + "全部执行") + sh ${root}/set-dns.sh + sh ${root}/set-ntp.sh + sh ${root}/stop-firewall.sh + configIpv4 + closeIpv6 + ;; + *) + printf "\n${RED}输入项不支持${RESET}\n" + main + ;; + esac + break + done } ######################################## MAIN ######################################## -filepath=$(cd "$(dirname "$0")"; -pwd) +root=$(pwd) +if [[ -n $1 ]]; then + root=$1 +fi printHeadInfo main From b3290172d32b006dba3235fee9dcfc65cf6b4335 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 29 Oct 2019 12:19:46 +0800 Subject: [PATCH 16/64] update scripts --- README.md | 3 +- codes/linux/soft/README.md | 46 ++++---- .../{redis-cluster => redis}/6381/redis.conf | 0 .../{redis-cluster => redis}/6382/redis.conf | 0 .../{redis-cluster => redis}/6383/redis.conf | 0 .../{redis-cluster => redis}/6384/redis.conf | 0 .../{redis-cluster => redis}/6385/redis.conf | 0 .../{redis-cluster => redis}/6386/redis.conf | 0 .../config/{redis-cluster => redis}/boot.sh | 0 .../{redis-cluster => redis}/create-cluster | 0 .../redis.conf} | 2 +- codes/linux/soft/config/redis/redis.service | 14 +++ codes/linux/soft/nginx-install-by-rpm.sh | 17 --- codes/linux/soft/nginx-install-by-yum.sh | 11 -- codes/linux/soft/nginx-install.sh | 64 +++++------ codes/linux/soft/redis-install.sh | 100 ++++++++++-------- docs/linux/soft/redis-ops.md | 30 +++++- 17 files changed, 162 insertions(+), 125 deletions(-) rename codes/linux/soft/config/{redis-cluster => redis}/6381/redis.conf (100%) rename codes/linux/soft/config/{redis-cluster => redis}/6382/redis.conf (100%) rename codes/linux/soft/config/{redis-cluster => redis}/6383/redis.conf (100%) rename codes/linux/soft/config/{redis-cluster => redis}/6384/redis.conf (100%) rename codes/linux/soft/config/{redis-cluster => redis}/6385/redis.conf (100%) rename codes/linux/soft/config/{redis-cluster => redis}/6386/redis.conf (100%) rename codes/linux/soft/config/{redis-cluster => redis}/boot.sh (100%) rename codes/linux/soft/config/{redis-cluster => redis}/create-cluster (100%) rename codes/linux/soft/config/{redis-remote-access.conf => redis/redis.conf} (99%) create mode 100644 codes/linux/soft/config/redis/redis.service delete mode 100644 codes/linux/soft/nginx-install-by-rpm.sh delete mode 100644 codes/linux/soft/nginx-install-by-yum.sh diff --git a/README.md b/README.md index 5a3349a6..7a163dcb 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ ## 软件运维 > 本章节内容介绍日常开发中常见的一些软件、工具的安装、配置。 +> +> 配套安装脚本:🐚 [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - 开发环境 - [JDK 安装](docs/linux/soft/jdk-install.md) @@ -38,7 +40,6 @@ ## 运维和脚本 - [系统运维脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) -- [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - [工具脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - [Vim 应用指南](docs/linux/ops/vim.md) - [Zsh 应用指南](docs/linux/ops/zsh.md) diff --git a/codes/linux/soft/README.md b/codes/linux/soft/README.md index 4ba17d88..fcd4f709 100644 --- a/codes/linux/soft/README.md +++ b/codes/linux/soft/README.md @@ -97,36 +97,33 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/mo ## Redis 安装配置 -说明: +**安装说明** -- 下载 redis `5.0.4` 并解压安装到 `/opt/redis` 路径下。 -- 替换配置,使得 Redis 可以远程访问,并设置默认密码为 123456。 -- 注册 redis 服务,并设置为开机自启动 +- 采用编译方式安装 Redis, 并将其注册为 systemd 服务 +- 安装路径为:`/usr/local/redis` +- 默认下载安装 `5.0.4` 版本,端口号为:`6379`,密码为空 -使用方法: +**使用方法** -执行以下任意命令即可按照默认配置安装脚本。 +- 默认安装 - 执行以下任意命令即可: ```sh curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash ``` -定制化配置 +- 自定义安装 - 下载脚本到本地,并按照以下格式执行: + ```sh -# 下载脚本到本地 -wget https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh -chmod +x redis-install.sh -./redis-install.sh 5.0.4 /opt/redis 6379 123456 +sh redis-install.sh [version] [port] [password] ``` -说明: +参数说明: -- 第一个参数是 redis 版本号; -- 第二个参数是 redis 安装路径; -- 第三个参数是 redis 服务端口号; -- 第四个参数是访问密码 +- `version` - redis 版本号 +- `port` - redis 服务端口号 +- `password` - 访问密码 ## Tomcat8 安装 @@ -195,19 +192,28 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zo ## Nginx 安装 -说明: +**安装说明** -采用编译方式安装 Nginx +- 采用编译方式安装 Nginx, 并将其注册为 systemd 服务 +- 安装路径为:`/usr/local/nginx` +- 默认下载安装 `1.16.0` 版本 -下载 nginx `1.16.0` 并解压安装到 `/opt/nginx` 路径下。 +**使用方法** + +- 默认安装 - 执行以下任意命令即可: -使用方法:执行以下任意命令即可执行脚本。 ```sh curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nginx-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nginx-install.sh | bash ``` +- 自定义安装 - 下载脚本到本地,并按照以下格式执行: + +```bash +sh nginx-install.sh [version] +``` + ## Fastdfs 安装 说明: diff --git a/codes/linux/soft/config/redis-cluster/6381/redis.conf b/codes/linux/soft/config/redis/6381/redis.conf similarity index 100% rename from codes/linux/soft/config/redis-cluster/6381/redis.conf rename to codes/linux/soft/config/redis/6381/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/6382/redis.conf b/codes/linux/soft/config/redis/6382/redis.conf similarity index 100% rename from codes/linux/soft/config/redis-cluster/6382/redis.conf rename to codes/linux/soft/config/redis/6382/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/6383/redis.conf b/codes/linux/soft/config/redis/6383/redis.conf similarity index 100% rename from codes/linux/soft/config/redis-cluster/6383/redis.conf rename to codes/linux/soft/config/redis/6383/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/6384/redis.conf b/codes/linux/soft/config/redis/6384/redis.conf similarity index 100% rename from codes/linux/soft/config/redis-cluster/6384/redis.conf rename to codes/linux/soft/config/redis/6384/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/6385/redis.conf b/codes/linux/soft/config/redis/6385/redis.conf similarity index 100% rename from codes/linux/soft/config/redis-cluster/6385/redis.conf rename to codes/linux/soft/config/redis/6385/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/6386/redis.conf b/codes/linux/soft/config/redis/6386/redis.conf similarity index 100% rename from codes/linux/soft/config/redis-cluster/6386/redis.conf rename to codes/linux/soft/config/redis/6386/redis.conf diff --git a/codes/linux/soft/config/redis-cluster/boot.sh b/codes/linux/soft/config/redis/boot.sh similarity index 100% rename from codes/linux/soft/config/redis-cluster/boot.sh rename to codes/linux/soft/config/redis/boot.sh diff --git a/codes/linux/soft/config/redis-cluster/create-cluster b/codes/linux/soft/config/redis/create-cluster similarity index 100% rename from codes/linux/soft/config/redis-cluster/create-cluster rename to codes/linux/soft/config/redis/create-cluster diff --git a/codes/linux/soft/config/redis-remote-access.conf b/codes/linux/soft/config/redis/redis.conf similarity index 99% rename from codes/linux/soft/config/redis-remote-access.conf rename to codes/linux/soft/config/redis/redis.conf index e77635a1..d6199543 100644 --- a/codes/linux/soft/config/redis-remote-access.conf +++ b/codes/linux/soft/config/redis/redis.conf @@ -85,7 +85,7 @@ bind 0.0.0.0 # you are sure you want clients from other hosts to connect to Redis # even if no authentication is configured, nor a specific set of interfaces # are explicitly listed using the "bind" directive. -protected-mode yes +protected-mode no # Accept connections on the specified port, default is 6379 (IANA #815344). # If port 0 is specified Redis will not listen on a TCP socket. diff --git a/codes/linux/soft/config/redis/redis.service b/codes/linux/soft/config/redis/redis.service new file mode 100644 index 00000000..1a96e266 --- /dev/null +++ b/codes/linux/soft/config/redis/redis.service @@ -0,0 +1,14 @@ +[Unit] +Description=Redis +After=network.target + +[Service] +Type=forking +PIDFile=/var/run/redis_6379.pid +ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis.conf +ExecReload=/bin/kill -s HUP $MAINPID +ExecStop=/bin/kill -s QUIT $MAINPID +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/codes/linux/soft/nginx-install-by-rpm.sh b/codes/linux/soft/nginx-install-by-rpm.sh deleted file mode 100644 index 07ea0834..00000000 --- a/codes/linux/soft/nginx-install-by-rpm.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -################################################################################### -# 安装 nginx 脚本 -# 仅适用于 CentOS 发行版本。 -# @author: Zhang Peng -################################################################################### - -echo -e "\n>>>>>>>>> install nginx" - -# Centos7 rpm 地址:http://nginx.org/packages/rhel/7/x86_64/RPMS/ -# CentOS6 rpm 地址:http://nginx.org/packages/rhel/6/x86_64/RPMS/ - -mkdir -p /opt/nginx -curl -o /opt/nginx/nginx.rpm http://nginx.org/packages/rhel/7/x86_64/RPMS/nginx-1.14.0-1.el7_4.ngx.x86_64.rpm -rpm -ivh /opt/nginx/nginx.rpm -nginx -v diff --git a/codes/linux/soft/nginx-install-by-yum.sh b/codes/linux/soft/nginx-install-by-yum.sh deleted file mode 100644 index bec957bb..00000000 --- a/codes/linux/soft/nginx-install-by-yum.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -################################################################################### -# 安装 nginx 脚本 -# 仅适用于 CentOS 发行版本。 -# @author: Zhang Peng -################################################################################### - -echo -e "\n>>>>>>>>> install nginx" - -yum -y install make nginx.x86_64 diff --git a/codes/linux/soft/nginx-install.sh b/codes/linux/soft/nginx-install.sh index a44726c6..a06f9f9a 100644 --- a/codes/linux/soft/nginx-install.sh +++ b/codes/linux/soft/nginx-install.sh @@ -12,68 +12,70 @@ CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### -printf "${BLUE}" +printf "${BLUE}\n" cat << EOF - ################################################################################### -# 采用编译方式安装 nginx 脚本 -# nginx 会被安装到 /usr/local/nginx 路径。 -# @system: 适用于所有 linux 发行版本。 +# 采用编译方式安装 Nginx, 并将其注册为 systemd 服务 +# 默认下载安装 1.16.0 版本,安装路径为:/usr/local/nginx +# @system: 适用于 CentOS7+ # @author: Zhang Peng ################################################################################### - EOF -printf "${RESET}" +printf "${RESET}\n" -printf "${GREEN}>>>>>>>> install maven begin.${RESET}\n" +command -v yum > /dev/null 2>&1 || { + printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; +} -command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; - exit 1; } +printf "\n${GREEN}>>>>>>>> install nginx begin${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" - printf "\t sh nginx-install.sh [version] [path]\n" - printf "\t Example: sh nginx-install.sh 1.16.0 /opt/nginx\n" + printf "\t Usage: sh nginx-install.sh [version] \n" + printf "\t Default: sh nginx-install.sh 1.16.0 \n" + printf "\t Example: sh nginx-install.sh 1.16.0 \n" printf "${RESET}\n" fi +temp=/opt/nginx version=1.16.0 if [[ -n $1 ]]; then version=$1 fi -path=/opt/nginx -if [[ -n $2 ]]; then - path=$2 -fi - # install info -printf "${PURPLE}[Info]\n" +printf "${PURPLE}[Install Info]\n" printf "\t version = ${version}\n" -printf "\t path = ${path}\n" printf "${RESET}\n" -printf "${GREEN}>>>>>>>> install required libs.${RESET}\n" +printf "${CYAN}>>>> install required libs${RESET}\n" yum install -y zlib zlib-devel gcc-c++ libtool openssl openssl-devel pcre # download and decompression -mkdir -p ${path} -curl -o ${path}/nginx-${version}.tar.gz http://nginx.org/download/nginx-${version}.tar.gz -tar zxf ${path}/nginx-${version}.tar.gz -C ${path} +printf "${CYAN}>>>> download nginx${RESET}\n" +mkdir -p ${temp} +curl -o ${temp}/nginx-${version}.tar.gz http://nginx.org/download/nginx-${version}.tar.gz +tar zxf ${temp}/nginx-${version}.tar.gz -C ${temp} # configure and makefile -cd ${path}/nginx-${version} +printf "${CYAN}>>>> compile nginx${RESET}\n" +cd ${temp}/nginx-${version} ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-pcre make && make install +rm -rf ${temp} +cd - -# setting service +# setting systemd service +printf "${CYAN}>>>> set nginx as a systemd service${RESET}\n" wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/nginx/nginx.service -O /usr/lib/systemd/system/nginx.service chmod +x /usr/lib/systemd/system/nginx.service -#设置nginx.service为系统服务 + +# boot nginx +printf "${CYAN}>>>> start nginx${RESET}\n" systemctl enable nginx.service -##通过系统服务操作nginx systemctl start nginx.service -#systemctl reload nginx.service -#systemctl restart nginx.service -#systemctl stop nginx.service -printf "${GREEN}<<<<<<<< install nginx end.${RESET}\n" + +printf "\n${GREEN}<<<<<<<< install nginx end${RESET}\n" +printf "\n${PURPLE}nginx service status: ${RESET}\n" +systemctl status nginx diff --git a/codes/linux/soft/redis-install.sh b/codes/linux/soft/redis-install.sh index ee942bfc..d9e8885d 100644 --- a/codes/linux/soft/redis-install.sh +++ b/codes/linux/soft/redis-install.sh @@ -10,26 +10,31 @@ BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" -################################################################################### +##########################################################################cd######### -printf "${BLUE}" +printf "${BLUE}\n" cat << EOF - ################################################################################### -# 安装 Redis 脚本 -# @system: 适用于 CentOS +# 采用编译方式安装 Redis +# @system: 适用于 CentOS7+ # @author: Zhang Peng ################################################################################### - EOF -printf "${RESET}" +printf "${RESET}\n" -command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; - exit 1; } +command -v yum > /dev/null 2>&1 || { + printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; +} + +printf "\n${GREEN}>>>>>>>> install redis begin${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]] || [[ $# -lt 3 ]] || [[ $# -lt 4 ]]; then - echo "Usage: sh redis-install.sh [version] [path] [port] [password]" - echo -e "Example: sh redis-install.sh 5.0.4 /opt/redis 6379 123456\n" + printf "${PURPLE}[Hint]\n" + printf "\t Usage: sh redis-install.sh [version] [port] [password] \n" + printf "\t Default: sh redis-install.sh 5.0.4 6379 \n" + printf "\t Example: sh redis-install.sh 5.0.4 6379 123456 \n" + printf "${RESET}\n" fi version=5.0.4 @@ -37,56 +42,65 @@ if [[ -n $1 ]]; then version=$1 fi -root=/opt/redis -if [[ -n $2 ]]; then - root=$2 -fi - port=6379 -if [[ -n $3 ]]; then - port=$3 +if [[ -n $2 ]]; then + port=$2 fi password= -if [[ -n $4 ]]; then - password=$4 +if [[ -n $3 ]]; then + password=$3 fi -printf "${GREEN}>>>>>>>> install redis begin.${RESET}\n" +# install info +printf "${PURPLE}[Install Info]\n" +printf "\t version = ${version}\n" +printf "\t port = ${port}\n" +printf "\t password = ${password}\n" +printf "${RESET}\n" -printf "\t${GREEN}Current execution: install redis ${version} to ${root}, service port = ${port}, password = ${password}${RESET}\n" +printf "${CYAN}>>>> install required libs${RESET}\n" yum install -y zlib zlib-devel gcc-c++ libtool openssl openssl-devel tcl -mkdir -p ${root} -curl -o ${root}/redis-${version}.tar.gz http://download.redis.io/releases/redis-${version}.tar.gz - -path=${root}/redis-${version} -tar zxf ${root}/redis-${version}.tar.gz -C ${root} +# download and decompression +printf "${CYAN}>>>> download redis${RESET}\n" +temp="/tmp/redis" +path="/usr/local/redis" +mkdir -p ${temp} +curl -o ${temp}/redis-${version}.tar.gz http://download.redis.io/releases/redis-${version}.tar.gz +tar zxf ${temp}/redis-${version}.tar.gz -C ${temp} +mv ${temp}/redis-${version} ${path} + +# configure and makefile +printf "${CYAN}>>>> compile redis${RESET}\n" cd ${path} make && make install +rm -rf ${temp} cd - -printf "\n${CYAN}>>>>>>>>> config redis${RESET}\n" +printf "${CYAN}>>>> modify redis config${RESET}\n" cp ${path}/redis.conf ${path}/redis.conf.default -wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis-remote-access.conf -O ${path}/redis.conf -mkdir -p /etc/redis -cp ${path}/redis.conf /etc/redis/${port}.conf -sed -i "s/^port 6379/port ${port}/g" /etc/redis/${port}.conf +wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis/redis.conf -O ${path}/redis.conf +sed -i "s/^port 6379/port ${port}/g" ${path}/redis.conf if [[ -n ${password} ]]; then - sed -i "s/^# requirepass/requirepass ${password}/g" /etc/redis/${port}.conf + sed -i "s/^protected-mode no/protected-mode yes/g" ${path}/redis.conf + sed -i "s/^# requirepass/requirepass ${password}/g" ${path}/redis.conf fi -printf "\n${CYAN}>>>>>>>>> add firewall port${RESET}\n" +printf "\n${CYAN}>>>> open redis port in firewall${RESET}\n" firewall-cmd --zone=public --add-port=${port}/tcp --permanent firewall-cmd --reload -printf "\n${CYAN}>>>>>>>>> add redis service${RESET}\n" -# 注册 redis 服务,并设置开机自启动 -cp ${path}/utils/redis_init_script /etc/init.d/ -mv /etc/init.d/redis_init_script /etc/init.d/redis_${port} -sed -i "s/^REDISPORT=.*/REDISPORT=${port}/g" /etc/init.d/redis_${port} -chmod +x /etc/init.d/redis_${port} -chkconfig --add redis_${port} -service redis_${port} start +# setting systemd service +printf "${CYAN}>>>> set redis as a systemd service${RESET}\n" +wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis/redis.service -O /usr/lib/systemd/system/redis.service +chmod +x /usr/lib/systemd/system/redis.service + +# boot redis +printf "${CYAN}>>>> start redis${RESET}\n" +systemctl enable redis.service +systemctl start redis.service -printf "\n${GREEN}<<<<<<<< install redis end.${RESET}\n" +printf "\n${GREEN}<<<<<<<< install redis end${RESET}\n" +printf "\n${PURPLE}redis service status: ${RESET}\n" +systemctl status redis diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md index 7840e871..00cd5f80 100644 --- a/docs/linux/soft/redis-ops.md +++ b/docs/linux/soft/redis-ops.md @@ -442,7 +442,35 @@ GET: 508388.41 requests per second ## 脚本 -如果想傻瓜式安装一个 Redis 单节点服务,可以使用我的 [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft#redis-%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE) +> CentOS7 环境安装脚本:[软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) + +**安装说明** + +- 采用编译方式安装 Redis, 并将其注册为 systemd 服务 +- 安装路径为:`/usr/local/redis` +- 默认下载安装 `5.0.4` 版本,端口号为:`6379`,密码为空 + +**使用方法** + +- 默认安装 - 执行以下任意命令即可: + +```sh +curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash +wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash +``` + +- 自定义安装 - 下载脚本到本地,并按照以下格式执行: + + +```sh +sh redis-install.sh [version] [port] [password] +``` + +参数说明: + +- `version` - redis 版本号 +- `port` - redis 服务端口号 +- `password` - 访问密码 ## 参考资料 From 74a1b7fb2ff4059a3b84ee8717269f8871d6ce02 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 29 Oct 2019 18:22:19 +0800 Subject: [PATCH 17/64] update scripts and docs --- README.md | 46 +-- codes/linux/README.md | 2 +- codes/linux/build/helper.sh | 48 +-- codes/linux/build/java-app-boot.sh | 110 ++--- codes/linux/build/java-app-release.sh | 68 ++-- codes/linux/build/java-app-run.sh | 66 +-- codes/linux/build/js-app-release.sh | 72 ++-- codes/linux/build/main.sh | 232 +++++------ codes/linux/build/spring-boot-run.sh | 46 +-- codes/linux/build/update-code.sh | 112 ++--- codes/linux/download.sh | 10 +- codes/linux/dunwu-ops.sh | 74 ++-- codes/linux/dunwu-soft.sh | 41 +- codes/linux/dunwu-sys.sh | 62 +-- codes/linux/soft/arthas-install.sh | 2 +- codes/linux/soft/config/nginx/nginx.conf | 59 +-- codes/linux/soft/config/redis/6381/redis.conf | 12 - codes/linux/soft/config/redis/6382/redis.conf | 12 - codes/linux/soft/config/redis/6383/redis.conf | 12 - codes/linux/soft/config/redis/6384/redis.conf | 12 - codes/linux/soft/config/redis/6385/redis.conf | 12 - codes/linux/soft/config/redis/6386/redis.conf | 12 - codes/linux/soft/config/redis/boot.sh | 7 - .../soft/config/redis/cluster/6381/redis.conf | 12 + .../soft/config/redis/cluster/6382/redis.conf | 12 + .../soft/config/redis/cluster/6383/redis.conf | 12 + .../soft/config/redis/cluster/6384/redis.conf | 12 + .../soft/config/redis/cluster/6385/redis.conf | 12 + .../soft/config/redis/cluster/6386/redis.conf | 12 + .../config/redis/cluster/redis-cluster.sh | 104 +++++ codes/linux/soft/config/redis/create-cluster | 102 ----- codes/linux/soft/config/redis/redis.conf | 2 +- codes/linux/soft/config/redis/redis.service | 2 +- codes/linux/soft/elk/boot-elk.sh | 84 ++-- codes/linux/soft/elk/config/filebeat.yml | 64 +-- codes/linux/soft/elk/install-elk.sh | 88 ++-- codes/linux/soft/elk/install_elasticserch.sh | 184 ++++----- codes/linux/soft/elk/install_filebeat.sh | 118 +++--- codes/linux/soft/elk/install_kibana.sh | 120 +++--- codes/linux/soft/elk/install_logstash.sh | 120 +++--- codes/linux/soft/fastdfs-install.sh | 30 +- codes/linux/soft/install_grafana.sh | 98 ++--- codes/linux/soft/jdk8-install.sh | 6 +- codes/linux/soft/kafka-install.sh | 18 +- codes/linux/soft/maven-install.sh | 18 +- codes/linux/soft/mongodb-install.sh | 12 +- codes/linux/soft/mysql-install.sh | 18 +- codes/linux/soft/nacos-install.sh | 20 +- codes/linux/soft/nginx-install.sh | 12 +- codes/linux/soft/nodejs-install.sh | 10 +- codes/linux/soft/redis-install.sh | 18 +- codes/linux/soft/rocketmq-install.sh | 12 +- codes/linux/soft/tomcat8-install.sh | 12 +- codes/linux/soft/zookeeper-install.sh | 12 +- codes/linux/soft/zsh-install.sh | 12 +- codes/linux/sys/change-yum-repo.sh | 20 +- codes/linux/sys/sys-settings.sh | 88 ++-- codes/linux/sys/syscheck | 325 --------------- codes/linux/sys/syscheck.sh | 328 +++++++++++++++ codes/linux/tool/Autoinstall_ELK_V1.3.sh | 156 +++---- codes/linux/tool/Cpu_Limit.sh | 42 +- codes/linux/tool/Custom_Rm.sh | 2 +- codes/linux/tool/Daily_Archive.sh | 48 +-- codes/linux/tool/Hourly_Archive.sh | 48 +-- codes/linux/tool/gitcheck | 354 ++++++++-------- ...50\346\210\267\350\204\232\346\234\254.sh" | 250 ++++++------ ...77\347\224\250\346\203\205\345\206\265.sh" | 16 +- codes/shell/README.md | 8 + codes/shell/demos/README.md | 3 - codes/shell/demos/array-demo.sh | 55 --- codes/shell/demos/function/function-demo.sh | 42 -- codes/shell/demos/function/function-demo2.sh | 24 -- codes/shell/demos/helloworld.sh | 3 - codes/shell/demos/operator/operator-demo2.sh | 59 --- codes/shell/demos/operator/operator-demo3.sh | 45 -- codes/shell/demos/operator/operator-demo6.sh | 49 --- codes/shell/demos/statement/break-demo.sh | 12 - codes/shell/demos/statement/case-demo.sh | 41 -- codes/shell/demos/statement/continue-demo.sh | 15 - codes/shell/demos/statement/for-demo.sh | 39 -- codes/shell/demos/statement/if-demo.sh | 35 -- codes/shell/demos/statement/until-demo.sh | 13 - codes/shell/demos/statement/while-demo.sh | 20 - codes/shell/demos/string-demo.sh | 55 --- codes/shell/demos/variable-demo.sh | 22 - ...47\350\241\214\350\204\232\346\234\254.sh" | 4 +- ...22\345\205\245\346\225\260\346\215\256.sh" | 22 + ...23\345\207\272\346\225\260\346\215\256.sh" | 4 +- ...21\351\200\201\345\221\275\344\273\244.sh" | 2 +- .../echo\347\244\272\344\276\213.sh" | 0 .../exit\345\221\275\344\273\244.sh" | 2 +- .../printf\347\244\272\344\276\213.sh" | 0 ...60\345\255\246\350\277\220\347\256\227.sh" | 2 +- ...27\350\241\250\350\276\276\345\274\217.sh" | 2 +- ...60\345\255\246\350\277\220\347\256\227.sh" | 2 +- ...67\347\232\204\344\275\277\347\224\250.sh" | 2 +- ...77\347\224\250\347\244\272\344\276\213.sh" | 0 ...\344\270\255\344\275\277\347\224\250bc.sh" | 2 +- ...77\347\224\250\347\244\272\344\276\213.sh" | 0 ...77\347\224\250\347\244\272\344\276\213.sh" | 0 ...14\347\231\273\345\275\225\350\200\205.sh" | 2 +- ...54\344\271\211\345\255\227\347\254\246.sh" | 2 +- ...50\351\207\212\347\244\272\344\276\213.sh" | 0 ...00\346\226\207\344\273\266\345\220\215.sh" | 2 +- ...47\350\241\214\350\204\232\346\234\254.sh" | 2 +- ...25\346\215\211\344\277\241\345\217\267.sh" | 8 +- ...54\347\232\204\351\200\200\345\207\272.sh" | 8 +- ...73\351\231\244\346\215\225\346\215\211.sh" | 8 +- ...73\350\277\220\347\256\227\347\254\246.sh" | 29 +- ...62\350\277\220\347\256\227\347\254\246.sh" | 26 +- ...24\350\277\220\347\256\227\347\254\246.sh" | 21 +- ...25\350\277\220\347\256\227\347\254\246.sh" | 29 +- ...57\350\277\220\347\256\227\347\254\246.sh" | 12 +- ...21\350\277\220\347\256\227\347\254\246.sh" | 13 +- .../gawk/gawk.sh" | 4 +- .../gawk\345\207\275\346\225\260\345\272\223" | 4 +- .../gawk/gawk\350\204\232\346\234\254" | 8 + .../gawk/script" | 0 .../gawk/test" | 0 ...77\347\224\250\345\217\230\351\207\217.sh" | 2 +- ...04\345\214\226\345\221\275\344\273\244.sh" | 2 +- ...32\344\271\211\345\207\275\346\225\260.sh" | 2 +- ...23\345\222\214\350\204\232\346\234\254.sh" | 2 +- .../gawk/\350\276\223\345\207\272.sh" | 4 +- ...07\344\273\266\350\256\241\346\225\260.sh" | 18 + ...56\344\273\266\351\252\214\350\257\201.sh" | 2 +- ...07\344\273\266\346\223\215\344\275\234.sh" | 2 +- ...21\345\231\250\345\237\272\347\241\200.sh" | 2 +- .../sed/test" | 0 ...35\346\214\201\347\251\272\351\227\264.sh" | 2 +- ...51\231\244html\346\240\207\347\255\276.sh" | 2 +- ...344\270\255\344\275\277\347\224\250sed.sh" | 2 +- ...22\351\231\244\345\221\275\344\273\244.sh" | 0 ...41\345\274\217\346\233\277\344\273\243.sh" | 2 +- .../sed/\346\265\213\350\257\225.sh" | 2 +- ...04\350\241\214\347\274\226\345\217\267.sh" | 2 +- .../sed/\350\267\263\350\275\254.sh" | 2 +- ...60\347\232\204\346\225\260\346\215\256.sh" | 2 +- ...345\220\221sed\350\276\223\345\207\272.sh" | 6 +- .../README.md" | 3 - ...22\345\205\245\346\225\260\346\215\256.sh" | 22 - ...44\346\226\255\347\244\272\344\276\213.sh" | 52 --- ...7\347\224\250\347\244\272\344\276\2133.sh" | 33 -- ...77\347\224\250\347\244\272\344\276\213.sh" | 55 --- .../gawk/gawk\350\204\232\346\234\254" | 6 - ...07\344\273\266\350\256\241\346\225\260.sh" | 18 - ...24\345\233\236\346\225\260\347\273\204.sh" | 25 -- ...\224\250return\345\221\275\344\273\244.sh" | 12 - ...22\347\232\204\345\217\202\346\225\260.sh" | 16 - ...75\346\225\260\351\200\222\345\275\222.sh" | 16 - ...60\347\273\204\346\225\260\346\215\256.sh" | 23 -- ...\224\250select\345\221\275\344\273\244.sh" | 37 -- ...32\346\234\254\350\217\234\345\215\225.sh" | 49 --- ...\224\250dialog\345\221\275\344\273\244.sh" | 47 --- .../\344\275\277\347\224\250getopts.sh" | 13 - ...71\345\222\214\345\217\202\346\225\260.sh" | 21 - ...\224\250getopt\345\221\275\344\273\244.sh" | 26 -- ...60\345\222\214\351\200\211\351\241\271.sh" | 23 -- ...74\347\232\204\351\200\211\351\241\271.sh" | 25 -- ...00\345\215\225\351\200\211\351\241\271.sh" | 14 - ...00\346\234\211\346\225\260\346\215\256.sh" | 17 - ...23\345\205\245\350\256\241\346\225\260.sh" | 23 -- ...77\347\224\250\347\244\272\344\276\213.sh" | 41 -- .../select-demo.sh" | 14 - ...14\345\234\206\346\213\254\345\217\267.sh" | 12 - ...14\346\226\271\346\213\254\345\217\267.sh" | 10 - ...345\271\266\344\277\256\346\224\271IFS.sh" | 15 - ...04\347\220\206\347\233\256\345\275\225.sh" | 13 - ...47\350\241\214\346\214\207\344\273\244.sh" | 3 +- ...50\346\210\267\347\256\241\347\220\206.sh" | 0 ...24\345\233\236\346\225\260\347\273\204.sh" | 25 ++ ...\224\250return\345\221\275\344\273\244.sh" | 12 + ...45\347\232\204\351\227\256\351\242\230.sh" | 10 +- ...75\346\225\260\350\276\223\345\207\272.sh" | 6 +- ...22\347\232\204\345\217\202\346\225\260.sh" | 16 + ...00\351\203\250\345\217\230\351\207\217.sh" | 10 +- ...50\345\272\223\345\207\275\346\225\260.sh" | 4 +- ...50\345\261\200\345\217\230\351\207\217.sh" | 4 +- ...75\346\225\260\345\205\245\345\217\202.sh" | 24 ++ ...5\346\225\260\345\205\245\345\217\2022.sh" | 26 +- ...72\346\234\254\347\244\272\344\276\213.sh" | 8 +- ...2\346\234\254\347\244\272\344\276\2132.sh" | 42 ++ ...75\346\225\260\351\200\222\345\275\222.sh" | 16 + ...77\347\224\250\345\217\202\346\225\260.sh" | 20 +- ...60\347\273\204\346\225\260\346\215\256.sh" | 23 ++ ...04\344\270\255\347\232\204\345\200\274.sh" | 18 +- .../\350\204\232\346\234\254\345\272\223.sh" | 10 +- ...72\347\212\266\346\200\201\347\240\201.sh" | 10 +- ...\224\250msgbox\351\203\250\344\273\266.sh" | 2 +- ...\224\250select\345\221\275\344\273\244.sh" | 37 ++ ...32\346\234\254\350\217\234\345\215\225.sh" | 49 +++ ...\224\250dialog\345\221\275\344\273\244.sh" | 47 +++ .../test" | 0 .../test1" | 0 ...66\351\207\215\345\256\232\345\220\221.sh" | 2 +- ...73\345\217\226\346\225\260\346\215\256.sh" | 6 +- .../\344\275\277\347\224\250getopts.sh" | 13 + ...71\345\222\214\345\217\202\346\225\260.sh" | 21 + ...\224\250getopt\345\221\275\344\273\244.sh" | 26 ++ ...7\224\250shift\345\221\275\344\273\244.sh" | 8 +- ...60\345\222\214\351\200\211\351\241\271.sh" | 23 ++ ...66\346\217\217\350\277\260\347\254\246.sh" | 2 +- ...64\346\227\266\347\233\256\345\275\225.sh" | 2 +- ...64\346\227\266\346\226\207\344\273\266.sh" | 2 +- ...46\344\270\255\346\201\242\345\244\215.sh" | 2 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 2 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 2 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 10 +- ...66\346\217\217\350\277\260\347\254\246.sh" | 2 +- ...02\346\225\260\350\256\241\346\225\260.sh" | 2 +- ...64\346\227\266\346\226\207\344\273\266.sh" | 2 +- ...32\345\220\221\350\276\223\345\205\245.sh" | 6 +- ...74\347\232\204\351\200\211\351\241\271.sh" | 25 ++ ...00\345\215\225\351\200\211\351\241\271.sh" | 14 + ...66\346\210\226\346\227\245\345\277\227.sh" | 2 +- ...00\346\234\211\346\225\260\346\215\256.sh" | 17 + ...05\351\207\215\345\256\232\345\220\221.sh" | 2 +- .../\346\265\213\350\257\225.txt" | 0 ...56\345\275\225\346\223\215\344\275\234.sh" | 0 ...50\346\210\267\350\276\223\345\205\245.sh" | 4 +- ...60\345\275\225\344\277\241\346\201\257.sh" | 2 +- ...73\345\217\226\345\217\202\346\225\260.sh" | 4 +- ...44\350\241\214\345\217\202\346\225\260.sh" | 2 +- ...26\347\250\213\345\272\217\345\220\215.sh" | 2 +- ...11\346\213\251\345\217\202\346\225\260.sh" | 8 +- ...23\345\205\245\350\256\241\346\225\260.sh" | 23 ++ ...73\345\217\226\346\225\260\346\215\256.sh" | 2 +- ...25\346\215\211\350\204\232\346\234\254.sh" | 2 +- ...5\220\257debug\346\250\241\345\274\217.sh" | 4 +- ...77\347\224\250\346\210\267\346\225\260.sh" | 2 +- ...25\346\215\211\350\204\232\346\234\254.sh" | 2 +- ...53\347\205\247\346\212\245\345\221\212.sh" | 12 +- ...23\345\207\272\351\242\234\350\211\262.sh" | 0 .../Update_Problem.sh" | 62 +-- ...45\346\211\276\351\227\256\351\242\230.sh" | 20 +- ...60\345\275\225\351\227\256\351\242\230.sh" | 16 +- ...06\346\265\256\347\202\271\346\225\260.sh" | 4 +- .../break\347\244\272\344\276\213.sh" | 0 .../case\347\244\272\344\276\213.sh" | 41 ++ .../continue\347\244\272\344\276\213.sh" | 0 ...52\347\216\257\347\244\272\344\276\213.sh" | 0 .../if-elif-else\347\244\272\344\276\213.sh" | 14 +- .../output.txt" | 0 ...34\345\215\225\347\244\272\344\276\213.sh" | 8 +- .../until\347\244\272\344\276\213.sh" | 0 ...52\347\216\257\347\244\272\344\276\213.sh" | 0 ...44\350\257\273\345\217\226\345\200\274.sh" | 4 +- ...14\345\234\206\346\213\254\345\217\267.sh" | 12 + ...14\346\226\271\346\213\254\345\217\267.sh" | 10 + ...16\345\260\217\344\272\216\345\217\267.sh" | 6 +- ...345\271\266\344\277\256\346\224\271IFS.sh" | 15 + ...26\351\207\215\345\256\232\345\220\221.sh" | 16 +- ...04\347\220\206\347\233\256\345\275\225.sh" | 13 + ...45\222\214sort\344\270\215\345\220\214.sh" | 6 +- ...15\346\235\202\347\232\204\345\200\274.sh" | 4 +- docs/README.md | 49 +-- docs/index.html | 385 +++++++++--------- docs/linux/ops/README.md | 1 - docs/linux/ops/shell.md | 338 +++++++++++++-- docs/linux/ops/systemd.md | 2 +- docs/linux/soft/redis-ops.md | 37 +- docs/package.json | 12 +- 262 files changed, 3556 insertions(+), 3878 deletions(-) delete mode 100644 codes/linux/soft/config/redis/6381/redis.conf delete mode 100644 codes/linux/soft/config/redis/6382/redis.conf delete mode 100644 codes/linux/soft/config/redis/6383/redis.conf delete mode 100644 codes/linux/soft/config/redis/6384/redis.conf delete mode 100644 codes/linux/soft/config/redis/6385/redis.conf delete mode 100644 codes/linux/soft/config/redis/6386/redis.conf delete mode 100644 codes/linux/soft/config/redis/boot.sh create mode 100644 codes/linux/soft/config/redis/cluster/6381/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6382/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6383/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6384/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6385/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6386/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/redis-cluster.sh delete mode 100644 codes/linux/soft/config/redis/create-cluster delete mode 100644 codes/linux/sys/syscheck create mode 100644 codes/linux/sys/syscheck.sh create mode 100644 codes/shell/README.md delete mode 100644 codes/shell/demos/README.md delete mode 100644 codes/shell/demos/array-demo.sh delete mode 100644 codes/shell/demos/function/function-demo.sh delete mode 100644 codes/shell/demos/function/function-demo2.sh delete mode 100644 codes/shell/demos/helloworld.sh delete mode 100644 codes/shell/demos/operator/operator-demo2.sh delete mode 100644 codes/shell/demos/operator/operator-demo3.sh delete mode 100644 codes/shell/demos/operator/operator-demo6.sh delete mode 100644 codes/shell/demos/statement/break-demo.sh delete mode 100644 codes/shell/demos/statement/case-demo.sh delete mode 100644 codes/shell/demos/statement/continue-demo.sh delete mode 100644 codes/shell/demos/statement/for-demo.sh delete mode 100644 codes/shell/demos/statement/if-demo.sh delete mode 100644 codes/shell/demos/statement/until-demo.sh delete mode 100644 codes/shell/demos/statement/while-demo.sh delete mode 100644 codes/shell/demos/string-demo.sh delete mode 100644 codes/shell/demos/variable-demo.sh rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" => "codes/shell/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" (57%) create mode 100644 "codes/shell/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" => "codes/shell/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" (90%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" => "codes/shell/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" (90%) rename codes/shell/demos/echo-demo.sh => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/echo\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" (88%) rename codes/shell/demos/printf-demo.sh => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/printf\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" (84%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" (89%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" (82%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" (87%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" (82%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" (90%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" (89%) rename codes/shell/demos/comment-demo.sh => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\263\250\351\207\212\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" => "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" (83%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" => "codes/shell/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" (68%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" => "codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" (67%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" => "codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" (55%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" => "codes/shell/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" (66%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\346\223\215\344\275\234\347\254\246/\345\205\263\347\263\273\350\277\220\347\256\227\347\254\246.sh" (50%) rename codes/shell/demos/operator/operator-demo5.sh => "codes/shell/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\350\277\220\347\256\227\347\254\246.sh" (50%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" => "codes/shell/\346\223\215\344\275\234\347\254\246/\345\270\203\345\260\224\350\277\220\347\256\227\347\254\246.sh" (50%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\346\265\213\350\257\225\350\277\220\347\256\227\347\254\246.sh" (51%) rename codes/shell/demos/operator/operator-demo.sh => "codes/shell/\346\223\215\344\275\234\347\254\246/\347\256\227\346\234\257\350\277\220\347\256\227\347\254\246.sh" (83%) rename codes/shell/demos/operator/operator-demo4.sh => "codes/shell/\346\223\215\344\275\234\347\254\246/\351\200\273\350\276\221\350\277\220\347\256\227\347\254\246.sh" (54%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" (85%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" (55%) create mode 100644 "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/script" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/script" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/test" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/test" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" (97%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" (95%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" (86%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" (78%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" (97%) create mode 100644 "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" (84%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" (93%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" (99%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/test" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/test" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" (92%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" (93%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" (80%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\216\222\351\231\244\345\221\275\344\273\244.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\216\222\351\231\244\345\221\275\344\273\244.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" (95%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" (91%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" (62%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" (92%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" (78%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" => "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" (73%) delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" delete mode 100644 "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" => "codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" (95%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" => "codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" (100%) create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" (56%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" (57%) create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" (54%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" (71%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" (75%) create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\202.sh" rename codes/shell/demos/function/function-demo3.sh => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\2022.sh" (51%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\237\272\346\234\254\347\232\204\350\204\232\346\234\254\345\207\275\346\225\260.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\213.sh" (65%) create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\2132.sh" create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" (67%) create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" (54%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" (61%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" => "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" (69%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" => "codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" (72%) create mode 100644 "codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" create mode 100644 "codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" create mode 100644 "codes/shell/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test1" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test1" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" (85%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" (60%) create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" (70%) create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" (84%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" (94%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" (94%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" (90%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" (92%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" (85%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" (60%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" (90%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" (90%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" (92%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" (64%) create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" (68%) create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" (94%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\265\213\350\257\225.txt" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\265\213\350\257\225.txt" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" (92%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" (93%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" (70%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" (88%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" (92%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" (82%) create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" => "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" (90%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" (97%) rename codes/shell/demos/debug-demo.sh => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\274\200\345\220\257debug\346\250\241\345\274\217.sh" (91%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" (71%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" (98%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" (88%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\350\276\223\345\207\272\351\242\234\350\211\262.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\350\276\223\345\207\272\351\242\234\350\211\262.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" (58%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" (68%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" => "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" (88%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" (79%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/break\347\244\272\344\276\213.sh" (100%) create mode 100644 "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/case\347\244\272\344\276\213.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/continue\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\347\244\272\344\276\213.sh" (72%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/output.txt" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/output.txt" (100%) rename codes/shell/demos/statement/select-demo.sh => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/select\350\217\234\345\215\225\347\244\272\344\276\213.sh" (57%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/until\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\347\244\272\344\276\213.sh" (100%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" (84%) create mode 100644 "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" create mode 100644 "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" (85%) create mode 100644 "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" (56%) create mode 100644 "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" (79%) rename "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" => "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" (72%) diff --git a/README.md b/README.md index 7a163dcb..6919623d 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) -| :wrench: | :shell: | :memo: | 📚 | -| :-------------------: | :-------------------: | :---------------: | :-------------------: | -| [软件运维](#软件运维) | [运维和脚本](#运维和脚本) | [知识点](#知识点) | [学习资源](#学习资源) | +| 🛠 | 🐚 | 📝 | 📚 | +| :-------------------: | :-----------------------: | :-----------: | :-------------------: | +| [软件运维](#软件运维) | [Shell 脚本](#Shell-脚本) | [教程](#教程) | [学习资源](#学习资源) | ## 软件运维 @@ -37,21 +37,21 @@ - [Mongodb 运维](docs/linux/soft/mongodb-ops.md) - [Redis 运维](docs/linux/soft/redis-ops.md) -## 运维和脚本 +## Shell 脚本 -- [系统运维脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) -- [工具脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) -- [Vim 应用指南](docs/linux/ops/vim.md) -- [Zsh 应用指南](docs/linux/ops/zsh.md) -- [Shell 教程](docs/linux/ops/shell.md) -- [Python 教程](docs/linux/ops/python.md) -- [Systemd 入门教程](docs/linux/ops/systemd.md) +### Shell 脚本大全 -> 提供一键式运维、配置软件脚本 +**Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 -## 知识点 +源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) -### Linux +### CentOS 常规操作运维脚本集合 + +本人作为一名 Java 后端,苦于经常在 CentOS 环境上开荒虚拟机。为提高效率,写了一套 Shell 脚本,提供如下功能:安装常用 lib 库、命令工具、设置 DNS、NTP、配置国内 yum 源、一键安装常用软件等。 + +源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) + +## 教程 - [Linux 命令教程](docs/linux/cli/README.md) - [查看 Linux 命令帮助信息](docs/linux/cli/查看Linux命令帮助信息.md) @@ -63,23 +63,23 @@ - [Linux 网络管理](docs/linux/cli/Linux网络管理.md) - [Linux 硬件管理](docs/linux/cli/Linux硬件管理.md) - [Linux 软件管理](docs/linux/cli/Linux硬件管理.md) -- [Linux 运维](docs/linux/ops/README.md) - - [linux 典型运维应用](docs/linux/ops/linux典型运维应用.md) - - [samba 使用详解](docs/linux/ops/samba使用详解.md) - -### Docker - - [Docker 教程](docs/docker) - [Docker 应用指南](docs/docker/docker.md) - [Docker Cheat Sheet](docs/docker/docker-cheat-sheet.md) - -### Git - - [Git 教程](docs/git/README.md) - [Git 快速指南](docs/git/git-quickstart.md) - [Git 配置](docs/git/git-configuration.md) - [git-flow 工作流](docs/git/git-flow.md) - [Git 常见问题](docs/git/git-faq.md) +- 运维 + - [linux 典型运维应用](docs/linux/ops/linux典型运维应用.md) + - [samba 使用详解](docs/linux/ops/samba使用详解.md) + - [Systemd 教程](docs/linux/ops/systemd.md) +- 脚本 + - [Vim 应用指南](docs/linux/ops/vim.md) + - [Zsh 应用指南](docs/linux/ops/zsh.md) + - [Shell 教程](docs/linux/ops/shell.md) + - [Python 教程](docs/linux/ops/python.md) ## 学习资源 diff --git a/codes/linux/README.md b/codes/linux/README.md index acd0e771..1cac0bea 100644 --- a/codes/linux/README.md +++ b/codes/linux/README.md @@ -1,4 +1,4 @@ -# Dunwu Shell 运维脚本 +# CentOS 常规操作运维脚本集合 > **本项目脚本代码用于在 [CentOS](https://www.centos.org/) 机器上安装常用命令工具或开发软件。** diff --git a/codes/linux/build/helper.sh b/codes/linux/build/helper.sh index 2466cd6c..4b780a02 100644 --- a/codes/linux/build/helper.sh +++ b/codes/linux/build/helper.sh @@ -2,7 +2,7 @@ # 打印UI页头信息 function printHeadInfo() { - cat << EOF + cat << EOF *********************************************************************************** * 欢迎使用项目引导式发布脚本。 * 输入任意键进入脚本操作。 @@ -12,7 +12,7 @@ EOF # 打印UI页尾信息 function printFootInfo() { - cat << EOF + cat << EOF *********************************************************************************** @@ -25,18 +25,18 @@ EOF # 检查文件是否存在,不存在则退出脚本 function checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 1 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 1 + fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { - if [ ! -d "$1" ]; then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 记录发布的版本信息 @@ -45,18 +45,18 @@ function createFolderIfNotExist() { # 第三个参数为代码分支 # 第四个参数为运行环境 function saveVersionInfo() { - if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then - echo "缺少参数,退出" - exit 1 - fi - - VERSION_LOG_FILE=$1/$2-version.log - rm -rf ${VERSION_LOG_FILE} - touch ${VERSION_LOG_FILE} - chmod 777 ${VERSION_LOG_FILE} - - echo -e "\n=================== $2 ===================" >> ${VERSION_LOG_FILE} - echo "Branch is: $3" >> ${VERSION_LOG_FILE} - echo "Profile is: $4" >> ${VERSION_LOG_FILE} - echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} + if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then + echo "缺少参数,退出" + exit 1 + fi + + VERSION_LOG_FILE=$1/$2-version.log + rm -rf ${VERSION_LOG_FILE} + touch ${VERSION_LOG_FILE} + chmod 777 ${VERSION_LOG_FILE} + + echo -e "\n=================== $2 ===================" >> ${VERSION_LOG_FILE} + echo "Branch is: $3" >> ${VERSION_LOG_FILE} + echo "Profile is: $4" >> ${VERSION_LOG_FILE} + echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} } diff --git a/codes/linux/build/java-app-boot.sh b/codes/linux/build/java-app-boot.sh index 85db9110..cbd8debe 100644 --- a/codes/linux/build/java-app-boot.sh +++ b/codes/linux/build/java-app-boot.sh @@ -7,76 +7,76 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { - if [ "${app}" == "" ] || [ "${oper}" == "" ] || [ "${javaArgs}" == "" ] || [ "${classpathArgs}" == "" ] || [ "${bootstrapClass}" == "" ]; then - echo "请输入脚本参数:app oper javaArgs classpathArgs bootstrapClass" - echo " app: 应用名。" - echo " oper: 运行环境(必填)。可选值:start|stop|restart" - echo " javaArgs: JVM 参数(必填)。" - echo " classpathArgs: classpath参数(必填)。" - echo " bootstrapClass: 启动类(必填)。" - exit 0 - fi + if [ "${app}" == "" ] || [ "${oper}" == "" ] || [ "${javaArgs}" == "" ] || [ "${classpathArgs}" == "" ] || [ "${bootstrapClass}" == "" ]; then + echo "请输入脚本参数:app oper javaArgs classpathArgs bootstrapClass" + echo " app: 应用名。" + echo " oper: 运行环境(必填)。可选值:start|stop|restart" + echo " javaArgs: JVM 参数(必填)。" + echo " classpathArgs: classpath参数(必填)。" + echo " bootstrapClass: 启动类(必填)。" + exit 0 + fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { - if [ ! -d "$1" ]; then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 检查服务是否已经启动 pids="" function checkStarted() { - pids=`ps -ef | grep java | grep ${app} | awk '{print $2}'` - if [ -n "${pids}" ]; then - return 0 - else - return 1 - fi + pids=`ps -ef | grep java | grep ${app} | awk '{print $2}'` + if [ -n "${pids}" ]; then + return 0 + else + return 1 + fi } function main() { - case "${oper}" in - start) - echo -n "starting server: " - # 检查服务是否已经启动 - if checkStarted; then - echo "ERROR: server already started!" - echo "PID: ${pids}" - exit 1 - fi + case "${oper}" in + start) + echo -n "starting server: " + # 检查服务是否已经启动 + if checkStarted; then + echo "ERROR: server already started!" + echo "PID: ${pids}" + exit 1 + fi - args="${javaArgs} -classpath ${classpathArgs} ${bootstrapClass}" - echo -e "statup params:\n ${args}" + args="${javaArgs} -classpath ${classpathArgs} ${bootstrapClass}" + echo -e "statup params:\n ${args}" - #启动服务 - touch ${LOG_DIR}/${app}-startup.log - nohup java ${args} > ${LOG_DIR}/${app}-startup.log 2>&1 & - # echo -e "执行参数:\n${args}" - echo -e "\nthe server is started..." - ;; - stop) - echo -n "stopping server: " - #dubbo提供优雅停机, 不能使用kill -9 - if checkStarted; then - kill ${pids} - echo -e "\nthe server is stopped..." - else - echo -e "\nno server to be stopped..." - fi - ;; - restart) - $0 ${app} stop "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" - sleep 5 - $0 ${app} start "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" - ;; - *) - echo "Invalid oper: ${oper}." - exit 1 - esac + #启动服务 + touch ${LOG_DIR}/${app}-startup.log + nohup java ${args} > ${LOG_DIR}/${app}-startup.log 2>&1 & + # echo -e "执行参数:\n${args}" + echo -e "\nthe server is started..." + ;; + stop) + echo -n "stopping server: " + #dubbo提供优雅停机, 不能使用kill -9 + if checkStarted; then + kill ${pids} + echo -e "\nthe server is stopped..." + else + echo -e "\nno server to be stopped..." + fi + ;; + restart) + $0 ${app} stop "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" + sleep 5 + $0 ${app} start "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" + ;; + *) + echo "Invalid oper: ${oper}." + exit 1 + esac - exit 0 + exit 0 } ######################################## MAIN ######################################## diff --git a/codes/linux/build/java-app-release.sh b/codes/linux/build/java-app-release.sh index 02b77c0f..7f34ab46 100644 --- a/codes/linux/build/java-app-release.sh +++ b/codes/linux/build/java-app-release.sh @@ -7,41 +7,41 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 checkInput() { - if [ "${branch}" == "" ] || [ "${profile}" == "" ]; then - echo "请输入脚本参数:branch profile" - echo " branch: git分支(必填)。如 feature/1.1.16, master" - echo " profile: 运行环境(必填)。可选值:development | test" - echo "例:./java-app-release.sh feature/1.1.16 test" - exit 0 - fi + if [ "${branch}" == "" ] || [ "${profile}" == "" ]; then + echo "请输入脚本参数:branch profile" + echo " branch: git分支(必填)。如 feature/1.1.16, master" + echo " profile: 运行环境(必填)。可选值:development | test" + echo "例:./java-app-release.sh feature/1.1.16 test" + exit 0 + fi } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } # 检查文件夹是否存在,不存在则创建 createFolderIfNotExist() { - if [ ! -d "$1" ]; then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 记录发布的版本信息 saveVersionInfo() { - rm -rf ${VERSION_LOG_FILE} - touch ${VERSION_LOG_FILE} - chmod 777 ${VERSION_LOG_FILE} - - echo -e "\n=================== Version Info ===================" >> ${VERSION_LOG_FILE} - echo "Branch is: ${branch}" >> ${VERSION_LOG_FILE} - echo "Profile is: ${profile}" >> ${VERSION_LOG_FILE} - echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} + rm -rf ${VERSION_LOG_FILE} + touch ${VERSION_LOG_FILE} + chmod 777 ${VERSION_LOG_FILE} + + echo -e "\n=================== Version Info ===================" >> ${VERSION_LOG_FILE} + echo "Branch is: ${branch}" >> ${VERSION_LOG_FILE} + echo "Profile is: ${profile}" >> ${VERSION_LOG_FILE} + echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} } ######################################## MAIN ######################################## @@ -74,10 +74,10 @@ echo ">>>>>>>>>>>>>> 2. 更新代码" ${UPDATE_CODE_SCRIPT_FILE} ${APP_NAME} ${branch} ${SOURCE_DIR} execode=$? if [ "${execode}" == "0" ]; then - echo "更新代码成功" + echo "更新代码成功" else - echo "更新代码失败" - exit 1 + echo "更新代码失败" + exit 1 fi echo ">>>>>>>>>>>>>> 3. 替换配置" @@ -88,12 +88,12 @@ cd ${SOURCE_DIR}/ck-lion mvn clean package -e -Dmaven.test.skip=true | tee ${MAVEN_LOG_FILE} eexecode=$? if [ "${execode}" == "0" ]; then - echo "构建编译成功" - echo "编译详情见:${MAVEN_LOG_FILE}" + echo "构建编译成功" + echo "编译详情见:${MAVEN_LOG_FILE}" else - echo "构建编译失败" - echo "编译详情见:${MAVEN_LOG_FILE}" - exit 1 + echo "构建编译失败" + echo "编译详情见:${MAVEN_LOG_FILE}" + exit 1 fi echo ">>>>>>>>>>>>>> 5. 启动应用" @@ -102,10 +102,10 @@ echo 3 > /proc/sys/vm/drop_caches ${SCRIPT_DIR}/java-app-run.sh ${profile} start execode=$? if [ "${execode}" == "0" ]; then - echo "启动应用成功" + echo "启动应用成功" else - echo "启动应用失败" - exit 1 + echo "启动应用失败" + exit 1 fi echo ">>>>>>>>>>>>>> 6. 记录发布的版本信息" diff --git a/codes/linux/build/java-app-run.sh b/codes/linux/build/java-app-run.sh index db81b243..69006de1 100644 --- a/codes/linux/build/java-app-run.sh +++ b/codes/linux/build/java-app-run.sh @@ -7,51 +7,51 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { - if [ "${profile}" == "" ] || [ "${oper}" == "" ]; then - echo "请输入脚本参数:profile oper [debug]" - echo " profile: 运行环境(必填)。可选值:development|test" - echo " oper: 运行环境(必填)。可选值:start|stop|restart" - echo " debug: debug启动开关。默认不填为不启动。" - exit 0 - fi + if [ "${profile}" == "" ] || [ "${oper}" == "" ]; then + echo "请输入脚本参数:profile oper [debug]" + echo " profile: 运行环境(必填)。可选值:development|test" + echo " oper: 运行环境(必填)。可选值:start|stop|restart" + echo " debug: debug启动开关。默认不填为不启动。" + exit 0 + fi } #检查文件是否存在,不存在则退出脚本 function checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } # 封装启动参数,调用启动脚本 function main() { - APP_NAME=ck-lion + APP_NAME=ck-lion - # JVM 参数 - JAVA_OPTS=" -Djava.awt.headless=true -Dfile.encoding=UTF8 -Djava.net.preferIPv4Stack=true -Ddubbo.shutdown.hook=true -Dspring.profiles.active=${profile} -Djava.security.egd=file:/dev/./urandom -Xms1024m -Xmx1024m -Xss2m " - JAVA_DEBUG_OPTS="" - if [ "$2" == "debug" ]; then - JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=2236,server=y,suspend=n " - shift - fi - javaArgs=" ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} " + # JVM 参数 + JAVA_OPTS=" -Djava.awt.headless=true -Dfile.encoding=UTF8 -Djava.net.preferIPv4Stack=true -Ddubbo.shutdown.hook=true -Dspring.profiles.active=${profile} -Djava.security.egd=file:/dev/./urandom -Xms1024m -Xmx1024m -Xss2m " + JAVA_DEBUG_OPTS="" + if [ "$2" == "debug" ]; then + JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=2236,server=y,suspend=n " + shift + fi + javaArgs=" ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} " - # classpath 参数 - classpathArgs="${SERVER_ROOT}/WEB-INF/classes:${SERVER_ROOT}/WEB-INF/lib/*" + # classpath 参数 + classpathArgs="${SERVER_ROOT}/WEB-INF/classes:${SERVER_ROOT}/WEB-INF/lib/*" - # 启动类 - bootstrapClass="com.alibaba.dubbo.container.Main" + # 启动类 + bootstrapClass="com.alibaba.dubbo.container.Main" - ${SCRIPT_DIR}/java-app-boot.sh ${APP_NAME} ${oper} "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" - execode=$? - if [ "${execode}" == "0" ]; then - echo "执行操作成功" - else - echo "执行操作失败" - exit 1 - fi + ${SCRIPT_DIR}/java-app-boot.sh ${APP_NAME} ${oper} "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" + execode=$? + if [ "${execode}" == "0" ]; then + echo "执行操作成功" + else + echo "执行操作失败" + exit 1 + fi } ######################################## MAIN ######################################## diff --git a/codes/linux/build/js-app-release.sh b/codes/linux/build/js-app-release.sh index 617d9ae3..aa08a21c 100644 --- a/codes/linux/build/js-app-release.sh +++ b/codes/linux/build/js-app-release.sh @@ -5,50 +5,50 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { - if [ "${branch}" == "" ]; then - echo "请输入脚本参数:branch" - echo " branch: git分支。如 feature/1.1.16, master" - exit 1 - fi + if [ "${branch}" == "" ]; then + echo "请输入脚本参数:branch" + echo " branch: git分支。如 feature/1.1.16, master" + exit 1 + fi } # 脚本主方法 function main() { - echo ">>>>>>>>>>>>>> 1. 更新代码" - ${SCRIPT_DIR}/update-code.sh ${APP} ${branch} ${SOURCE_DIR} - execode=$? - if [ "${execode}" == "0" ]; then - echo "更新代码成功" - else - echo "更新代码失败" - exit 1 - fi + echo ">>>>>>>>>>>>>> 1. 更新代码" + ${SCRIPT_DIR}/update-code.sh ${APP} ${branch} ${SOURCE_DIR} + execode=$? + if [ "${execode}" == "0" ]; then + echo "更新代码成功" + else + echo "更新代码失败" + exit 1 + fi - echo ">>>>>>>>>>>>>> 2. 替换配置" - # 有的应用此处可能需要替换配置 + echo ">>>>>>>>>>>>>> 2. 替换配置" + # 有的应用此处可能需要替换配置 - echo ">>>>>>>>>>>>>> 3. 构建编译" - cd ${SOURCE_DIR}/${APP} - source "${HOME}/.nvm/nvm.sh" - nvm use 8.9 - npm install - if [ "${profile}" == "develop" ] || [ "${profile}" == "test" ]; then - npm start - elif [ "${profile}" == "preview" ] || [ "${profile}" == "product" ]; then - npm run build - fi - execode=$? - if [ "${execode}" == "0" ]; then - echo "构建编译成功" - else - echo "构建编译失败" - exit 1 - fi + echo ">>>>>>>>>>>>>> 3. 构建编译" + cd ${SOURCE_DIR}/${APP} + source "${HOME}/.nvm/nvm.sh" + nvm use 8.9 + npm install + if [ "${profile}" == "develop" ] || [ "${profile}" == "test" ]; then + npm start + elif [ "${profile}" == "preview" ] || [ "${profile}" == "product" ]; then + npm run build + fi + execode=$? + if [ "${execode}" == "0" ]; then + echo "构建编译成功" + else + echo "构建编译失败" + exit 1 + fi - echo ">>>>>>>>>>>>>> 4. 记录发布的版本信息" - saveVersionInfo ${LOG_DIR} ${APP} ${branch} ${profile} + echo ">>>>>>>>>>>>>> 4. 记录发布的版本信息" + saveVersionInfo ${LOG_DIR} ${APP} ${branch} ${profile} - echo ">>>>>>>>>>>>>> 发布应用结束" + echo ">>>>>>>>>>>>>> 发布应用结束" } ######################################## MAIN ######################################## diff --git a/codes/linux/build/main.sh b/codes/linux/build/main.sh index 26d92fea..7e8f2abe 100644 --- a/codes/linux/build/main.sh +++ b/codes/linux/build/main.sh @@ -7,7 +7,7 @@ # 选择应用 function chooseAppName() { - cat << EOF + cat << EOF 请选择应用名(数字或关键字均可)。 可选值如下: [0] all (所有应用) @@ -15,28 +15,28 @@ function chooseAppName() { [2] APP2 EOF - while read app - do - case ${app} in - 0) - app=all - break ;; - 1) - app=js-app - break ;; - 2) - app=APP2 - break ;; - all | js-app | APP2) - break ;; - *) echo "无法识别 ${app}" ;; - esac - done + while read app + do + case ${app} in + 0) + app=all + break ;; + 1) + app=js-app + break ;; + 2) + app=APP2 + break ;; + all | js-app | APP2) + break ;; + *) echo "无法识别 ${app}" ;; + esac + done } # 选择操作 function chooseOper() { - cat << EOF + cat << EOF 请选择想要执行的操作(数字或关键字均可)。 可选值如下: [1] start @@ -44,44 +44,44 @@ function chooseOper() { [3] stop EOF - while read oper - do - case ${oper} in - 1) - oper=start - break ;; - 2) - oper=restart - break ;; - 3) - oper=stop - break ;; - start | restart | stop) - break ;; - *) echo "无法识别 ${oper}" ;; - esac - done + while read oper + do + case ${oper} in + 1) + oper=start + break ;; + 2) + oper=restart + break ;; + 3) + oper=stop + break ;; + start | restart | stop) + break ;; + *) echo "无法识别 ${oper}" ;; + esac + done } # 选择代码分支 function chooseBranch() { - cat << EOF + cat << EOF 请输入 git 分支。 如:develop、master、feature/xxx EOF - read branch - if [[ "${branch}" =~ ^ ( feature/ ) ( [^ \f\n\r\t\v]+ ) ]] || [ "${branch}" == "develop" ] || [ "${branch}" == "master" ]; then - echo "输入了 ${branch}" - else - echo "无法识别 ${branch}" - chooseBranch - fi + read branch + if [[ "${branch}" =~ ^ ( feature/ ) ( [^ \f\n\r\t\v]+ ) ]] || [ "${branch}" == "develop" ] || [ "${branch}" == "master" ]; then + echo "输入了 ${branch}" + else + echo "无法识别 ${branch}" + chooseBranch + fi } # 选择运行环境 function chooseProfile() { - cat << EOF + cat << EOF 请选择运行环境(数字或关键字均可)。 可选值: [1] develop (开发环境) @@ -90,32 +90,32 @@ function chooseProfile() { [4] product (生产环境) EOF - while read profile - do - case ${profile} in - 1) - profile=develop - break ;; - 2) - profile=test - break ;; - 3) - profile=preview - break ;; - 4) - profile=product - break ;; - develop | test | preview | product) - break ;; - *) echo "无法识别 ${profile}" ;; - esac - done + while read profile + do + case ${profile} in + 1) + profile=develop + break ;; + 2) + profile=test + break ;; + 3) + profile=preview + break ;; + 4) + profile=product + break ;; + develop | test | preview | product) + break ;; + *) echo "无法识别 ${profile}" ;; + esac + done } # 确认选择 function confirmChoice() { - cat << EOF + cat << EOF =================================================== 请确认您的选择:Y/N app: ${app} @@ -125,66 +125,66 @@ function confirmChoice() { =================================================== EOF - while read confirm - do - case ${confirm} in - y | Y) - echo -e "\n\n>>>>>>>>>>>>>> 开始发布应用" - break ;; - n | N) - echo -e "重新输入发布参数\n" - inputParams ;; - *) - echo "无法识别 ${confirm}" ;; - esac - done + while read confirm + do + case ${confirm} in + y | Y) + echo -e "\n\n>>>>>>>>>>>>>> 开始发布应用" + break ;; + n | N) + echo -e "重新输入发布参数\n" + inputParams ;; + *) + echo "无法识别 ${confirm}" ;; + esac + done } # 引导式发布应用 function releaseApp() { - # 输入执行参数 - app="" - branch="" - profile="" - chooseAppName - chooseOper - if [ "${oper}" == "stop" ]; then - confirmChoice - if [ "${app}" == "all" ]; then - ${SCRIPT_DIR}/${app}-run.sh stop ${profile} - else - ${SCRIPT_DIR}/${app}-run.sh stop ${profile} - fi - else - chooseBranch - chooseProfile - confirmChoice - if [ "${app}" == "all" ]; then - ${SCRIPT_DIR}/js-app-release.sh ${branch} ${profile} - else - ${SCRIPT_DIR}/${app}-release.sh ${branch} ${profile} - fi - fi + # 输入执行参数 + app="" + branch="" + profile="" + chooseAppName + chooseOper + if [ "${oper}" == "stop" ]; then + confirmChoice + if [ "${app}" == "all" ]; then + ${SCRIPT_DIR}/${app}-run.sh stop ${profile} + else + ${SCRIPT_DIR}/${app}-run.sh stop ${profile} + fi + else + chooseBranch + chooseProfile + confirmChoice + if [ "${app}" == "all" ]; then + ${SCRIPT_DIR}/js-app-release.sh ${branch} ${profile} + else + ${SCRIPT_DIR}/${app}-release.sh ${branch} ${profile} + fi + fi } # 脚本主方法 function main() { - printHeadInfo - while read sign - do - case ${sign} in - exit) - echo "主动退出脚本" - exit 0 ;; - *) - releaseApp ;; - esac - - # 装载函数库 - printFootInfo - done + printHeadInfo + while read sign + do + case ${sign} in + exit) + echo "主动退出脚本" + exit 0 ;; + *) + releaseApp ;; + esac + + # 装载函数库 + printFootInfo + done } ######################################## MAIN ######################################## diff --git a/codes/linux/build/spring-boot-run.sh b/codes/linux/build/spring-boot-run.sh index d41a048a..2d644c8b 100644 --- a/codes/linux/build/spring-boot-run.sh +++ b/codes/linux/build/spring-boot-run.sh @@ -38,7 +38,7 @@ packageJavaOpts() { JAVA_OPTS="${JAVA_OPTS} -XX:HeapDumpPath=${LOG_PATH}/${APP_NAME}.heapdump.hprof" # JMX OPTS - IP=`ip addr|grep "inet "|grep -v 127.0.0.1|awk '{print $2}'|cut -d/ -f1` + IP=`ip addr | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1` JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote=true" JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" JAVA_OPTS="${JAVA_OPTS} -Djava.rmi.server.hostname=${IP} -Dcom.sun.management.jmxremote.port=18889" @@ -55,30 +55,30 @@ packageJavaOpts() { # 检查服务是否已经启动 pid="" checkStarted() { - pid=`ps -ef | grep java | grep ${APP_NAME} | awk '{print $2}'` - if [[ -n "${pid}" ]]; then - return 0 - else - return 1 - fi + pid=`ps -ef | grep java | grep ${APP_NAME} | awk '{print $2}'` + if [[ -n "${pid}" ]]; then + return 0 + else + return 1 + fi } main() { case "${oper}" in - start ) - startServer + start) + startServer ;; - stop ) - stopServer + stop) + stopServer ;; - restart ) - stopServer - sleep 5 - startServer + restart) + stopServer + sleep 5 + startServer ;; - * ) - echo "Invalid oper: ${oper}." - exit 1 + *) + echo "Invalid oper: ${oper}." + exit 1 esac exit 0 @@ -86,7 +86,7 @@ main() { stopServer() { echo -n "stopping server: " - if checkStarted ;then + if checkStarted; then kill -9 ${pid} printf "${GREEN}\n${APP_NAME} is stopped.${RESET}\n" else @@ -96,7 +96,7 @@ stopServer() { startServer() { printf "${BLUE}starting ${APP_NAME}...${RESET}\n" - if checkStarted ;then + if checkStarted; then printf "${YELLOW}[WARN] ${APP_NAME} already started!${RESET}\n" printf "PID: ${pid}\n" exit 1 @@ -121,7 +121,7 @@ LOG_PATH=${ROOT_DIR}/../logs mkdir -p ${LOG_PATH} declare -a serial -serial=(start stop restart) +serial=( start stop restart ) echo -n "请选择操作(可选值:start|stop|restart):" read oper if ! echo "${serial[@]}" | grep -q ${oper}; then @@ -131,7 +131,7 @@ fi if [[ ${oper} == "start" ]] || [[ "${oper}" == "restart" ]]; then declare -a serial2 - serial2=(prod dev test) + serial2=( prod dev test ) echo -n "选择 profile(可选值:prod|dev|test):" read profile if ! echo "${serial2[@]}" | grep -q ${profile}; then @@ -140,7 +140,7 @@ if [[ ${oper} == "start" ]] || [[ "${oper}" == "restart" ]]; then fi declare -a serial3 - serial3=(on off) + serial3=( on off ) echo -n "是否启动 debug 模式(可选值:on|off):" read debug if ! echo "${serial3[@]}" | grep -q ${debug}; then diff --git a/codes/linux/build/update-code.sh b/codes/linux/build/update-code.sh index cadaa7e7..c3b14f68 100644 --- a/codes/linux/build/update-code.sh +++ b/codes/linux/build/update-code.sh @@ -8,78 +8,78 @@ # 检查脚本参数,如必要参数未传入,退出脚本。 checkInput() { - if [ "${repository}" == "" ] || [ "${branch}" == "" ]; then - echo "请输入脚本参数:repository branch [source] [target]" - echo " repository: git 仓储(必填)。" - echo " branch: git 分支(必填)。如 master/develop" - echo " source: 代码存放目录。默认为/home/zp/source。" - echo " target: 代码存放目录。默认为脚本所在目录。" - exit 1 - fi + if [ "${repository}" == "" ] || [ "${branch}" == "" ]; then + echo "请输入脚本参数:repository branch [source] [target]" + echo " repository: git 仓储(必填)。" + echo " branch: git 分支(必填)。如 master/develop" + echo " source: 代码存放目录。默认为/home/zp/source。" + echo " target: 代码存放目录。默认为脚本所在目录。" + exit 1 + fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { - if [ ! -d "$1" ]; then - mkdir -p "$1" - fi + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi } # 判断 git 版本库是否存在。根据实际结果修改 ${gitok} 值。 gitok=false function isGitExist() { - cd ${SOURCE_DIR} - if [ -d "${SOURCE_DIR}/${repository}/${target}" ]; then - cd ${SOURCE_DIR}/${repository}/${target} - #(1)删除git状态零时文件 - if [ -f "gitstatus.tmp" ]; then - rm -rf gitstatus.tmp - fi - - #(2) 判断是否存在.git目录 - if [ -d "./.git" ]; then - #(3) 判断git是否可用 - git status &> gitstatus.tmp - grep -iwq 'not a git repository' gitstatus.tmp && gitok=false || gitok=true - fi - - #返回到主目录 - cd ${SOURCE_DIR} - fi + cd ${SOURCE_DIR} + if [ -d "${SOURCE_DIR}/${repository}/${target}" ]; then + cd ${SOURCE_DIR}/${repository}/${target} + #(1)删除git状态零时文件 + if [ -f "gitstatus.tmp" ]; then + rm -rf gitstatus.tmp + fi + + #(2) 判断是否存在.git目录 + if [ -d "./.git" ]; then + #(3) 判断git是否可用 + git status &> gitstatus.tmp + grep -iwq 'not a git repository' gitstatus.tmp && gitok=false || gitok=true + fi + + #返回到主目录 + cd ${SOURCE_DIR} + fi } # 如果 git 版本库存在(根据 ${gitok} 值),执行 fetch 操作;反之,执行 clone 操作。 function doFetchOrClone() { - if ${gitok}; then - cd ${SOURCE_DIR}/${repository}/${target} - git reset --hard - git clean -ffdx - git fetch - echo "git fetch ${repository} remote repository 到本地成功" - else - #删除所有内容,便于重新进行git clone - rm -rf ${repository} - git clone --no-checkout git@github.com:${GITHUB_ACCOUNT}/${repository}.git ${SOURCE_DIR}/${repository}/${target} - echo "git clone ${repository} remote repository 到本地成功" - cd ${SOURCE_DIR}/${repository}/${target} - fi + if ${gitok}; then + cd ${SOURCE_DIR}/${repository}/${target} + git reset --hard + git clean -ffdx + git fetch + echo "git fetch ${repository} remote repository 到本地成功" + else + #删除所有内容,便于重新进行git clone + rm -rf ${repository} + git clone --no-checkout git@github.com:${GITHUB_ACCOUNT}/${repository}.git ${SOURCE_DIR}/${repository}/${target} + echo "git clone ${repository} remote repository 到本地成功" + cd ${SOURCE_DIR}/${repository}/${target} + fi } # 切换到 ${branch} 分支 function doCheckout() { - echo "检出 ${repository} ${branch} 分支代码" - isRemoteBranch=false - gitRemoteBranch=`git branch -r` - echo -e "$gitRemoteBranch" | grep -iwq ${branch} && isRemoteBranch=true || isRemoteBranch=false - if ${isRemoteBranch}; then - echo "找到 ${branch} 分支。" - git checkout -f 'origin/'${branch} - else - echo "未找到 ${branch} 分支!" - exit 2 - fi - echo "更新子模块代码" - git submodule update --init --recursive --force + echo "检出 ${repository} ${branch} 分支代码" + isRemoteBranch=false + gitRemoteBranch=`git branch -r` + echo -e "$gitRemoteBranch" | grep -iwq ${branch} && isRemoteBranch=true || isRemoteBranch=false + if ${isRemoteBranch}; then + echo "找到 ${branch} 分支。" + git checkout -f 'origin/'${branch} + else + echo "未找到 ${branch} 分支!" + exit 2 + fi + echo "更新子模块代码" + git submodule update --init --recursive --force } ######################################## MAIN ######################################## @@ -96,7 +96,7 @@ checkInput GITHUB_ACCOUNT=dunwu SOURCE_DIR=/home/xyz/source if [ "${source}" != "" ]; then - SOURCE_DIR=${source} + SOURCE_DIR=${source} fi createFolderIfNotExist ${SOURCE_DIR} diff --git a/codes/linux/download.sh b/codes/linux/download.sh index 96ae8815..0438d157 100644 --- a/codes/linux/download.sh +++ b/codes/linux/download.sh @@ -29,7 +29,7 @@ root=/home/scripts/linux-tutorial printf "\n${GREEN}>>>>>>>> Download linux-tutorial to ${root} begin.${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "\n${RED}Not detected yum.${RESET}"; - exit 1; + exit 1; } command -v git > /dev/null 2>&1 || { @@ -38,11 +38,11 @@ command -v git > /dev/null 2>&1 || { } if [[ -d ${root} ]]; then - cd ${root} - git pull + cd ${root} + git pull else - mkdir -p ${root} - git clone https://gitee.com/turnon/linux-tutorial.git ${root} + mkdir -p ${root} + git clone https://gitee.com/turnon/linux-tutorial.git ${root} fi chmod +x -R ${root} printf "\n${GREEN}<<<<<<<< Download linux-tutorial to ${root} end.${RESET}\n" diff --git a/codes/linux/dunwu-ops.sh b/codes/linux/dunwu-ops.sh index 100bd72e..14a1b2b6 100644 --- a/codes/linux/dunwu-ops.sh +++ b/codes/linux/dunwu-ops.sh @@ -14,67 +14,67 @@ RESET="$(tput sgr0)" # 打印头部信息 printHeadInfo() { -printf "${BLUE}\n" -cat << EOF + printf "${BLUE}\n" + cat << EOF ################################################################################### # 欢迎使用 Dunwu Shell 运维脚本 # 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### EOF -printf "${RESET}\n" + printf "${RESET}\n" } # 打印尾部信息 printFootInfo() { -printf "${BLUE}\n" -cat << EOF + printf "${BLUE}\n" + cat << EOF ################################################################################### # 脚本执行结束,感谢使用! ################################################################################### EOF -printf "${RESET}\n" + printf "${RESET}\n" } # 检查操作系统环境 checkOsVersion() { - if (($1 == 1)); then - platform=`uname -i` - if [[ ${platform} != "x86_64" ]]; then - printf "\n${RED}脚本仅支持 64 位操作系统!${RESET}\n" - exit 1 - fi + if (($1 == 1)); then + platform=`uname -i` + if [[ ${platform} != "x86_64" ]]; then + printf "\n${RED}脚本仅支持 64 位操作系统!${RESET}\n" + exit 1 + fi - version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` - if [[ ${version} != 7 ]]; then - printf "\n${RED}脚本仅支持 CentOS 7!${RESET}\n" - exit 1 - fi - fi + version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` + if [[ ${version} != 7 ]]; then + printf "\n${RED}脚本仅支持 CentOS 7!${RESET}\n" + exit 1 + fi + fi } menus=( "配置系统" "安装软件" "退出" ) selectAndExecTask() { printHeadInfo - PS3="请输入命令编号:" - select item in "${menus[@]}" - do - case ${item} in - "配置系统") - ./dunwu-sys.sh - selectAndExecTask ;; - "安装软件") - ./dunwu-soft.sh - selectAndExecTask ;; - "退出") - printFootInfo - exit 0 ;; - *) - printf "\n${RED}输入项不支持!${RESET}\n" - selectAndExecTask ;; - esac - break - done + PS3="请输入命令编号:" + select item in "${menus[@]}" + do + case ${item} in + "配置系统") + ./dunwu-sys.sh + selectAndExecTask ;; + "安装软件") + ./dunwu-soft.sh + selectAndExecTask ;; + "退出") + printFootInfo + exit 0 ;; + *) + printf "\n${RED}输入项不支持!${RESET}\n" + selectAndExecTask ;; + esac + break + done } diff --git a/codes/linux/dunwu-soft.sh b/codes/linux/dunwu-soft.sh index deb166eb..77573110 100644 --- a/codes/linux/dunwu-soft.sh +++ b/codes/linux/dunwu-soft.sh @@ -41,26 +41,27 @@ printMenu() { # exec shell to install soft main() { printMenu - read -t 30 index - if [[ -n ${index} ]]; then - no=`expr ${index} - 1` - len=${#menus[*]} - if [[ ${index} -gt ${len} ]]; then - printf "${RED}输入项不支持!\n${RESET}" - exit -1 - fi - key=${menus[$no]} - if [[ ${key} == 'exit' ]]; then - printf "${GREEN}退出 Dunwu 软件安装脚本。\n${RESET}" - exit 0 - fi - sh soft/${key}-install.sh - printf "\n" - main - else - printf "${RED}输入项不支持!\n${RESET}" - exit -1 - fi + read -t 30 index + if [[ -n ${index} ]]; then + no=`expr ${index} - 1` + len=${#menus[*]} + if [[ ${index} -gt ${len} ]]; then + printf "${RED}输入项不支持!\n${RESET}" + exit -1 + fi + key=${menus[$no]} + if [[ ${key} == 'exit' ]]; then + printf "${GREEN}退出 Dunwu 软件安装脚本。\n${RESET}" + exit 0 + fi + sh soft/${key}-install.sh + printf "\n" + main + else + printf "${RED}输入项不支持!\n${RESET}" + exit -1 + fi } +######################################## MAIN ######################################## main diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index c40725d1..bf2d3fbb 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -24,38 +24,38 @@ printf "${RESET}\n" menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { - PS3="请输入命令编号:" - select item in "${menus[@]}" - do - case ${item} in - "替换yum镜像") - sh ${root}/sys/change-yum-repo.sh - main ;; - "安装基本的命令工具") - sh ${root}/sys/install-tools.sh - main ;; - "安装常用libs") - sh ${root}/sys/install-libs.sh - main ;; - "系统配置") - sh ${root}/sys/sys-settings.sh ${root}/sys - main ;; - "全部执行") - sh ${root}/sys/change-yum-repo.sh - sh ${root}/sys/install-tools.sh - sh ${root}/sys/install-libs.sh - sh ${root}/sys/sys-settings.sh ${root}/sys - printf "${GREEN}执行完毕,退出。${RESET}\n" ;; - "退出") - exit 0 ;; - *) - printf "${RED}输入项不支持!${RESET}\n" - main ;; - esac - break - done + PS3="请输入命令编号:" + select item in "${menus[@]}" + do + case ${item} in + "替换yum镜像") + sh ${path}/sys/change-yum-repo.sh + main ;; + "安装基本的命令工具") + sh ${path}/sys/install-tools.sh + main ;; + "安装常用libs") + sh ${path}/sys/install-libs.sh + main ;; + "系统配置") + sh ${path}/sys/sys-settings.sh ${path}/sys + main ;; + "全部执行") + sh ${path}/sys/change-yum-repo.sh + sh ${path}/sys/install-tools.sh + sh ${path}/sys/install-libs.sh + sh ${path}/sys/sys-settings.sh ${path}/sys + printf "${GREEN}执行完毕,退出。${RESET}\n" ;; + "退出") + exit 0 ;; + *) + printf "${RED}输入项不支持!${RESET}\n" + main ;; + esac + break + done } ######################################## MAIN ######################################## -root=$(pwd) +path=$(pwd) main diff --git a/codes/linux/soft/arthas-install.sh b/codes/linux/soft/arthas-install.sh index 3266f88f..0a08892e 100644 --- a/codes/linux/soft/arthas-install.sh +++ b/codes/linux/soft/arthas-install.sh @@ -25,7 +25,7 @@ printf "${BLUE}>>>>>>>> begin.\n${RESET}" root=/opt/arthas if [[ -n $1 ]]; then - root=$1 + root=$1 fi mkdir -p ${root} diff --git a/codes/linux/soft/config/nginx/nginx.conf b/codes/linux/soft/config/nginx/nginx.conf index 5e4d737f..00a389bf 100644 --- a/codes/linux/soft/config/nginx/nginx.conf +++ b/codes/linux/soft/config/nginx/nginx.conf @@ -1,36 +1,37 @@ -worker_processes 1; +worker_processes 1; -#error_log logs/error.log; -#error_log logs/error.log notice; -#error_log logs/error.log info; -pid /usr/local/nginx/logs/nginx.pid; +error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; +pid /usr/local/nginx/logs/nginx.pid; events { - worker_connections 1024; + worker_connections 1024; } http { - default_type application/octet-stream; - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - #access_log logs/nginx-http-access.log; - sendfile on; - rewrite_log on; - keepalive_timeout 65; - - client_max_body_size 20m; - client_body_buffer_size 128k; - - #common header set - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - - include mime.types; - include conf/*.conf; + include mime.types; + include conf.d/*.conf; + + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + #access_log logs/nginx-http-access.log; + + sendfile on; + keepalive_timeout 65; + + client_max_body_size 20m; + client_body_buffer_size 128k; + + #common header set + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; } diff --git a/codes/linux/soft/config/redis/6381/redis.conf b/codes/linux/soft/config/redis/6381/redis.conf deleted file mode 100644 index f0315bec..00000000 --- a/codes/linux/soft/config/redis/6381/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6381 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /opt/redis/redis-5.0.4/cluster/6381/6381.conf -cluster-node-timeout 10000 - -appendonly yes -dir /opt/redis/redis-5.0.4/cluster/6381 -pidfile /var/run/redis-cluster/redis-6381.pid -logfile /opt/redis/redis-5.0.4/cluster/6381/6381.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/6382/redis.conf b/codes/linux/soft/config/redis/6382/redis.conf deleted file mode 100644 index 6ca21eaa..00000000 --- a/codes/linux/soft/config/redis/6382/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6382 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /opt/redis/redis-5.0.4/cluster/6382/6382.conf -cluster-node-timeout 10000 - -appendonly yes -dir /opt/redis/redis-5.0.4/cluster/6382 -pidfile /var/run/redis-cluster/redis-6382.pid -logfile /opt/redis/redis-5.0.4/cluster/6382/6382.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/6383/redis.conf b/codes/linux/soft/config/redis/6383/redis.conf deleted file mode 100644 index 509f9d38..00000000 --- a/codes/linux/soft/config/redis/6383/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6383 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /opt/redis/redis-5.0.4/cluster/6383/6383.conf -cluster-node-timeout 10000 - -appendonly yes -dir /opt/redis/redis-5.0.4/cluster/6383 -pidfile /var/run/redis-cluster/redis-6383.pid -logfile /opt/redis/redis-5.0.4/cluster/6383/6383.log diff --git a/codes/linux/soft/config/redis/6384/redis.conf b/codes/linux/soft/config/redis/6384/redis.conf deleted file mode 100644 index 10a64db2..00000000 --- a/codes/linux/soft/config/redis/6384/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6384 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /opt/redis/redis-5.0.4/cluster/6384/6384.conf -cluster-node-timeout 10000 - -appendonly yes -dir /opt/redis/redis-5.0.4/cluster/6384 -pidfile /var/run/redis-cluster/redis-6384.pid -logfile /opt/redis/redis-5.0.4/cluster/6384/6384.log diff --git a/codes/linux/soft/config/redis/6385/redis.conf b/codes/linux/soft/config/redis/6385/redis.conf deleted file mode 100644 index 33b66a68..00000000 --- a/codes/linux/soft/config/redis/6385/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6385 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /opt/redis/redis-5.0.4/cluster/6385/6385.conf -cluster-node-timeout 10000 - -appendonly yes -dir /opt/redis/redis-5.0.4/cluster/6385 -pidfile /var/run/redis-cluster/redis-6385.pid -logfile /opt/redis/redis-5.0.4/cluster/6385/6385.log diff --git a/codes/linux/soft/config/redis/6386/redis.conf b/codes/linux/soft/config/redis/6386/redis.conf deleted file mode 100644 index 21af8615..00000000 --- a/codes/linux/soft/config/redis/6386/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6386 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /opt/redis/redis-5.0.4/cluster/6386/6386.conf -cluster-node-timeout 10000 - -appendonly yes -dir /opt/redis/redis-5.0.4/cluster/6386 -pidfile /var/run/redis-cluster/redis-6386.pid -logfile /opt/redis/redis-5.0.4/cluster/6386/6386.log diff --git a/codes/linux/soft/config/redis/boot.sh b/codes/linux/soft/config/redis/boot.sh deleted file mode 100644 index 731bd463..00000000 --- a/codes/linux/soft/config/redis/boot.sh +++ /dev/null @@ -1,7 +0,0 @@ - -/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6381/redis.conf -/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6382/redis.conf -/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6383/redis.conf -/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6384/redis.conf -/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6385/redis.conf -/opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/6386/redis.conf diff --git a/codes/linux/soft/config/redis/cluster/6381/redis.conf b/codes/linux/soft/config/redis/cluster/6381/redis.conf new file mode 100644 index 00000000..943eff09 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6381/redis.conf @@ -0,0 +1,12 @@ +port 6381 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/cluster/6381/6381.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/cluster/6381 +pidfile /var/run/redis/redis-6381.pid +logfile /usr/local/redis/cluster/6381/6381.log diff --git a/codes/linux/soft/config/redis/cluster/6382/redis.conf b/codes/linux/soft/config/redis/cluster/6382/redis.conf new file mode 100644 index 00000000..7f1406c3 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6382/redis.conf @@ -0,0 +1,12 @@ +port 6382 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/cluster/6382/6382.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/cluster/6382 +pidfile /var/run/redis/redis-6382.pid +logfile /usr/local/redis/cluster/6382/6382.log diff --git a/codes/linux/soft/config/redis/cluster/6383/redis.conf b/codes/linux/soft/config/redis/cluster/6383/redis.conf new file mode 100644 index 00000000..22d2aa87 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6383/redis.conf @@ -0,0 +1,12 @@ +port 6383 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/cluster/6383/6383.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/cluster/6383 +pidfile /var/run/redis/redis-6383.pid +logfile /usr/local/redis/cluster/6383/6383.log diff --git a/codes/linux/soft/config/redis/cluster/6384/redis.conf b/codes/linux/soft/config/redis/cluster/6384/redis.conf new file mode 100644 index 00000000..b2756e64 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6384/redis.conf @@ -0,0 +1,12 @@ +port 6384 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/cluster/6384/6384.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/cluster/6384 +pidfile /var/run/redis/redis-6384.pid +logfile /usr/local/redis/cluster/6384/6384.log diff --git a/codes/linux/soft/config/redis/cluster/6385/redis.conf b/codes/linux/soft/config/redis/cluster/6385/redis.conf new file mode 100644 index 00000000..28d36cf8 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6385/redis.conf @@ -0,0 +1,12 @@ +port 6385 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/cluster/6385/6385.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/cluster/6385 +pidfile /var/run/redis/redis-6385.pid +logfile /usr/local/redis/cluster/6385/6385.log diff --git a/codes/linux/soft/config/redis/cluster/6386/redis.conf b/codes/linux/soft/config/redis/cluster/6386/redis.conf new file mode 100644 index 00000000..5ab8bf7c --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6386/redis.conf @@ -0,0 +1,12 @@ +port 6386 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/cluster/6386/6386.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/cluster/6386 +pidfile /var/run/redis/redis-6386.pid +logfile /usr/local/redis/cluster/6386/6386.log diff --git a/codes/linux/soft/config/redis/cluster/redis-cluster.sh b/codes/linux/soft/config/redis/cluster/redis-cluster.sh new file mode 100644 index 00000000..23a0051b --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/redis-cluster.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +# --------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# --------------------------------------------------------------------------------- + +printf "${BLUE}\n" +cat << EOF +################################################################################### +# Redis 集群控制脚本 +# @system: 适用于 CentOS7+ +# @author: Zhang Peng +################################################################################### +EOF +printf "${RESET}\n" + +# Settings +PORT=6380 +NODES=6 +ENDPORT=$((PORT + NODES)) +TIMEOUT=2000 +REPLICAS=0 +PATH="/usr/local/redis" + +######################################## MAIN ######################################## +printf "${PURPLE}\n" +printf "Usage: $0 [start|create|stop|watch|tail|clean]\n" +printf "start -- Launch Redis Cluster instances.\n" +printf "create -- Create a cluster using redis-cli --cluster create.\n" +printf "stop -- Stop Redis Cluster instances.\n" +printf "watch -- Show CLUSTER NODES output (first 30 lines) of first node.\n" +printf "tail -- Run tail -f of instance at base port + ID.\n" +printf "clean -- Remove all instances data, logs, configs.\n" +printf "clean-logs -- Remove just instances logs.\n" +printf "${RESET}\n" + +case $1 in + "start") + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT + 1)) + echo "Starting $PORT" + if [[ -e "${PATH}/cluster/${PORT}/redis.conf" ]]; then + ${PATH}/src/redis-server "${PATH}/cluster/${PORT}/redis.conf" + fi + done + ;; + "create") + HOSTS="" + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT + 1)) + HOSTS="$HOSTS 127.0.0.1:$PORT" + done + ${PATH}/src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS + ;; + "stop") + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT + 1)) + echo "Stopping $PORT" + ${PATH}/src/redis-cli -p $PORT shutdown nosave + done + ;; + "watch") + PORT=$((PORT + 1)) + while [[ 1 ]]; do + clear + date + ${PATH}/src/redis-cli -p $PORT cluster nodes | head -30 + sleep 1 + done + ;; + "tail") + INSTANCE=$2 + PORT=$((PORT + INSTANCE)) + tail -f ${PORT}.log + ;; + "call") + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT + 1)) + ${PATH}/src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 + done + ;; + "clean") + rm -rf **/*.log + rm -rf **/appendonly*.aof + rm -rf **/dump*.rdb + rm -rf **/nodes*.conf + ;; + "clean-logs") + rm -rf **/*.log + ;; + "exit") + printf "${RED}Invalid option!${RESET}\n" + main + exit 0 + ;; +esac diff --git a/codes/linux/soft/config/redis/create-cluster b/codes/linux/soft/config/redis/create-cluster deleted file mode 100644 index 3de81525..00000000 --- a/codes/linux/soft/config/redis/create-cluster +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash - -# Settings -PORT=6380 -TIMEOUT=2000 -NODES=6 -REPLICAS=1 - -# You may want to put the above config parameters into config.sh in order to -# override the defaults without modifying this script. - -if [ -a config.sh ] -then - source "config.sh" -fi - -# Computed vars -ENDPORT=$((PORT+NODES)) - -if [ "$1" == "start" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - echo "Starting $PORT" - /opt/redis/redis-5.0.4/src/redis-server /opt/redis/redis-5.0.4/cluster/${PORT}/redis.conf - done - exit 0 -fi - -if [ "$1" == "create" ] -then - HOSTS="" - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - HOSTS="$HOSTS 127.0.0.1:$PORT" - done - /opt/redis/redis-5.0.4/src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS - exit 0 -fi - -if [ "$1" == "stop" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - echo "Stopping $PORT" - /opt/redis/redis-5.0.4/src/redis-cli -p $PORT shutdown nosave - done - exit 0 -fi - -if [ "$1" == "watch" ] -then - PORT=$((PORT+1)) - while [ 1 ]; do - clear - date - /opt/redis/redis-5.0.4/src/redis-cli -p $PORT cluster nodes | head -30 - sleep 1 - done - exit 0 -fi - -if [ "$1" == "tail" ] -then - INSTANCE=$2 - PORT=$((PORT+INSTANCE)) - tail -f ${PORT}.log - exit 0 -fi - -if [ "$1" == "call" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - /opt/redis/redis-5.0.4/src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 - done - exit 0 -fi - -if [ "$1" == "clean" ] -then - rm -rf *.log - rm -rf appendonly*.aof - rm -rf dump*.rdb - rm -rf nodes*.conf - exit 0 -fi - -if [ "$1" == "clean-logs" ] -then - rm -rf *.log - exit 0 -fi - -echo "Usage: $0 [start|create|stop|watch|tail|clean]" -echo "start -- Launch Redis Cluster instances." -echo "create -- Create a cluster using redis-cli --cluster create." -echo "stop -- Stop Redis Cluster instances." -echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." -echo "tail -- Run tail -f of instance at base port + ID." -echo "clean -- Remove all instances data, logs, configs." -echo "clean-logs -- Remove just instances logs." diff --git a/codes/linux/soft/config/redis/redis.conf b/codes/linux/soft/config/redis/redis.conf index d6199543..798b8799 100644 --- a/codes/linux/soft/config/redis/redis.conf +++ b/codes/linux/soft/config/redis/redis.conf @@ -155,7 +155,7 @@ supervised no # # Creating a pid file is best effort: if Redis is not able to create it # nothing bad happens, the server will start and run normally. -pidfile /var/run/redis_6379.pid +pidfile /var/run/redis/redis-6379.pid # Specify the server verbosity level. # This can be one of: diff --git a/codes/linux/soft/config/redis/redis.service b/codes/linux/soft/config/redis/redis.service index 1a96e266..53825745 100644 --- a/codes/linux/soft/config/redis/redis.service +++ b/codes/linux/soft/config/redis/redis.service @@ -4,7 +4,7 @@ After=network.target [Service] Type=forking -PIDFile=/var/run/redis_6379.pid +PIDFile=/var/run/redis/redis-6379.pid ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID diff --git a/codes/linux/soft/elk/boot-elk.sh b/codes/linux/soft/elk/boot-elk.sh index 501e36e7..52fed31f 100644 --- a/codes/linux/soft/elk/boot-elk.sh +++ b/codes/linux/soft/elk/boot-elk.sh @@ -2,50 +2,50 @@ # 检查脚本输入参数 checkInput() { - if [ "${app}" == "" ] || [ "${oper}" == "" ]; then - echo "请输入脚本参数:name" - echo " app: 要启动的进程关键字(必填)。可选值:elasticsearch|logstash|kibana|filebeat" - echo " oper: 执行操作(必填)。可选值:start|stop" - echo "例:./boot-elk.sh logstash start" - exit 0 - fi + if [ "${app}" == "" ] || [ "${oper}" == "" ]; then + echo "请输入脚本参数:name" + echo " app: 要启动的进程关键字(必填)。可选值:elasticsearch|logstash|kibana|filebeat" + echo " oper: 执行操作(必填)。可选值:start|stop" + echo "例:./boot-elk.sh logstash start" + exit 0 + fi - if [ "${app}" != "elasticsearch" ] && [ "${app}" != "logstash" ] && [ "${app}" != "kibana" ] && [ "${app}" != "filebeat" ]; then - echo "name 输入错误" - echo "可选值:elasticsearch|logstash|kibana|filebeat" - exit 0 - fi + if [ "${app}" != "elasticsearch" ] && [ "${app}" != "logstash" ] && [ "${app}" != "kibana" ] && [ "${app}" != "filebeat" ]; then + echo "name 输入错误" + echo "可选值:elasticsearch|logstash|kibana|filebeat" + exit 0 + fi } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } startup() { - if [ "${app}" == "elasticsearch" ]; then - checkFileExist ${ELASTICSEARCH_BIN_PATH}/elasticsearch - nohup sh ${ELASTICSEARCH_BIN_PATH}/elasticsearch >> ${ELASTICSEARCH_BIN_PATH}/nohup.out 2>&1 & - elif [ "${app}" == "logstash" ]; then - checkFileExist ${LOGSTASH_BIN_PATH}/logstash - nohup sh ${LOGSTASH_BIN_PATH}/logstash -f ${LOGSTASH_BIN_PATH}/logstash.conf >> ${LOGSTASH_BIN_PATH}/nohup.out 2>&1 & - elif [ "${app}" == "kibana" ]; then - checkFileExist ${KIBANA_BIN_PATH}/kibana - nohup sh ${KIBANA_BIN_PATH}/kibana >> ${KIBANA_BIN_PATH}/nohup.out 2>&1 & - elif [ "${app}" == "filebeat" ]; then - checkFileExist ${FILEBEAT_PATH}/filebeat - touch ${FILEBEAT_PATH}/nohup.out - nohup ${FILEBEAT_PATH}/filebeat -e -c ${FILEBEAT_PATH}/filebeat.yml -d "publish" >> ${FILEBEAT_PATH}/nohup.out 2>&1 & - fi + if [ "${app}" == "elasticsearch" ]; then + checkFileExist ${ELASTICSEARCH_BIN_PATH}/elasticsearch + nohup sh ${ELASTICSEARCH_BIN_PATH}/elasticsearch >> ${ELASTICSEARCH_BIN_PATH}/nohup.out 2>&1 & + elif [ "${app}" == "logstash" ]; then + checkFileExist ${LOGSTASH_BIN_PATH}/logstash + nohup sh ${LOGSTASH_BIN_PATH}/logstash -f ${LOGSTASH_BIN_PATH}/logstash.conf >> ${LOGSTASH_BIN_PATH}/nohup.out 2>&1 & + elif [ "${app}" == "kibana" ]; then + checkFileExist ${KIBANA_BIN_PATH}/kibana + nohup sh ${KIBANA_BIN_PATH}/kibana >> ${KIBANA_BIN_PATH}/nohup.out 2>&1 & + elif [ "${app}" == "filebeat" ]; then + checkFileExist ${FILEBEAT_PATH}/filebeat + touch ${FILEBEAT_PATH}/nohup.out + nohup ${FILEBEAT_PATH}/filebeat -e -c ${FILEBEAT_PATH}/filebeat.yml -d "publish" >> ${FILEBEAT_PATH}/nohup.out 2>&1 & + fi } shutdown() { - pid=`ps -ef | grep java | grep ${app} | awk '{print $2}'` - kill -9 ${pid} + pid=`ps -ef | grep java | grep ${app} | awk '{print $2}'` + kill -9 ${pid} } ##############################__MAIN__######################################## @@ -60,14 +60,14 @@ FILEBEAT_PATH=/opt/elastic/filebeat-${version}-linux-x86_64 checkInput case ${oper} in - start) - echo "启动 ${app}" - startup - ;; - stop) - echo "终止 ${app}" - shutdown - ;; - *) echo "${oper} is invalid oper" ;; + start) + echo "启动 ${app}" + startup + ;; + stop) + echo "终止 ${app}" + shutdown + ;; + *) echo "${oper} is invalid oper" ;; esac diff --git a/codes/linux/soft/elk/config/filebeat.yml b/codes/linux/soft/elk/config/filebeat.yml index ce1df792..256854a7 100644 --- a/codes/linux/soft/elk/config/filebeat.yml +++ b/codes/linux/soft/elk/config/filebeat.yml @@ -20,48 +20,48 @@ filebeat.prospectors: - type: log - # Change to true to enable this prospector configuration. - enabled: true + # Change to true to enable this prospector configuration. + enabled: true - # Paths that should be crawled and fetched. Glob based paths. - paths: - #- /var/log/*.log - #- c:\programdata\elasticsearch\logs\* - - /home/zp/log/*.log + # Paths that should be crawled and fetched. Glob based paths. + paths: + #- /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + - /home/zp/log/*.log - # Exclude lines. A list of regular expressions to match. It drops the lines that are - # matching any regular expression from the list. - #exclude_lines: ['^DBG'] + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] - # Include lines. A list of regular expressions to match. It exports the lines that are - # matching any regular expression from the list. - #include_lines: ['^ERR', '^WARN'] + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] - # Exclude files. A list of regular expressions to match. Filebeat drops the files that - # are matching any regular expression from the list. By default, no files are dropped. - #exclude_files: ['.gz$'] + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #exclude_files: ['.gz$'] - # Optional additional fields. These fields can be freely picked - # to add additional information to the crawled log files for filtering - #fields: - # level: debug - # review: 1 + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 - ### Multiline options + ### Multiline options - # Mutiline can be used for log messages spanning multiple lines. This is common - # for Java Stack Traces or C-Line Continuation + # Mutiline can be used for log messages spanning multiple lines. This is common + # for Java Stack Traces or C-Line Continuation - # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ - #multiline.pattern: ^\[ + # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ + #multiline.pattern: ^\[ - # Defines if the pattern set under pattern should be negated or not. Default is false. - #multiline.negate: false + # Defines if the pattern set under pattern should be negated or not. Default is false. + #multiline.negate: false - # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern - # that was (not) matched before or after or as long as a pattern is not matched based on negate. - # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash - #multiline.match: after + # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern + # that was (not) matched before or after or as long as a pattern is not matched based on negate. + # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash + #multiline.match: after #============================= Filebeat modules =============================== diff --git a/codes/linux/soft/elk/install-elk.sh b/codes/linux/soft/elk/install-elk.sh index 9b39fec9..eb96cb74 100644 --- a/codes/linux/soft/elk/install-elk.sh +++ b/codes/linux/soft/elk/install-elk.sh @@ -9,97 +9,97 @@ # 获取当前设备IP ipaddr='127.0.0.1' function getDeviceIp() { - ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') + ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { - if [ ! -f "$1" ] - then - echo "关键文件 $1 找不到,脚本执行结束" - exit 0 - fi + if [ ! -f "$1" ] + then + echo "关键文件 $1 找不到,脚本执行结束" + exit 0 + fi } init() { - mkdir -p ${ELASTIC_SOFTWARE_PATH} - getDeviceIp + mkdir -p ${ELASTIC_SOFTWARE_PATH} + getDeviceIp } # 安装 elasticsearch installElasticsearch() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${version}.tar.gz - tar -xzf elasticsearch-${version}.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${version}.tar.gz + tar -xzf elasticsearch-${version}.tar.gz } installRuby() { - cd ${RUBY_SOFTWARE_PATH} - wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz - tar -xzf ruby-2.5.0.tar.gz - cd ruby-2.5.0 - ./configure - make & make install + cd ${RUBY_SOFTWARE_PATH} + wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz + tar -xzf ruby-2.5.0.tar.gz + cd ruby-2.5.0 + ./configure + make & make install } # 安装 logstash installLogstash() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/logstash/logstash-${version}.tar.gz - tar -xzf logstash-${version}.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/logstash/logstash-${version}.tar.gz + tar -xzf logstash-${version}.tar.gz } # 安装 kibana installKibana() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/kibana/kibana-${version}-linux-x86_64.tar.gz - tar -xzf kibana-${version}-linux-x86_64.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/kibana/kibana-${version}-linux-x86_64.tar.gz + tar -xzf kibana-${version}-linux-x86_64.tar.gz } # 安装 filebeat installFilebeat() { - cd ${ELASTIC_SOFTWARE_PATH} - wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${version}-linux-x86_64.tar.gz - tar -zxf filebeat-${version}-linux-x86_64.tar.gz + cd ${ELASTIC_SOFTWARE_PATH} + wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${version}-linux-x86_64.tar.gz + tar -zxf filebeat-${version}-linux-x86_64.tar.gz } # 替换 Elasticsearch 配置 # 1. 替换 192.168.0.1 为本机 IP replaceElasticsearchConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml.bak - sed -i "s/#network.host: 192.168.0.1/network.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml - touch ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/bin/nohup.out + cp ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml.bak + sed -i "s/#network.host: 192.168.0.1/network.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml + touch ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/bin/nohup.out } replaceLogstashConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml.bak - sed -i "s/# http.host: \"127.0.0.1\"/ http.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml - touch ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin/nohup.out - cd ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin - wget "https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/elk/config/logstash.conf" + cp ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml.bak + sed -i "s/# http.host: \"127.0.0.1\"/ http.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml + touch ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin/nohup.out + cd ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin + wget "https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/elk/config/logstash.conf" } # 替换 Kibana 配置 # 1. 替换 localhost 为本机 IP replaceKibanaConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml.bak - sed -i "s/#server.host: \"localhost\"/server.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml - sed -i "s/#elasticsearch.url: \"http://localhost:9200\"/#elasticsearch.url: \"${IP}\"/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml - touch ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/bin/nohup.out + cp ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml.bak + sed -i "s/#server.host: \"localhost\"/server.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml + sed -i "s/#elasticsearch.url: \"http://localhost:9200\"/#elasticsearch.url: \"${IP}\"/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml + touch ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/bin/nohup.out } # 替换 Filebeat 配置 replaceFilebeatConfig() { - cp ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml.bak - cd ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64 - wget https://github.com/dunwu/OS/blob/master/codes/deploy/tool/elk/config/filebeat.yml - sed -i 's/127.0.0.1/'"${IP}"'/g' ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml + cp ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml.bak + cd ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64 + wget https://github.com/dunwu/OS/blob/master/codes/deploy/tool/elk/config/filebeat.yml + sed -i 's/127.0.0.1/'"${IP}"'/g' ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml } # 为 elk.elk 用户设置权限 setPrivilegeForUser() { - chown -R elk.elk ${ELASTIC_SOFTWARE_PATH} - chown -R elk.elk /var/log/ + chown -R elk.elk ${ELASTIC_SOFTWARE_PATH} + chown -R elk.elk /var/log/ } ######################################## MAIN ######################################## diff --git a/codes/linux/soft/elk/install_elasticserch.sh b/codes/linux/soft/elk/install_elasticserch.sh index 479b3e3a..bae8250f 100644 --- a/codes/linux/soft/elk/install_elasticserch.sh +++ b/codes/linux/soft/elk/install_elasticserch.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # auth:kaliarch # version:v1.0 @@ -31,113 +31,113 @@ echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-5.4.1.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-5.4.1.tar.gz" elif [ "${softversion}" == "2" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.0.1.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.0.1.tar.gz" elif [ "${softversion}" == "3" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.3.1.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.3.1.tar.gz" elif [ "${softversion}" == "4" ]; then - echo "you choce channel!" - exit 1; + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4|5}" - exit 0; + echo "input Error! Place input{1|2|3|4|5}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*; do - action $msg /bin/true - done + for msg in $*; do + action $msg /bin/true + done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 > /dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { - output_msg "目录检查" - for dirname in $*; do - [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 - echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} - done + output_msg "目录检查" + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 + echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} + done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { - output_msg "下载源码包" - mkdir -p $download_path - for file in $*; do - wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "下载源码包" + mkdir -p $download_path + for file in $*; do + wget $file -c -P $download_path &> /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*; do - if [ "${file##*.}" == "gz" ]; then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ]; then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { - output_msg "环境变量配置" + output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" > ${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 配置主机名,第一个为主机名 config_hostname() { - if [ ${sysversion} -eq 6 ]; then - hostname $1 - elif [ ${sysversion} -eq 7 ]; then - hostnamectl set-hostname $1 - else - echo "`date +%F' '%H:%M:%S` hostname $1 config fail" >> ${install_log_path}${install_log_name} - fi + if [ ${sysversion} -eq 6 ]; then + hostname $1 + elif [ ${sysversion} -eq 7 ]; then + hostnamectl set-hostname $1 + else + echo "`date +%F' '%H:%M:%S` hostname $1 config fail" >> ${install_log_path}${install_log_name} + fi } config_limits() { - output_msg "配置limits" - cat >> /etc/security/limits.conf << EOF + output_msg "配置limits" + cat >> /etc/security/limits.conf << EOF * soft nofile 65536 * hard nofile 65536 * soft nproc 65536 * hard nproc 65536 EOF - echo "vm.max_map_count = 655360" >> /etc/sysctl.conf - sysctl -p > /dev/null 2>&1 + echo "vm.max_map_count = 655360" >> /etc/sysctl.conf + sysctl -p > /dev/null 2>&1 } # 添加配置文件 add_config() { - cat > $1 << EOF + cat > $1 << EOF cluster.name: my-application node.name: ${hostname} path.data: /usr/local/elasticsearch/data @@ -149,46 +149,46 @@ EOF } config_user() { - useradd $1 > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` $1 user add success" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $1 user add fail" >> ${install_log_path}${install_log_name} && exit 1 - fi - chown ${1}.${1} ${install_path}elasticsearch/ -R + useradd $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $1 user add success" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $1 user add fail" >> ${install_log_path}${install_log_name} && exit 1 + fi + chown ${1}.${1} ${install_path}elasticsearch/ -R } config_jvm() { - if [ ${sys_mem} -eq 0 ]; then - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} - else - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} - fi + if [ ${sys_mem} -eq 0 ]; then + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} + else + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} + fi } main() { - check_dir $install_log_path $install_path - check_yum_command wget wget - download_file $URL - config_hostname $hostname - - software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') - for filename in `ls $download_path`; do - extract_file ${download_path}$filename - done - - rm -fr ${download_path} - ln -s $install_path$software_name ${install_path}elasticsearch - add_config $software_config_file - check_dir ${install_path}elasticsearch/{data,logs} - config_user elasticsearch - config_env ${install_path}elasticsearch/bin - config_limits - config_jvm - echo "请使用一下命令启动服务:'su - elasticsearch -c 'nohup /usr/local/elasticsearch/bin/elasticsearch &'" + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + config_hostname $hostname + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + + rm -fr ${download_path} + ln -s $install_path$software_name ${install_path}elasticsearch + add_config $software_config_file + check_dir ${install_path}elasticsearch/{data,logs} + config_user elasticsearch + config_env ${install_path}elasticsearch/bin + config_limits + config_jvm + echo "请使用一下命令启动服务:'su - elasticsearch -c 'nohup /usr/local/elasticsearch/bin/elasticsearch &'" } diff --git a/codes/linux/soft/elk/install_filebeat.sh b/codes/linux/soft/elk/install_filebeat.sh index bd8265ee..812f81ce 100644 --- a/codes/linux/soft/elk/install_filebeat.sh +++ b/codes/linux/soft/elk/install_filebeat.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # auth:kaliarch # version:v1.0 @@ -28,88 +28,88 @@ echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-5.6.1-linux-x86_64.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-5.6.1-linux-x86_64.tar.gz" elif [ "${softversion}" == "2" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.1.3-linux-x86_64.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.1.3-linux-x86_64.tar.gz" elif [ "${softversion}" == "3" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.3.2-linux-x86_64.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.3.2-linux-x86_64.tar.gz" elif [ "${softversion}" == "4" ]; then - echo "you choce channel!" - exit 1; + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*; do - action $msg /bin/true - done + for msg in $*; do + action $msg /bin/true + done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 > /dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { - output_msg "目录检查" - for dirname in $*; do - [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 - echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} - done + output_msg "目录检查" + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 + echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} + done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { - output_msg "下载源码包" - mkdir -p $download_path - for file in $*; do - wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "下载源码包" + mkdir -p $download_path + for file in $*; do + wget $file -c -P $download_path &> /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*; do - if [ "${file##*.}" == "gz" ]; then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ]; then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { - output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" > ${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} + output_msg "环境变量配置" + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { - cat > $1 << EOF + cat > $1 << EOF filebeat.prospectors: - input_type: log paths: @@ -120,17 +120,17 @@ EOF } main() { - check_dir $install_log_path $install_path - check_yum_command wget wget - download_file $URL - - software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') - for filename in `ls $download_path`; do - extract_file ${download_path}$filename - done - rm -fr ${download_path} - ln -s $install_path$software_name ${install_path}filebeat - add_config ${software_config_file} + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + rm -fr ${download_path} + ln -s $install_path$software_name ${install_path}filebeat + add_config ${software_config_file} } main diff --git a/codes/linux/soft/elk/install_kibana.sh b/codes/linux/soft/elk/install_kibana.sh index 7359d2d7..7e77ecb2 100644 --- a/codes/linux/soft/elk/install_kibana.sh +++ b/codes/linux/soft/elk/install_kibana.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # auth:kaliarch # version:v1.0 @@ -28,88 +28,88 @@ echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.0.1-linux-x86_64.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.0.1-linux-x86_64.tar.gz" elif [ "${softversion}" == "2" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.2.4-linux-x86_64.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.2.4-linux-x86_64.tar.gz" elif [ "${softversion}" == "3" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.3.1-linux-x86_64.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.3.1-linux-x86_64.tar.gz" elif [ "${softversion}" == "4" ]; then - echo "you choce channel!" - exit 1; + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*; do - action $msg /bin/true - done + for msg in $*; do + action $msg /bin/true + done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 > /dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { - output_msg "目录检查" - for dirname in $*; do - [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 - echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} - done + output_msg "目录检查" + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 + echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} + done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { - output_msg "下载源码包" - mkdir -p $download_path - for file in $*; do - wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "下载源码包" + mkdir -p $download_path + for file in $*; do + wget $file -c -P $download_path &> /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*; do - if [ "${file##*.}" == "gz" ]; then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ]; then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { - output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" > ${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} + output_msg "环境变量配置" + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { - cat > $1 << EOF + cat > $1 << EOF server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "http://127.0.0.1:9200" @@ -117,18 +117,18 @@ EOF } main() { - check_dir $install_log_path $install_path - check_yum_command wget wget - download_file $URL - - software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') - for filename in `ls $download_path`; do - extract_file ${download_path}$filename - done - rm -fr ${download_path} - ln -s ${install_path}$software_name ${install_path}kibana - add_config ${software_config_file} - config_env ${install_path}kibana/bin + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + rm -fr ${download_path} + ln -s ${install_path}$software_name ${install_path}kibana + add_config ${software_config_file} + config_env ${install_path}kibana/bin } main diff --git a/codes/linux/soft/elk/install_logstash.sh b/codes/linux/soft/elk/install_logstash.sh index 7300bff3..92546afe 100644 --- a/codes/linux/soft/elk/install_logstash.sh +++ b/codes/linux/soft/elk/install_logstash.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # auth:kaliarch # version:v1.0 @@ -28,88 +28,88 @@ echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-5.4.1.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-5.4.1.tar.gz" elif [ "${softversion}" == "2" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.1.3.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.1.3.tar.gz" elif [ "${softversion}" == "3" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.3.2.tar.gz" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.3.2.tar.gz" elif [ "${softversion}" == "4" ]; then - echo "you choce channel!" - exit 1; + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*; do - action $msg /bin/true - done + for msg in $*; do + action $msg /bin/true + done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 > /dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { - output_msg "目录检查" - for dirname in $*; do - [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 - echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} - done + output_msg "目录检查" + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 + echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} + done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { - output_msg "下载源码包" - mkdir -p $download_path - for file in $*; do - wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "下载源码包" + mkdir -p $download_path + for file in $*; do + wget $file -c -P $download_path &> /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { - output_msg "解压源码" - for file in $*; do - if [ "${file##*.}" == "gz" ]; then - tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - elif [ "${file##*.}" == "zip" ]; then - unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "解压源码" + for file in $*; do + if [ "${file##*.}" == "gz" ]; then + tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + elif [ "${file##*.}" == "zip" ]; then + unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { - output_msg "环境变量配置" - echo "export PATH=\$PATH:$1" > ${env_file} - source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} + output_msg "环境变量配置" + echo "export PATH=\$PATH:$1" > ${env_file} + source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { - cat > $1 << EOF + cat > $1 << EOF input { beats { port => "5044" @@ -125,18 +125,18 @@ EOF } main() { - check_dir $install_log_path $install_path - check_yum_command wget wget - download_file $URL - - software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') - for filename in `ls $download_path`; do - extract_file ${download_path}$filename - done - rm -fr ${download_path} - ln -s $install_path$software_name ${install_path}logstash - add_config ${software_config_file} - config_env ${install_path}logstash/bin + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + + software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') + for filename in `ls $download_path`; do + extract_file ${download_path}$filename + done + rm -fr ${download_path} + ln -s $install_path$software_name ${install_path}logstash + add_config ${software_config_file} + config_env ${install_path}logstash/bin } main diff --git a/codes/linux/soft/fastdfs-install.sh b/codes/linux/soft/fastdfs-install.sh index 65617f69..95cfb009 100644 --- a/codes/linux/soft/fastdfs-install.sh +++ b/codes/linux/soft/fastdfs-install.sh @@ -28,21 +28,25 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install fastdfs begin.${RESET}\n" -command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; - exit 1; } -command -v git > /dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; - exit 1; } +command -v yum > /dev/null 2>&1 || { + printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; +} +command -v git > /dev/null 2>&1 || { + printf "${RED}Require git but it's not installed.${RESET}\n"; + exit 1; +} if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh fastdfs-install.sh [path]\n" - printf "\t Example: sh fastdfs-install.sh /opt/fastdfs\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh fastdfs-install.sh [path]\n" + printf "\t Example: sh fastdfs-install.sh /opt/fastdfs\n" + printf "${RESET}\n" fi path=/opt/fdfs if [[ -n $1 ]]; then - path=$1 + path=$1 fi nginx_version=1.16.0 @@ -57,8 +61,8 @@ path=/opt/fdfs mkdir -p ${path}/libfastcommon curl -o ${path}/libfastcommon.zip http://dunwu.test.upcdn.net/soft/fdfs/libfastcommon.zip if [[ ! -f ${path}/libfastcommon.zip ]]; then - printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" - exit 1 + printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" + exit 1 fi unzip -o ${path}/libfastcommon.zip -d ${path} @@ -71,7 +75,7 @@ printf "${GREEN}>>>>>>>>> install fastdfs${RESET}" mkdir -p ${path}/fastdfs curl -o ${path}/fastdfs.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs.zip if [[ ! -f ${path}/fastdfs.zip ]]; then - printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" + printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" fi unzip -o ${path}/fastdfs.zip -d ${path} cd ${path}/fastdfs @@ -82,7 +86,7 @@ printf "${GREEN}>>>>>>>>> install fastdfs-nginx-module${RESET}\n" mkdir -p ${path}/fastdfs-nginx-module curl -o ${path}/fastdfs-nginx-module.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs-nginx-module.zip if [[ ! -f ${path}/fastdfs-nginx-module.zip ]]; then - printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" + printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" fi unzip -o ${path}/fastdfs-nginx-module.zip -d ${path} diff --git a/codes/linux/soft/install_grafana.sh b/codes/linux/soft/install_grafana.sh index 1a235db5..4364429f 100644 --- a/codes/linux/soft/install_grafana.sh +++ b/codes/linux/soft/install_grafana.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # auth:kaliarch # version:v1.0 @@ -27,84 +27,84 @@ echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.0-1.x86_64.rpm" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.0-1.x86_64.rpm" elif [ "${softversion}" == "2" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.5-1.x86_64.rpm" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.5-1.x86_64.rpm" elif [ "${softversion}" == "3" ]; then - URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.2.2-1.x86_64.rpm" + URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.2.2-1.x86_64.rpm" elif [ "${softversion}" == "4" ]; then - echo "you choce channel!" - exit 1; + echo "you choce channel!" + exit 1; else - echo "input Error! Place input{1|2|3|4}" - exit 0; + echo "input Error! Place input{1|2|3|4}" + exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { - for msg in $*; do - action $msg /bin/true - done + for msg in $*; do + action $msg /bin/true + done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { - output_msg "命令检查:$1" - hash $1 > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 - else - yum -y install $2 > /dev/null 2>&1 - # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } - fi + output_msg "命令检查:$1" + hash $1 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 + else + yum -y install $2 > /dev/null 2>&1 + # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } + fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { - output_msg "目录检查" - for dirname in $*; do - [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 - echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} - done + output_msg "目录检查" + for dirname in $*; do + [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 + echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} + done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { - output_msg "下载源码包" - mkdir -p $download_path - for file in $*; do - wget $file -c -P $download_path &> /dev/null - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 - fi - done + output_msg "下载源码包" + mkdir -p $download_path + for file in $*; do + wget $file -c -P $download_path &> /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 + fi + done } # 安装grafana插件,传入安装的插件的名称 install_grafana_plugins() { - output_msg "grafana插件安装" - check_yum_command grafana-cli - grafana-cli plugins install $* > /dev/null - if [ $? -eq 0 ]; then - echo "`date +%F' '%H:%M:%S` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} - else - echo "`date +%F' '%H:%M:%s` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} && exit 1 - fi + output_msg "grafana插件安装" + check_yum_command grafana-cli + grafana-cli plugins install $* > /dev/null + if [ $? -eq 0 ]; then + echo "`date +%F' '%H:%M:%S` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} + else + echo "`date +%F' '%H:%M:%s` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} && exit 1 + fi } main() { - check_dir $install_log_path $install_path - check_yum_command wget wget - download_file $URL - for filename in `ls $download_path`; do - yum -y install $download_path$filename > /dev/null 2>&1 - done - install_grafana_plugins alexanderzobnin-zabbix-app + check_dir $install_log_path $install_path + check_yum_command wget wget + download_file $URL + for filename in `ls $download_path`; do + yum -y install $download_path$filename > /dev/null 2>&1 + done + install_grafana_plugins alexanderzobnin-zabbix-app } main diff --git a/codes/linux/soft/jdk8-install.sh b/codes/linux/soft/jdk8-install.sh index 9ac84ea2..b08733e7 100644 --- a/codes/linux/soft/jdk8-install.sh +++ b/codes/linux/soft/jdk8-install.sh @@ -27,8 +27,10 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install jdk8 begin.${RESET}\n" -command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; - exit 1; } +command -v yum > /dev/null 2>&1 || { + printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; +} yum -y install java-1.8.0-openjdk-devel.x86_64 java -version diff --git a/codes/linux/soft/kafka-install.sh b/codes/linux/soft/kafka-install.sh index 6a31a87c..a8cced56 100644 --- a/codes/linux/soft/kafka-install.sh +++ b/codes/linux/soft/kafka-install.sh @@ -26,24 +26,26 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install kafka begin.${RESET}\n" -command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; - exit 1; } +command -v java > /dev/null 2>&1 || { + printf "${RED}Require java but it's not installed.${RESET}\n"; + exit 1; +} if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh kafka-install.sh [version] [path]\n" - printf "\t Example: sh kafka-install.sh 2.2.0 /opt/kafka\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh kafka-install.sh [version] [path]\n" + printf "\t Example: sh kafka-install.sh 2.2.0 /opt/kafka\n" + printf "${RESET}\n" fi version=2.2.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/kafka if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/maven-install.sh b/codes/linux/soft/maven-install.sh index e562f71e..821bcbef 100644 --- a/codes/linux/soft/maven-install.sh +++ b/codes/linux/soft/maven-install.sh @@ -28,24 +28,26 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install maven begin.${RESET}\n" -command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; - exit 1; } +command -v java > /dev/null 2>&1 || { + printf "${RED}Require java but it's not installed.${RESET}\n"; + exit 1; +} if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh maven-install.sh [version] [path]\n" - printf "\t Example: sh maven-install.sh 3.6.0 /opt/maven\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh maven-install.sh [version] [path]\n" + printf "\t Example: sh maven-install.sh 3.6.0 /opt/maven\n" + printf "${RESET}\n" fi version=3.6.2 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/maven if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/mongodb-install.sh b/codes/linux/soft/mongodb-install.sh index f92d02e0..4f39bb4d 100644 --- a/codes/linux/soft/mongodb-install.sh +++ b/codes/linux/soft/mongodb-install.sh @@ -27,20 +27,20 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install mongodb begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh mongodb-install.sh [version] [path]\n" - printf "\t Example: sh mongodb-install.sh 4.0.9 /opt/mongodb\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh mongodb-install.sh [version] [path]\n" + printf "\t Example: sh mongodb-install.sh 4.0.9 /opt/mongodb\n" + printf "${RESET}\n" fi version=4.0.9 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/mongodb if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/mysql-install.sh b/codes/linux/soft/mysql-install.sh index 0b3971b5..2f96450c 100644 --- a/codes/linux/soft/mysql-install.sh +++ b/codes/linux/soft/mysql-install.sh @@ -26,12 +26,18 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install mysql begin.${RESET}\n" -command -v wget > /dev/null 2>&1 || { printf "${RED}Require wget but it's not installed.${RESET}\n"; - exit 1; } -command -v rpm > /dev/null 2>&1 || { printf "${RED}Require rpm but it's not installed.${RESET}\n"; - exit 1; } -command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; - exit 1; } +command -v wget > /dev/null 2>&1 || { + printf "${RED}Require wget but it's not installed.${RESET}\n"; + exit 1; +} +command -v rpm > /dev/null 2>&1 || { + printf "${RED}Require rpm but it's not installed.${RESET}\n"; + exit 1; +} +command -v yum > /dev/null 2>&1 || { + printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; +} # 使用 rpm 安装 mysql wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm diff --git a/codes/linux/soft/nacos-install.sh b/codes/linux/soft/nacos-install.sh index 41828591..300a69c0 100644 --- a/codes/linux/soft/nacos-install.sh +++ b/codes/linux/soft/nacos-install.sh @@ -11,24 +11,28 @@ cat << EOF EOF -command -v java > /dev/null 2>&1 || { echo >&2 "Require java but it's not installed."; - exit 1; } -command -v mvn > /dev/null 2>&1 || { echo >&2 "Require mvn but it's not installed."; - exit 1; } +command -v java > /dev/null 2>&1 || { + printf "${RED}Require java but it's not installed.${RESET}\n"; + exit 1; +} +command -v mvn > /dev/null 2>&1 || { + printf "${RED}Require mvn but it's not installed.${RESET}\n"; + exit 1; +} if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - echo "Usage: sh nacos-install.sh [version] [path]" - printf "Example: sh nacos-install.sh 1.0.0 /opt/nacos\n" + echo "Usage: sh nacos-install.sh [version] [path]" + printf "Example: sh nacos-install.sh 1.0.0 /opt/nacos\n" fi version=1.0.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi root=/opt/nacos if [[ -n $2 ]]; then - root=$2 + root=$2 fi echo "Current execution: install nacos ${version} to ${root}" diff --git a/codes/linux/soft/nginx-install.sh b/codes/linux/soft/nginx-install.sh index a06f9f9a..0aa0ae68 100644 --- a/codes/linux/soft/nginx-install.sh +++ b/codes/linux/soft/nginx-install.sh @@ -31,17 +31,17 @@ command -v yum > /dev/null 2>&1 || { printf "\n${GREEN}>>>>>>>> install nginx begin${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t Usage: sh nginx-install.sh [version] \n" - printf "\t Default: sh nginx-install.sh 1.16.0 \n" - printf "\t Example: sh nginx-install.sh 1.16.0 \n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t Usage: sh nginx-install.sh [version] \n" + printf "\t Default: sh nginx-install.sh 1.16.0 \n" + printf "\t Example: sh nginx-install.sh 1.16.0 \n" + printf "${RESET}\n" fi temp=/opt/nginx version=1.16.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi # install info diff --git a/codes/linux/soft/nodejs-install.sh b/codes/linux/soft/nodejs-install.sh index ca11b597..9894c3a2 100644 --- a/codes/linux/soft/nodejs-install.sh +++ b/codes/linux/soft/nodejs-install.sh @@ -28,15 +28,15 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install nodejs begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh nodejs-install.sh [version]\n" - printf "\t Example: sh nodejs-install.sh 10.15.2\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh nodejs-install.sh [version]\n" + printf "\t Example: sh nodejs-install.sh 10.15.2\n" + printf "${RESET}\n" fi version=10.15.2 if [[ -n $1 ]]; then - version=$1 + version=$1 fi # install info diff --git a/codes/linux/soft/redis-install.sh b/codes/linux/soft/redis-install.sh index d9e8885d..b391568c 100644 --- a/codes/linux/soft/redis-install.sh +++ b/codes/linux/soft/redis-install.sh @@ -31,25 +31,25 @@ printf "\n${GREEN}>>>>>>>> install redis begin${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]] || [[ $# -lt 3 ]] || [[ $# -lt 4 ]]; then printf "${PURPLE}[Hint]\n" - printf "\t Usage: sh redis-install.sh [version] [port] [password] \n" - printf "\t Default: sh redis-install.sh 5.0.4 6379 \n" - printf "\t Example: sh redis-install.sh 5.0.4 6379 123456 \n" - printf "${RESET}\n" + printf "\t Usage: sh redis-install.sh [version] [port] [password] \n" + printf "\t Default: sh redis-install.sh 5.0.4 6379 \n" + printf "\t Example: sh redis-install.sh 5.0.4 6379 123456 \n" + printf "${RESET}\n" fi version=5.0.4 if [[ -n $1 ]]; then - version=$1 + version=$1 fi port=6379 if [[ -n $2 ]]; then - port=$2 + port=$2 fi password= if [[ -n $3 ]]; then - password=$3 + password=$3 fi # install info @@ -83,8 +83,8 @@ cp ${path}/redis.conf ${path}/redis.conf.default wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis/redis.conf -O ${path}/redis.conf sed -i "s/^port 6379/port ${port}/g" ${path}/redis.conf if [[ -n ${password} ]]; then - sed -i "s/^protected-mode no/protected-mode yes/g" ${path}/redis.conf - sed -i "s/^# requirepass/requirepass ${password}/g" ${path}/redis.conf + sed -i "s/^protected-mode no/protected-mode yes/g" ${path}/redis.conf + sed -i "s/^# requirepass/requirepass ${password}/g" ${path}/redis.conf fi printf "\n${CYAN}>>>> open redis port in firewall${RESET}\n" diff --git a/codes/linux/soft/rocketmq-install.sh b/codes/linux/soft/rocketmq-install.sh index cf4fff85..10875113 100644 --- a/codes/linux/soft/rocketmq-install.sh +++ b/codes/linux/soft/rocketmq-install.sh @@ -27,20 +27,20 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install tomcat begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh rocketmq-install.sh [version] [path]\n" - printf "\t Example: sh rocketmq-install.sh 4.5.0 /opt/rocketmq\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh rocketmq-install.sh [version] [path]\n" + printf "\t Example: sh rocketmq-install.sh 4.5.0 /opt/rocketmq\n" + printf "${RESET}\n" fi version=4.5.0 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/rocketmq if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/tomcat8-install.sh b/codes/linux/soft/tomcat8-install.sh index f967bd0f..db76cda1 100644 --- a/codes/linux/soft/tomcat8-install.sh +++ b/codes/linux/soft/tomcat8-install.sh @@ -27,20 +27,20 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install tomcat begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh tomcat8-install.sh [version] [path]\n" - printf "\t Example: sh tomcat8-install.sh 8.5.28 /opt/tomcat8\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh tomcat8-install.sh [version] [path]\n" + printf "\t Example: sh tomcat8-install.sh 8.5.28 /opt/tomcat8\n" + printf "${RESET}\n" fi version=8.5.28 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/tomcat if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/zookeeper-install.sh b/codes/linux/soft/zookeeper-install.sh index 68cacef7..37fc5f46 100644 --- a/codes/linux/soft/zookeeper-install.sh +++ b/codes/linux/soft/zookeeper-install.sh @@ -27,20 +27,20 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install zookeeper begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then - printf "${PURPLE}[Hint]\n" - printf "\t sh zookeeper-install.sh [version] [path]\n" - printf "\t Example: sh zookeeper-install.sh 3.4.12 /opt/zookeeper\n" - printf "${RESET}\n" + printf "${PURPLE}[Hint]\n" + printf "\t sh zookeeper-install.sh [version] [path]\n" + printf "\t Example: sh zookeeper-install.sh 3.4.12 /opt/zookeeper\n" + printf "${RESET}\n" fi version=3.4.12 if [[ -n $1 ]]; then - version=$1 + version=$1 fi path=/opt/zookeeper if [[ -n $2 ]]; then - path=$2 + path=$2 fi # install info diff --git a/codes/linux/soft/zsh-install.sh b/codes/linux/soft/zsh-install.sh index ce7a0ecc..1a01c633 100644 --- a/codes/linux/soft/zsh-install.sh +++ b/codes/linux/soft/zsh-install.sh @@ -27,10 +27,14 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install zsh begin.${RESET}\n" -command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; - exit 1; } -command -v git > /dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; - exit 1; } +command -v yum > /dev/null 2>&1 || { + printf "${RED}Require yum but it's not installed.${RESET}\n"; + exit 1; +} +command -v git > /dev/null 2>&1 || { + printf "${RED}Require git but it's not installed.${RESET}\n"; + exit 1; +} # install zsh yum install -y zsh diff --git a/codes/linux/sys/change-yum-repo.sh b/codes/linux/sys/change-yum-repo.sh index 9027a18f..65611c87 100644 --- a/codes/linux/sys/change-yum-repo.sh +++ b/codes/linux/sys/change-yum-repo.sh @@ -33,21 +33,21 @@ version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` # 根据发型版本选择相应 yum 镜像 if [[ ${version} == 5 ]]; then - # Cento5 已废弃,只能使用 http://vault.CentOS.org/ 替换,但由于是国外镜像,速度较慢 - wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-5.repo -O /etc/yum.repos.d/CentOS-Base.repo + # Cento5 已废弃,只能使用 http://vault.CentOS.org/ 替换,但由于是国外镜像,速度较慢 + wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-5.repo -O /etc/yum.repos.d/CentOS-Base.repo - # 根据实际发型版本情况替换 - detailVersion=`lsb_release -r | awk '{print substr($2,1,3)}'` - sed -i 's/$releasever/'"${detailVersion}"'/g' /etc/yum.repos.d/CentOS-Base.repo + # 根据实际发型版本情况替换 + detailVersion=`lsb_release -r | awk '{print substr($2,1,3)}'` + sed -i 's/$releasever/'"${detailVersion}"'/g' /etc/yum.repos.d/CentOS-Base.repo - # 不替换下面的开关,可能会出现错误:Could not open/read repomd.xml - sed -i 's/enabled=1/enabled=0/g' /etc/yum.repos.d/CentOS-Media.repo + # 不替换下面的开关,可能会出现错误:Could not open/read repomd.xml + sed -i 's/enabled=1/enabled=0/g' /etc/yum.repos.d/CentOS-Media.repo elif [[ ${version} == 6 ]]; then - wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-6.repo -O /etc/yum.repos.d/CentOS-Base.repo + wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-6.repo -O /etc/yum.repos.d/CentOS-Base.repo elif [[ ${version} == 7 ]]; then - wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-7.repo -O /etc/yum.repos.d/CentOS-Base.repo + wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-7.repo -O /etc/yum.repos.d/CentOS-Base.repo else - printf "\n${RED}版本不支持,替换 yum repo 失败${RESET}\n" + printf "\n${RED}版本不支持,替换 yum repo 失败${RESET}\n" fi # 更新缓存 diff --git a/codes/linux/sys/sys-settings.sh b/codes/linux/sys/sys-settings.sh index 96adb9b5..eb93a30e 100644 --- a/codes/linux/sys/sys-settings.sh +++ b/codes/linux/sys/sys-settings.sh @@ -13,57 +13,57 @@ RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- printHeadInfo() { -printf "${BLUE}\n" -cat << EOF + printf "${BLUE}\n" + cat << EOF ################################################################################### # Linux Centos7 系统配置脚本(根据需要选择) # @author: Zhang Peng ################################################################################### EOF -printf "${RESET}\n" + printf "${RESET}\n" } setLimit() { -cat >> /etc/security/limits.conf << EOF + cat >> /etc/security/limits.conf << EOF * - nofile 65535 * - nproc 65535 EOF } setLang() { -cat > /etc/sysconfig/i18n << EOF + cat > /etc/sysconfig/i18n << EOF LANG="zh_CN.UTF-8" EOF } closeShutdownShortkey() { - printf "\n${CYAN}>>>> 关闭 Ctrl+Alt+Del 快捷键防止重新启动${RESET}\n" - sed -i 's#exec /sbin/shutdown -r now#\#exec /sbin/shutdown -r now#' /etc/init/control-alt-delete.conf + printf "\n${CYAN}>>>> 关闭 Ctrl+Alt+Del 快捷键防止重新启动${RESET}\n" + sed -i 's#exec /sbin/shutdown -r now#\#exec /sbin/shutdown -r now#' /etc/init/control-alt-delete.conf } closeSelinux() { - # see http://blog.51cto.com/13570193/2093299 - printf "\n${CYAN}>>>> 关闭 selinux${RESET}\n" - sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config + # see http://blog.51cto.com/13570193/2093299 + printf "\n${CYAN}>>>> 关闭 selinux${RESET}\n" + sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config } setBootMode() { - # 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) - # 2. 单用户模式,就像 Win9X 下的安全模式 - # 3. 多用户,但是没有 NFS - # 4. 完全多用户模式,准则的运行级 - # 5. 通常不用,在一些特殊情况下可以用它来做一些事情 - # 6. X11,即进到 X-Window 系统 - # 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) - printf "\n${CYAN}>>>> 配置 Linux 启动模式${RESET}\n" - sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab + # 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) + # 2. 单用户模式,就像 Win9X 下的安全模式 + # 3. 多用户,但是没有 NFS + # 4. 完全多用户模式,准则的运行级 + # 5. 通常不用,在一些特殊情况下可以用它来做一些事情 + # 6. X11,即进到 X-Window 系统 + # 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) + printf "\n${CYAN}>>>> 配置 Linux 启动模式${RESET}\n" + sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab } # 配置 IPv4 configIpv4() { - printf "\n${CYAN}>>>> 配置 IPv4${RESET}\n" + printf "\n${CYAN}>>>> 配置 IPv4${RESET}\n" -cat >> /etc/sysctl.conf << EOF + cat >> /etc/sysctl.conf << EOF net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 2 @@ -90,14 +90,14 @@ EOF # 关闭 IPv6 closeIpv6() { - printf "\n${CYAN}>>>> 关闭 IPv6${RESET}\n" + printf "\n${CYAN}>>>> 关闭 IPv6${RESET}\n" -cat > /etc/modprobe.d/ipv6.conf << EOF + cat > /etc/modprobe.d/ipv6.conf << EOF alias net-pf-10 off options ipv6 disable=1 EOF - echo "NETWORKING_IPV6=off" >> /etc/sysconfig/network + echo "NETWORKING_IPV6=off" >> /etc/sysconfig/network } # 入口函数 @@ -107,26 +107,26 @@ main() { do case ${ITEM} in - "配置 DNS") - sh ${root}/set-dns.sh ;; - "配置 NTP") - sh ${root}/set-ntp.sh ;; - "关闭防火墙") - sh ${root}/stop-firewall.sh ;; - "配置 IPv4") - configIpv4 ;; - "关闭 IPv6") - closeIpv6 ;; - "全部执行") - sh ${root}/set-dns.sh - sh ${root}/set-ntp.sh - sh ${root}/stop-firewall.sh - configIpv4 - closeIpv6 + "配置 DNS") + sh ${root}/set-dns.sh ;; + "配置 NTP") + sh ${root}/set-ntp.sh ;; + "关闭防火墙") + sh ${root}/stop-firewall.sh ;; + "配置 IPv4") + configIpv4 ;; + "关闭 IPv6") + closeIpv6 ;; + "全部执行") + sh ${root}/set-dns.sh + sh ${root}/set-ntp.sh + sh ${root}/stop-firewall.sh + configIpv4 + closeIpv6 ;; - *) - printf "\n${RED}输入项不支持${RESET}\n" - main + *) + printf "\n${RED}输入项不支持${RESET}\n" + main ;; esac break @@ -136,7 +136,7 @@ main() { ######################################## MAIN ######################################## root=$(pwd) if [[ -n $1 ]]; then - root=$1 + root=$1 fi printHeadInfo diff --git a/codes/linux/sys/syscheck b/codes/linux/sys/syscheck deleted file mode 100644 index 6ced5856..00000000 --- a/codes/linux/sys/syscheck +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -# console color -C_RESET="$(tput sgr0)" -C_BLACK="\033[1;30m" -C_RED="\033[1;31m" -C_GREEN="\033[1;32m" -C_YELLOW="\033[1;33m" -C_BLUE="\033[1;34m" -C_PURPLE="\033[1;35m" -C_CYAN="\033[1;36m" -C_WHITE="\033[1;37m" -############################################################################## - -printf "${C_PURPLE}" -cat << EOF - -################################################################################### -# 系统信息检查脚本 -# @author: Zhang Peng -################################################################################### - -EOF -printf "${C_RESET}" - -[[ $(id -u) -gt 0 ]] && echo "请用root用户执行此脚本!" && exit 1 -sysversion=$(rpm -q centos-release|cut -d- -f3) -double_line="===============================================================" -line="----------------------------------------------" - -# 打印头部信息 -printHeadInfo() { -cat << EOF - -+---------------------------------------------------------------------------------+ -| 欢迎使用 【系统信息检查脚本】 | -| @author: Zhang Peng | -+---------------------------------------------------------------------------------+ - -EOF -} - -# 打印尾部信息 -printFootInfo() { -cat << EOF - -+---------------------------------------------------------------------------------+ -| 脚本执行结束,感谢使用! | -+---------------------------------------------------------------------------------+ - -EOF -} - -options=("获取系统信息" "获取服务信息" "获取CPU信息" "获取系统网络信息" "获取系统内存信息" "获取系统磁盘信息" "获取CPU/内存占用TOP10" "获取系统用户信息" "输出所有信息" "退出") -printMenu() { -printf "${C_BLUE}" -printf "主菜单:\n" -for i in "${!options[@]}"; do - index=`expr ${i} + 1` - val=`expr ${index} % 2` - printf "\t(%02d) %-30s" "${index}" "${options[$i]}" - if [[ ${val} -eq 0 ]]; then - printf "\n" - fi -done -printf "${C_BLUE}请输入需要执行的指令:\n" -printf "${C_RESET}" -} - -# 获取系统信息 -get_systatus_info() { - sys_os=$(uname -o) - sys_release=$(cat /etc/redhat-release) - sys_kernel=$(uname -r) - sys_hostname=$(hostname) - sys_selinux=$(getenforce) - sys_lang=$(echo $LANG) - sys_lastreboot=$(who -b | awk '{print $3,$4}') - sys_runtime=$(uptime |awk '{print $3,$4}'|cut -d, -f1) - sys_time=$(date) - sys_load=$(uptime |cut -d: -f5) - -cat << EOF -【系统信息】 - -系统: ${sys_os} -发行版本: ${sys_release} -系统内核: ${sys_kernel} -主机名: ${sys_hostname} -selinux状态: ${sys_selinux} -系统语言: ${sys_lang} -系统当前时间: ${sys_time} -系统最后重启时间: ${sys_lastreboot} -系统运行时间: ${sys_runtime} -系统负载: ${sys_load} -EOF -} - -# 获取CPU信息 -get_cpu_info() { - Physical_CPUs=$(grep "physical id" /proc/cpuinfo| sort | uniq | wc -l) - Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l) - CPU_Kernels=$(grep "cores" /proc/cpuinfo|uniq| awk -F ': ' '{print $2}') - CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq) - CPU_Arch=$(uname -m) -cat << EOF -【CPU信息】 - -物理CPU个数:$Physical_CPUs -逻辑CPU个数:$Virt_CPUs -每CPU核心数:$CPU_Kernels -CPU型号:$CPU_Type -CPU架构:$CPU_Arch -EOF -} - -# 获取服务信息 -get_service_info() { - port_listen=$(netstat -lntup|grep -v "Active Internet") - kernel_config=$(sysctl -p 2>/dev/null) - if [[ ${sysversion} -gt 6 ]];then - service_config=$(systemctl list-unit-files --type=service --state=enabled|grep "enabled") - run_service=$(systemctl list-units --type=service --state=running |grep ".service") - else - service_config=$(/sbin/chkconfig | grep -E ":on|:启用" |column -t) - run_service=$(/sbin/service --status-all|grep -E "running") - fi -cat << EOF -【服务信息】 - -${service_config} -${line} -运行的服务: - -${run_service} -${line} -监听端口: - -${port_listen} -${line} -内核参考配置: - -${kernel_config} -EOF -} - -# 获取系统内存信息 -get_mem_info() { - check_mem=$(free -m) - MemTotal=$(grep MemTotal /proc/meminfo| awk '{print $2}') #KB - MemFree=$(grep MemFree /proc/meminfo| awk '{print $2}') #KB - let MemUsed=MemTotal-MemFree - MemPercent=$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}") - report_MemTotal="$((MemTotal/1024))""MB" #内存总容量(MB) - report_MemFree="$((MemFree/1024))""MB" #内存剩余(MB) - report_MemUsedPercent=$(free | sed -n '2p' | gawk 'x = int(( $3 / $2 ) * 100) {print x}' | sed 's/$/%/') - -cat << EOF -【内存信息】 - -内存总容量(MB): ${report_MemTotal} -内存剩余量(MB):${report_MemFree} -内存使用率: ${report_MemUsedPercent} -EOF -} - -# 获取系统网络信息 -get_net_info() { - pri_ipadd=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') - pub_ipadd=$(curl ifconfig.me -s) - gateway=$(ip route | grep default | awk '{print $3}') - mac_info=$(ip link| egrep -v "lo"|grep link|awk '{print $2}') - dns_config=$(egrep -v "^$|^#" /etc/resolv.conf) - route_info=$(route -n) -cat << EOF -【网络信息】 - -系统公网地址:${pub_ipadd} -系统私网地址:${pri_ipadd} -网关地址:${gateway} -MAC地址:${mac_info} - -路由信息: -${route_info} - -DNS 信息: -${dns_config} -EOF -} - -# 获取系统磁盘信息 -get_disk_info() { - disk_info=$(fdisk -l|grep "Disk /dev"|cut -d, -f1) - disk_use=$(df -hTP|awk '$2!="tmpfs"{print}') - disk_percent=$(free | sed -n '2p' | gawk 'x = int(( $3 / $2 ) * 100) {print x}' | sed 's/$/%/') - disk_inode=$(df -hiP|awk '$1!="tmpfs"{print}') - -cat << EOF -【磁盘信息】 - -${disk_info} - -磁盘使用: ${disk_use} -磁盘使用百分比: ${disk_percent} -inode信息: ${disk_inode} -EOF -} - -# 获取系统用户信息 -get_sys_user() { - login_user=$(awk -F: '{if ($NF=="/bin/bash") print $0}' /etc/passwd) - ssh_config=$(egrep -v "^#|^$" /etc/ssh/sshd_config) - sudo_config=$(egrep -v "^#|^$" /etc/sudoers |grep -v "^Defaults") - host_config=$(egrep -v "^#|^$" /etc/hosts) - crond_config=$(for cronuser in /var/spool/cron/* ;do ls ${cronuser} 2>/dev/null|cut -d/ -f5;egrep -v "^$|^#" ${cronuser} 2>/dev/null;echo "";done) -cat << EOF -【用户信息】 - -系统登录用户: - -${login_user} -${line} -ssh 配置信息: - -${ssh_config} -${line} -sudo 配置用户: - -${sudo_config} -${line} -定时任务配置: - -${crond_config} -${line} -hosts 信息: - -${host_config} -EOF -} - -# 获取CPU/内存占用TOP10 -get_process_top_info() { - - top_title=$(top -b n1 | head -7 | tail -1) - cpu_top10=$(top -b n1 | head -17 | tail -11) - mem_top10=$(top -b n1 | head -17 | tail -10 | sort -k10 -r) - -cat << EOF -【TOP10】 -CPU占用TOP10: - -${cpu_top10} - -内存占用TOP10: - -${top_title} -${mem_top10} -EOF -} - -show_dead_process() { - printf "僵尸进程:\n" - ps -al | gawk '{print $2,$4}' | grep Z -} - -get_all_info() { - get_systatus_info - echo ${double_line} - get_service_info - echo ${double_line} - get_cpu_info - echo ${double_line} - get_net_info - echo ${double_line} - get_mem_info - echo ${double_line} - get_disk_info - echo ${double_line} - get_process_top_info - echo ${double_line} - get_sys_user -} - -main() { -while [[ 1 ]] -do - printMenu - read option - local index=$[${option} - 1] - case ${options[${index}]} in - "获取系统信息") - get_systatus_info;; - "获取服务信息") - get_service_info ;; - "获取CPU信息") - get_cpu_info ;; - "获取系统网络信息") - get_net_info ;; - "获取系统内存信息") - get_mem_info ;; - "获取系统磁盘信息") - get_disk_info ;; - "获取CPU/内存占用TOP10") - get_process_top_info ;; - "获取系统用户信息") - get_sys_user ;; - "输出所有信息") - get_all_info > sys.log - printf "${C_GREEN}信息已经输出到 sys.log 中。${C_RESET}\n\n" - ;; - "退出") - exit ;; - *) - clear - echo "抱歉,不支持此选项";; - esac -done -} - -######################################## MAIN ######################################## -printHeadInfo -main -printFootInfo -printf "${C_RESET}" diff --git a/codes/linux/sys/syscheck.sh b/codes/linux/sys/syscheck.sh new file mode 100644 index 00000000..c5ac52a6 --- /dev/null +++ b/codes/linux/sys/syscheck.sh @@ -0,0 +1,328 @@ +#!/usr/bin/env bash + +############################################################################## +# console color +C_RESET="$(tput sgr0)" +C_BLACK="\033[1;30m" +C_RED="\033[1;31m" +C_GREEN="\033[1;32m" +C_YELLOW="\033[1;33m" +C_BLUE="\033[1;34m" +C_PURPLE="\033[1;35m" +C_CYAN="\033[1;36m" +C_WHITE="\033[1;37m" +############################################################################## + +printf "${C_PURPLE}" +cat << EOF + +################################################################################### +# 系统信息检查脚本 +# @author: Zhang Peng +################################################################################### + +EOF +printf "${C_RESET}" + +[[ $(id -u) -gt 0 ]] && echo "请用root用户执行此脚本!" && exit 1 +sysversion=$(rpm -q centos-release | cut -d- -f3) +double_line="===============================================================" +line="----------------------------------------------" + +# 打印头部信息 +printHeadInfo() { + cat << EOF + ++---------------------------------------------------------------------------------+ +| 欢迎使用 【系统信息检查脚本】 | +| @author: Zhang Peng | ++---------------------------------------------------------------------------------+ + +EOF +} + +# 打印尾部信息 +printFootInfo() { + cat << EOF + ++---------------------------------------------------------------------------------+ +| 脚本执行结束,感谢使用! | ++---------------------------------------------------------------------------------+ + +EOF +} + +options=( "获取系统信息" "获取服务信息" "获取CPU信息" "获取系统网络信息" "获取系统内存信息" "获取系统磁盘信息" "获取CPU/内存占用TOP10" "获取系统用户信息" "输出所有信息" "退出" ) +printMenu() { + printf "${C_BLUE}" + printf "主菜单:\n" + for i in "${!options[@]}"; do + index=`expr ${i} + 1` + val=`expr ${index} % 2` + printf "\t(%02d) %-30s" "${index}" "${options[$i]}" + if [[ ${val} -eq 0 ]]; then + printf "\n" + fi + done + printf "${C_BLUE}请输入需要执行的指令:\n" + printf "${C_RESET}" +} + +# 获取系统信息 +get_systatus_info() { + sys_os=$(uname -o) + sys_release=$(cat /etc/redhat-release) + sys_kernel=$(uname -r) + sys_hostname=$(hostname) + sys_selinux=$(getenforce) + sys_lang=$(echo $LANG) + sys_lastreboot=$(who -b | awk '{print $3,$4}') + sys_runtime=$(uptime | awk '{print $3,$4}' | cut -d, -f1) + sys_time=$(date) + sys_load=$(uptime | cut -d: -f5) + + cat << EOF +【系统信息】 + +系统: ${sys_os} +发行版本: ${sys_release} +系统内核: ${sys_kernel} +主机名: ${sys_hostname} +selinux状态: ${sys_selinux} +系统语言: ${sys_lang} +系统当前时间: ${sys_time} +系统最后重启时间: ${sys_lastreboot} +系统运行时间: ${sys_runtime} +系统负载: ${sys_load} +EOF +} + +# 获取CPU信息 +get_cpu_info() { + Physical_CPUs=$(grep "physical id" /proc/cpuinfo | sort | uniq | wc -l) + Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l) + CPU_Kernels=$(grep "cores" /proc/cpuinfo | uniq | awk -F ': ' '{print $2}') + CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq) + CPU_Arch=$(uname -m) + cat << EOF +【CPU信息】 + +物理CPU个数:$Physical_CPUs +逻辑CPU个数:$Virt_CPUs +每CPU核心数:$CPU_Kernels +CPU型号:$CPU_Type +CPU架构:$CPU_Arch +EOF +} + +# 获取服务信息 +get_service_info() { + port_listen=$(netstat -lntup | grep -v "Active Internet") + kernel_config=$(sysctl -p 2> /dev/null) + if [[ ${sysversion} -gt 6 ]]; then + service_config=$(systemctl list-unit-files --type=service --state=enabled | grep "enabled") + run_service=$(systemctl list-units --type=service --state=running | grep ".service") + else + service_config=$(/sbin/chkconfig | grep -E ":on|:启用" | column -t) + run_service=$(/sbin/service --status-all | grep -E "running") + fi + cat << EOF +【服务信息】 + +${service_config} + ${line} +运行的服务: + +${run_service} + ${line} +监听端口: + +${port_listen} + ${line} +内核参考配置: + +${kernel_config} +EOF +} + +# 获取系统内存信息 +get_mem_info() { + check_mem=$(free -m) + MemTotal=$(grep MemTotal /proc/meminfo | awk '{print $2}') #KB + MemFree=$(grep MemFree /proc/meminfo | awk '{print $2}') #KB + let MemUsed=MemTotal-MemFree + MemPercent=$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}") + report_MemTotal="$((MemTotal/1024))" "MB" #内存总容量(MB) + report_MemFree="$((MemFree/1024))" "MB" #内存剩余(MB) + report_MemUsedPercent=$(free | sed -n '2p' | gawk 'x = int(( $3 / $2 ) * 100) {print x}' | sed 's/$/%/') + + cat << EOF +【内存信息】 + +内存总容量(MB): ${report_MemTotal} +内存剩余量(MB):${report_MemFree} +内存使用率: ${report_MemUsedPercent} +EOF +} + +# 获取系统网络信息 +get_net_info() { + pri_ipadd=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') + pub_ipadd=$(curl ifconfig.me -s) + gateway=$(ip route | grep default | awk '{print $3}') + mac_info=$(ip link | egrep -v "lo" | grep link | awk '{print $2}') + dns_config=$(egrep -v "^$|^#" /etc/resolv.conf) + route_info=$(route -n) + cat << EOF +【网络信息】 + +系统公网地址:${pub_ipadd} +系统私网地址:${pri_ipadd} +网关地址:${gateway} +MAC地址:${mac_info} + +路由信息: +${route_info} + +DNS 信息: +${dns_config} +EOF +} + +# 获取系统磁盘信息 +get_disk_info() { + disk_info=$(fdisk -l | grep "Disk /dev" | cut -d, -f1) + disk_use=$(df -hTP | awk '$2!="tmpfs"{print}') + disk_percent=$(free | sed -n '2p' | gawk 'x = int(( $3 / $2 ) * 100) {print x}' | sed 's/$/%/') + disk_inode=$(df -hiP | awk '$1!="tmpfs"{print}') + + cat << EOF +【磁盘信息】 + +${disk_info} + +磁盘使用: ${disk_use} +磁盘使用百分比: ${disk_percent} +inode信息: ${disk_inode} +EOF +} + +# 获取系统用户信息 +get_sys_user() { + login_user=$(awk -F: '{if ($NF=="/bin/bash") print $0}' /etc/passwd) + ssh_config=$(egrep -v "^#|^$" /etc/ssh/sshd_config) + sudo_config=$(egrep -v "^#|^$" /etc/sudoers | grep -v "^Defaults") + host_config=$(egrep -v "^#|^$" /etc/hosts) + crond_config=$(for cronuser in /var/spool/cron/*; do + ls ${cronuser} 2> /dev/null | cut -d/ -f5; egrep -v "^$|^#" ${cronuser} 2> /dev/null; + echo ""; + done) + cat << EOF +【用户信息】 + +系统登录用户: + +${login_user} + ${line} +ssh 配置信息: + +${ssh_config} + ${line} +sudo 配置用户: + +${sudo_config} + ${line} +定时任务配置: + +${crond_config} + ${line} +hosts 信息: + +${host_config} +EOF +} + +# 获取CPU/内存占用TOP10 +get_process_top_info() { + + top_title=$(top -b n1 | head -7 | tail -1) + cpu_top10=$(top -b n1 | head -17 | tail -11) + mem_top10=$(top -b n1 | head -17 | tail -10 | sort -k10 -r) + + cat << EOF +【TOP10】 +CPU占用TOP10: + +${cpu_top10} + +内存占用TOP10: + +${top_title} + ${mem_top10} +EOF +} + +show_dead_process() { + printf "僵尸进程:\n" + ps -al | gawk '{print $2,$4}' | grep Z +} + +get_all_info() { + get_systatus_info + echo ${double_line} + get_service_info + echo ${double_line} + get_cpu_info + echo ${double_line} + get_net_info + echo ${double_line} + get_mem_info + echo ${double_line} + get_disk_info + echo ${double_line} + get_process_top_info + echo ${double_line} + get_sys_user +} + +main() { + while [[ 1 ]] + do + printMenu + read option + local index=$[ ${option} - 1 ] + case ${options[${index}]} in + "获取系统信息") + get_systatus_info ;; + "获取服务信息") + get_service_info ;; + "获取CPU信息") + get_cpu_info ;; + "获取系统网络信息") + get_net_info ;; + "获取系统内存信息") + get_mem_info ;; + "获取系统磁盘信息") + get_disk_info ;; + "获取CPU/内存占用TOP10") + get_process_top_info ;; + "获取系统用户信息") + get_sys_user ;; + "输出所有信息") + get_all_info > sys.log + printf "${C_GREEN}信息已经输出到 sys.log 中。${C_RESET}\n\n" + ;; + "退出") + exit ;; + *) + clear + echo "抱歉,不支持此选项" ;; + esac + done +} + +######################################## MAIN ######################################## +printHeadInfo +main +printFootInfo +printf "${C_RESET}" diff --git a/codes/linux/tool/Autoinstall_ELK_V1.3.sh b/codes/linux/tool/Autoinstall_ELK_V1.3.sh index 87557ddf..f1ad94e0 100644 --- a/codes/linux/tool/Autoinstall_ELK_V1.3.sh +++ b/codes/linux/tool/Autoinstall_ELK_V1.3.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #mail:xuel@anchnet.com #data:2017/9/7 @@ -23,29 +23,29 @@ sys_mem=`free -m | grep Mem: | awk '{print $2}' | awk '{sum+=$1} END {print sum/ #wget software wget_fun() { - if [ ! -d ${software_dir} ]; then - mkdir -p ${software_dir} && cd ${software_dir} - else - cd ${software_dir} - fi - for software in $elasticsearch_url $kibana_url $logstash_url $filebeat_url - do - wget -c $software - done - clear + if [ ! -d ${software_dir} ]; then + mkdir -p ${software_dir} && cd ${software_dir} + else + cd ${software_dir} + fi + for software in $elasticsearch_url $kibana_url $logstash_url $filebeat_url + do + wget -c $software + done + clear } #initial system:install java wget;set hostname;disable firewalld init_sys() { - [ -f /etc/init.d/functions ] && . /etc/init.d/functions - [ "${sys_version}" != "7" ] && echo "Error:This Scripts Support Centos7.xx" && exit 1 - [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 - sed -i "s/SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config - setenforce 0 - yum install -y java-1.8.0-openjdk wget net-tools - hostnamectl set-hostname elk-server - systemctl stop firewalld - cat >> /etc/security/limits.conf << EOF + [ -f /etc/init.d/functions ] && . /etc/init.d/functions + [ "${sys_version}" != "7" ] && echo "Error:This Scripts Support Centos7.xx" && exit 1 + [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 + sed -i "s/SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config + setenforce 0 + yum install -y java-1.8.0-openjdk wget net-tools + hostnamectl set-hostname elk-server + systemctl stop firewalld + cat >> /etc/security/limits.conf << EOF * soft nofile 65536 * hard nofile 65536 * soft nGproc 65536 @@ -55,21 +55,21 @@ EOF #install elasticsearch install_elasticsearch() { - cd $software_dir - tar zxf elasticsearch-5.4.1.tar.gz - mv elasticsearch-5.4.1 /usr/local/elasticsearch - mkdir -p /usr/local/elasticsearch/data /usr/local/elasticsearch/logs - useradd elasticsearch - chown -R elasticsearch:elasticsearch /usr/local/elasticsearch - echo "vm.max_map_count = 655360" >> /etc/sysctl.conf && sysctl -p - if [ ${sys_mem} -eq 0 ]; then - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} - else - sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} - sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} - fi - cat >> /usr/local/elasticsearch/config/elasticsearch.yml << EOF + cd $software_dir + tar zxf elasticsearch-5.4.1.tar.gz + mv elasticsearch-5.4.1 /usr/local/elasticsearch + mkdir -p /usr/local/elasticsearch/data /usr/local/elasticsearch/logs + useradd elasticsearch + chown -R elasticsearch:elasticsearch /usr/local/elasticsearch + echo "vm.max_map_count = 655360" >> /etc/sysctl.conf && sysctl -p + if [ ${sys_mem} -eq 0 ]; then + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} + else + sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} + sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} + fi + cat >> /usr/local/elasticsearch/config/elasticsearch.yml << EOF cluster.name: my-application node.name: elk-server path.data: /usr/local/elasticsearch/data @@ -78,15 +78,15 @@ network.host: 127.0.0.1 http.port: 9200 discovery.zen.ping.unicast.hosts: ["elk-server"] EOF - su - elasticsearch -c "nohup /usr/local/elasticsearch/bin/elasticsearch &" + su - elasticsearch -c "nohup /usr/local/elasticsearch/bin/elasticsearch &" } #install logstash install_logstash() { - cd $software_dir - tar -zxf logstash-5.4.1.tar.gz - mv logstash-5.4.1 /usr/local/logstash - cat > /usr/local/logstash/config/01-syslog.conf << EOF + cd $software_dir + tar -zxf logstash-5.4.1.tar.gz + mv logstash-5.4.1 /usr/local/logstash + cat > /usr/local/logstash/config/01-syslog.conf << EOF input { beats { port => "5044" @@ -99,15 +99,15 @@ output { stdout { codec => rubydebug } } EOF - nohup /usr/local/logstash/bin/logstash -f /usr/local/logstash/config/01-syslog.conf & > /dev/null + nohup /usr/local/logstash/bin/logstash -f /usr/local/logstash/config/01-syslog.conf & > /dev/null } #install filebeat install_filebeat() { - cd $software_dir - tar -zxf filebeat-5.4.1-linux-x86_64.tar.gz - mv filebeat-5.4.1-linux-x86_64 /usr/local/filebeat - cat > /usr/local/filebeat/filebeat.yml << EOF + cd $software_dir + tar -zxf filebeat-5.4.1-linux-x86_64.tar.gz + mv filebeat-5.4.1-linux-x86_64 /usr/local/filebeat + cat > /usr/local/filebeat/filebeat.yml << EOF filebeat.prospectors: - input_type: log paths: @@ -115,53 +115,53 @@ filebeat.prospectors: output.logstash: hosts: ["127.0.0.1:5044"] EOF - cd /usr/local/filebeat/ - nohup /usr/local/filebeat/filebeat & > /dev/null + cd /usr/local/filebeat/ + nohup /usr/local/filebeat/filebeat & > /dev/null } #install kibana install_kibana() { - cd $software_dir - tar -zxf kibana-5.4.1-linux-x86_64.tar.gz - mv kibana-5.4.1-linux-x86_64 /usr/local/kibana - cat >> /usr/local/kibana/config/kibana.yml << EOF + cd $software_dir + tar -zxf kibana-5.4.1-linux-x86_64.tar.gz + mv kibana-5.4.1-linux-x86_64 /usr/local/kibana + cat >> /usr/local/kibana/config/kibana.yml << EOF server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "http://127.0.0.1:9200" EOF - nohup /usr/local/kibana/bin/kibana & > /dev/null + nohup /usr/local/kibana/bin/kibana & > /dev/null } check() { - port=$1 - program=$2 - check_port=`netstat -lntup | grep ${port} | wc -l` - check_program=`ps -ef | grep ${program} | grep -v grep | wc -l` - if [ $check_port -gt 0 ] && [ $check_program -gt 0 ]; then - action "${program} run is ok!" /bin/true - else - action "${program} run is error!" /bin/false - fi + port=$1 + program=$2 + check_port=`netstat -lntup | grep ${port} | wc -l` + check_program=`ps -ef | grep ${program} | grep -v grep | wc -l` + if [ $check_port -gt 0 ] && [ $check_program -gt 0 ]; then + action "${program} run is ok!" /bin/true + else + action "${program} run is error!" /bin/false + fi } main() { - init_sys - wget_fun - install_elasticsearch - install_filebeat - install_logstash - install_kibana - echo -e "\033[32m Checking Elasticsearch...\033[0m" - sleep 20 - check :9200 "elasticsearch" - echo -e "\033[32m Checking Logstash...\033[0m" - sleep 2 - check ":9600" "logstash" - echo -e "\033[32m Checking Kibana...\033[0m" - sleep 2 - check ":5601" "kibana" - action "ELK install is success!" /bin/true - echo "url:http://$IP:5601" + init_sys + wget_fun + install_elasticsearch + install_filebeat + install_logstash + install_kibana + echo -e "\033[32m Checking Elasticsearch...\033[0m" + sleep 20 + check :9200 "elasticsearch" + echo -e "\033[32m Checking Logstash...\033[0m" + sleep 2 + check ":9600" "logstash" + echo -e "\033[32m Checking Kibana...\033[0m" + sleep 2 + check ":5601" "kibana" + action "ELK install is success!" /bin/true + echo "url:http://$IP:5601" } main diff --git a/codes/linux/tool/Cpu_Limit.sh b/codes/linux/tool/Cpu_Limit.sh index 5e082e0b..2cfa6de0 100644 --- a/codes/linux/tool/Cpu_Limit.sh +++ b/codes/linux/tool/Cpu_Limit.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # auth:kaliarch # func:sys info check @@ -21,34 +21,34 @@ PIDARG=$(ps -aux | awk -v CPU=${PEC_CPU} '{if($3 > CPU) print $2}') CPULIMITCMD=$(which cpulimit) install_cpulimit() { - [ ! -d /tmp ] && mkdir /tmp || cd /tmp - wget -c https://github.com/opsengine/cpulimit/archive/v0.2.tar.gz - tar -zxf v0.2.tar.gz - cd cpulimit-0.2 && make - [ $? -eq 0 ] && cp src/cpulimit /usr/bin/ + [ ! -d /tmp ] && mkdir /tmp || cd /tmp + wget -c https://github.com/opsengine/cpulimit/archive/v0.2.tar.gz + tar -zxf v0.2.tar.gz + cd cpulimit-0.2 && make + [ $? -eq 0 ] && cp src/cpulimit /usr/bin/ } do_cpulimit() { - [ ! -d ${LOG_DIR} ] && mkdir -p ${LOG_DIR} - for i in ${PIDARG}; - do - MSG=$(ps -aux | awk -v pid=$i '{if($2 == pid) print $0}') - echo ${MSG} - [ ! -d /tmp ] && mkdir /tmp || cd /tmp - nohup ${CPULIMITCMD} -p $i -l ${LIMIT_CPU} & - echo "$(date) -- ${MSG}" >> ${LOG_DIR}$(date +%F).log - done + [ ! -d ${LOG_DIR} ] && mkdir -p ${LOG_DIR} + for i in ${PIDARG}; + do + MSG=$(ps -aux | awk -v pid=$i '{if($2 == pid) print $0}') + echo ${MSG} + [ ! -d /tmp ] && mkdir /tmp || cd /tmp + nohup ${CPULIMITCMD} -p $i -l ${LIMIT_CPU} & + echo "$(date) -- ${MSG}" >> ${LOG_DIR}$(date +%F).log + done } main() { - hash cpulimit - if [ $? -eq 0 ]; then - do_cpulimit - else - install_cpulimit && do_cpulimit - fi + hash cpulimit + if [ $? -eq 0 ]; then + do_cpulimit + else + install_cpulimit && do_cpulimit + fi } main diff --git a/codes/linux/tool/Custom_Rm.sh b/codes/linux/tool/Custom_Rm.sh index 8b948ee7..be19d78b 100644 --- a/codes/linux/tool/Custom_Rm.sh +++ b/codes/linux/tool/Custom_Rm.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # function:自定义rm命令,每天晚上定时清理 diff --git a/codes/linux/tool/Daily_Archive.sh b/codes/linux/tool/Daily_Archive.sh index 9fbaafe4..3d2e7c4a 100644 --- a/codes/linux/tool/Daily_Archive.sh +++ b/codes/linux/tool/Daily_Archive.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Daily_Archive - Archive designated files & directories @@ -23,13 +23,13 @@ DESTINATION=/home/tiandi/archive/$FILE # if [ -f $CONFIG_FILE ] #Make sure the config file still exists then - echo + echo else - echo - echo "$CONFIG_FILE does not exist." - echo "Backup not completed due to missing Configuration file" - echo - exit + echo + echo "$CONFIG_FILE does not exist." + echo "Backup not completed due to missing Configuration file" + echo + exit fi # # Build the names of all the files to backup @@ -41,23 +41,23 @@ read FILE_NAME # Read 1st record # while [ $? -eq 0 ] do - # Make sure the file or directory exists. - if [ -f $FILE_NAME -o -d $FILE_NAME ] - then - # If file exists, add its name to the lists - FILE_LIST="$FILE_LIST $FILE_NAME" - else - # If file doesn't exist, issue warning - echo - echo "$FILE_NAME, does not exist." - echo "Obviously, I will not include it in this archive." - echo "It is listed on line $FILE_NO of the config file." - echo "Continuing to build archive file." - echo - fi - # - FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one - read FILE_NAME # Read next record. + # Make sure the file or directory exists. + if [ -f $FILE_NAME -o -d $FILE_NAME ] + then + # If file exists, add its name to the lists + FILE_LIST="$FILE_LIST $FILE_NAME" + else + # If file doesn't exist, issue warning + echo + echo "$FILE_NAME, does not exist." + echo "Obviously, I will not include it in this archive." + echo "It is listed on line $FILE_NO of the config file." + echo "Continuing to build archive file." + echo + fi + # + FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one + read FILE_NAME # Read next record. done ########################################################### # diff --git a/codes/linux/tool/Hourly_Archive.sh b/codes/linux/tool/Hourly_Archive.sh index d29a08eb..3220fcf1 100644 --- a/codes/linux/tool/Hourly_Archive.sh +++ b/codes/linux/tool/Hourly_Archive.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Hourly_Archive - Every hour create an archive @@ -30,13 +30,13 @@ DESTINATION=$BASEDEST/$MONTH/$DAY/archive$TIME.tar.gz # if [ -f $CONFIG_FILE ] #Make sure the config file still exists then - echo + echo else - echo - echo "$CONFIG_FILE does not exist." - echo "Backup not completed due to missing Configuration file" - echo - exit + echo + echo "$CONFIG_FILE does not exist." + echo "Backup not completed due to missing Configuration file" + echo + exit fi # # Build the names of all the files to backup @@ -48,23 +48,23 @@ read FILE_NAME # Read 1st record # while [ $? -eq 0 ] do - # Make sure the file or directory exists. - if [ -f $FILE_NAME -o -d $FILE_NAME ] - then - # If file exists, add its name to the lists - FILE_LIST="$FILE_LIST $FILE_NAME" - else - # If file doesn't exist, issue warning - echo - echo "$FILE_NAME, does not exist." - echo "Obviously, I will not include it in this archive." - echo "It is listed on line $FILE_NO of the config file." - echo "Continuing to build archive file." - echo - fi - # - FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one - read FILE_NAME # Read next record. + # Make sure the file or directory exists. + if [ -f $FILE_NAME -o -d $FILE_NAME ] + then + # If file exists, add its name to the lists + FILE_LIST="$FILE_LIST $FILE_NAME" + else + # If file doesn't exist, issue warning + echo + echo "$FILE_NAME, does not exist." + echo "Obviously, I will not include it in this archive." + echo "It is listed on line $FILE_NO of the config file." + echo "Continuing to build archive file." + echo + fi + # + FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one + read FILE_NAME # Read next record. done ########################################################### # diff --git a/codes/linux/tool/gitcheck b/codes/linux/tool/gitcheck index c650199b..3f21766e 100644 --- a/codes/linux/tool/gitcheck +++ b/codes/linux/tool/gitcheck @@ -8,14 +8,14 @@ # MIT license if [ -t 1 ]; then - # Our output is not being redirected, so we can use colors. - C_RED="\033[1;31m" - C_GREEN="\033[1;32m" - C_YELLOW="\033[1;33m" - C_BLUE="\033[1;34m" - C_PURPLE="\033[1;35m" - C_CYAN="\033[1;36m" - C_RESET="$(tput sgr0)" + # Our output is not being redirected, so we can use colors. + C_RED="\033[1;31m" + C_GREEN="\033[1;32m" + C_YELLOW="\033[1;33m" + C_BLUE="\033[1;34m" + C_PURPLE="\033[1;35m" + C_CYAN="\033[1;36m" + C_RESET="$(tput sgr0)" fi C_OK="$C_GREEN" @@ -29,8 +29,8 @@ C_STASHES="$C_YELLOW" DEBUG=0 -usage () { - cat << EOF >&2 +usage() { + cat << EOF >&2 Usage: $0 [-w] [-e] [-f] [--no-X] [DIR] [DEPTH=2] @@ -64,183 +64,185 @@ NO_UNTRACKED=0 NO_STASHES=0 while [ \! -z "$1" ]; do - # Stop reading when we've run out of options. - [ "$(echo "$1" | cut -c 1)" != "-" ] && break - - if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then - usage - exit 1 - fi - if [ "$1" = "-w" ]; then - WARN_NOT_REPO=1 - fi - if [ "$1" = "-e" ]; then - EXCLUDE_OK=1 - fi - if [ "$1" = "-f" ]; then - DO_FETCH=1 - fi - if [ "$1" = "--no-push" ]; then - NO_PUSH=1 - fi - if [ "$1" = "--no-pull" ]; then - NO_PULL=1 - fi - if [ "$1" = "--no-upstream" ]; then - NO_UPSTREAM=1 - fi - if [ "$1" = "--no-uncommitted" ]; then - NO_UNCOMMITTED=1 - fi - if [ "$1" = "--no-untracked" ]; then - NO_UNTRACKED=1 - fi - if [ "$1" = "--no-stashes" ]; then - NO_STASHES=1 - fi - - shift + # Stop reading when we've run out of options. + [ "$(echo "$1" | cut -c 1)" != "-" ] && break + + if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + usage + exit 1 + fi + if [ "$1" = "-w" ]; then + WARN_NOT_REPO=1 + fi + if [ "$1" = "-e" ]; then + EXCLUDE_OK=1 + fi + if [ "$1" = "-f" ]; then + DO_FETCH=1 + fi + if [ "$1" = "--no-push" ]; then + NO_PUSH=1 + fi + if [ "$1" = "--no-pull" ]; then + NO_PULL=1 + fi + if [ "$1" = "--no-upstream" ]; then + NO_UPSTREAM=1 + fi + if [ "$1" = "--no-uncommitted" ]; then + NO_UNCOMMITTED=1 + fi + if [ "$1" = "--no-untracked" ]; then + NO_UNTRACKED=1 + fi + if [ "$1" = "--no-stashes" ]; then + NO_STASHES=1 + fi + + shift done if [ -z "$1" ]; then - ROOT_DIR="." + ROOT_DIR="." else - ROOT_DIR="$1" + ROOT_DIR="$1" fi if [ -z "$2" ]; then - DEPTH=2 + DEPTH=2 else - DEPTH="$2" + DEPTH="$2" fi # Find all .git dirs, up to DEPTH levels deep find -L "$ROOT_DIR" -maxdepth "$DEPTH" -type d | while read -r PROJ_DIR do - GIT_DIR="$PROJ_DIR/.git" - - # If this dir is not a repo, and WARN_NOT_REPO is 1, tell the user. - if [ \! -d "$GIT_DIR" ]; then - if [ "$WARN_NOT_REPO" -eq 1 ] && [ "$PROJ_DIR" != "." ]; then - printf "${PROJ_DIR}: not a git repo\n" - fi - continue - fi - - [ $DEBUG -eq 1 ] && echo "${PROJ_DIR}" - - # Check if repo is locked - if [ -f "$GIT_DIR/index.lock" ]; then - printf "${PROJ_DIR}: ${C_LOCKED}Locked. Skipping.${C_RESET}\n" - continue - fi - - # Do a 'git fetch' if requested - if [ "$DO_FETCH" -eq 1 ]; then - git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" fetch -q >/dev/null - fi - - # Refresh the index, or we might get wrong results. - git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" update-index -q --refresh >/dev/null 2>&1 - - # Find all remote branches that have been checked out and figure out if - # they need a push or pull. We do this with various tests and put the name - # of the branches in NEEDS_XXXX, seperated by newlines. After we're done, - # we remove duplicates from NEEDS_XXX. - NEEDS_PUSH_BRANCHES="" - NEEDS_PULL_BRANCHES="" - NEEDS_UPSTREAM_BRANCHES="" - - for REF_HEAD in $(cd "$GIT_DIR/refs/heads" && find . -type 'f' | sed "s/^\.\///"); do - # Check if this branch is tracking an upstream (local/remote branch) - UPSTREAM=$(git --git-dir "$GIT_DIR" rev-parse --abbrev-ref --symbolic-full-name "$REF_HEAD@{u}" 2>/dev/null) - EXIT_CODE="$?" - if [ "$EXIT_CODE" -eq 0 ]; then - # Branch is tracking a remote branch. Find out how much behind / - # ahead it is of that remote branch. - CNT_AHEAD_BEHIND=$(git --git-dir "$GIT_DIR" rev-list --left-right --count "$REF_HEAD...$UPSTREAM") - CNT_AHEAD=$(echo "$CNT_AHEAD_BEHIND" | awk '{ print $1 }') - CNT_BEHIND=$(echo "$CNT_AHEAD_BEHIND" | awk '{ print $2 }') - - [ $DEBUG -eq 1 ] && echo "CNT_AHEAD_BEHIND: $CNT_AHEAD_BEHIND" - [ $DEBUG -eq 1 ] && echo "CNT_AHEAD: $CNT_AHEAD" - [ $DEBUG -eq 1 ] && echo "CNT_BEHIND: $CNT_BEHIND" - - if [ "$CNT_AHEAD" -gt 0 ]; then - NEEDS_PUSH_BRANCHES="${NEEDS_PUSH_BRANCHES}\n$REF_HEAD" - fi - if [ "$CNT_BEHIND" -gt 0 ]; then - NEEDS_PULL_BRANCHES="${NEEDS_PULL_BRANCHES}\n$REF_HEAD" - fi - - # Check if this branch is a branch off another branch. and if it needs - # to be updated. - REV_LOCAL=$(git --git-dir "$GIT_DIR" rev-parse --verify "$REF_HEAD" 2>/dev/null) - REV_REMOTE=$(git --git-dir "$GIT_DIR" rev-parse --verify "$UPSTREAM" 2>/dev/null) - REV_BASE=$(git --git-dir "$GIT_DIR" merge-base "$REF_HEAD" "$UPSTREAM" 2>/dev/null) - - [ $DEBUG -eq 1 ] && echo "REV_LOCAL: $REV_LOCAL" - [ $DEBUG -eq 1 ] && echo "REV_REMOTE: $REV_REMOTE" - [ $DEBUG -eq 1 ] && echo "REV_BASE: $REV_BASE" - - if [ "$REV_LOCAL" = "$REV_REMOTE" ]; then - : # NOOP - else - if [ "$REV_LOCAL" = "$REV_BASE" ]; then - NEEDS_PULL_BRANCHES="${NEEDS_PULL_BRANCHES}\n$REF_HEAD" - fi - if [ "$REV_REMOTE" = "$REV_BASE" ]; then - NEEDS_PUSH_BRANCHES="${NEEDS_PUSH_BRANCHES}\n$REF_HEAD" - fi - fi - else - # Branch does not have an upstream (local/remote branch). - NEEDS_UPSTREAM_BRANCHES="${NEEDS_UPSTREAM_BRANCHES}\n$REF_HEAD" - fi - - done - - # Remove duplicates from NEEDS_XXXX and make comma-seperated - NEEDS_PUSH_BRANCHES=$(printf "$NEEDS_PUSH_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") - NEEDS_PULL_BRANCHES=$(printf "$NEEDS_PULL_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") - NEEDS_UPSTREAM_BRANCHES=$(printf "$NEEDS_UPSTREAM_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") - - # Find out if there are unstaged, uncommitted or untracked changes - UNSTAGED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-index --quiet HEAD -- 2>/dev/null; echo $?) - UNCOMMITTED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-files --quiet --ignore-submodules --; echo $?) - UNTRACKED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" ls-files --exclude-standard --others) - cd "$(dirname "$GIT_DIR")" || exit - STASHES=$(git stash list | wc -l) - cd "$OLDPWD" || exit - - # Build up the status string - IS_OK=0 # 0 = Repo needs something, 1 = Repo needs nothing ('ok') - STATUS_NEEDS="" - if [ \! -z "$NEEDS_PUSH_BRANCHES" ] && [ "$NO_PUSH" -eq 0 ]; then - STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_PUSH}Needs push ($NEEDS_PUSH_BRANCHES)${C_RESET} " - fi - if [ \! -z "$NEEDS_PULL_BRANCHES" ] && [ "$NO_PULL" -eq 0 ]; then - STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_PULL}Needs pull ($NEEDS_PULL_BRANCHES)${C_RESET} " - fi - if [ \! -z "$NEEDS_UPSTREAM_BRANCHES" ] && [ "$NO_UPSTREAM" -eq 0 ]; then - STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_UPSTREAM}Needs upstream ($NEEDS_UPSTREAM_BRANCHES)${C_RESET} " - fi - if [ "$UNSTAGED" -ne 0 ] || [ "$UNCOMMITTED" -ne 0 ] && [ "$NO_UNCOMMITTED" -eq 0 ]; then - STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_COMMIT}Uncommitted changes${C_RESET} " - fi - if [ "$UNTRACKED" != "" ] && [ "$NO_UNTRACKED" -eq 0 ]; then - STATUS_NEEDS="${STATUS_NEEDS}${C_UNTRACKED}Untracked files${C_RESET} " - fi - if [ "$STASHES" -ne 0 ] && [ "$NO_STASHES" -eq 0 ]; then - STATUS_NEEDS="${STATUS_NEEDS}${C_STASHES}$STASHES stashes${C_RESET} " - fi - if [ "$STATUS_NEEDS" = "" ]; then - IS_OK=1 - STATUS_NEEDS="${STATUS_NEEDS}${C_OK}ok${C_RESET} " - fi - - # Print the output, unless repo is 'ok' and -e was specified - if [ "$IS_OK" -ne 1 ] || [ "$EXCLUDE_OK" -ne 1 ]; then - printf "${PROJ_DIR}: $STATUS_NEEDS\n" - fi + GIT_DIR="$PROJ_DIR/.git" + + # If this dir is not a repo, and WARN_NOT_REPO is 1, tell the user. + if [ \! -d "$GIT_DIR" ]; then + if [ "$WARN_NOT_REPO" -eq 1 ] && [ "$PROJ_DIR" != "." ]; then + printf "${PROJ_DIR}: not a git repo\n" + fi + continue + fi + + [ $DEBUG -eq 1 ] && echo "${PROJ_DIR}" + + # Check if repo is locked + if [ -f "$GIT_DIR/index.lock" ]; then + printf "${PROJ_DIR}: ${C_LOCKED}Locked. Skipping.${C_RESET}\n" + continue + fi + + # Do a 'git fetch' if requested + if [ "$DO_FETCH" -eq 1 ]; then + git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" fetch -q > /dev/null + fi + + # Refresh the index, or we might get wrong results. + git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" update-index -q --refresh > /dev/null 2>&1 + + # Find all remote branches that have been checked out and figure out if + # they need a push or pull. We do this with various tests and put the name + # of the branches in NEEDS_XXXX, seperated by newlines. After we're done, + # we remove duplicates from NEEDS_XXX. + NEEDS_PUSH_BRANCHES="" + NEEDS_PULL_BRANCHES="" + NEEDS_UPSTREAM_BRANCHES="" + + for REF_HEAD in $(cd "$GIT_DIR/refs/heads" && find . -type 'f' | sed "s/^\.\///"); do + # Check if this branch is tracking an upstream (local/remote branch) + UPSTREAM=$(git --git-dir "$GIT_DIR" rev-parse --abbrev-ref --symbolic-full-name "$REF_HEAD@{u}" 2> /dev/null) + EXIT_CODE="$?" + if [ "$EXIT_CODE" -eq 0 ]; then + # Branch is tracking a remote branch. Find out how much behind / + # ahead it is of that remote branch. + CNT_AHEAD_BEHIND=$(git --git-dir "$GIT_DIR" rev-list --left-right --count "$REF_HEAD...$UPSTREAM") + CNT_AHEAD=$(echo "$CNT_AHEAD_BEHIND" | awk '{ print $1 }') + CNT_BEHIND=$(echo "$CNT_AHEAD_BEHIND" | awk '{ print $2 }') + + [ $DEBUG -eq 1 ] && echo "CNT_AHEAD_BEHIND: $CNT_AHEAD_BEHIND" + [ $DEBUG -eq 1 ] && echo "CNT_AHEAD: $CNT_AHEAD" + [ $DEBUG -eq 1 ] && echo "CNT_BEHIND: $CNT_BEHIND" + + if [ "$CNT_AHEAD" -gt 0 ]; then + NEEDS_PUSH_BRANCHES="${NEEDS_PUSH_BRANCHES}\n$REF_HEAD" + fi + if [ "$CNT_BEHIND" -gt 0 ]; then + NEEDS_PULL_BRANCHES="${NEEDS_PULL_BRANCHES}\n$REF_HEAD" + fi + + # Check if this branch is a branch off another branch. and if it needs + # to be updated. + REV_LOCAL=$(git --git-dir "$GIT_DIR" rev-parse --verify "$REF_HEAD" 2> /dev/null) + REV_REMOTE=$(git --git-dir "$GIT_DIR" rev-parse --verify "$UPSTREAM" 2> /dev/null) + REV_BASE=$(git --git-dir "$GIT_DIR" merge-base "$REF_HEAD" "$UPSTREAM" 2> /dev/null) + + [ $DEBUG -eq 1 ] && echo "REV_LOCAL: $REV_LOCAL" + [ $DEBUG -eq 1 ] && echo "REV_REMOTE: $REV_REMOTE" + [ $DEBUG -eq 1 ] && echo "REV_BASE: $REV_BASE" + + if [ "$REV_LOCAL" = "$REV_REMOTE" ]; then + : # NOOP + else + if [ "$REV_LOCAL" = "$REV_BASE" ]; then + NEEDS_PULL_BRANCHES="${NEEDS_PULL_BRANCHES}\n$REF_HEAD" + fi + if [ "$REV_REMOTE" = "$REV_BASE" ]; then + NEEDS_PUSH_BRANCHES="${NEEDS_PUSH_BRANCHES}\n$REF_HEAD" + fi + fi + else + # Branch does not have an upstream (local/remote branch). + NEEDS_UPSTREAM_BRANCHES="${NEEDS_UPSTREAM_BRANCHES}\n$REF_HEAD" + fi + + done + + # Remove duplicates from NEEDS_XXXX and make comma-seperated + NEEDS_PUSH_BRANCHES=$(printf "$NEEDS_PUSH_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") + NEEDS_PULL_BRANCHES=$(printf "$NEEDS_PULL_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") + NEEDS_UPSTREAM_BRANCHES=$(printf "$NEEDS_UPSTREAM_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") + + # Find out if there are unstaged, uncommitted or untracked changes + UNSTAGED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-index --quiet HEAD -- 2> /dev/null; + echo $?) + UNCOMMITTED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-files --quiet --ignore-submodules --; + echo $?) + UNTRACKED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" ls-files --exclude-standard --others) + cd "$(dirname "$GIT_DIR")" || exit + STASHES=$(git stash list | wc -l) + cd "$OLDPWD" || exit + + # Build up the status string + IS_OK=0 # 0 = Repo needs something, 1 = Repo needs nothing ('ok') + STATUS_NEEDS="" + if [ \! -z "$NEEDS_PUSH_BRANCHES" ] && [ "$NO_PUSH" -eq 0 ]; then + STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_PUSH}Needs push ($NEEDS_PUSH_BRANCHES)${C_RESET} " + fi + if [ \! -z "$NEEDS_PULL_BRANCHES" ] && [ "$NO_PULL" -eq 0 ]; then + STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_PULL}Needs pull ($NEEDS_PULL_BRANCHES)${C_RESET} " + fi + if [ \! -z "$NEEDS_UPSTREAM_BRANCHES" ] && [ "$NO_UPSTREAM" -eq 0 ]; then + STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_UPSTREAM}Needs upstream ($NEEDS_UPSTREAM_BRANCHES)${C_RESET} " + fi + if [ "$UNSTAGED" -ne 0 ] || [ "$UNCOMMITTED" -ne 0 ] && [ "$NO_UNCOMMITTED" -eq 0 ]; then + STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_COMMIT}Uncommitted changes${C_RESET} " + fi + if [ "$UNTRACKED" != "" ] && [ "$NO_UNTRACKED" -eq 0 ]; then + STATUS_NEEDS="${STATUS_NEEDS}${C_UNTRACKED}Untracked files${C_RESET} " + fi + if [ "$STASHES" -ne 0 ] && [ "$NO_STASHES" -eq 0 ]; then + STATUS_NEEDS="${STATUS_NEEDS}${C_STASHES}$STASHES stashes${C_RESET} " + fi + if [ "$STATUS_NEEDS" = "" ]; then + IS_OK=1 + STATUS_NEEDS="${STATUS_NEEDS}${C_OK}ok${C_RESET} " + fi + + # Print the output, unless repo is 'ok' and -e was specified + if [ "$IS_OK" -ne 1 ] || [ "$EXCLUDE_OK" -ne 1 ]; then + printf "${PROJ_DIR}: $STATUS_NEEDS\n" + fi done diff --git "a/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" "b/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" index 2da94f74..a7db9303 100644 --- "a/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" +++ "b/codes/linux/tool/\345\210\240\351\231\244\347\224\250\346\210\267\350\204\232\346\234\254.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Delete_User - Automates the 4 steps to remove an account @@ -9,81 +9,81 @@ # ################################################################# function get_answer { - # - unset ANSWER - ASK_COUNT=0 - # - while [ -z "$ANSWER" ] # while no answer is given, keep asking - do - ASK_COUNT=$[ $ASK_COUNT + 1 ] - # - case $ASK_COUNT in - # If user gives no answer in time allowed - 2) - echo - echo "Please answer the question." - echo - ;; - 3) - echo - echo "One last try... please answer the question." - echo - ;; - 4) - echo - echo "Since you refuse to answer the question..." - echo "exiting program." - echo - # - exit - ;; - esac - # - echo - # - if [ -n "$LINE2" ] - then - echo $LINE1 # Print 2 lines - echo -e $LINE2" \c" - else - # Print 1 line - echo -e $LINE1" \c" - fi - # - # Allow 60 seconds to answer before time-out - read -t 60 ANSWER - done - # - # Do a little variable clean-up - # - unset LINE1 - unset LINE2 - # + # + unset ANSWER + ASK_COUNT=0 + # + while [ -z "$ANSWER" ] # while no answer is given, keep asking + do + ASK_COUNT=$[ $ASK_COUNT + 1 ] + # + case $ASK_COUNT in + # If user gives no answer in time allowed + 2) + echo + echo "Please answer the question." + echo + ;; + 3) + echo + echo "One last try... please answer the question." + echo + ;; + 4) + echo + echo "Since you refuse to answer the question..." + echo "exiting program." + echo + # + exit + ;; + esac + # + echo + # + if [ -n "$LINE2" ] + then + echo $LINE1 # Print 2 lines + echo -e $LINE2" \c" + else + # Print 1 line + echo -e $LINE1" \c" + fi + # + # Allow 60 seconds to answer before time-out + read -t 60 ANSWER + done + # + # Do a little variable clean-up + # + unset LINE1 + unset LINE2 + # } #end of get_answer function # ################################################################# function process_answer { - # - case $ANSWER in - y | Y | YES | yes | yEs | yeS | YEs | yES) - # If user answers "yes".do nothing. - ;; - *) - # If user answers anything but "yes", exit script - echo - echo $EXIT_LINE1 - echo $EXIT_LINE2 - echo - exit - ;; - esac - # - # Do a little variable clean-up - unset EXIT_LINE1 - unset EXIT_LINE2 - # + # + case $ANSWER in + y | Y | YES | yes | yEs | yeS | YEs | yES) + # If user answers "yes".do nothing. + ;; + *) + # If user answers anything but "yes", exit script + echo + echo $EXIT_LINE1 + echo $EXIT_LINE2 + echo + exit + ;; + esac + # + # Do a little variable clean-up + unset EXIT_LINE1 + unset EXIT_LINE2 + # } #End of process_answer function @@ -117,11 +117,11 @@ USER_ACCOUNT_RECORD=$(cat /etc/passwd | grep -w $USER_ACCOUNT) # if [ $? -eq 1 ] # If the account is not found, exit script then - echo - echo "Account, $USER_ACCOUNT, not found." - echo "Leaving the script..." - echo - exit + echo + echo "Account, $USER_ACCOUNT, not found." + echo "Leaving the script..." + echo + exit fi # echo @@ -153,56 +153,56 @@ echo ps -u $USER_ACCOUNT #List the processes running # case $? in - 1) # No processes running for this User Account - # - echo "There are no processes for this account currently running." - echo - ;; - 0) # Processes running for this User Account. - # Ask Script User if wants us to kill the processes. - # - unset ANSWER # I think this line is not needed - LINE1="Would you like me to kill the process(es)? [y/n]:" - get_answer - # - case $ANSWER in - y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) # if user answer "yes", - #kill User Account processes - # - echo - # - # Clean-up temp file upon signals - # - trap "rm$USER_ACCOUNT_Running_Process.rpt" SIGTERM SIGINT SIGQUIT - # - # List user processes running - ps -u $USER_ACCOUNT > $USER_ACCOUNT_Running_Process.rpt - # - exec < $USER_ACCOUNT_Running_Process.rpt # Make report Std Input - # - read USER_PROCESS_REC # First record will be blank - read USER_PROCESS_REC - # - while [ $? -eq 0 ] - do - # obtain PID - USER_PID=$(echo $USER_PROCESS_REC | cut -d " " -f1) - kill -9 $USER_PID - echo "Killed process $USER_PID" - read USER_PROCESS_REC - done - # - echo - # - rm $USER_ACCOUNT_Running_Process.rpt # Remove temp report - ;; - *) # If user answers anything but "yes", do not kill. - echo - echo "Will not kill the process(es)." - echo - ;; - esac - ;; + 1) # No processes running for this User Account + # + echo "There are no processes for this account currently running." + echo + ;; + 0) # Processes running for this User Account. + # Ask Script User if wants us to kill the processes. + # + unset ANSWER # I think this line is not needed + LINE1="Would you like me to kill the process(es)? [y/n]:" + get_answer + # + case $ANSWER in + y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) # if user answer "yes", + #kill User Account processes + # + echo + # + # Clean-up temp file upon signals + # + trap "rm$USER_ACCOUNT_Running_Process.rpt" SIGTERM SIGINT SIGQUIT + # + # List user processes running + ps -u $USER_ACCOUNT > $USER_ACCOUNT_Running_Process.rpt + # + exec < $USER_ACCOUNT_Running_Process.rpt # Make report Std Input + # + read USER_PROCESS_REC # First record will be blank + read USER_PROCESS_REC + # + while [ $? -eq 0 ] + do + # obtain PID + USER_PID=$(echo $USER_PROCESS_REC | cut -d " " -f1) + kill -9 $USER_PID + echo "Killed process $USER_PID" + read USER_PROCESS_REC + done + # + echo + # + rm $USER_ACCOUNT_Running_Process.rpt # Remove temp report + ;; + *) # If user answers anything but "yes", do not kill. + echo + echo "Will not kill the process(es)." + echo + ;; + esac + ;; esac ################################################################################### # diff --git "a/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" "b/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" index d3c46ea4..64db7315 100644 --- "a/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" +++ "b/codes/linux/tool/\346\237\245\347\234\213\346\214\207\345\256\232\347\233\256\345\275\225\347\243\201\347\233\230\344\275\277\347\224\250\346\203\205\345\206\265.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #DIRS="/var/log /home /opt" @@ -9,11 +9,11 @@ echo "Top Ten Disk Space Usage" echo "for ${DIRS} Directories" for DIR in ${DIRS} do - echo "" - echo "The ${DIR} Directory:" - du -S ${DIR} 2> /dev/null | - sort -rn | - sed '{11,$D; =}' | - sed 'N; s/\n/ /' | - gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}' + echo "" + echo "The ${DIR} Directory:" + du -S ${DIR} 2> /dev/null | + sort -rn | + sed '{11,$D; =}' | + sed 'N; s/\n/ /' | + gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}' done diff --git a/codes/shell/README.md b/codes/shell/README.md new file mode 100644 index 00000000..541bda19 --- /dev/null +++ b/codes/shell/README.md @@ -0,0 +1,8 @@ +# Shell 脚本大全 + +> **Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 +> +> 当您面临以下问题时,**Shell 脚本大全** 也许可以给你一些借鉴: +> +> - 你想要执行某个操作,却不知 Shell 命令如何写 +> - 你想要快速掌握 Shell 基本用法 diff --git a/codes/shell/demos/README.md b/codes/shell/demos/README.md deleted file mode 100644 index 7f6702e6..00000000 --- a/codes/shell/demos/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Shell 示例源码 - -> 本目录的代码是和 『[一篇文章让你彻底掌握 shell 语言](https://github.com/dunwu/linux-tutorial/blob/master/docs/linux/scripts/shell.md)』 相配套的示例代码。 diff --git a/codes/shell/demos/array-demo.sh b/codes/shell/demos/array-demo.sh deleted file mode 100644 index 5c412e2c..00000000 --- a/codes/shell/demos/array-demo.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -# 创建数组 -nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 ) -colors=( red yellow "dark blue" ) - -# 访问数组的单个元素 -echo ${nums[1]} -# Output: 1 - -# 访问数组的所有元素 -echo ${colors[*]} -# Output: red yellow dark blue - -echo ${colors[@]} -# Output: red yellow dark blue - -printf "+ %s\n" ${colors[*]} -# Output: -# + red -# + yellow -# + dark -# + blue - -printf "+ %s\n" "${colors[*]}" -# Output: -# + red yellow dark blue - -printf "+ %s\n" "${colors[@]}" -# Output: -# + red -# + yellow -# + dark blue - -# 访问数组的部分元素 -echo ${nums[@]:0:2} -# Output: -# 0 1 - -# 访问数组长度 -echo ${#nums[*]} -# Output: -# 3 - -# 向数组中添加元素 -colors=( white "${colors[@]}" green black ) -echo ${colors[@]} -# Output: -# white red yellow dark blue green black - -# 从数组中删除元素 -unset nums[ 0 ] -echo ${nums[@]} -# Output: -# 1 2 diff --git a/codes/shell/demos/function/function-demo.sh b/codes/shell/demos/function/function-demo.sh deleted file mode 100644 index 40584696..00000000 --- a/codes/shell/demos/function/function-demo.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -calc() { - PS3="choose the oper: " - select oper in + - \* / # 生成操作符选择菜单 - do - echo -n "enter first num: " && read x # 读取输入参数 - echo -n "enter second num: " && read y # 读取输入参数 - exec - case ${oper} in - "+") - return $((${x} + ${y})) - ;; - "-") - return $((${x} - ${y})) - ;; - "*") - return $((${x} * ${y})) - ;; - "/") - return $((${x} / ${y})) - ;; - *) - echo "${oper} is not support!" - return 0 - ;; - esac - break - done -} - -calc -echo "the result is: $?" # $? 获取 calc 函数返回值 -# $ ./function-demo.sh -# 1) + -# 2) - -# 3) * -# 4) / -# choose the oper: 3 -# enter first num: 10 -# enter second num: 10 -# the result is: 100 diff --git a/codes/shell/demos/function/function-demo2.sh b/codes/shell/demos/function/function-demo2.sh deleted file mode 100644 index 9e5f40c7..00000000 --- a/codes/shell/demos/function/function-demo2.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -x=0 -if [[ -n $1 ]]; then - echo "第一个参数为:$1" - x=$1 -else - echo "第一个参数为空" -fi - -y=0 -if [[ -n $2 ]]; then - echo "第二个参数为:$2" - y=$2 -else - echo "第二个参数为空" -fi - -paramsFunction() { - echo "函数第一个入参:$1" - echo "函数第二个入参:$2" -} - -paramsFunction ${x} ${y} diff --git a/codes/shell/demos/helloworld.sh b/codes/shell/demos/helloworld.sh deleted file mode 100644 index 9f3f770b..00000000 --- a/codes/shell/demos/helloworld.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo "Hello, world!" diff --git a/codes/shell/demos/operator/operator-demo2.sh b/codes/shell/demos/operator/operator-demo2.sh deleted file mode 100644 index 166c539b..00000000 --- a/codes/shell/demos/operator/operator-demo2.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash - -x=10 -if [[ -n $1 ]]; then - x=$1 -fi - -y=20 -if [[ -n $2 ]]; then - y=$2 -fi - -echo "x=${x}, y=${y}" - -if [[ ${x} -eq ${y} ]]; then - echo "${x} -eq ${y} : x 等于 y" -else - echo "${x} -eq ${y}: x 不等于 y" -fi - -if [[ ${x} -ne ${y} ]]; then - echo "${x} -ne ${y}: x 不等于 y" -else - echo "${x} -ne ${y}: x 等于 y" -fi - -if [[ ${x} -gt ${y} ]]; then - echo "${x} -gt ${y}: x 大于 y" -else - echo "${x} -gt ${y}: x 不大于 y" -fi - -if [[ ${x} -lt ${y} ]]; then - echo "${x} -lt ${y}: x 小于 y" -else - echo "${x} -lt ${y}: x 不小于 y" -fi - -if [[ ${x} -ge ${y} ]]; then - echo "${x} -ge ${y}: x 大于或等于 y" -else - echo "${x} -ge ${y}: x 小于 y" -fi - -if [[ ${x} -le ${y} ]]; then - echo "${x} -le ${y}: x 小于或等于 y" -else - echo "${x} -le ${y}: x 大于 y" -fi - -# Execute: ./operator-demo2.sh -# Output: -# x=10, y=20 -# 10 -eq 20: x 不等于 y -# 10 -ne 20: x 不等于 y -# 10 -gt 20: x 不大于 y -# 10 -lt 20: x 小于 y -# 10 -ge 20: x 小于 y -# 10 -le 20: x 小于或等于 y diff --git a/codes/shell/demos/operator/operator-demo3.sh b/codes/shell/demos/operator/operator-demo3.sh deleted file mode 100644 index e644253b..00000000 --- a/codes/shell/demos/operator/operator-demo3.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -x=10 -if [[ -n $1 ]]; then - x=$1 -fi - -y=20 -if [[ -n $2 ]]; then - y=$2 -fi - -echo "x=${x}, y=${y}" - -if [[ ${x} != ${y} ]]; then - echo "${x} != ${y} : x 不等于 y" -else - echo "${x} != ${y}: x 等于 y" -fi - -if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then - echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" -else - echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" -fi - -if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then - echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" -else - echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" -fi - -if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then - echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" -else - echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" -fi - -# Execute: ./operator-demo3.sh -# Output: -# x=10, y=20 -# 10 != 20 : x 不等于 y -# 10 小于 100 且 20 大于 15 : 返回 true -# 10 小于 100 或 20 大于 100 : 返回 true -# 10 小于 5 或 20 大于 100 : 返回 false diff --git a/codes/shell/demos/operator/operator-demo6.sh b/codes/shell/demos/operator/operator-demo6.sh deleted file mode 100644 index b0a99b41..00000000 --- a/codes/shell/demos/operator/operator-demo6.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash - -file="/etc/hosts" - -if [[ -r ${file} ]]; then - echo "${file} 文件可读" -else - echo "${file} 文件不可读" -fi -if [[ -w ${file} ]]; then - echo "${file} 文件可写" -else - echo "${file} 文件不可写" -fi -if [[ -x ${file} ]]; then - echo "${file} 文件可执行" -else - echo "${file} 文件不可执行" -fi -if [[ -f ${file} ]]; then - echo "${file} 文件为普通文件" -else - echo "${file} 文件为特殊文件" -fi -if [[ -d ${file} ]]; then - echo "${file} 文件是个目录" -else - echo "${file} 文件不是个目录" -fi -if [[ -s ${file} ]]; then - echo "${file} 文件不为空" -else - echo "${file} 文件为空" -fi -if [[ -e ${file} ]]; then - echo "${file} 文件存在" -else - echo "${file} 文件不存在" -fi - -# Execute: ./operator-demo6.sh -# Output:(根据文件的实际情况,输出结果可能不同) -# /etc/hosts 文件可读 -# /etc/hosts 文件可写 -# /etc/hosts 文件不可执行 -# /etc/hosts 文件为普通文件 -# /etc/hosts 文件不是个目录 -# /etc/hosts 文件不为空 -# /etc/hosts 文件存在 diff --git a/codes/shell/demos/statement/break-demo.sh b/codes/shell/demos/statement/break-demo.sh deleted file mode 100644 index a00055bc..00000000 --- a/codes/shell/demos/statement/break-demo.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# 查找 10 以内第一个能整除 2 和 3 的正整数 -i=1 -while [[ ${i} -lt 10 ]]; do - if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then - echo ${i} - break; - fi - i=`expr ${i} + 1` -done -# Output: 6 diff --git a/codes/shell/demos/statement/case-demo.sh b/codes/shell/demos/statement/case-demo.sh deleted file mode 100644 index 29f70676..00000000 --- a/codes/shell/demos/statement/case-demo.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -echo "input param: " $1 $2 $3 - -x=0 -if [[ -n $1 ]]; then - x=$1 -fi - -y=0 -if [[ -n $2 ]]; then - y=$2 -fi - -oper="" -if [[ -n $3 ]]; then - oper=$3 -fi - -exec -case ${oper} in - "+") - val=`expr ${x} + ${y}` - echo "${x} + ${y} = ${val}" - ;; - "-") - val=`expr ${x} - ${y}` - echo "${x} - ${y} = ${val}" - ;; - "*") - val=`expr ${x} \* ${y}` - echo "${x} * ${y} = ${val}" - ;; - "/") - val=`expr ${x} / ${y}` - echo "${x} / ${y} = ${val}" - ;; - *) - echo "Unknown oper!" - ;; -esac diff --git a/codes/shell/demos/statement/continue-demo.sh b/codes/shell/demos/statement/continue-demo.sh deleted file mode 100644 index ef8c2217..00000000 --- a/codes/shell/demos/statement/continue-demo.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -# 打印10以内的奇数 -for (( i = 0; i < 10; i ++ )); do - if [[ $((i % 2)) -eq 0 ]]; then - continue; - fi - echo ${i} -done -# Output: -# 1 -# 3 -# 5 -# 7 -# 9 diff --git a/codes/shell/demos/statement/for-demo.sh b/codes/shell/demos/statement/for-demo.sh deleted file mode 100644 index 43c94a09..00000000 --- a/codes/shell/demos/statement/for-demo.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -################### for 语句 ################### -echo "print 0 to 9" -for (( j = 0; j < 10; j ++ )); do - echo ${j} -done -# Output: -# print 0 to 9 -# 0 -# 1 -# 2 -# 3 -# 4 -# 5 -# 6 -# 7 -# 8 -# 9 - -################### for in 语句 ################### -echo "print 1 to 5" -for i in {1..5}; do - echo ${i}; -done -# Output: -# print 1 to 5 -# 1 -# 2 -# 3 -# 4 -# 5 - -################### for in 语句遍历文件 ################### -DIR=/home/zp -for FILE in ${DIR}/*.sh; do - mv "$FILE" "${DIR}/scripts" -done -# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts diff --git a/codes/shell/demos/statement/if-demo.sh b/codes/shell/demos/statement/if-demo.sh deleted file mode 100644 index 408e32a2..00000000 --- a/codes/shell/demos/statement/if-demo.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -################### if 语句 ################### -# 写成一行 -if [[ 1 -eq 1 ]]; then - echo "1 -eq 1 result is: true"; -fi -# Output: 1 -eq 1 result is: true - -# 写成多行 -if [[ "abc" -eq "abc" ]] -then - echo ""abc" -eq "abc" result is: true" -fi -# Output: abc -eq abc result is: true - -################### if else 语句 ################### -if [[ 2 -ne 1 ]]; then - echo "true" -else - echo "false" -fi -# Output: true - -################### if elif else 语句 ################### -x=10 -y=20 -if [[ ${x} > ${y} ]]; then - echo "${x} > ${y}" -elif [[ ${x} < ${y} ]]; then - echo "${x} < ${y}" -else - echo "${x} = ${y}" -fi -# Output: 10 < 20 diff --git a/codes/shell/demos/statement/until-demo.sh b/codes/shell/demos/statement/until-demo.sh deleted file mode 100644 index 58a0fce5..00000000 --- a/codes/shell/demos/statement/until-demo.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -x=0 -until [[ ${x} -ge 5 ]]; do - echo ${x} - x=`expr ${x} + 1` -done -# Output: -# 0 -# 1 -# 2 -# 3 -# 4 diff --git a/codes/shell/demos/statement/while-demo.sh b/codes/shell/demos/statement/while-demo.sh deleted file mode 100644 index 9492b0a8..00000000 --- a/codes/shell/demos/statement/while-demo.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -### 0到9之间每个数的平方 -x=0 -### x小于10 -while [[ ${x} -lt 10 ]]; do - echo $((x * x)) - x=$((x + 1)) -done -# Output: -# 0 -# 1 -# 4 -# 9 -# 16 -# 25 -# 36 -# 49 -# 64 -# 81 diff --git a/codes/shell/demos/string-demo.sh b/codes/shell/demos/string-demo.sh deleted file mode 100644 index d3030fab..00000000 --- a/codes/shell/demos/string-demo.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -################### 单引号和双引号 ################### -################### 拼接字符串 ################### -# 使用单引号拼接 -name1='white' -str1='hello, '${name1}'' -str2='hello, ${name1}' -echo ${str1}_${str2} -# Output: -# hello, white_hello, ${name1} - -# 使用双引号拼接 -name2="black" -str3="hello, "${name2}"" -str4="hello, ${name2}" -echo ${str3}_${str4} -# Output: -# hello, black_hello, black - -################### 获取字符串长度 ################### -text="12345" -echo "${text} length is: ${#text}" -# Output: -# 12345 length is: 5 - -################### 获取字符串长度 ################### -text="12345" -echo ${text:2:2} -# Output: -# 34 - -################### 查找子字符串 ################### -text="hello" -echo `expr index "${text}" ll` -# Output: -# 3 - -################### 截取关键字左边内容 ################### -full_branch="feature/1.0.0" -branch=`echo ${full_branch#feature/}` -echo "branch is ${branch}" - -################### 截取关键字右边内容 ################### -full_version="0.0.1-SNAPSHOT" -version=`echo ${full_version%-SNAPSHOT}` -echo "version is ${version}" - -################### 判断字符串中是否包含子字符串 ################### -result=$(echo "${str}" | grep "feature/") -if [[ "$result" != "" ]]; then - echo "feature/ 是 ${str} 的子字符串" -else - echo "feature/ 不是 ${str} 的子字符串" -fi diff --git a/codes/shell/demos/variable-demo.sh b/codes/shell/demos/variable-demo.sh deleted file mode 100644 index db59ca7a..00000000 --- a/codes/shell/demos/variable-demo.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -################### 声明变量 ################### -name="world" -echo "hello ${name}" -# Output: hello world - -################### 只读变量 ################### -rword="hello" -echo ${rword} -# Output: hello -readonly rword -# rword="bye" # 如果放开注释,执行时会报错 - -################### 删除变量 ################### -dword="hello" # 声明变量 -echo ${dword} # 输出变量值 -# Output: hello - -unset dword # 删除变量 -echo ${dword} -# Output: (空) diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" "b/codes/shell/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" similarity index 57% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" rename to "codes/shell/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" index 39d40f47..89c92fc0 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" +++ "b/codes/shell/mysql/SQL\346\211\271\345\244\204\347\220\206\346\211\247\350\241\214\350\204\232\346\234\254.sh" @@ -6,7 +6,7 @@ database='test' for f in `ls */*.sql` do - echo ${f}; - mysql -u${user} -p${password} -f ${database} -e "source $f"; + echo ${f}; + mysql -u${user} -p${password} -f ${database} -e "source $f"; done echo 'OK!' diff --git "a/codes/shell/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" "b/codes/shell/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" new file mode 100644 index 00000000..c57f31fc --- /dev/null +++ "b/codes/shell/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# send data to the the table in the MYSQL database + +MYSQL=`which mysql` + +if [ $# -ne 2 ] +then + echo "Usage:mtest2 emplid lastname firstname salary" +else + #脚本变量一定要用双引号,字符串变量使用单引号 + statement=" insert into em_admin values(NULL, '$1', $2)" + $MYSQL emwjs -u test << EOF + $statement +EOF + if [ $? -eq 0 ] + then + echo Data successfully added + else + echo Problem adding data + fi +fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" "b/codes/shell/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" rename to "codes/shell/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" index 93478f6a..acf89698 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" +++ "b/codes/shell/mysql/\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272\346\225\260\346\215\256.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #redirecting SQL output to a variable @@ -6,7 +6,7 @@ MYSQL=`which mysql` dbs=`$MYSQL emwjs -u test -Bse 'show tables;'` for db in $dbs do - echo $db + echo $db done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" "b/codes/shell/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" rename to "codes/shell/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" index 70bccc6c..6a022cb4 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" +++ "b/codes/shell/mysql/\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\345\271\266\345\217\221\351\200\201\345\221\275\344\273\244.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #连接数据库 mysql=`which mysql` diff --git a/codes/shell/demos/echo-demo.sh "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/echo\347\244\272\344\276\213.sh" similarity index 100% rename from codes/shell/demos/echo-demo.sh rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/echo\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" similarity index 88% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" index b40c9aaf..a248af6b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/exit\345\221\275\344\273\244.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #退出状态码,最大为255,超过则进行模运算 #testing the exit status diff --git a/codes/shell/demos/printf-demo.sh "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/printf\347\244\272\344\276\213.sh" similarity index 100% rename from codes/shell/demos/printf-demo.sh rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/printf\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" similarity index 84% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" index 8f8bf0a0..7770a971 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250expr\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #An example of using the expr command diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" similarity index 89% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" index cec71c5c..5ce4000f 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\345\206\205\350\201\224\351\207\215\345\256\232\345\220\221\350\256\241\347\256\227\350\241\250\350\276\276\345\274\217.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash var1=10.45 var2=43.67 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" similarity index 82% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" index ffa0f4d7..6c7870e0 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\344\275\277\347\224\250\346\226\271\346\213\254\345\217\267\346\211\247\350\241\214\346\225\260\345\255\246\350\277\220\347\256\227.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash var1=10 var2=50 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" similarity index 87% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" index ba502933..8fde29ca 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\215\345\274\225\345\217\267\347\232\204\344\275\277\347\224\250.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #using the backtick character 会把反引号里面当作一条命令来执行 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\217\230\351\207\217\344\275\277\347\224\250\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" similarity index 82% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" index c327846f..b868434e 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250bc.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash var1=100 var2=45 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\345\255\227\347\254\246\344\270\262\344\275\277\347\224\250\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\225\260\347\273\204\344\275\277\347\224\250\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" index 4ac3b2f1..d53162b3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\346\227\266\351\227\264\345\222\214\347\231\273\345\275\225\350\200\205.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #This script displays the date and who's logged on diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" similarity index 89% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" index 1c9b8122..03b4eb74 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\230\276\347\244\272\347\263\273\347\273\237\345\217\230\351\207\217\345\222\214\350\275\254\344\271\211\345\255\227\347\254\246.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #display user information from system diff --git a/codes/shell/demos/comment-demo.sh "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\263\250\351\207\212\347\244\272\344\276\213.sh" similarity index 100% rename from codes/shell/demos/comment-demo.sh rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\346\263\250\351\207\212\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" similarity index 83% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" rename to "codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" index bc6d6913..088afd58 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" +++ "b/codes/shell/\345\237\272\346\234\254\350\204\232\346\234\254/\351\200\232\350\277\207\345\217\215\345\274\225\345\217\267\350\216\267\345\276\227\345\275\223\345\211\215\346\227\245\346\234\237\345\271\266\347\224\237\346\210\220\345\224\257\344\270\200\346\226\207\344\273\266\345\220\215.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #copy the /usr/bin directory listing to a log file diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" "b/codes/shell/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" similarity index 68% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" rename to "codes/shell/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" index 44a40495..e070c0d5 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" +++ "b/codes/shell/\346\216\247\345\210\266/\345\256\232\346\227\266\346\211\247\350\241\214\350\204\232\346\234\254.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing the at command diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" "b/codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" similarity index 67% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" rename to "codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" index 61d5d470..91dfad38 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" +++ "b/codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\344\277\241\345\217\267.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing signal trapping @@ -10,7 +10,7 @@ count=1 while [ $count -le 10 ] do - echo "Loop #$count" - sleep 5 - count=$[ $count + 1 ] + echo "Loop #$count" + sleep 5 + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" "b/codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" similarity index 55% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" rename to "codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" index fdccacf6..70abca93 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" +++ "b/codes/shell/\346\216\247\345\210\266/\346\215\225\346\215\211\350\204\232\346\234\254\347\232\204\351\200\200\345\207\272.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # trapping the script exit @@ -7,7 +7,7 @@ trap "echobyebye" EXIT count=1 while [ $count -le 5 ] do - echo "Loop #$count" - sleep 3 - count=$[ $count + 1 ] + echo "Loop #$count" + sleep 3 + count=$[ $count + 1 ] done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" "b/codes/shell/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" similarity index 66% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" rename to "codes/shell/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" index 60a5e7e5..306abdcf 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" +++ "b/codes/shell/\346\216\247\345\210\266/\347\247\273\351\231\244\346\215\225\346\215\211.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # removeing a set trap @@ -7,9 +7,9 @@ trap "echobyebye" EXIT count=1 while [ $count -le 5 ] do - echo "Loop #$count" - sleep 3 - count=$[ $count + 1 ] + echo "Loop #$count" + sleep 3 + count=$[ $count + 1 ] done #移除捕捉 trap - EXIT diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\346\223\215\344\275\234\347\254\246/\345\205\263\347\263\273\350\277\220\347\256\227\347\254\246.sh" similarity index 50% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\346\223\215\344\275\234\347\254\246/\345\205\263\347\263\273\350\277\220\347\256\227\347\254\246.sh" index 166c539b..c3af9f48 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" +++ "b/codes/shell/\346\223\215\344\275\234\347\254\246/\345\205\263\347\263\273\350\277\220\347\256\227\347\254\246.sh" @@ -2,53 +2,52 @@ x=10 if [[ -n $1 ]]; then - x=$1 + x=$1 fi y=20 if [[ -n $2 ]]; then - y=$2 + y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} -eq ${y} ]]; then - echo "${x} -eq ${y} : x 等于 y" + echo "${x} -eq ${y} : x 等于 y" else - echo "${x} -eq ${y}: x 不等于 y" + echo "${x} -eq ${y}: x 不等于 y" fi if [[ ${x} -ne ${y} ]]; then - echo "${x} -ne ${y}: x 不等于 y" + echo "${x} -ne ${y}: x 不等于 y" else - echo "${x} -ne ${y}: x 等于 y" + echo "${x} -ne ${y}: x 等于 y" fi if [[ ${x} -gt ${y} ]]; then - echo "${x} -gt ${y}: x 大于 y" + echo "${x} -gt ${y}: x 大于 y" else - echo "${x} -gt ${y}: x 不大于 y" + echo "${x} -gt ${y}: x 不大于 y" fi if [[ ${x} -lt ${y} ]]; then - echo "${x} -lt ${y}: x 小于 y" + echo "${x} -lt ${y}: x 小于 y" else - echo "${x} -lt ${y}: x 不小于 y" + echo "${x} -lt ${y}: x 不小于 y" fi if [[ ${x} -ge ${y} ]]; then - echo "${x} -ge ${y}: x 大于或等于 y" + echo "${x} -ge ${y}: x 大于或等于 y" else - echo "${x} -ge ${y}: x 小于 y" + echo "${x} -ge ${y}: x 小于 y" fi if [[ ${x} -le ${y} ]]; then - echo "${x} -le ${y}: x 小于或等于 y" + echo "${x} -le ${y}: x 小于或等于 y" else - echo "${x} -le ${y}: x 大于 y" + echo "${x} -le ${y}: x 大于 y" fi -# Execute: ./operator-demo2.sh # Output: # x=10, y=20 # 10 -eq 20: x 不等于 y diff --git a/codes/shell/demos/operator/operator-demo5.sh "b/codes/shell/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\350\277\220\347\256\227\347\254\246.sh" similarity index 50% rename from codes/shell/demos/operator/operator-demo5.sh rename to "codes/shell/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\350\277\220\347\256\227\347\254\246.sh" index 0182f1db..3a3c6475 100644 --- a/codes/shell/demos/operator/operator-demo5.sh +++ "b/codes/shell/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\350\277\220\347\256\227\347\254\246.sh" @@ -1,48 +1,48 @@ + #!/usr/bin/env bash x="abc" if [[ -n $1 ]]; then - x=$1 + x=$1 fi y="xyz" if [[ -n $2 ]]; then - y=$2 + y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} = ${y} ]]; then - echo "${x} = ${y} : x 等于 y" + echo "${x} = ${y} : x 等于 y" else - echo "${x} = ${y}: x 不等于 y" + echo "${x} = ${y}: x 不等于 y" fi if [[ ${x} != ${y} ]]; then - echo "${x} != ${y} : x 不等于 y" + echo "${x} != ${y} : x 不等于 y" else - echo "${x} != ${y}: x 等于 y" + echo "${x} != ${y}: x 等于 y" fi if [[ -z ${x} ]]; then - echo "-z ${x} : 字符串长度为 0" + echo "-z ${x} : 字符串长度为 0" else - echo "-z ${x} : 字符串长度不为 0" + echo "-z ${x} : 字符串长度不为 0" fi if [[ -n "${x}" ]]; then - echo "-n ${x} : 字符串长度不为 0" + echo "-n ${x} : 字符串长度不为 0" else - echo "-n ${x} : 字符串长度为 0" + echo "-n ${x} : 字符串长度为 0" fi if [[ ${x} ]]; then - echo "${x} : 字符串不为空" + echo "${x} : 字符串不为空" else - echo "${x} : 字符串为空" + echo "${x} : 字符串为空" fi -# Execute: ./operator-demo5.sh # Output: # x=abc, y=xyz # abc = xyz: x 不等于 y diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" "b/codes/shell/\346\223\215\344\275\234\347\254\246/\345\270\203\345\260\224\350\277\220\347\256\227\347\254\246.sh" similarity index 50% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" rename to "codes/shell/\346\223\215\344\275\234\347\254\246/\345\270\203\345\260\224\350\277\220\347\256\227\347\254\246.sh" index e644253b..abd0f720 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2132.sh" +++ "b/codes/shell/\346\223\215\344\275\234\347\254\246/\345\270\203\345\260\224\350\277\220\347\256\227\347\254\246.sh" @@ -2,41 +2,40 @@ x=10 if [[ -n $1 ]]; then - x=$1 + x=$1 fi y=20 if [[ -n $2 ]]; then - y=$2 + y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} != ${y} ]]; then - echo "${x} != ${y} : x 不等于 y" + echo "${x} != ${y} : x 不等于 y" else - echo "${x} != ${y}: x 等于 y" + echo "${x} != ${y}: x 等于 y" fi if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then - echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" + echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" else - echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" + echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" fi if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then - echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" + echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" else - echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" + echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" fi if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then - echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" + echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" else - echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" + echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" fi -# Execute: ./operator-demo3.sh # Output: # x=10, y=20 # 10 != 20 : x 不等于 y diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\346\265\213\350\257\225\350\277\220\347\256\227\347\254\246.sh" similarity index 51% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\346\265\213\350\257\225\350\277\220\347\256\227\347\254\246.sh" index b0a99b41..b57d1518 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\347\233\256\345\275\225\345\210\244\346\226\255\344\275\277\347\224\250\347\244\272\344\276\213.sh" +++ "b/codes/shell/\346\223\215\344\275\234\347\254\246/\346\226\207\344\273\266\346\265\213\350\257\225\350\277\220\347\256\227\347\254\246.sh" @@ -3,42 +3,41 @@ file="/etc/hosts" if [[ -r ${file} ]]; then - echo "${file} 文件可读" + echo "${file} 文件可读" else - echo "${file} 文件不可读" + echo "${file} 文件不可读" fi if [[ -w ${file} ]]; then - echo "${file} 文件可写" + echo "${file} 文件可写" else - echo "${file} 文件不可写" + echo "${file} 文件不可写" fi if [[ -x ${file} ]]; then - echo "${file} 文件可执行" + echo "${file} 文件可执行" else - echo "${file} 文件不可执行" + echo "${file} 文件不可执行" fi if [[ -f ${file} ]]; then - echo "${file} 文件为普通文件" + echo "${file} 文件为普通文件" else - echo "${file} 文件为特殊文件" + echo "${file} 文件为特殊文件" fi if [[ -d ${file} ]]; then - echo "${file} 文件是个目录" + echo "${file} 文件是个目录" else - echo "${file} 文件不是个目录" + echo "${file} 文件不是个目录" fi if [[ -s ${file} ]]; then - echo "${file} 文件不为空" + echo "${file} 文件不为空" else - echo "${file} 文件为空" + echo "${file} 文件为空" fi if [[ -e ${file} ]]; then - echo "${file} 文件存在" + echo "${file} 文件存在" else - echo "${file} 文件不存在" + echo "${file} 文件不存在" fi -# Execute: ./operator-demo6.sh # Output:(根据文件的实际情况,输出结果可能不同) # /etc/hosts 文件可读 # /etc/hosts 文件可写 diff --git a/codes/shell/demos/operator/operator-demo.sh "b/codes/shell/\346\223\215\344\275\234\347\254\246/\347\256\227\346\234\257\350\277\220\347\256\227\347\254\246.sh" similarity index 83% rename from codes/shell/demos/operator/operator-demo.sh rename to "codes/shell/\346\223\215\344\275\234\347\254\246/\347\256\227\346\234\257\350\277\220\347\256\227\347\254\246.sh" index 8e62f92c..270f89ff 100644 --- a/codes/shell/demos/operator/operator-demo.sh +++ "b/codes/shell/\346\223\215\344\275\234\347\254\246/\347\256\227\346\234\257\350\277\220\347\256\227\347\254\246.sh" @@ -2,12 +2,12 @@ x=10 if [[ -n $1 ]]; then - x=$1 + x=$1 fi y=20 if [[ -n $2 ]]; then - y=$2 + y=$2 fi echo "x=${x}, y=${y}" @@ -28,13 +28,13 @@ val=`expr ${y} % ${x}` echo "${y} % ${x} = $val" if [[ ${x} == ${y} ]]; then - echo "${x} = ${y}" + echo "${x} = ${y}" fi if [[ ${x} != ${y} ]]; then - echo "${x} != ${y}" + echo "${x} != ${y}" fi -# Execute: ./operator-demo.sh +# Execute: ./算术运算符.sh # Output: # x=10, y=20 # 10 + 20 = 30 @@ -44,7 +44,7 @@ fi # 20 % 10 = 0 # 10 != 20 -# Execute: ./operator-demo.sh 10 30 +# Execute: ./算术运算符.sh 10 30 # Output: # x=10, y=30 # 10 + 30 = 40 diff --git a/codes/shell/demos/operator/operator-demo4.sh "b/codes/shell/\346\223\215\344\275\234\347\254\246/\351\200\273\350\276\221\350\277\220\347\256\227\347\254\246.sh" similarity index 54% rename from codes/shell/demos/operator/operator-demo4.sh rename to "codes/shell/\346\223\215\344\275\234\347\254\246/\351\200\273\350\276\221\350\277\220\347\256\227\347\254\246.sh" index e0ac1c4a..c6223133 100644 --- a/codes/shell/demos/operator/operator-demo4.sh +++ "b/codes/shell/\346\223\215\344\275\234\347\254\246/\351\200\273\350\276\221\350\277\220\347\256\227\347\254\246.sh" @@ -2,31 +2,30 @@ x=10 if [[ -n $1 ]]; then - x=$1 + x=$1 fi y=20 if [[ -n $2 ]]; then - y=$2 + y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} -lt 100 && ${y} -gt 100 ]] then - echo "${x} -lt 100 && ${y} -gt 100 返回 true" + echo "${x} -lt 100 && ${y} -gt 100 返回 true" else - echo "${x} -lt 100 && ${y} -gt 100 返回 false" + echo "${x} -lt 100 && ${y} -gt 100 返回 false" fi if [[ ${x} -lt 100 || ${y} -gt 100 ]] then - echo "${x} -lt 100 || ${y} -gt 100 返回 true" + echo "${x} -lt 100 || ${y} -gt 100 返回 true" else - echo "${x} -lt 100 || ${y} -gt 100 返回 false" + echo "${x} -lt 100 || ${y} -gt 100 返回 false" fi -# Execute: ./operator-demo4.sh # Output: # x=10, y=20 # 10 -lt 100 && 20 -gt 100 返回 false diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" similarity index 85% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" index a6d98fc6..6baee2eb 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash BEGIN { print "The latest list of users and shells" @@ -8,7 +8,7 @@ FS=":" } { - print $1 " " $7 + print $1 " " $7 } END { diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" similarity index 55% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" index c6673c1c..99bc48d7 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\345\207\275\346\225\260\345\272\223" @@ -5,8 +5,8 @@ function myprint() printf "%-16s - %s", $1, $4 } -function myrand(limit) +function myrand ( limit ) { - return int(limit * rand()) +return int ( limit * rand ( ) ) } diff --git "a/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" new file mode 100644 index 00000000..1028bcdf --- /dev/null +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" @@ -0,0 +1,8 @@ +#!/bin/bash + +BEGIN{ FS="\n"; +RS=""} +{ +myprint() + +} diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/script" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/script" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/script" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/script" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/test" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/test" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/test" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/test" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" similarity index 97% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" index 817bd65b..51f26513 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\345\217\230\351\207\217.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #使用内建变量 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" similarity index 95% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" index db7b78c9..6e61f422 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\344\275\277\347\224\250\346\250\241\345\274\217\357\274\214\347\273\223\346\236\204\345\214\226\345\221\275\344\273\244.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #正则表达式 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" similarity index 86% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" index 81e13af9..a026581a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\207\252\345\256\232\344\271\211\345\207\275\346\225\260.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #gawk 自定义函数 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" similarity index 78% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" index 0ffaa675..fc871ed5 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\260\203\347\224\250\345\207\275\346\225\260\345\272\223\345\222\214\350\204\232\346\234\254.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #使用函数库和gawk脚本 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" similarity index 97% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" index cd609480..a26f5c72 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/\350\276\223\345\207\272.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #print用于产生简单输出 #多个表达式的字符串值之间用输出字段分隔符分开 @@ -62,5 +62,5 @@ END{ for(c in pop) printf("%15-s%6d\n", c, pop[c]) | "sort -nk 2"; close("sort - #在同一个程序中,如果你写了一个文件,而待会儿想要读取它,那么就需要调用close。 #某一时刻,同时处于打开状态的文件或管道数量最大值由实现定义。 -close ( " sort -nk 2 " ) +close ( " sort -nk 2 " ) diff --git "a/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" new file mode 100644 index 00000000..af048364 --- /dev/null +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# count number of files in your PATH + +mypath=`echo $PATH | sed 's/:/ /g'` +count=0 +for directory in $mypath +do + check=`ls $directory` + echo $check + for item in $check + do + count=$[ $count + 1 ] + done + echo "$directory - $count" + count=0 +done + diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" similarity index 84% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" index 1857ac8c..e5e28abd 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\351\202\256\344\273\266\351\252\214\350\257\201.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #验证邮件 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" similarity index 93% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" index 046001ef..9185c9ef 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\346\226\207\344\273\266\346\223\215\344\275\234.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #向文件写入 sed '1,2w test1' test1 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" similarity index 99% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" index f6ce62dd..06e9c46c 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/sed\347\274\226\350\276\221\345\231\250\345\237\272\347\241\200.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #sed编辑器基础 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/test" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/test" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/test" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/test" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" similarity index 92% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" index 3c75f7de..0772420b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\344\277\235\346\214\201\347\251\272\351\227\264.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #h将模式空间保存到保持空间 #H将模式空间附加到保持空间 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" similarity index 93% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" index 2dcdd57c..d37ebaa9 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\210\240\351\231\244\346\214\207\345\256\232\347\232\204\347\251\272\347\231\275\350\241\214\345\222\214\345\210\240\351\231\244html\346\240\207\347\255\276.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #多个空格只保留一个 #sed '/./,/^$/!d' test diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" similarity index 80% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" index 75142e4f..5a1a5fa0 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250sed.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # shell wrapper for sed editor script to reverse lines diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\216\222\351\231\244\345\221\275\344\273\244.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\216\222\351\231\244\345\221\275\344\273\244.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\216\222\351\231\244\345\221\275\344\273\244.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\216\222\351\231\244\345\221\275\344\273\244.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" similarity index 95% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" index 8682f6ed..70585d17 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\250\241\345\274\217\346\233\277\344\273\243.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #and符号,代表替换命令中的匹配模式,不管预定义模式是什么文本,都可以用and符号替换,and符号会提取匹配替换命令中指定替换模式中的所有字符串 echo "The cat sleeps in his hat" | sed 's/.at/"&"/g' diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" similarity index 91% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" index 65ef7064..88befd75 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\346\265\213\350\257\225.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #测试,如果测试成功,如果没有标签,sed会跳转到结尾,如果有标签,就跳转到标签,如果测试失败,则不会跳转 sed -n '{s/first/matched/; t; s/This is the/No match on/}' test diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" similarity index 62% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" index 07bd0e69..9ed6f148 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\347\273\231\346\226\207\344\273\266\344\270\255\347\232\204\350\241\214\347\274\226\345\217\267.sh" @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash sed '=' test | sed 'N; s/\n/ /' diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" similarity index 92% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" index 69eff6b1..a352ad5e 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\267\263\350\275\254.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #跳转到指定脚本 sed '{/first/b jump1; s/This is the/No jump on/; :jump1; s/This is the/Jump here on/}' test diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" similarity index 78% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" index 7e4efc90..8bddf0ec 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\350\276\223\345\207\272\346\234\253\345\260\276\346\214\207\345\256\232\350\241\214\346\225\260\347\232\204\346\225\260\346\215\256.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #输出末尾10行数据 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" similarity index 73% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" rename to "codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" index 715a75b5..674afaa3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" +++ "b/codes/shell/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/sed/\351\207\215\345\256\232\345\220\221sed\350\276\223\345\207\272.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # add commas to numbers in factorial answer @@ -8,8 +8,8 @@ number=$1 while [ $counter -le $number ] do - factorial=$[ $factorial * $counter ] - counter=$[ $counter + 1 ] + factorial=$[ $factorial * $counter ] + counter=$[ $counter + 1 ] done result=`echo $factorial | sed '{ diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" deleted file mode 100644 index 88386601..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/README.md" +++ /dev/null @@ -1,3 +0,0 @@ -# Shell 脚本实战 - -> 本目录的代码是本人在学习、开发中收集、总结的一些面向实战的 Shell 脚本代码。 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" deleted file mode 100644 index 73c73d13..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/mysql/\345\220\221\346\225\260\346\215\256\345\272\223\344\270\255\346\217\222\345\205\245\346\225\260\346\215\256.sh" +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# send data to the the table in the MYSQL database - -MYSQL=`which mysql` - -if [ $# -ne 2 ] -then - echo "Usage:mtest2 emplid lastname firstname salary" -else - #脚本变量一定要用双引号,字符串变量使用单引号 - statement=" insert into em_admin values(NULL, '$1', $2)" - $MYSQL emwjs -u test << EOF - $statement -EOF - if [ $? -eq 0 ] - then - echo Data successfully added - else - echo Problem adding data - fi -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" deleted file mode 100644 index 0182f1db..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\345\255\227\347\254\246\344\270\262\346\257\224\350\276\203\345\210\244\346\226\255\347\244\272\344\276\213.sh" +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash - -x="abc" -if [[ -n $1 ]]; then - x=$1 -fi - -y="xyz" -if [[ -n $2 ]]; then - y=$2 -fi - -echo "x=${x}, y=${y}" - -if [[ ${x} = ${y} ]]; then - echo "${x} = ${y} : x 等于 y" -else - echo "${x} = ${y}: x 不等于 y" -fi - -if [[ ${x} != ${y} ]]; then - echo "${x} != ${y} : x 不等于 y" -else - echo "${x} != ${y}: x 等于 y" -fi - -if [[ -z ${x} ]]; then - echo "-z ${x} : 字符串长度为 0" -else - echo "-z ${x} : 字符串长度不为 0" -fi - -if [[ -n "${x}" ]]; then - echo "-n ${x} : 字符串长度不为 0" -else - echo "-n ${x} : 字符串长度为 0" -fi - -if [[ ${x} ]]; then - echo "${x} : 字符串不为空" -else - echo "${x} : 字符串为空" -fi - -# Execute: ./operator-demo5.sh -# Output: -# x=abc, y=xyz -# abc = xyz: x 不等于 y -# abc != xyz : x 不等于 y -# -z abc : 字符串长度不为 0 -# -n abc : 字符串长度不为 0 -# abc : 字符串不为空 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" deleted file mode 100644 index e0ac1c4a..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\346\257\224\350\276\203\347\254\246\344\275\277\347\224\250\347\244\272\344\276\2133.sh" +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -x=10 -if [[ -n $1 ]]; then - x=$1 -fi - -y=20 -if [[ -n $2 ]]; then - y=$2 -fi - -echo "x=${x}, y=${y}" - -if [[ ${x} -lt 100 && ${y} -gt 100 ]] -then - echo "${x} -lt 100 && ${y} -gt 100 返回 true" -else - echo "${x} -lt 100 && ${y} -gt 100 返回 false" -fi - -if [[ ${x} -lt 100 || ${y} -gt 100 ]] -then - echo "${x} -lt 100 || ${y} -gt 100 返回 true" -else - echo "${x} -lt 100 || ${y} -gt 100 返回 false" -fi - -# Execute: ./operator-demo4.sh -# Output: -# x=10, y=20 -# 10 -lt 100 && 20 -gt 100 返回 false -# 10 -lt 100 || 20 -gt 100 返回 true diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" deleted file mode 100644 index 8e62f92c..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\223\215\344\275\234\347\254\246/\350\256\241\347\256\227\347\254\246\344\275\277\347\224\250\347\244\272\344\276\213.sh" +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -x=10 -if [[ -n $1 ]]; then - x=$1 -fi - -y=20 -if [[ -n $2 ]]; then - y=$2 -fi - -echo "x=${x}, y=${y}" - -val=`expr ${x} + ${y}` -echo "${x} + ${y} = $val" - -val=`expr ${x} - ${y}` -echo "${x} - ${y} = $val" - -val=`expr ${x} \* ${y}` -echo "${x} * ${y} = $val" - -val=`expr ${y} / ${x}` -echo "${y} / ${x} = $val" - -val=`expr ${y} % ${x}` -echo "${y} % ${x} = $val" - -if [[ ${x} == ${y} ]]; then - echo "${x} = ${y}" -fi -if [[ ${x} != ${y} ]]; then - echo "${x} != ${y}" -fi - -# Execute: ./operator-demo.sh -# Output: -# x=10, y=20 -# 10 + 20 = 30 -# 10 - 20 = -10 -# 10 * 20 = 200 -# 20 / 10 = 2 -# 20 % 10 = 0 -# 10 != 20 - -# Execute: ./operator-demo.sh 10 30 -# Output: -# x=10, y=30 -# 10 + 30 = 40 -# 10 - 30 = -20 -# 10 * 30 = 300 -# 30 / 10 = 3 -# 30 % 10 = 0 -# 10 不等于 30 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" deleted file mode 100644 index 35597f9c..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/gawk/gawk\350\204\232\346\234\254" +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -BEGIN{FS="\n"; RS=""} -{ - myprint() -} diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" deleted file mode 100644 index c9fa536e..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\346\237\245\346\211\276\346\233\277\346\215\242\346\226\207\346\234\254/regex/\347\233\256\345\275\225\346\226\207\344\273\266\350\256\241\346\225\260.sh" +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# count number of files in your PATH - -mypath=`echo $PATH | sed 's/:/ /g'` -count=0 -for directory in $mypath -do - check=`ls $directory` - echo $check - for item in $check - do - count=$[ $count + 1 ] - done - echo "$directory - $count" - count=0 -done - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" deleted file mode 100644 index f468d2f2..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# returning an array value - -function arraydblr { - local origarry - local newarray - local elements - local i - origarry=( `echo "$@"` ) - newarray=( `echo "$@"` ) - elements=$[ $# - 1 ] - for (( i = 0; i <= $elements; i ++ )) - { - newarray [ $i ] = $[ ${origarry[$i]} * 2 ] - } - - echo ${newarray[*]} -} - -myarray=( 1 2 3 4 5 ) -echo "The original array is : ${myarray[*]}" -arg1=`echo ${myarray[*]}` -result=( `arraydblr $arg1` ) -echo "The new array is : ${result[*]}" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" deleted file mode 100644 index 3a0908e4..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# using the return command in a function - -function db1 { - read -p "Enter a value:" value - echo "doubling the value" - return $[ $value * 2 ] -} - -db1 -echo "The new value is $?" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" deleted file mode 100644 index d168adc9..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# using a global variable to pass a value - -function db1 { - # $1和$2 不能从命令行中传递,只能调用函数时,手动传递 - echo $[ $1 * $2 ] -} - -if [ $# -eq 2 ] -then - value=`db1 $1 $2` - echo "The result is $value" -else - echo "Usage: badtest1 a b" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" deleted file mode 100644 index ae151193..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -function factorial { - if [ $1 -eq 1 ] - then - echo 1 - else - local temp=$[ $1 - 1 ] - local result=`factorial $temp` - echo $[ $result * $1 ] - fi -} - -read -p "Please input a value: " value -result=`factorial $value` -echo "The factorial of $value is: $result" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" deleted file mode 100644 index c0028ee2..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# trying to pass an array variable - -function testit { - echo "The parameters are : $@" - - #函数只会读取数组变量的第一个值 - thisarray=$1 - echo "The received array is ${thisarray[*]}" - - local newarray - newarray=( `echo "$@"` ) - echo "The new array value is : ${newarray[*]}" -} - -myarray=( 1 2 3 4 5 ) -echo "The original array is : ${myarray[*]}" - -#将数组变量当成一个函数参数,函数只会去函数变量第一个值 -#testit $myarray - -testit ${myarray[*]} diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" deleted file mode 100644 index 31ea5a4f..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# using select in the menu - -function diskspace { - clear - df -k -} - -function whoseon { - clear - who -} - -function menusage { - clear - cat /proc/meminfo -} - -PS3="Enter option:" -select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program" -do -case $option in - "Exit program") - break ;; - "Display disk space") - diskspace ;; - "Display logged on users") - whoseon ;; - "Display memory usage") - menusage ;; - *) - clear - echo "Sorry, wrong selection" ;; -esac -done -clear diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" deleted file mode 100644 index 1b3130a4..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -function menu { - clear - echo - echo -e "\t\tSys Admin Menu\n" - echo -e "\t1. Display disk space" - echo -e "\t2. Display logged on users" - echo -e "\t3. Display memory usage" - echo -e "\t0. Exit program\n\n" - echo -en "\t\tEnter option:" - read -n 1 option -} - -function diskspace { - clear - df -k -} - -function whoseon { - clear - who -} - -function menusage { - clear - cat /proc/meminfo -} - -while [ 1 ] -do - menu - case $option in - 0) - break ;; - 1) - diskspace ;; - 2) - whoseon ;; - 3) - menusage ;; - *) - clear - echo "Sorry, wrong selection" ;; - esac - echo -en "\n\n\t\tHit any key to continue" - read -n 1 line -done -clear diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" deleted file mode 100644 index 5d067a3b..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# using dialog to create a menu - -temp=`mktemp -t test.XXXXXX` -temp2=`mktemp -t test2.XXXXXX` - -function diskspace { - df -k > $temp - dialog --textbox $temp 20 60 -} - -function whoseon { - who > $temp - dialog --textbox $temp 20 50 -} - -function menusage { - cat /proc/meminfo > $temp - dialog --textbox $temp 20 50 -} - -while [ 1 ] -do - dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2 - if [ $? -eq 1 ] - then - break - fi - - selection=`cat $temp2` - - case $selection in - 1) - diskspace ;; - 2) - whoseon ;; - 3) - menusage ;; - 0) - break ;; - *) - dialog --msgbox "Sorry,invalid selection" 10 30 - esac -done -rm -f $temp 2> /dev/null -rm -f $temp2 2> /dev/null diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" deleted file mode 100644 index 1a486718..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# simple demonstration of the getopts command - -while getopts :ab:c opt -do - case "$opt" in - a) echo "Found the -a option" ;; - b) echo "Found the -b option, with value $OPTARG" ;; - c) echo "Found the -c option" ;; - *) echo "Unknown option:$opt" ;; - esac -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" deleted file mode 100644 index 8bcb5831..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# processing options and parameters with getopts - -while getopts :ab:cd opt -do - case "$opt" in - a) echo "Found the -a option" ;; - b) echo "Found the -b option,with value $OPTARG" ;; - c) echo "Found the -c option" ;; - d) echo "Found the -d option" ;; - *) echo "Unknown option: $opt" ;; - esac -done -shift $[ $OPTIND - 1 ] -count=1 -for param in "$@" -do - echo "Parameter $count: $param" - count=$[ $count + 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" deleted file mode 100644 index 999b235d..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -#extracting command line options and values with getopt -# getopt command is not goot at dealing with space,we can use getopts -set -- `getopt -q ab:c "$@"` -while [ -n "$1" ] -do - case "$1" in - -a) echo "Found the -a option" ;; - -b) param="$2" - echo "Found the -b option,with parameter value $param" - shift ;; - -c) echo "Found the -c option" ;; - --) shift - break ;; - *) echo "$1 is not an option" ;; - esac - shift -done - -count=1 -for param in "$@" -do - echo "Parameter #$count: $param" - count=$[ $count + 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" deleted file mode 100644 index 1e20cf7a..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -#extracting options and parameters - -while [ -n "$1" ] -do - case "$1" in - -a) echo "Found the -a option" ;; - -b) echo "Found the -b option" ;; - -c) echo "Found the -c option" ;; - --) shift - break ;; - *) echo "$1 is not an option" ;; - esac - shift -done - -count=1 -for param in $@ -do - echo "Parameter #$count: $param" - count=$[ $count + 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" deleted file mode 100644 index 63163ed7..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# extracting command line options and values - -while [ -n "$1" ] -do - case "$1" in - -a) echo "Found the -a option" ;; - -b) param="$2" - echo "Found the -b option, with parameter value $param" - shift ;; - -c) echo "Found the -c option" ;; - --) shift - break ;; - *) echo "$1 is not an option" ;; - esac - shift -done - -count=1 -for param in "$@" -do - echo "Parameter #$count : $param" - count=$[ $count + 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" deleted file mode 100644 index 9e679959..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# extracting command line options as parameters - -while [ -n "$1" ] -do - case "$1" in - -a) echo "Found the -a option" ;; - -b) echo "Found the -b optins" ;; - -c) echo "Found the -c optins" ;; - *) echo "$1 is not a valid options" ;; - esac - shift -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" deleted file mode 100644 index 38075263..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# testing $* and $@ - -count=1 -for param in "$*" -do - echo "\$* Parameter #$count = $param" - count=$[ $count + 1 ] -done - -count=1 -for param in "$@" -do - echo "\$@ Paramenter #$count = $param" - count=$[ $count + 1 ] -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" deleted file mode 100644 index 5879db36..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# timing the data entry - -if read -t 5 -p "Please enter your name:" name -then - echo "Hello, $name, welcome to my script" -else - #起到换行的作用 - echo - #输入计数 -n1 - read -n1 -p "Do you want to continue [Y/N]?" answer - case $answer in - Y | y) echo - echo "Fine, continue on..." ;; - N | n) echo - echo "OK,goodbye" ;; - *) echo - echo "OK, wrong, goodbye" - esac - echo "Sorry, this is the end of the script" -fi - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" deleted file mode 100644 index 1f29b951..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/case\344\275\277\347\224\250\347\244\272\344\276\213.sh" +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -echo "input param: " $1 $2 $3 - -x=0 -if [[ -n $1 ]]; then - x=$1 -fi - -oper="" -if [[ -n $2 ]]; then - oper=$2 -fi - -y=0 -if [[ -n $3 ]]; then - y=$3 -fi - -exec -case ${oper} in - + | add) - val=`expr ${x} + ${y}` - echo "${x} + ${y} = ${val}" - ;; - - | sub) - val=`expr ${x} - ${y}` - echo "${x} - ${y} = ${val}" - ;; - * | mul) - val=`expr ${x} \* ${y}` - echo "${x} * ${y} = ${val}" - ;; - / | div) - val=`expr ${x} / ${y}` - echo "${x} / ${y} = ${val}" - ;; - *) - echo "Unknown oper!" - ;; -esac diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" deleted file mode 100644 index 4e59b5a3..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/select-demo.sh" +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -PS3="Choose the package manager: " -select ITEM in bower npm gem pip -do -echo -n "Enter the package name: " && read PACKAGE -case ${ITEM} in - bower) bower install ${PACKAGE} ;; - npm) npm install ${PACKAGE} ;; - gem) gem install ${PACKAGE} ;; - pip) pip install ${PACKAGE} ;; -esac -break # 避免无限循环 -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" deleted file mode 100644 index a2f99ad5..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# using double parenthesis - -var1=10 - -if (($var1 ** 2 > 90)) -then - ((var2 = $var1 ** 2)) - echo "The square of $var1 if $var2" -fi - diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" deleted file mode 100644 index 3f73c78f..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# using pattern matching - -if [[ $USER == r* ]] -then - echo "Hello $USER" -else - echo "Sorry, I do not know you" -fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" deleted file mode 100644 index fae9a1f5..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -#changing the IFS value - -IFS.OLD=$IFS -IFS=$'\n' -for entry in `cat /etc/passwd` -do - echo "Values in $entry -" - IFS=: - for value in $entry - do - echo " $value" - done -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" "b/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" deleted file mode 100644 index c872f2ed..00000000 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -#iterate through all the files in a directory -for file in /home/tiandi/test/* -do - if [ -d "$file" ] - then - echo "$file is a directory" - elif [ -f "$file" ] - then - echo "$file is a file" - fi -done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" "b/codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" similarity index 95% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" rename to "codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" index 758232c4..c6a050e3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" +++ "b/codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\346\216\247\345\210\266\350\277\234\347\250\213\346\234\215\345\212\241\345\231\250\346\211\247\350\241\214\346\214\207\344\273\244.sh" @@ -1,4 +1,5 @@ -#!/bin/bashost="127.0.0.2" +#!/usr/bin/expect + user="root" password="root" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" "b/codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" rename to "codes/shell/\347\263\273\347\273\237\347\256\241\347\220\206/\347\263\273\347\273\237\347\224\250\346\210\267\347\256\241\347\220\206.sh" diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" new file mode 100644 index 00000000..1a9ddaf9 --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\273\216\345\207\275\346\225\260\350\277\224\345\233\236\346\225\260\347\273\204.sh" @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# returning an array value + +function arraydblr { + local origarry + local newarray + local elements + local i + origarry=( `echo "$@"` ) + newarray=( `echo "$@"` ) + elements=$[ $# - 1 ] + for (( i = 0; i <= $elements; i ++ )) + { + newarray [ $i ] = $[ ${origarry[$i]} * 2 ] + } + + echo ${newarray[*]} +} + +myarray=( 1 2 3 4 5 ) +echo "The original array is : ${myarray[*]}" +arg1=`echo ${myarray[*]}` +result=( `arraydblr $arg1` ) +echo "The new array is : ${result[*]}" diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" new file mode 100644 index 00000000..b79d3411 --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250return\345\221\275\344\273\244.sh" @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# using the return command in a function + +function db1 { + read -p "Enter a value:" value + echo "doubling the value" + return $[ $value * 2 ] +} + +db1 +echo "The new value is $?" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" similarity index 56% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" index a680cd0e..aba7b8a2 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\205\250\345\261\200\345\217\230\351\207\217\345\270\246\346\235\245\347\232\204\351\227\256\351\242\230.sh" @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # demonstrating a bad use of variables function func1 { - temp=$[ $value + 5 ] - result=$[ $temp * 2 ] + temp=$[ $value + 5 ] + result=$[ $temp * 2 ] } temlp=4 @@ -15,7 +15,7 @@ echo "The result is $result" if [ $temp -gt $value ] then - echo "Temp is larger" + echo "Temp is larger" else - echo "temp is smaller" + echo "temp is smaller" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" similarity index 57% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" index 78972920..1b3098ad 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\207\275\346\225\260\350\276\223\345\207\272.sh" @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # using the echo to return a value function db1 { - read -p "Enter a value:" value - echo $[ $value * 2 ] + read -p "Enter a value:" value + echo $[ $value * 2 ] } result=`db1` diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" new file mode 100644 index 00000000..8020e32f --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\221\275\344\273\244\350\241\214\344\270\255\344\274\240\351\200\222\347\232\204\345\217\202\346\225\260.sh" @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# using a global variable to pass a value + +function db1 { + # $1和$2 不能从命令行中传递,只能调用函数时,手动传递 + echo $[ $1 * $2 ] +} + +if [ $# -eq 2 ] +then + value=`db1 $1 $2` + echo "The result is $value" +else + echo "Usage: badtest1 a b" +fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" similarity index 54% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" index ec89112f..5c03d1f3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\261\200\351\203\250\345\217\230\351\207\217.sh" @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # demonstrating the local keyword function func1 { - local temp=$[ $value + 5 ] - result=$[ $temp * 2 ] + local temp=$[ $value + 5 ] + result=$[ $temp * 2 ] } temp=4 @@ -15,7 +15,7 @@ func1 echo "The result is $result" if [ $temp -gt $value ] then - echo "temp is larger" + echo "temp is larger" else - echo "temp is smaller" + echo "temp is smaller" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" similarity index 71% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" index 4b048c3c..d2b9e918 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash #using a library file the wrong way -. ./脚本库.sh +../脚本库.sh result=`addem 10 15` echo "The result is $result" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" similarity index 75% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" index 058dd0d3..f1915c2d 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\205\250\345\261\200\345\217\230\351\207\217.sh" @@ -1,9 +1,9 @@ -#!/bin/bash +#!/usr/bin/env bash # using a global variable to pass a value function db1 { - value=$[ $value * 2 ] + value=$[ $value * 2 ] } read -p "Enter a value: " value diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\202.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\202.sh" new file mode 100644 index 00000000..57fd0f3d --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\202.sh" @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +x=0 +if [[ -n $1 ]]; then + echo "第一个参数为:$1" + x=$1 +else + echo "第一个参数为空" +fi + +y=0 +if [[ -n $2 ]]; then + echo "第二个参数为:$2" + y=$2 +else + echo "第二个参数为空" +fi + +paramsFunction() { + echo "函数第一个入参:$1" + echo "函数第二个入参:$2" +} + +paramsFunction ${x} ${y} diff --git a/codes/shell/demos/function/function-demo3.sh "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\2022.sh" similarity index 51% rename from codes/shell/demos/function/function-demo3.sh rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\2022.sh" index fe269cdb..1da51a04 100644 --- a/codes/shell/demos/function/function-demo3.sh +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\205\245\345\217\2022.sh" @@ -1,23 +1,23 @@ #!/usr/bin/env bash runner() { - return 0 + return 0 } name=zp paramsFunction() { - echo "函数第一个入参:$1" - echo "函数第二个入参:$2" - echo "传递到脚本的参数个数:$#" - echo "所有参数:" - printf "+ %s\n" "$*" - echo "脚本运行的当前进程 ID 号:$$" - echo "后台运行的最后一个进程的 ID 号:$!" - echo "所有参数:" - printf "+ %s\n" "$@" - echo "Shell 使用的当前选项:$-" - runner - echo "runner 函数的返回值:$?" + echo "函数第一个入参:$1" + echo "函数第二个入参:$2" + echo "传递到脚本的参数个数:$#" + echo "所有参数:" + printf "+ %s\n" "$*" + echo "脚本运行的当前进程 ID 号:$$" + echo "后台运行的最后一个进程的 ID 号:$!" + echo "所有参数:" + printf "+ %s\n" "$@" + echo "Shell 使用的当前选项:$-" + runner + echo "runner 函数的返回值:$?" } paramsFunction 1 "abc" "hello, \"zp\"" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\237\272\346\234\254\347\232\204\350\204\232\346\234\254\345\207\275\346\225\260.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\213.sh" similarity index 65% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\237\272\346\234\254\347\232\204\350\204\232\346\234\254\345\207\275\346\225\260.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\213.sh" index 81093fd9..e9b0ddff 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\237\272\346\234\254\347\232\204\350\204\232\346\234\254\345\207\275\346\225\260.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\213.sh" @@ -1,16 +1,16 @@ -#!/bin/bash +#!/usr/bin/env bash # using a function in script function func1 { - echo "This is an example of a function" + echo "This is an example of a function" } count=1 while [ $count -le 5 ] do - func1 - count=$[ $count + 1 ] + func1 + count=$[ $count + 1 ] done echo "This is the end of the loop" func1 diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\2132.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\2132.sh" new file mode 100644 index 00000000..390ba308 --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\345\237\272\346\234\254\347\244\272\344\276\2132.sh" @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +calc() { + PS3="choose the oper: " + select oper in "+" "-" "*" "/" # 生成操作符选择菜单 + do + echo -n "enter first num: " && read x # 读取输入参数 + echo -n "enter second num: " && read y # 读取输入参数 + exec + case ${oper} in + "+") + return $((${x} + ${y})) + ;; + "-") + return $((${x} - ${y})) + ;; + "*") + return $((${x} * ${y})) + ;; + "/") + return $((${x} / ${y})) + ;; + *) + echo "${oper} is not support!" + return 0 + ;; + esac + break + done +} + +calc +echo "the result is: $?" # $? 获取 calc 函数返回值 +# $ ./function-demo.sh +# 1) + +# 2) - +# 3) * +# 4) / +# choose the oper: 3 +# enter first num: 10 +# enter second num: 10 +# the result is: 100 diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" new file mode 100644 index 00000000..7ee9c38c --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\207\275\346\225\260\351\200\222\345\275\222.sh" @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +function factorial { + if [ $1 -eq 1 ] + then + echo 1 + else + local temp=$[ $1 - 1 ] + local result=`factorial $temp` + echo $[ $result * $1 ] + fi +} + +read -p "Please input a value: " value +result=`factorial $value` +echo "The factorial of $value is: $result" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" similarity index 67% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" index a3f62ba5..01e59063 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\345\234\250\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\217\202\346\225\260.sh" @@ -1,17 +1,17 @@ -#!/bin/bash +#!/usr/bin/env bash # passing parameters to a function function addem { - if [ $# -eq 0 ] || [ $# -gt 2 ] - then - echo -1 - elif [ $# -eq 1 ] - then - echo $[ $1 + $1 ] - else - echo $[ $1 + $2 ] - fi + if [ $# -eq 0 ] || [ $# -gt 2 ] + then + echo -1 + elif [ $# -eq 1 ] + then + echo $[ $1 + $1 ] + else + echo $[ $1 + $2 ] + fi } echo -n "Adding 10 and 15:" diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" new file mode 100644 index 00000000..5f48c129 --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\346\203\263\345\207\275\346\225\260\344\274\240\346\225\260\347\273\204\346\225\260\346\215\256.sh" @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# trying to pass an array variable + +function testit { + echo "The parameters are : $@" + + #函数只会读取数组变量的第一个值 + thisarray=$1 + echo "The received array is ${thisarray[*]}" + + local newarray + newarray=( `echo "$@"` ) + echo "The new array value is : ${newarray[*]}" +} + +myarray=( 1 2 3 4 5 ) +echo "The original array is : ${myarray[*]}" + +#将数组变量当成一个函数参数,函数只会去函数变量第一个值 +#testit $myarray + +testit ${myarray[*]} diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" similarity index 54% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" index 9ca49964..59fdd5f3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\347\264\257\345\212\240\346\225\260\347\273\204\344\270\255\347\232\204\345\200\274.sh" @@ -1,16 +1,16 @@ -#!/bin/bash +#!/usr/bin/env bash #adding values in the array function addarray { - local sum=0 - local newarray - newarray=( `echo "$@"` ) - for value in ${newarray[*]} - do - sum=$[ $sum + $value ] - done - echo $sum + local sum=0 + local newarray + newarray=( `echo "$@"` ) + for value in ${newarray[*]} + do + sum=$[ $sum + $value ] + done + echo $sum } myarray=( 1 2 3 4 5 ) diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" similarity index 61% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" index 8dd479bd..06cc93cf 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" @@ -1,16 +1,18 @@ -#!/bin/bash +#!/usr/bin/env bash # myscript functions function addem { - echo $[ $1 + $2 ] + echo $[ $1 + $2 ] } function multem { - echo $[ $1 * $2 ] + echo $[ $1 * $2 ] } -function divem { +function divem + +{ if [ $2 -ne 0]; then echo $[ $1 / $2 ] else diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" similarity index 69% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" rename to "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" index f2cca07f..329f8f38 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\351\273\230\350\256\244\351\200\200\345\207\272\347\212\266\346\200\201\347\240\201.sh" @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # testing the exit status of a function func1() { - echo "Trying to display a non-existent file" - ls -l badfile + echo "Trying to display a non-existent file" + ls -l badfile } #由于最后一条命令未执行成功,返回的状态码非0 @@ -13,8 +13,8 @@ func1 echo "The exit status is : $?" func2() { - ls -l badfile - echo "Another test to display a non-existent file" + ls -l badfile + echo "Another test to display a non-existent file" } #由于最后一条命令echo执行成功,返回的状态码为0 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" "b/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" similarity index 72% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" rename to "codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" index c9aa8261..d7c1d354 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" +++ "b/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250msgbox\351\203\250\344\273\266.sh" @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash dialog --title text --msgbox "This is a test" 10 20 diff --git "a/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" "b/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" new file mode 100644 index 00000000..ddcbee2c --- /dev/null +++ "b/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250select\345\221\275\344\273\244.sh" @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# using select in the menu + +function diskspace { + clear + df -k +} + +function whoseon { + clear + who +} + +function menusage { + clear + cat /proc/meminfo +} + +PS3="Enter option:" +select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program" +do +case $option in + "Exit program") + break ;; + "Display disk space") + diskspace ;; + "Display logged on users") + whoseon ;; + "Display memory usage") + menusage ;; + *) + clear + echo "Sorry, wrong selection" ;; +esac +done +clear diff --git "a/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" "b/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" new file mode 100644 index 00000000..7137078d --- /dev/null +++ "b/codes/shell/\350\217\234\345\215\225/\344\275\277\347\224\250\350\204\232\346\234\254\350\217\234\345\215\225.sh" @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +function menu { + clear + echo + echo -e "\t\tSys Admin Menu\n" + echo -e "\t1. Display disk space" + echo -e "\t2. Display logged on users" + echo -e "\t3. Display memory usage" + echo -e "\t0. Exit program\n\n" + echo -en "\t\tEnter option:" + read -n 1 option +} + +function diskspace { + clear + df -k +} + +function whoseon { + clear + who +} + +function menusage { + clear + cat /proc/meminfo +} + +while [ 1 ] +do + menu + case $option in + 0) + break ;; + 1) + diskspace ;; + 2) + whoseon ;; + 3) + menusage ;; + *) + clear + echo "Sorry, wrong selection" ;; + esac + echo -en "\n\n\t\tHit any key to continue" + read -n 1 line +done +clear diff --git "a/codes/shell/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" "b/codes/shell/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" new file mode 100644 index 00000000..d8c05d27 --- /dev/null +++ "b/codes/shell/\350\217\234\345\215\225/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250dialog\345\221\275\344\273\244.sh" @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# using dialog to create a menu + +temp=`mktemp -t test.XXXXXX` +temp2=`mktemp -t test2.XXXXXX` + +function diskspace { + df -k > $temp + dialog --textbox $temp 20 60 +} + +function whoseon { + who > $temp + dialog --textbox $temp 20 50 +} + +function menusage { + cat /proc/meminfo > $temp + dialog --textbox $temp 20 50 +} + +while [ 1 ] +do + dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2 + if [ $? -eq 1 ] + then + break + fi + + selection=`cat $temp2` + + case $selection in + 1) + diskspace ;; + 2) + whoseon ;; + 3) + menusage ;; + 0) + break ;; + *) + dialog --msgbox "Sorry,invalid selection" 10 30 + esac +done +rm -f $temp 2> /dev/null +rm -f $temp2 2> /dev/null diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test1" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test1" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test1" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/test1" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" similarity index 85% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" index fce0459e..3aa211f3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\270\264\346\227\266\351\207\215\345\256\232\345\220\221.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing STDERR messages diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" similarity index 60% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" index 2115cc78..2a63ea07 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" @@ -1,12 +1,12 @@ -#!/bin/bash +#!/usr/bin/env bash # reading data from a file count=1 cat test | while read line do - echo "Line $count: $line" - count=$[ $count + 1 ] + echo "Line $count: $line" + count=$[ $count + 1 ] done echo "Finished processing the file" diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" new file mode 100644 index 00000000..4cb921e7 --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts.sh" @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# simple demonstration of the getopts command + +while getopts :ab:c opt +do + case "$opt" in + a) echo "Found the -a option" ;; + b) echo "Found the -b option, with value $OPTARG" ;; + c) echo "Found the -c option" ;; + *) echo "Unknown option:$opt" ;; + esac +done diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" new file mode 100644 index 00000000..ef63a4d8 --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopts\345\244\204\347\220\206\351\200\211\351\241\271\345\222\214\345\217\202\346\225\260.sh" @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# processing options and parameters with getopts + +while getopts :ab:cd opt +do + case "$opt" in + a) echo "Found the -a option" ;; + b) echo "Found the -b option,with value $OPTARG" ;; + c) echo "Found the -c option" ;; + d) echo "Found the -d option" ;; + *) echo "Unknown option: $opt" ;; + esac +done +shift $[ $OPTIND - 1 ] +count=1 +for param in "$@" +do + echo "Parameter $count: $param" + count=$[ $count + 1 ] +done diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" new file mode 100644 index 00000000..0004309a --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250getopt\345\221\275\344\273\244.sh" @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +#extracting command line options and values with getopt +# getopt command is not goot at dealing with space,we can use getopts +set -- `getopt -q ab:c "$@"` +while [ -n "$1" ] +do + case "$1" in + -a) echo "Found the -a option" ;; + -b) param="$2" + echo "Found the -b option,with parameter value $param" + shift ;; + -c) echo "Found the -c option" ;; + --) shift + break ;; + *) echo "$1 is not an option" ;; + esac + shift +done + +count=1 +for param in "$@" +do + echo "Parameter #$count: $param" + count=$[ $count + 1 ] +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" similarity index 70% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" index 0f249e45..c580f8e6 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\275\277\347\224\250shift\345\221\275\344\273\244.sh" @@ -1,13 +1,13 @@ -#!/bin/bash +#!/usr/bin/env bash # shift n 移动变量 count=1 while [ -n "$1" ] do - echo "Parameter #$count = $1" - count=$[ $count + 1 ] - shift + echo "Parameter #$count = $1" + count=$[ $count + 1 ] + shift done echo -e "\n" diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" new file mode 100644 index 00000000..51c0d5be --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\206\347\246\273\345\217\202\346\225\260\345\222\214\351\200\211\351\241\271.sh" @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +#extracting options and parameters + +while [ -n "$1" ] +do + case "$1" in + -a) echo "Found the -a option" ;; + -b) echo "Found the -b option" ;; + -c) echo "Found the -c option" ;; + --) shift + break ;; + *) echo "$1 is not an option" ;; + esac + shift +done + +count=1 +for param in $@ +do + echo "Parameter #$count: $param" + count=$[ $count + 1 ] +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" similarity index 84% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index 7df0bfd0..f4b44acd 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\227\345\207\272\345\275\223\345\211\215\350\204\232\346\234\254\346\211\223\345\274\200\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing lsof with file descriptors diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" similarity index 94% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" index 8409e2cc..b841042e 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # using a temporary directory diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" similarity index 94% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" index 8659114b..82533a9b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # creating and using a temp file diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" index ba8cf187..36dae530 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\344\273\216\344\273\245\351\207\215\345\256\232\345\220\221\347\232\204\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246\344\270\255\346\201\242\345\244\215.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #storing STDOUT, then coming back to it diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" similarity index 92% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index 4d091cf4..dc0ec40b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\205\263\351\227\255\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing closing file descriptors diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" similarity index 85% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index c8e25d1d..4ad4b6ea 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\257\273\345\206\231\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing inpiut/output file descriptor diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" similarity index 60% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index df248e95..8aadb6d8 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\205\245\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # redirecting input file descriptors @@ -11,13 +11,13 @@ exec 0< test count=1 while read line do - echo "Line #$count: $line" - count=$[ $count + 1 ] + echo "Line #$count: $line" + count=$[ $count + 1 ] done exec 0<&6 read -p "Are you done now?" answer case $answer in - Y | y) echo "Goodbye" ;; - N | n) echo "Sorry, this is the end" ;; + Y | y) echo "Goodbye" ;; + N | n) echo "Sorry, this is the end" ;; esac diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" index 02c18437..356a8e4d 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\350\207\252\345\267\261\347\232\204\351\207\215\345\256\232\345\220\221/\345\210\233\345\273\272\350\276\223\345\207\272\346\226\207\344\273\266\346\217\217\350\277\260\347\254\246.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # using an alternative file descriptor diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" index 1ed6071b..866e2b25 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\217\202\346\225\260\350\256\241\346\225\260.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # getting the number of parameters diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" similarity index 92% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" index 90ebad0e..66d02dd3 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # creating a temp file in /tmp diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" similarity index 64% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" index d95a3688..0ad827ac 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250\350\204\232\346\234\254\344\270\255\344\275\277\347\224\250\351\207\215\345\256\232\345\220\221\350\276\223\345\205\245.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # redirecting the inpiut @@ -7,7 +7,7 @@ exec 0< test count=1 while read line do - echo "Line #$count : $line " - count=$[ $count + 1 ] + echo "Line #$count : $line " + count=$[ $count + 1 ] done diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" new file mode 100644 index 00000000..3fbd65da --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\345\270\246\345\200\274\347\232\204\351\200\211\351\241\271.sh" @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# extracting command line options and values + +while [ -n "$1" ] +do + case "$1" in + -a) echo "Found the -a option" ;; + -b) param="$2" + echo "Found the -b option, with parameter value $param" + shift ;; + -c) echo "Found the -c option" ;; + --) shift + break ;; + *) echo "$1 is not an option" ;; + esac + shift +done + +count=1 +for param in "$@" +do + echo "Parameter #$count : $param" + count=$[ $count + 1 ] +done diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" new file mode 100644 index 00000000..f5e5de35 --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\244\204\347\220\206\347\256\200\345\215\225\351\200\211\351\241\271.sh" @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# extracting command line options as parameters + +while [ -n "$1" ] +do + case "$1" in + -a) echo "Found the -a option" ;; + -b) echo "Found the -b optins" ;; + -c) echo "Found the -c optins" ;; + *) echo "$1 is not a valid options" ;; + esac + shift +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" similarity index 68% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" index bd9fa647..b7b1f749 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\277\253\351\200\237\346\270\205\351\231\244\346\226\207\344\273\266\346\210\226\346\227\245\345\277\227.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 清除日志 diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" new file mode 100644 index 00000000..fb7fa738 --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\212\223\345\217\226\346\211\200\346\234\211\346\225\260\346\215\256.sh" @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# testing $* and $@ + +count=1 +for param in "$*" +do + echo "\$* Parameter #$count = $param" + count=$[ $count + 1 ] +done + +count=1 +for param in "$@" +do + echo "\$@ Paramenter #$count = $param" + count=$[ $count + 1 ] +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" similarity index 94% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" index ee7a5a06..82e51e30 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\260\270\344\271\205\351\207\215\345\256\232\345\220\221.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing STDERR messages # redirecting all to a file diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\265\213\350\257\225.txt" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\265\213\350\257\225.txt" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\265\213\350\257\225.txt" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\346\265\213\350\257\225.txt" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" similarity index 92% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" index c8e33c21..24fbde30 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\216\267\345\217\226\347\224\250\346\210\267\350\276\223\345\205\245.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing the reading command @@ -19,7 +19,7 @@ read -p "Enter a number:" factorial=1 for (( count = 1; count <= $REPLY; count ++ )) do - factorial=$[ $factorial * $count ] + factorial=$[ $factorial * $count ] done echo "The factorial of $REPLY is $factorial" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" similarity index 93% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" index 84d31dbc..907d7e4f 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\256\260\345\275\225\344\277\241\346\201\257.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # using the tee command for logging #将输入一边发送到STDOUT,一边发送到日志文件 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" similarity index 70% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" index c5f89f5a..be23518b 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\217\202\346\225\260.sh" @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # using one command line parameter factorial=1 for (( number = 1; number <= $1; number ++ )) do - factorial=$[ $factorial * $number ] + factorial=$[ $factorial * $number ] done echo The factor of $1 is $factorial diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" similarity index 88% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" index 6d2efcf6..2f2f6d04 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\345\244\232\344\270\252\345\221\275\344\273\244\350\241\214\345\217\202\346\225\260.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # handing lots of parameters diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" similarity index 92% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" index 0637a453..36941cff 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\347\250\213\345\272\217\345\220\215.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # testing the $0 parameter diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" similarity index 82% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" index 037aa914..2e2e38f8 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\257\273\345\217\226\351\200\211\346\213\251\345\217\202\346\225\260.sh" @@ -11,8 +11,8 @@ serial=( start stop restart ) echo -n "请选择操作(可选值:start|stop|restart):" read oper if ! echo ${serial[@]} | grep -q ${oper}; then - echo "请选择正确操作(可选值:start|stop|restart)" - exit 1 + echo "请选择正确操作(可选值:start|stop|restart)" + exit 1 fi declare -a serial2 @@ -20,6 +20,6 @@ serial2=( dev test prod ) echo -n "请选择运行环境(可选值:dev|test|prod):" read profile if ! echo ${serial2[@]} | grep -q ${profile}; then - echo "请选择正确运行环境(可选值:dev|test|prod)" - exit 1 + echo "请选择正确运行环境(可选值:dev|test|prod)" + exit 1 fi diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" new file mode 100644 index 00000000..024c4a66 --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\350\266\205\346\227\266\345\222\214\350\276\223\345\205\245\350\256\241\346\225\260.sh" @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# timing the data entry + +if read -t 5 -p "Please enter your name:" name +then + echo "Hello, $name, welcome to my script" +else + #起到换行的作用 + echo + #输入计数 -n1 + read -n1 -p "Do you want to continue [Y/N]?" answer + case $answer in + Y | y) echo + echo "Fine, continue on..." ;; + N | n) echo + echo "OK,goodbye" ;; + *) echo + echo "OK, wrong, goodbye" + esac + echo "Sorry, this is the end of the script" +fi + diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" similarity index 90% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" rename to "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" index ba9b2c5d..27869813 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\351\232\220\350\227\217\346\226\271\345\274\217\350\257\273\345\217\226\346\225\260\346\215\256.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # hiding input data from monitor diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" similarity index 97% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" index dcd43a10..4c86de9a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Capture_Stats - Gather System Performance Statistics diff --git a/codes/shell/demos/debug-demo.sh "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\274\200\345\220\257debug\346\250\241\345\274\217.sh" similarity index 91% rename from codes/shell/demos/debug-demo.sh rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\274\200\345\220\257debug\346\250\241\345\274\217.sh" index f1e80a9d..0f75ff11 100644 --- a/codes/shell/demos/debug-demo.sh +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\345\274\200\345\220\257debug\346\250\241\345\274\217.sh" @@ -3,7 +3,7 @@ # 开启 debug set -x for (( i = 0; i < 3; i ++ )); do - printf ${i} + printf ${i} done # 关闭 debug set +x @@ -22,7 +22,7 @@ set +x # + set +x for i in {1..5}; do - printf ${i}; + printf ${i}; done printf "\n" # Output: 12345 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" similarity index 71% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" index ebc0eab2..2ac01f72 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\346\237\245\347\234\213uptime\350\216\267\345\217\226\345\234\250\347\272\277\347\224\250\346\210\267\346\225\260.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # uptime | sed 's/user.*$//' | gawk '{print $NF}' diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" similarity index 98% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" index 7b3d6462..da989496 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\224\237\346\210\220\346\212\245\345\221\212\350\204\232\346\234\254-\345\237\272\344\272\216\345\210\233\345\273\272\346\215\225\346\215\211\350\204\232\346\234\254.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Report_Stats - Generates Rpt from Captured Perf Stats diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" similarity index 88% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" index 5fa363d7..f3ad3c25 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\347\263\273\347\273\237\345\277\253\347\205\247\346\212\245\345\221\212.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Snapshot_Stats - produces a report for system stats @@ -47,8 +47,8 @@ uptime | sed -n '/,/s/,/ /gp' | gawk '{if($4 == "days" || $4 == "day") {print $2 echo for DISK in $DISK_TO_MONITOR # loop to check disk space do - echo -e "$DISK usage: \c" - df -h $DISK | sed -n '/% \//p' | gawk '{ print $5 }' + echo -e "$DISK usage: \c" + df -h $DISK | sed -n '/% \//p' | gawk '{ print $5 }' done # ################################################################## @@ -69,10 +69,10 @@ ZOMBIE_CHECK=`ps -al | gawk '{print $2,$4}' | grep Z` # if [ "$ZOMBIE_CHECK" = "" ] then - echo "No Zombie Process on System at this Time" + echo "No Zombie Process on System at this Time" else - echo "Current System Zombie Processes" - ps -al | gawk '{print $2,$4}' | grep Z + echo "Current System Zombie Processes" + ps -al | gawk '{print $2,$4}' | grep Z fi echo # diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\350\276\223\345\207\272\351\242\234\350\211\262.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\350\276\223\345\207\272\351\242\234\350\211\262.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\350\276\223\345\207\272\351\242\234\350\211\262.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\350\276\223\345\207\272\351\242\234\350\211\262.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" similarity index 58% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" index 99196b33..602547aa 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/Update_Problem.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Update_Problem - updates problem record in database @@ -15,27 +15,27 @@ MYSQL=`which mysql`" Problem_Trek -u root" # if [ $# -eq 0 ] # Check if id number was passed then -# If not passed ask for it - # - # Check if any unfinished records exist. - RECORDS_EXIST=`$MYSQL -Bse 'SELECT id_number FROM problem_logger where fixed_date="0000-00-00" OR prob_solutions=""'` - # - if [ "$RECORDS_EXIST" != "" ] - then - echo - echo "The following record(s) need updating..." - $MYSQL << EOF + # If not passed ask for it + # + # Check if any unfinished records exist. + RECORDS_EXIST=`$MYSQL -Bse 'SELECT id_number FROM problem_logger where fixed_date="0000-00-00" OR prob_solutions=""'` + # + if [ "$RECORDS_EXIST" != "" ] + then + echo + echo "The following record(s) need updating..." + $MYSQL << EOF SELECT id_number, report_date, prob_symptoms FROM problem_logger WHERE fixed_date="0000-00-00" OR prob_solutions=""\G EOF - fi - # - echo - echo "What is the ID number for the" - echo -e "problem you want to update?: \c" - read ANSWER - ID_NUMBER=$ANSWER + fi + # + echo + echo "What is the ID number for the" + echo -e "problem you want to update?: \c" + read ANSWER + ID_NUMBER=$ANSWER else - ID_NUMBER=$1 + ID_NUMBER=$1 fi # ########################################################## @@ -47,18 +47,18 @@ echo -e "Was Problem solved today? (y/n) \c" read ANSWER # case $ANSWER in - y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) - # - FIXED_DATE=`date +%Y%m%d` - ;; - *) - # if answer is anything but "yes", ask for date - echo - echo -e "What was the date of resolution? [YYYYMMDD] \c" - read ANSWER - # - FIXED_DATE=$ANSWER - ;; + y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) + # + FIXED_DATE=`date +%Y%m%d` + ;; + *) + # if answer is anything but "yes", ask for date + echo + echo -e "What was the date of resolution? [YYYYMMDD] \c" + read ANSWER + # + FIXED_DATE=$ANSWER + ;; esac # ######################################################## diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" similarity index 68% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" index 253805d3..04ca1e36 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\346\237\245\346\211\276\351\227\256\351\242\230.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Find_Problem - finds problem records using keywords @@ -15,17 +15,17 @@ MYSQL=`which mysql`" Problem_Trek -u root" # if [ -n "$1" ] # Check if a keyword was passed then -# Grab all the passed keywords - # - KEYWORDS=$@ # Grab all the params as separate words, same string + # Grab all the passed keywords + # + KEYWORDS=$@ # Grab all the params as separate words, same string # else -# Keyword(s) not passed, Ask for them - echo - echo "What keywords would you like to search for?" - echo -e "Please separate words by a space: \c" - read ANSWER - KEYWORDS=$ANSWER + # Keyword(s) not passed, Ask for them + echo + echo "What keywords would you like to search for?" + echo -e "Please separate words by a space: \c" + read ANSWER + KEYWORDS=$ANSWER fi # ####################################################### diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" similarity index 88% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" rename to "codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" index 4e976be2..9c1b0b02 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" +++ "b/codes/shell/\350\277\233\351\230\266\350\204\232\346\234\254/\351\227\256\351\242\230\350\267\237\350\270\252\346\225\260\346\215\256\345\272\223/\350\256\260\345\275\225\351\227\256\351\242\230.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Record_Problem - records system problems in database @@ -54,13 +54,13 @@ echo -e "Do you have a solution yet?(y/n) \c" read ANSWER # case $ANSWER in - y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) - ./Update_Problem.sh $id - # - ;; - *) - # if answer is anything but yes, just exit script - ;; + y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) + ./Update_Problem.sh $id + # + ;; + *) + # if answer is anything but yes, just exit script + ;; esac # ############################################################ diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" similarity index 79% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" index 85a1c541..35eefb5d 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/bash-shell\346\227\240\346\263\225\345\244\204\347\220\206\346\265\256\347\202\271\346\225\260.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #bash shell 仅能处理浮点数值,test命令无法处理val1变量中存储的浮点值 @@ -8,5 +8,5 @@ val1=`echo "scale=4; 10 / 3" | bc` echo "The test value is $val1" if [ $val1 -gt 3 ] then - echo "The result is larger than 3" + echo "The result is larger than 3" fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/break\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/break\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/break\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/case\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/case\347\244\272\344\276\213.sh" new file mode 100644 index 00000000..ad9dbb16 --- /dev/null +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/case\347\244\272\344\276\213.sh" @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +echo "input param: " $1 $2 $3 + +x=0 +if [[ -n $1 ]]; then + x=$1 +fi + +oper="" +if [[ -n $2 ]]; then + oper=$2 +fi + +y=0 +if [[ -n $3 ]]; then + y=$3 +fi + +exec +case ${oper} in + + | add) + val=`expr ${x} + ${y}` + echo "${x} + ${y} = ${val}" + ;; + - | sub) + val=`expr ${x} - ${y}` + echo "${x} - ${y} = ${val}" + ;; + * | mul) + val=`expr ${x} \* ${y}` + echo "${x} * ${y} = ${val}" + ;; + / | div) + val=`expr ${x} / ${y}` + echo "${x} / ${y} = ${val}" + ;; + *) + echo "Unknown oper!" + ;; +esac diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/continue\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/continue\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/continue\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/for\345\276\252\347\216\257\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\347\244\272\344\276\213.sh" similarity index 72% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\347\244\272\344\276\213.sh" index 408e32a2..e9ed676a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\344\275\277\347\224\250\347\244\272\344\276\213.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/if-elif-else\347\244\272\344\276\213.sh" @@ -3,22 +3,22 @@ ################### if 语句 ################### # 写成一行 if [[ 1 -eq 1 ]]; then - echo "1 -eq 1 result is: true"; + echo "1 -eq 1 result is: true"; fi # Output: 1 -eq 1 result is: true # 写成多行 if [[ "abc" -eq "abc" ]] then - echo ""abc" -eq "abc" result is: true" + echo ""abc" -eq "abc" result is: true" fi # Output: abc -eq abc result is: true ################### if else 语句 ################### if [[ 2 -ne 1 ]]; then - echo "true" + echo "true" else - echo "false" + echo "false" fi # Output: true @@ -26,10 +26,10 @@ fi x=10 y=20 if [[ ${x} > ${y} ]]; then - echo "${x} > ${y}" + echo "${x} > ${y}" elif [[ ${x} < ${y} ]]; then - echo "${x} < ${y}" + echo "${x} < ${y}" else - echo "${x} = ${y}" + echo "${x} = ${y}" fi # Output: 10 < 20 diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/output.txt" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/output.txt" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/output.txt" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/output.txt" diff --git a/codes/shell/demos/statement/select-demo.sh "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/select\350\217\234\345\215\225\347\244\272\344\276\213.sh" similarity index 57% rename from codes/shell/demos/statement/select-demo.sh rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/select\350\217\234\345\215\225\347\244\272\344\276\213.sh" index 4e59b5a3..ef786daf 100644 --- a/codes/shell/demos/statement/select-demo.sh +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/select\350\217\234\345\215\225\347\244\272\344\276\213.sh" @@ -5,10 +5,10 @@ select ITEM in bower npm gem pip do echo -n "Enter the package name: " && read PACKAGE case ${ITEM} in - bower) bower install ${PACKAGE} ;; - npm) npm install ${PACKAGE} ;; - gem) gem install ${PACKAGE} ;; - pip) pip install ${PACKAGE} ;; + bower) bower install ${PACKAGE} ;; + npm) npm install ${PACKAGE} ;; + gem) gem install ${PACKAGE} ;; + pip) pip install ${PACKAGE} ;; esac break # 避免无限循环 done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/until\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/until\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/until\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\347\244\272\344\276\213.sh" similarity index 100% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\344\275\277\347\224\250\347\244\272\344\276\213.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/while\345\276\252\347\216\257\347\244\272\344\276\213.sh" diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" similarity index 84% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" index 8e978887..3f8c4969 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\273\216\345\221\275\344\273\244\350\257\273\345\217\226\345\200\274.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #reading values from a file @@ -15,5 +15,5 @@ IFS=$'\n' for state in `cat $file` do - echo "Visit beautiful $state" + echo "Visit beautiful $state" done diff --git "a/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" new file mode 100644 index 00000000..26002125 --- /dev/null +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\345\234\206\346\213\254\345\217\267.sh" @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# using double parenthesis + +var1=10 + +if (($var1 ** 2 > 90)) +then + ((var2 = $var1 ** 2)) + echo "The square of $var1 if $var2" +fi + diff --git "a/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" new file mode 100644 index 00000000..8b9ce3f9 --- /dev/null +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\217\214\346\226\271\346\213\254\345\217\267.sh" @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# using pattern matching + +if [[ $USER == r* ]] +then + echo "Hello $USER" +else + echo "Sorry, I do not know you" +fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" similarity index 85% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" index 728832c7..dcb58809 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\244\247\344\272\216\345\260\217\344\272\216\345\217\267.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # 大于小于号必须转义,否则shell会将它们当做重定向符号而把字符串值当做文件名处理 # 大于小于号顺序和sort命令所采用的有所不同 @@ -10,9 +10,9 @@ val2=hockey ################### 错误使用大于小于号 ################## if [[ $val1 > $val2 ]] then - echo "$val1 is greater than $val2" + echo "$val1 is greater than $val2" else - echo "$val1 is less than $val2" + echo "$val1 is less than $val2" fi ################### 正确使用大于小于号 ################### diff --git "a/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" new file mode 100644 index 00000000..b1c406cc --- /dev/null +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\345\265\214\345\245\227\345\276\252\347\216\257\345\271\266\344\277\256\346\224\271IFS.sh" @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +#changing the IFS value + +IFS.OLD=$IFS +IFS=$'\n' +for entry in `cat /etc/passwd` +do + echo "Values in $entry -" + IFS=: + for value in $entry + do + echo " $value" + done +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" similarity index 56% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" index 9e201002..354c6c5a 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\347\256\241\351\201\223\346\210\226\351\207\215\345\256\232\345\220\221.sh" @@ -1,20 +1,20 @@ -#!/bin/bash +#!/usr/bin/env bash # redirecting the for output to a file for file in /home/tiandi/* do - if [ -d "$file" ] - then - echo "$file is a directory" - else - echo "$file is a file" - fi + if [ -d "$file" ] + then + echo "$file is a directory" + else + echo "$file is a file" + fi done > output.txt # piping a loop to another command for state in "North Dakota" Connecticut do - echo "$state is next place to go" + echo "$state is next place to go" done | sort echo "This completes our travels" diff --git "a/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" new file mode 100644 index 00000000..81d03d74 --- /dev/null +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\344\275\277\347\224\250\351\200\232\351\205\215\347\254\246\345\244\204\347\220\206\347\233\256\345\275\225.sh" @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +#iterate through all the files in a directory +for file in /home/tiandi/test/* +do + if [ -d "$file" ] + then + echo "$file is a directory" + elif [ -f "$file" ] + then + echo "$file is a file" + fi +done diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" similarity index 79% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" index 2ea2de5e..282001b7 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\346\263\250\346\204\217test\345\244\247\345\260\217\345\206\231\351\241\272\345\272\217\345\222\214sort\344\270\215\345\220\214.sh" @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash #test命令中,大小字母会被当成小于小写字符,而在sort中,小写字母会先出现,test使用标准的ASCII排序,sort使用本地化语言设置进行排序,对于英语,本地化设置制定了排序顺序中小写字母出现在大写字母之前 @@ -7,7 +7,7 @@ var2=testing if [ $val1 \> $val2 ] then - echo '$val1 is greater than $val2' + echo '$val1 is greater than $val2' else - echo '$val1 is less than $val2' + echo '$val1 is less than $val2' fi diff --git "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" similarity index 72% rename from "codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" rename to "codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" index d8723d7c..bae02da2 100644 --- "a/codes/shell/\347\244\272\344\276\213\350\204\232\346\234\254/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" +++ "b/codes/shell/\351\200\273\350\276\221\346\216\247\345\210\266/\350\257\273\345\217\226\351\207\214\350\241\250\344\270\255\345\244\215\346\235\202\347\232\204\345\200\274.sh" @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash # another example of how not to use the for command for test in I don't know if this'll work do - echo "word:$test" + echo "word:$test" done diff --git a/docs/README.md b/docs/README.md index 04ce5d0b..086c9330 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,13 +4,15 @@ > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) -| :wrench: | :shell: | :memo: | 📚 | -| :-------------------: | :-------------------: | :---------------: | :-------------------: | -| [软件运维](#软件运维) | [运维和脚本](#运维和脚本) | [知识点](#知识点) | [学习资源](#学习资源) | +| 🛠 | 🐚 | 📝 | 📚 | +| :-------------------: | :-----------------------: | :-----------: | :-------------------: | +| [软件运维](#软件运维) | [Shell 脚本](#Shell-脚本) | [教程](#教程) | [学习资源](#学习资源) | ## 软件运维 > 本章节内容介绍日常开发中常见的一些软件、工具的安装、配置。 +> +> 配套安装脚本:🐚 [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - 开发环境 - [JDK 安装](linux/soft/jdk-install.md) @@ -35,22 +37,21 @@ - [Mongodb 运维](linux/soft/mongodb-ops.md) - [Redis 运维](linux/soft/redis-ops.md) -## 运维和脚本 +## Shell 脚本 + +### Shell 脚本大全 + +**Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 -- [系统运维脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) -- [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) -- [工具脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) -- [Vim 应用指南](linux/ops/vim.md) -- [Zsh 应用指南](linux/ops/zsh.md) -- [Shell 教程](linux/ops/shell.md) -- [Python 教程](linux/ops/python.md) -- [Systemd 入门教程](linux/ops/systemd.md) +源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) -> 提供一键式运维、配置软件脚本 +### CentOS 常规操作运维脚本集合 -## 知识点 +本人作为一名 Java 后端,苦于经常在 CentOS 环境上开荒虚拟机。为提高效率,写了一套 Shell 脚本,提供如下功能:安装常用 lib 库、命令工具、设置 DNS、NTP、配置国内 yum 源、一键安装常用软件等。 -### Linux +源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) + +## 教程 - [Linux 命令教程](linux/cli/README.md) - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) @@ -62,23 +63,23 @@ - [Linux 网络管理](linux/cli/Linux网络管理.md) - [Linux 硬件管理](linux/cli/Linux硬件管理.md) - [Linux 软件管理](linux/cli/Linux硬件管理.md) -- [Linux 运维](linux/ops/README.md) - - [linux 典型运维应用](linux/ops/linux典型运维应用.md) - - [samba 使用详解](linux/ops/samba使用详解.md) - -### Docker - - [Docker 教程](docker) - [Docker 应用指南](docker/docker.md) - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - -### Git - - [Git 教程](git/README.md) - [Git 快速指南](git/git-quickstart.md) - [Git 配置](git/git-configuration.md) - [git-flow 工作流](git/git-flow.md) - [Git 常见问题](git/git-faq.md) +- 运维 + - [linux 典型运维应用](linux/ops/linux典型运维应用.md) + - [samba 使用详解](linux/ops/samba使用详解.md) + - [Systemd 教程](linux/ops/systemd.md) +- 脚本 + - [Vim 应用指南](linux/ops/vim.md) + - [Zsh 应用指南](linux/ops/zsh.md) + - [Shell 教程](linux/ops/shell.md) + - [Python 教程](linux/ops/python.md) ## 学习资源 diff --git a/docs/index.html b/docs/index.html index 41697360..3c2dab52 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,220 +1,235 @@ - - Linux Tutorial - - - - - - - + + - + - + + @media (max-width: 600px) { + .markdown-section pre > code { + padding-top: 5px; + padding-bottom: 5px; + } + + pre:after { + content: "" !important; + } + } + + @media (min-width: 600px) { + pre code { + padding-left: 20px !important; + } + } + + @media (max-width: 600px) { + pre { + padding-left: 0px !important; + padding-right: 0px !important; + } + } +
正在加载...
- - - - + @media (max-width: 600px) { + pre { + padding-left: 0 !important; + padding-right: 0 !important; + } + } + + + +
正在加载...
+ + + + + - - - - - - - + + + + + + diff --git a/docs/linux/ops/python.md b/docs/linux/ops/python.md deleted file mode 100644 index 10b1bfd7..00000000 --- a/docs/linux/ops/python.md +++ /dev/null @@ -1,898 +0,0 @@ ---- -title: Python -date: 2018-06-28 -categories: -- linux -tags: -- linux -- python ---- - -# Python - - - -- [解释器](#解释器) -- [注释](#注释) -- [数据类型](#数据类型) -- [操作符](#操作符) - - [算术运算符](#算术运算符) - - [比较运算符](#比较运算符) - - [赋值运算符](#赋值运算符) - - [位运算符](#位运算符) - - [逻辑运算符](#逻辑运算符) - - [成员运算符](#成员运算符) - - [身份运算符](#身份运算符) - - [运算符优先级](#运算符优先级) -- [控制语句](#控制语句) - - [条件语句](#条件语句) - - [循环语句](#循环语句) -- [函数](#函数) - - [函数变量作用域](#函数变量作用域) - - [关键字参数](#关键字参数) - - [可变参数列表](#可变参数列表) - - [返回值](#返回值) -- [异常](#异常) - - [异常处理](#异常处理) - - [抛出异常](#抛出异常) - - [自定义异常](#自定义异常) -- [面向对象](#面向对象) - - [面向对象技术简介](#面向对象技术简介) - - [类定义](#类定义) - - [类对象](#类对象) - - [类的方法](#类的方法) - - [继承](#继承) - - [多继承](#多继承) - - [方法重写](#方法重写) - - [类属性与方法](#类属性与方法) -- [标准库概览](#标准库概览) - - [操作系统接口](#操作系统接口) - - [文件通配符](#文件通配符) - - [命令行参数](#命令行参数) - - [错误输出重定向和程序终止](#错误输出重定向和程序终止) - - [字符串正则匹配](#字符串正则匹配) - - [数学](#数学) - - - -# Python 编程 - -## 解释器 - -Linux/Unix 的系统上,Python 解释器通常被安装在 `/usr/local/bin/python3.4` 这样的有效路径(目录)里。 - -我们可以将路径 `/usr/local/bin` 添加到您的 Linux/Unix 操作系统的环境变量中,这样您就可以通过 shell 终端输入下面的命令来启动 Python 。 - -在 Linux/Unix 系统中,你可以在脚本顶部添加以下命令让 Python 脚本可以像 SHELL 脚本一样可直接执行: - -```python -#! /usr/bin/env python3.4 -``` - -## 注释 - -Python 中的注释有三种形式: - -- 以 `#` 开头 -- 以 `'''` 开始,以 `'''` 结尾 -- 以 `"""` 开始,以 `"""` 结尾 - -```python -# 单行注释 - -''' -这是多行注释,用三个单引号 -这是多行注释,用三个单引号 -这是多行注释,用三个单引号 -''' - -""" -这是多行注释,用三个双引号 -这是多行注释,用三个双引号 -这是多行注释,用三个双引号 -""" -``` - -## 数据类型 - -Python3 中有六个标准的数据类型: - -- Numbers(数字) -- String(字符串) -- List(列表) -- Tuple(元组) -- Sets(集合) -- Dictionaries(字典) - -## 操作符 - -Python 语言支持以下类型的运算符: - -- 算术运算符 - -- 比较(关系)运算符 - -- 赋值运算符 - -- 逻辑运算符 - -- 位运算符 - -- 成员运算符 - -- 身份运算符 - -- 运算符优先级 - -### 算术运算符 - -| 运算符 | 描述 | 实例 | -| ------ | ----------------------------------------------- | --------------------------------------- | -| + | 加 - 两个对象相加 | a + b 输出结果 31 | -| - | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 -11 | -| \* | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a \* b 输出结果 210 | -| / | 除 - x 除以 y | b / a 输出结果 2.1 | -| % | 取模 - 返回除法的余数 | b % a 输出结果 1 | -| \*\* | 幂 - 返回 x 的 y 次幂 | a\*\*b 为 10 的 21 次方 | -| // | 取整除 - 返回商的整数部分 | 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0 | - -### 比较运算符 - -| 运算符 | 描述 | 实例 | -| ------ | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| == | 等于 - 比较对象是否相等 | (a == b) 返回 False。 | -| != | 不等于 - 比较两个对象是否不相等 | (a != b) 返回 True. | -| > | 大于 - 返回 x 是否大于 y | (a > b) 返回 False。 | -| < | 小于 - 返回 x 是否小于 y。所有比较运算符返回 1 表示真,返回 0 表示假。这分别与特殊的变量 True 和 False 等价。注意,这些变量名的大写。 | (a < b) 返回 True。 | -| >= | 大于等于 - 返回 x 是否大于等于 y。 | (a >= b) 返回 False。 | -| <= | 小于等于 - 返回 x 是否小于等于 y。 | (a <= b) 返回 True。 | - -### 赋值运算符 - -| 运算符 | 描述 | 实例 | -| ------ | ---------------- | ------------------------------------- | -| = | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c | -| += | 加法赋值运算符 | c += a 等效于 c = c + a | -| -= | 减法赋值运算符 | c -= a 等效于 c = c - a | -| \*= | 乘法赋值运算符 | c _= a 等效于 c = c _ a | -| /= | 除法赋值运算符 | c /= a 等效于 c = c / a | -| %= | 取模赋值运算符 | c %= a 等效于 c = c % a | -| \*\*= | 幂赋值运算符 | c **= a 等效于 c = c ** a | -| //= | 取整除赋值运算符 | c //= a 等效于 c = c // a | - -### 位运算符 - -| 运算符 | 描述 | 实例 | -| ------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -| & | 按位与运算符:参与运算的两个值,如果两个相应位都为 1,则该位的结果为 1,否则为 0 | (a & b) 输出结果 12 ,二进制解释: 0000 1100 | -| \| | 按位或运算符:只要对应的二个二进位有一个为 1 时,结果位就为 1。 | (a \| b) 输出结果 61 ,二进制解释: 0011 1101 | -| ^ | 按位异或运算符:当两对应的二进位相异时,结果为 1 | (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 | -| \~ | 按位取反运算符:对数据的每个二进制位取反,即把 1 变为 0,把 0 变为 1 | (\~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。 | -| << | 左移动运算符:运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补 0。 | a << 2 输出结果 240 ,二进制解释: 1111 0000 | -| >> | 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数 | a >> 2 输出结果 15 ,二进制解释: 0000 1111 | - -### 逻辑运算符 - -| 运算符 | 逻辑表达式 | 描述 | 实例 | -| ------ | ---------- | ----------------------------------------------------------------------- | ----------------------- | -| and | x and y | 布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 | (a and b) 返回 20。 | -| or | x or y | 布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。 | (a or b) 返回 10。 | -| not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b) 返回 False | - -### 成员运算符 - -| 运算符 | 描述 | 实例 | -| ------ | ------------------------------------------------------- | ------------------------------------------------- | -| in | 如果在指定的序列中找到值返回 True,否则返回 False。 | x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 | -| not in | 如果在指定的序列中没有找到值返回 True,否则返回 False。 | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 | - -### 身份运算符 - -| 运算符 | 描述 | 实例 | -| ------ | ------------------------------------------- | ---------------------------------------------------------- | -| is | is 是判断两个标识符是不是引用自一个对象 | x is y, 如果 id(x) 等于 id(y) , **is** 返回结果 1 | -| is not | is not 是判断两个标识符是不是引用自不同对象 | x is not y, 如果 id(x) 不等于 id(y). **is not** 返回结果 1 | - -### 运算符优先级 - -| 运算符 | 描述 | -| --------------------------- | ------------------------------------------------------ | -| \*\* | 指数 (最高优先级) | -| \~ + - | 按位翻转, 一元加号和减号 (最后两个的方法名为 +@ 和 -@) | -| \* / % // | 乘,除,取模和取整除 | -| + - | 加法减法 | -| >> << | 右移,左移运算符 | -| & | 位 'AND' | -| ^ \| | 位运算符 | -| <= < > >= | 比较运算符 | -| <> == != | 等于运算符 | -| = %= /= //= -= += \*= \*\*= | 赋值运算符 | -| is is not | 身份运算符 | -| in not in | 成员运算符 | -| not or and | 逻辑运算符 | - -## 控制语句 - -### 条件语句 - -```python -if condition_1: - statement_block_1 -elif condition_2: - statement_block_2 -else: - statement_block_3 -``` - -### 循环语句 - -#### while - -```python -while 判断条件: - statements -``` - -#### for - -```python -for in : - -``` - -#### range() - -```python -for i in range(0, 10, 3) : - print(i) -``` - -#### break 和 continue - -- break 语句可以跳出 for 和 while 的循环体。 -- continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。 - -#### pass - -pass 语句什么都不做。它只在语法上需要一条语句但程序不需要任何操作时使用.例如: - -```python -while True: - pass # 等待键盘中断 (Ctrl+C) -``` - -## 函数 - -Python 定义函数使用 def 关键字,一般格式如下: - -```python -def 函数名(参数列表): - 函数体 -``` - -### 函数变量作用域 - -```python -#!/usr/bin/env python3 -a = 4 # 全局变量 - -def print_func1(): - a = 17 # 局部变量 - print("in print_func a = ", a) -def print_func2(): - print("in print_func a = ", a) -print_func1() -print_func2() -print("a = ", a) -``` - -以上实例运行结果如下: - -```python -in print_func a = 17 -in print_func a = 4 -a = 4 -``` - -### 关键字参数 - -函数也可以使用 kwarg=value 的关键字参数形式被调用.例如,以下函数: - -```python -def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): - print("-- This parrot wouldn't", action, end=' ') - print("if you put", voltage, "volts through it.") - print("-- Lovely plumage, the", type) - print("-- It's", state, "!") -``` - -可以以下几种方式被调用: - -```python -parrot(1000) # 1 positional argument -parrot(voltage=1000) # 1 keyword argument -parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments -parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments -parrot('a million', 'bereft of life', 'jump') # 3 positional arguments -parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword -``` - -以下为错误调用方法: - -``` -parrot() # required argument missing -parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument -parrot(110, voltage=220) # duplicate value for the same argument -parrot(actor='John Cleese') # unknown keyword argument -``` - -### 可变参数列表 - -最后,一个最不常用的选择是可以让函数调用可变个数的参数.这些参数被包装进一个元组(查看元组和序列).在这些可变个数的参数之前,可以有零到多个普通的参数: - -```python -def arithmetic_mean(*args): - sum = 0 - for x in args: - sum += x - return sum -``` - -### 返回值 - -Python 的函数的返回值使用 return 语句,可以将函数作为一个值赋值给指定变量: - -```python -def return_sum(x,y): - c = x + y - return c -``` - -## 异常 - -### 异常处理 - -try 语句按照如下方式工作; - -- 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句) -- 如果没有异常发生,忽略 except 子句,try 子句执行后结束。 -- 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。最后执行 try 语句之后的代码。 -- 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。 -- 不管 try 子句里面有没有发生异常,finally 子句都会执行。 - -```python -import sys - -try: - f = open('myfile.txt') - s = f.readline() - i = int(s.strip()) -except OSError as err: - print("OS error: {0}".format(err)) -except ValueError: - print("Could not convert data to an integer.") -except: - print("Unexpected error:", sys.exc_info()[0]) - raise -finally: - # 清理行为 -``` - -### 抛出异常 - -Python 使用 raise 语句抛出一个指定的异常。例如: - -```python ->>> raise NameError('HiThere') -Traceback (most recent call last): - File "", line 1, in ? -NameError: HiThere -``` - -### 自定义异常 - -可以通过创建一个新的 exception 类来拥有自己的异常。异常应该继承自 Exception 类,或者直接继承,或者间接继承。 - -当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是为这个包建立一个基础异常类,然后基于这个基础类为不同的错误情况创建不同的子类: - -```python -class Error(Exception): - """Base class for exceptions in this module.""" - pass - -class InputError(Error): - """Exception raised for errors in the input. - - Attributes: - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - -class TransitionError(Error): - """Raised when an operation attempts a state transition that's not - allowed. - - Attributes: - previous -- state at beginning of transition - next -- attempted new state - message -- explanation of why the specific transition is not allowed - """ - - def __init__(self, previous, next, message): - self.previous = previous - self.next = next - self.message = message -``` - -大多数的异常的名字都以"Error"结尾,就跟标准的异常命名一样。 - -## 面向对象 - -### 面向对象技术简介 - -- **类(Class):** 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。 - -- **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。 - -- **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。 - -- **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。 - -- **实例变量:**定义在方法中的变量,只作用于当前实例的类。 - -- **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个 Dog 类型的对象派生自 Animal 类,这是模拟"是一个(is-a)"关系(例图,Dog 是一个 Animal)。 - -- **实例化:**创建一个类的实例,类的具体对象。 - -- **方法:**类中定义的函数。 - -- **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。 - -### 类定义 - -语法格式如下: - -```python -class ClassName: - - . - . - . - -``` - -类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性。 - -### 类对象 - -类对象支持两种操作:属性引用和实例化。 - -属性引用使用和 Python 中所有的属性引用一样的标准语法:**obj.name**。 - -类对象创建后,类命名空间中所有的命名都是有效属性名。所以如果类定义是这样: - -```python -#!/usr/bin/python3 - -class MyClass: - """一个简单的类实例""" - i = 12345 - def f(self): - return 'hello world' - -# 实例化类 -x = MyClass() - -# 访问类的属性和方法 -print("MyClass 类的属性 i 为:", x.i) -print("MyClass 类的方法 f 输出为:", x.f()) -``` - -实例化类: - -```python -# 实例化类 -x = MyClass() -# 访问类的属性和方法 -``` - -以上创建了一个新的类实例并将该对象赋给局部变量 x,x 为空的对象。 - -执行以上程序输出结果为: - -``` -MyClass 类的属性 i 为: 12345 -MyClass 类的方法 f 输出为: hello world -``` - -很多类都倾向于将对象创建为有初始状态的。因此类可能会定义一个名为 **init**() 的特殊方法(构造方法),像下面这样: - -```python -def __init__(self): - self.data = [] -``` - -类定义了 **init**() 方法的话,类的实例化操作会自动调用 **init**() 方法。所以在下例中,可以这样创建一个新的实例: - -```python -x = MyClass() -``` - -当然, **init**() 方法可以有参数,参数通过 **init**() 传递到类的实例化操作上。例如: - -```python ->>> class Complex: -... def __init__(self, realpart, imagpart): -... self.r = realpart -... self.i = imagpart -... ->>> x = Complex(3.0, -4.5) ->>> x.r, x.i -(3.0, -4.5) -``` - -### 类的方法 - -在类地内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数: - -```python -#!/usr/bin/python3 - -#类定义 -class people: - #定义基本属性 - name = '' - age = 0 - #定义私有属性,私有属性在类外部无法直接进行访问 - __weight = 0 - #定义构造方法 - def __init__(self,n,a,w): - self.name = n - self.age = a - self.__weight = w - def speak(self): - print("%s 说: 我 %d 岁。" %(self.name,self.age)) - -# 实例化类 -p = people('W3Cschool',10,30) -p.speak() -``` - -执行以上程序输出结果为: - -``` -W3Cschool 说: 我 10 岁。 -``` - -### 继承 - -Python 同样支持类的继承,如果一种语言不支持继承就,类就没有什么意义。派生类的定义如下所示: - -```python -class DerivedClassName(BaseClassName1): - - . - . - . - -``` - -需要注意圆括号中基类的顺序,若是基类中有相同的方法名,而在子类使用时未指定,python 从左至右搜索 即方法在子类中未找到时,从左到右查找基类中是否包含方法。 - -BaseClassName(示例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用: - -``` -class DerivedClassName(modname.BaseClassName): -``` - -**实例** - -```python -#!/usr/bin/python3 - -#类定义 -class people: - #定义基本属性 - name = '' - age = 0 - #定义私有属性,私有属性在类外部无法直接进行访问 - __weight = 0 - #定义构造方法 - def __init__(self,n,a,w): - self.name = n - self.age = a - self.__weight = w - def speak(self): - print("%s 说: 我 %d 岁。" %(self.name,self.age)) - -#单继承示例 -class student(people): - grade = '' - def __init__(self,n,a,w,g): - #调用父类的构函 - people.__init__(self,n,a,w) - self.grade = g - #覆写父类的方法 - def speak(self): - print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) - - - -s = student('ken',10,60,3) -s.speak() -``` - -执行以上程序输出结果为: - -``` -ken 说: 我 10 岁了,我在读 3 年级 -``` - -### 多继承 - -Python 同样有限的支持多继承形式。多继承的类定义形如下例: - -```python -class DerivedClassName(Base1, Base2, Base3): - - . - . - . - -``` - -需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python 从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。 - -```python -#!/usr/bin/python3 - -#类定义 -class people: - #定义基本属性 - name = '' - age = 0 - #定义私有属性,私有属性在类外部无法直接进行访问 - __weight = 0 - #定义构造方法 - def __init__(self,n,a,w): - self.name = n - self.age = a - self.__weight = w - def speak(self): - print("%s 说: 我 %d 岁。" %(self.name,self.age)) - -#单继承示例 -class student(people): - grade = '' - def __init__(self,n,a,w,g): - #调用父类的构函 - people.__init__(self,n,a,w) - self.grade = g - #覆写父类的方法 - def speak(self): - print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) - -#另一个类,多重继承之前的准备 -class speaker(): - topic = '' - name = '' - def __init__(self,n,t): - self.name = n - self.topic = t - def speak(self): - print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic)) - -#多重继承 -class sample(speaker,student): - a ='' - def __init__(self,n,a,w,g,t): - student.__init__(self,n,a,w,g) - speaker.__init__(self,n,t) - -test = sample("Tim",25,80,4,"Python") -test.speak() #方法名同,默认调用的是在括号中排前地父类的方法 -``` - -执行以上程序输出结果为: - -``` -我叫 Tim,我是一个演说家,我演讲的主题是 Python -``` - -### 方法重写 - -如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法,实例如下: - -```python -#!/usr/bin/python3 - -class Parent: # 定义父类 - def myMethod(self): - print ('调用父类方法') - -class Child(Parent): # 定义子类 - def myMethod(self): - print ('调用子类方法') - -c = Child() # 子类实例 -c.myMethod() # 子类调用重写方法 -``` - -执行以上程序输出结果为: - -``` -调用子类方法 -``` - -### 类属性与方法 - -#### 类的私有属性 - -**\_\_private_attrs**:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时**self.\_\_private_attrs**。 - -#### 类的方法 - -在类地内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数 - -#### 类的私有方法 - -**\_\_private_method**:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 **slef.\_\_private_methods**。 - -实例如下: - -```python -#!/usr/bin/python3 - -class JustCounter: - __secretCount = 0 # 私有变量 - publicCount = 0 # 公开变量 - - def count(self): - self.__secretCount += 1 - self.publicCount += 1 - print (self.__secretCount) - -counter = JustCounter() -counter.count() -counter.count() -print (counter.publicCount) -print (counter.__secretCount) # 报错,实例不能访问私有变量 -``` - -执行以上程序输出结果为: - -``` -1 -2 -2 -Traceback (most recent call last): - File "test.py", line 16, in - print (counter.__secretCount) # 报错,实例不能访问私有变量 -AttributeError: 'JustCounter' object has no attribute '__secretCount' -``` - -#### 类的专有方法: - -- \***\*init** :\*\* 构造函数,在生成对象时调用 -- \***\*del** :\*\* 析构函数,释放对象时使用 -- \***\*repr** :\*\* 打印,转换 -- \***\*setitem** :\*\* 按照索引赋值 -- \***\*getitem**:\*\* 按照索引获取值 -- \***\*len**:\*\* 获得长度 -- \***\*cmp**:\*\* 比较运算 -- \***\*call**:\*\* 函数调用 -- \***\*add**:\*\* 加运算 -- \***\*sub**:\*\* 减运算 -- \***\*mul**:\*\* 乘运算 -- \***\*div**:\*\* 除运算 -- \***\*mod**:\*\* 求余运算 -- \***\*pow**:\*\* 乘方 - -#### 运算符重载 - -Python 同样支持运算符重载,我么可以对类的专有方法进行重载,实例如下: - -```python -#!/usr/bin/python3 - -class Vector: - def __init__(self, a, b): - self.a = a - self.b = b - - def __str__(self): - return 'Vector (%d, %d)' % (self.a, self.b) - - def __add__(self,other): - return Vector(self.a + other.a, self.b + other.b) - -v1 = Vector(2,10) -v2 = Vector(5,-2) -print (v1 + v2) -``` - -以上代码执行结果如下所示: - -``` -Vector(7,8) -``` - -## 标准库概览 - -### 操作系统接口 - -os 模块提供了不少与操作系统相关联的函数。 - -```python ->>> import os ->>> os.getcwd() # 返回当前的工作目录 -'C:\\Python34' ->>> os.chdir('/server/accesslogs') # 修改当前的工作目录 ->>> os.system('mkdir today') # 执行系统命令 mkdir -0 -``` - -### 文件通配符 - -glob 模块提供了一个函数用于从目录通配符搜索中生成文件列表: - -```python ->>> import glob ->>> glob.glob('*.py') -['primes.py', 'random.py', 'quote.py'] -``` - -### 命令行参数 - -通用工具脚本经常调用命令行参数。这些命令行参数以链表形式存储于 sys 模块的 argv 变量。例如在命令行中执行 `python demo.py one two three` 后可以得到以下输出结果: - -```python ->>> import sys ->>> print(sys.argv) -['demo.py', 'one', 'two', 'three'] -``` - -### 错误输出重定向和程序终止 - -sys 还有 stdin,stdout 和 stderr 属性,即使在 stdout 被重定向时,后者也可以用于显示警告和错误信息。 - -```python ->>> sys.stderr.write('Warning, log file not found starting a new one\n') -Warning, log file not found starting a new one -``` - -### 字符串正则匹配 - -re 模块为高级字符串处理提供了正则表达式工具。对于复杂的匹配和处理,正则表达式提供了简洁、优化的解决方案: - -```python ->>> import re ->>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') -['foot', 'fell', 'fastest'] ->>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat') -'cat in the hat' -``` - -### 数学 - -math 模块为浮点运算提供了对底层 C 函数库的访问: - -```python ->>> import math ->>> math.cos(math.pi / 4) -0.70710678118654757 ->>> math.log(1024, 2) -10.0 -``` - -# 资料 - -- https://github.com/vinta/awesome-python - 资源大全 -- https://github.com/jobbole/awesome-python-cn - 资源大全 -- https://github.com/scrapy/scrapy - python 爬虫框架 -- https://github.com/faif/python-patterns - python 设计模式 -- https://github.com/kennethreitz/python-guide - python 最佳实践 diff --git a/docs/linux/ops/shell.md b/docs/linux/ops/shell.md deleted file mode 100644 index dc954bce..00000000 --- a/docs/linux/ops/shell.md +++ /dev/null @@ -1,1945 +0,0 @@ -# 一篇文章让你彻底掌握 shell 语言 - -> 由于 bash 是 Linux 标准默认的 shell 解释器,可以说 bash 是 shell 编程的基础。 -> -> 本文主要介绍 bash 的语法,对于 linux 指令不做任何介绍。 -> -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog)」 -> 🔁 本文的源码已归档到 [linux-tutorial](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell/demos) - -``` -███████╗██╗ ██╗███████╗██╗ ██╗ -██╔════╝██║ ██║██╔════╝██║ ██║ -███████╗███████║█████╗ ██║ ██║ -╚════██║██╔══██║██╔══╝ ██║ ██║ -███████║██║ ██║███████╗███████╗███████╗ -``` - - - -- [1. 简介](#1-简介) - - [1.1. 什么是 shell](#11-什么是-shell) - - [1.2. 什么是 shell 脚本](#12-什么是-shell-脚本) - - [1.3. Shell 环境](#13-shell-环境) - - [1.4. 模式](#14-模式) -- [2. 基本语法](#2-基本语法) - - [2.1. 解释器](#21-解释器) - - [2.2. 注释](#22-注释) - - [2.3. echo](#23-echo) - - [2.4. printf](#24-printf) -- [3. 变量](#3-变量) - - [3.1. 变量命名原则](#31-变量命名原则) - - [3.2. 声明变量](#32-声明变量) - - [3.3. 只读变量](#33-只读变量) - - [3.4. 删除变量](#34-删除变量) - - [3.5. 变量类型](#35-变量类型) - - [3.6. 变量示例源码](#36-变量示例源码) -- [4. 字符串](#4-字符串) - - [4.1. 单引号和双引号](#41-单引号和双引号) - - [4.2. 拼接字符串](#42-拼接字符串) - - [4.3. 获取字符串长度](#43-获取字符串长度) - - [4.4. 截取子字符串](#44-截取子字符串) - - [4.5. 查找子字符串](#45-查找子字符串) - - [4.6. 字符串示例源码](#46-字符串示例源码) -- [5. 数组](#5-数组) - - [5.1. 创建数组](#51-创建数组) - - [5.2. 访问数组元素](#52-访问数组元素) - - [5.3. 访问数组长度](#53-访问数组长度) - - [5.4. 向数组中添加元素](#54-向数组中添加元素) - - [5.5. 从数组中删除元素](#55-从数组中删除元素) - - [5.6. 数组示例源码](#56-数组示例源码) -- [6. 运算符](#6-运算符) - - [6.1. 算术运算符](#61-算术运算符) - - [6.2. 关系运算符](#62-关系运算符) - - [6.3. 布尔运算符](#63-布尔运算符) - - [6.4. 逻辑运算符](#64-逻辑运算符) - - [6.5. 字符串运算符](#65-字符串运算符) - - [6.6. 文件测试运算符](#66-文件测试运算符) -- [7. 控制语句](#7-控制语句) - - [7.1. 条件语句](#71-条件语句) - - [7.2. 循环语句](#72-循环语句) -- [8. 函数](#8-函数) - - [8.1. 位置参数](#81-位置参数) - - [8.2. 函数处理参数](#82-函数处理参数) -- [9. Shell 扩展](#9-shell-扩展) -- [10. 流和重定向](#10-流和重定向) - - [10.1. 输入、输出流](#101-输入输出流) - - [10.2. 重定向](#102-重定向) - - [10.3. `/dev/null` 文件](#103-devnull-文件) -- [11. Debug](#11-debug) -- [12. 更多内容](#12-更多内容) - - - -## 1. 简介 - -### 1.1. 什么是 shell - -- Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。 -- Shell 既是一种命令语言,又是一种程序设计语言。 -- Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问 Linux 内核的服务。 - -Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。 - -### 1.2. 什么是 shell 脚本 - -Shell 脚本(shell script),是一种为 shell 编写的脚本程序,一般文件后缀为 `.sh`。 - -业界所说的 shell 通常都是指 shell 脚本,但 shell 和 shell script 是两个不同的概念。 - -### 1.3. Shell 环境 - -Shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。 - -Shell 的解释器种类众多,常见的有: - -- [sh](https://www.gnu.org/software/bash/) - 即 Bourne Shell。sh 是 Unix 标准默认的 shell。 -- [bash](https://www.gnu.org/software/bash/) - 即 Bourne Again Shell。bash 是 Linux 标准默认的 shell。 -- [fish](https://fishshell.com/) - 智能和用户友好的命令行 shell。 -- [xiki](http://xiki.org/) - 使 shell 控制台更友好,更强大。 -- [zsh](http://www.zsh.org/) - 功能强大的 shell 与脚本语言。 - -#### 指定脚本解释器 - -在 shell 脚本,`#!` 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。`#!` 被称作[shebang(也称为 Hashbang )](https://zh.wikipedia.org/wiki/Shebang)。 - -所以,你应该会在 shell 中,见到诸如以下的注释: - -- 指定 sh 解释器 - -```bash -#!/bin/sh -``` - -- 指定 bash 解释器 - -```bash -#!/bin/bash -``` - -> **注意** -> -> 上面的指定解释器的方式是比较常见的,但有时候,你可能也会看到下面的方式: -> -> ```bash -> #!/usr/bin/env bash -> ``` -> -> 这样做的好处是,系统会自动在 `PATH` 环境变量中查找你指定的程序(本例中的`bash`)。相比第一种写法,你应该尽量用这种写法,因为程序的路径是不确定的。这样写还有一个好处,操作系统的`PATH`变量有可能被配置为指向程序的另一个版本。比如,安装完新版本的`bash`,我们可能将其路径添加到`PATH`中,来“隐藏”老版本。如果直接用`#!/bin/bash`,那么系统会选择老版本的`bash`来执行脚本,如果用`#!/usr/bin/env bash`,则会使用新版本。 - -### 1.4. 模式 - -shell 有交互和非交互两种模式。 - -#### 交互模式 - -> 简单来说,你可以将 shell 的交互模式理解为执行命令行。 - -看到形如下面的东西,说明 shell 处于交互模式下: - -```bash -user@host:~$ -``` - -接着,便可以输入一系列 Linux 命令,比如 `ls`,`grep`,`cd`,`mkdir`,`rm` 等等。 - -#### 非交互模式 - -> 简单来说,你可以将 shell 的非交互模式理解为执行 shell 脚本。 - -在非交互模式下,shell 从文件或者管道中读取命令并执行。 - -当 shell 解释器执行完文件中的最后一个命令,shell 进程终止,并回到父进程。 - -可以使用下面的命令让 shell 以非交互模式运行: - -```bash -sh /path/to/script.sh -bash /path/to/script.sh -source /path/to/script.sh -./path/to/script.sh -``` - -上面的例子中,`script.sh`是一个包含 shell 解释器可以识别并执行的命令的普通文本文件,`sh`和`bash`是 shell 解释器程序。你可以使用任何喜欢的编辑器创建`script.sh`(vim,nano,Sublime Text, Atom 等等)。 - -其中,`source /path/to/script.sh` 和 `./path/to/script.sh` 是等价的。 - -除此之外,你还可以通过`chmod`命令给文件添加可执行的权限,来直接执行脚本文件: - -```bash -chmod +x /path/to/script.sh #使脚本具有执行权限 -/path/to/test.sh -``` - -这种方式要求脚本文件的第一行必须指明运行该脚本的程序,比如: - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash -echo "Hello, world!" -``` - -上面的例子中,我们使用了一个很有用的命令`echo`来输出字符串到屏幕上。 - -## 2. 基本语法 - -### 2.1. 解释器 - -前面虽然两次提到了`#!` ,但是本着重要的事情说三遍的精神,这里再强调一遍: - -在 shell 脚本,`#!` 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。`#!` 被称作[shebang(也称为 Hashbang )](https://zh.wikipedia.org/wiki/Shebang)。 - -`#!` 决定了脚本可以像一个独立的可执行文件一样执行,而不用在终端之前输入`sh`, `bash`, `python`, `php`等。 - -```bash -# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好 -#!/bin/bash -#!/usr/bin/env bash -``` - -### 2.2. 注释 - -注释可以说明你的代码是什么作用,以及为什么这样写。 - -shell 语法中,注释是特殊的语句,会被 shell 解释器忽略。 - -- 单行注释 - 以 `#` 开头,到行尾结束。 -- 多行注释 - 以 `:< test.txt -``` - -输出执行结果 - -```bash -echo `pwd` -# Output:(当前目录路径) -``` - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -# 输出普通字符串 -echo "hello, world" -# Output: hello, world - -# 输出含变量的字符串 -echo "hello, \"zp\"" -# Output: hello, "zp" - -# 输出含变量的字符串 -name=zp -echo "hello, \"${name}\"" -# Output: hello, "zp" - -# 输出含换行符的字符串 -echo "YES\nNO" -# Output: YES\nNO -echo -e "YES\nNO" # -e 开启转义 -# Output: -# YES -# NO - -# 输出含不换行符的字符串 -echo "YES" -echo "NO" -# Output: -# YES -# NO - -echo -e "YES\c" # -e 开启转义 \c 不换行 -echo "NO" -# Output: -# YESNO - -# 输出内容定向至文件 -echo "test" > test.txt - -# 输出执行结果 -echo `pwd` -# Output:(当前目录路径) - -``` - -### 2.4. printf - -printf 用于格式化输出字符串。 - -默认,printf 不会像 echo 一样自动添加换行符,如果需要换行可以手动添加 `\n`。 - -**⌨️ 『示例源码』** - -```bash -# 单引号 -printf '%d %s\n' 1 "abc" -# Output:1 abc - -# 双引号 -printf "%d %s\n" 1 "abc" -# Output:1 abc - -# 无引号 -printf %s abcdef -# Output: abcdef(并不会换行) - -# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出 -printf "%s\n" abc def -# Output: -# abc -# def - -printf "%s %s %s\n" a b c d e f g h i j -# Output: -# a b c -# d e f -# g h i -# j - -# 如果没有参数,那么 %s 用 NULL 代替,%d 用 0 代替 -printf "%s and %d \n" -# Output: -# and 0 - -# 格式化输出 -printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg -printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 -printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543 -printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876 -# Output: -# 姓名 性别 体重kg -# 郭靖 男 66.12 -# 杨过 男 48.65 -# 郭芙 女 47.99 -``` - -#### printf 的转义符 - -| 序列 | 说明 | -| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `\a` | 警告字符,通常为 ASCII 的 BEL 字符 | -| `\b` | 后退 | -| `\c` | 抑制(不显示)输出结果中任何结尾的换行字符(只在%b 格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 | -| `\f` | 换页(formfeed) | -| `\n` | 换行 | -| `\r` | 回车(Carriage return) | -| `\t` | 水平制表符 | -| `\v` | 垂直制表符 | -| `\\` | 一个字面上的反斜杠字符 | -| `\ddd` | 表示 1 到 3 位数八进制值的字符。仅在格式字符串中有效 | -| `\0ddd` | 表示 1 到 3 位的八进制值字符 | - -## 3. 变量 - -跟许多程序设计语言一样,你可以在 bash 中创建变量。 - -Bash 中没有数据类型,bash 中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。 - -### 3.1. 变量命名原则 - -- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。 -- 中间不能有空格,可以使用下划线(\_)。 -- 不能使用标点符号。 -- 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。 - -### 3.2. 声明变量 - -访问变量的语法形式为:`${var}` 和 `$var` 。 - -变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。 - -```bash -word="hello" -echo ${word} -# Output: hello -``` - -### 3.3. 只读变量 - -使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。 - -```bash -rword="hello" -echo ${rword} -readonly rword -# rword="bye" # 如果放开注释,执行时会报错 -``` - -### 3.4. 删除变量 - -使用 unset 命令可以删除变量。变量被删除后不能再次使用。unset 命令不能删除只读变量。 - -```bash -dword="hello" # 声明变量 -echo ${dword} # 输出变量值 -# Output: hello - -unset dword # 删除变量 -echo ${dword} -# Output: (空) -``` - -### 3.5. 变量类型 - -- **局部变量** - 局部变量是仅在某个脚本内部有效的变量。它们不能被其他的程序和脚本访问。 -- **环境变量** - 环境变量是对当前 shell 会话内所有的程序或脚本都可见的变量。创建它们跟创建局部变量类似,但使用的是 `export` 关键字,shell 脚本也可以定义环境变量。 - -常见的环境变量: - -| 变量 | 描述 | -| --------- | -------------------------------------------------- | -| `$HOME` | 当前用户的用户目录 | -| `$PATH` | 用分号分隔的目录列表,shell 会到这些目录中查找命令 | -| `$PWD` | 当前工作目录 | -| `$RANDOM` | 0 到 32767 之间的整数 | -| `$UID` | 数值类型,当前用户的用户 ID | -| `$PS1` | 主要系统输入提示符 | -| `$PS2` | 次要系统输入提示符 | - -[这里](http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html###sect_03_02_04) 有一张更全面的 Bash 环境变量列表。 - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -################### 声明变量 ################### -name="world" -echo "hello ${name}" -# Output: hello world - -################### 输出变量 ################### -folder=$(pwd) -echo "current path: ${folder}" - -################### 只读变量 ################### -rword="hello" -echo ${rword} -# Output: hello -readonly rword -# rword="bye" # 如果放开注释,执行时会报错 - -################### 删除变量 ################### -dword="hello" # 声明变量 -echo ${dword} # 输出变量值 -# Output: hello - -unset dword # 删除变量 -echo ${dword} -# Output: (空) - -################### 系统变量 ################### -echo "UID:$UID" -echo LOGNAME:$LOGNAME -echo User:$USER -echo HOME:$HOME -echo PATH:$PATH -echo HOSTNAME:$HOSTNAME -echo SHELL:$SHELL -echo LANG:$LANG - -################### 自定义变量 ################### -days=10 -user="admin" -echo "$user logged in $days days age" -days=5 -user="root" -echo "$user logged in $days days age" -# Output: -# admin logged in 10 days age -# root logged in 5 days age - -################### 从变量读取列表 ################### -colors="Red Yellow Blue" -colors=$colors" White Black" - -for color in $colors -do - echo " $color" -done -``` - -## 4. 字符串 - -### 4.1. 单引号和双引号 - -shell 字符串可以用单引号 `''`,也可以用双引号 `“”`,也可以不用引号。 - -- 单引号的特点 - - 单引号里不识别变量 - - 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。 -- 双引号的特点 - - 双引号里识别变量 - - 双引号里可以出现转义字符 - -综上,推荐使用双引号。 - -### 4.2. 拼接字符串 - -```bash -# 使用单引号拼接 -name1='white' -str1='hello, '${name1}'' -str2='hello, ${name1}' -echo ${str1}_${str2} -# Output: -# hello, white_hello, ${name1} - -# 使用双引号拼接 -name2="black" -str3="hello, "${name2}"" -str4="hello, ${name2}" -echo ${str3}_${str4} -# Output: -# hello, black_hello, black -``` - -### 4.3. 获取字符串长度 - -```bash -text="12345" -echo ${#text} -# Output: -# 5 -``` - -### 4.4. 截取子字符串 - -```bash -text="12345" -echo ${text:2:2} -# Output: -# 34 -``` - -从第 3 个字符开始,截取 2 个字符 - -### 4.5. 查找子字符串 - -```bash -#!/usr/bin/env bash - -text="hello" -echo `expr index "${text}" ll` - -# Execute: ./str-demo5.sh -# Output: -# 3 -``` - -查找 `ll` 子字符在 `hello` 字符串中的起始位置。 - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -################### 使用单引号拼接字符串 ################### -name1='white' -str1='hello, '${name1}'' -str2='hello, ${name1}' -echo ${str1}_${str2} -# Output: -# hello, white_hello, ${name1} - -################### 使用双引号拼接字符串 ################### -name2="black" -str3="hello, "${name2}"" -str4="hello, ${name2}" -echo ${str3}_${str4} -# Output: -# hello, black_hello, black - -################### 获取字符串长度 ################### -text="12345" -echo "${text} length is: ${#text}" -# Output: -# 12345 length is: 5 - -# 获取子字符串 -text="12345" -echo ${text:2:2} -# Output: -# 34 - -################### 查找子字符串 ################### -text="hello" -echo `expr index "${text}" ll` -# Output: -# 3 - -################### 判断字符串中是否包含子字符串 ################### -result=$(echo "${str}" | grep "feature/") -if [[ "$result" != "" ]]; then - echo "feature/ 是 ${str} 的子字符串" -else - echo "feature/ 不是 ${str} 的子字符串" -fi - -################### 截取关键字左边内容 ################### -full_branch="feature/1.0.0" -branch=`echo ${full_branch#feature/}` -echo "branch is ${branch}" - -################### 截取关键字右边内容 ################### -full_version="0.0.1-SNAPSHOT" -version=`echo ${full_version%-SNAPSHOT}` -echo "version is ${version}" - -################### 字符串分割成数组 ################### -str="0.0.0.1" -OLD_IFS="$IFS" -IFS="." -array=( ${str} ) -IFS="$OLD_IFS" -size=${#array[*]} -lastIndex=`expr ${size} - 1` -echo "数组长度:${size}" -echo "最后一个数组元素:${array[${lastIndex}]}" -for item in ${array[@]} -do - echo "$item" -done - -################### 判断字符串是否为空 ################### -#-n 判断长度是否非零 -#-z 判断长度是否为零 - -str=testing -str2='' -if [[ -n "$str" ]] -then - echo "The string $str is not empty" -else - echo "The string $str is empty" -fi - -if [[ -n "$str2" ]] -then - echo "The string $str2 is not empty" -else - echo "The string $str2 is empty" -fi - -# Output: -# The string testing is not empty -# The string is empty - -################### 字符串比较 ################### -str=hello -str2=world -if [[ $str = "hello" ]]; then - echo "str equals hello" -else - echo "str not equals hello" -fi - -if [[ $str2 = "hello" ]]; then - echo "str2 equals hello" -else - echo "str2 not equals hello" -fi -``` - -## 5. 数组 - -bash 只支持一维数组。 - -数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。 - -### 5.1. 创建数组 - -```bash -# 创建数组的不同方式 -nums=([2]=2 [0]=0 [1]=1) -colors=(red yellow "dark blue") -``` - -### 5.2. 访问数组元素 - -- **访问数组的单个元素:** - -```bash -echo ${nums[1]} -# Output: 1 -``` - -- **访问数组的所有元素:** - -```bash -echo ${colors[*]} -# Output: red yellow dark blue - -echo ${colors[@]} -# Output: red yellow dark blue -``` - -上面两行有很重要(也很微妙)的区别: - -为了将数组中每个元素单独一行输出,我们用 `printf` 命令: - -```bash -printf "+ %s\n" ${colors[*]} -# Output: -# + red -# + yellow -# + dark -# + blue -``` - -为什么`dark`和`blue`各占了一行?尝试用引号包起来: - -```bash -printf "+ %s\n" "${colors[*]}" -# Output: -# + red yellow dark blue -``` - -现在所有的元素都在一行输出 —— 这不是我们想要的!让我们试试`${colors[@]}` - -```bash -printf "+ %s\n" "${colors[@]}" -# Output: -# + red -# + yellow -# + dark blue -``` - -在引号内,`${colors[@]}`将数组中的每个元素扩展为一个单独的参数;数组元素中的空格得以保留。 - -- **访问数组的部分元素:** - -```bash -echo ${nums[@]:0:2} -# Output: -# 0 1 -``` - -在上面的例子中,`${array[@]}` 扩展为整个数组,`:0:2`取出了数组中从 0 开始,长度为 2 的元素。 - -### 5.3. 访问数组长度 - -```bash -echo ${#nums[*]} -# Output: -# 3 -``` - -### 5.4. 向数组中添加元素 - -向数组中添加元素也非常简单: - -```bash -colors=(white "${colors[@]}" green black) -echo ${colors[@]} -# Output: -# white red yellow dark blue green black -``` - -上面的例子中,`${colors[@]}` 扩展为整个数组,并被置换到复合赋值语句中,接着,对数组`colors`的赋值覆盖了它原来的值。 - -### 5.5. 从数组中删除元素 - -用`unset`命令来从数组中删除一个元素: - -```bash -unset nums[0] -echo ${nums[@]} -# Output: -# 1 2 -``` - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -################### 创建数组 ################### -nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 ) -colors=( red yellow "dark blue" ) - -################### 访问数组的单个元素 ################### -echo ${nums[1]} -# Output: 1 - -################### 访问数组的所有元素 ################### -echo ${colors[*]} -# Output: red yellow dark blue - -echo ${colors[@]} -# Output: red yellow dark blue - -printf "+ %s\n" ${colors[*]} -# Output: -# + red -# + yellow -# + dark -# + blue - -printf "+ %s\n" "${colors[*]}" -# Output: -# + red yellow dark blue - -printf "+ %s\n" "${colors[@]}" -# Output: -# + red -# + yellow -# + dark blue - -################### 访问数组的部分元素 ################### -echo ${nums[@]:0:2} -# Output: -# 0 1 - -################### 获取数组长度 ################### -echo ${#nums[*]} -# Output: -# 3 - -################### 向数组中添加元素 ################### -colors=( white "${colors[@]}" green black ) -echo ${colors[@]} -# Output: -# white red yellow dark blue green black - -################### 从数组中删除元素 ################### -unset nums[ 0 ] -echo ${nums[@]} -# Output: -# 1 2 -``` - -## 6. 运算符 - -### 6.1. 算术运算符 - -下表列出了常用的算术运算符,假定变量 x 为 10,变量 y 为 20: - -| 运算符 | 说明 | 举例 | -| ------ | --------------------------------------------- | ------------------------------ | -| + | 加法 | `expr $x + $y` 结果为 30。 | -| - | 减法 | `expr $x - $y` 结果为 -10。 | -| \* | 乘法 | `expr $x * $y` 结果为 200。 | -| / | 除法 | `expr $y / $x` 结果为 2。 | -| % | 取余 | `expr $y % $x` 结果为 0。 | -| = | 赋值 | `x=$y` 将把变量 y 的值赋给 x。 | -| == | 相等。用于比较两个数字,相同则返回 true。 | `[ $x == $y ]` 返回 false。 | -| != | 不相等。用于比较两个数字,不相同则返回 true。 | `[ $x != $y ]` 返回 true。 | - -**注意:**条件表达式要放在方括号之间,并且要有空格,例如: `[$x==$y]` 是错误的,必须写成 `[ $x == $y ]`。 - -**⌨️ 『示例源码』** - -```bash -x=10 -y=20 - -echo "x=${x}, y=${y}" - -val=`expr ${x} + ${y}` -echo "${x} + ${y} = $val" - -val=`expr ${x} - ${y}` -echo "${x} - ${y} = $val" - -val=`expr ${x} \* ${y}` -echo "${x} * ${y} = $val" - -val=`expr ${y} / ${x}` -echo "${y} / ${x} = $val" - -val=`expr ${y} % ${x}` -echo "${y} % ${x} = $val" - -if [[ ${x} == ${y} ]] -then - echo "${x} = ${y}" -fi -if [[ ${x} != ${y} ]] -then - echo "${x} != ${y}" -fi - -# Output: -# x=10, y=20 -# 10 + 20 = 30 -# 10 - 20 = -10 -# 10 * 20 = 200 -# 20 / 10 = 2 -# 20 % 10 = 0 -# 10 != 20 -``` - -### 6.2. 关系运算符 - -关系运算符只支持数字,不支持字符串,除非字符串的值是数字。 - -下表列出了常用的关系运算符,假定变量 x 为 10,变量 y 为 20: - -| 运算符 | 说明 | 举例 | -| ------ | ----------------------------------------------------- | ---------------------------- | -| `-eq` | 检测两个数是否相等,相等返回 true。 | `[ $a -eq $b ]`返回 false。 | -| `-ne` | 检测两个数是否相等,不相等返回 true。 | `[ $a -ne $b ]` 返回 true。 | -| `-gt` | 检测左边的数是否大于右边的,如果是,则返回 true。 | `[ $a -gt $b ]` 返回 false。 | -| `-lt` | 检测左边的数是否小于右边的,如果是,则返回 true。 | `[ $a -lt $b ]` 返回 true。 | -| `-ge` | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | `[ $a -ge $b ]` 返回 false。 | -| `-le` | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | `[ $a -le $b ]`返回 true。 | - -**⌨️ 『示例源码』** - -```bash -x=10 -y=20 - -echo "x=${x}, y=${y}" - -if [[ ${x} -eq ${y} ]]; then - echo "${x} -eq ${y} : x 等于 y" -else - echo "${x} -eq ${y}: x 不等于 y" -fi - -if [[ ${x} -ne ${y} ]]; then - echo "${x} -ne ${y}: x 不等于 y" -else - echo "${x} -ne ${y}: x 等于 y" -fi - -if [[ ${x} -gt ${y} ]]; then - echo "${x} -gt ${y}: x 大于 y" -else - echo "${x} -gt ${y}: x 不大于 y" -fi - -if [[ ${x} -lt ${y} ]]; then - echo "${x} -lt ${y}: x 小于 y" -else - echo "${x} -lt ${y}: x 不小于 y" -fi - -if [[ ${x} -ge ${y} ]]; then - echo "${x} -ge ${y}: x 大于或等于 y" -else - echo "${x} -ge ${y}: x 小于 y" -fi - -if [[ ${x} -le ${y} ]]; then - echo "${x} -le ${y}: x 小于或等于 y" -else - echo "${x} -le ${y}: x 大于 y" -fi - -# Output: -# x=10, y=20 -# 10 -eq 20: x 不等于 y -# 10 -ne 20: x 不等于 y -# 10 -gt 20: x 不大于 y -# 10 -lt 20: x 小于 y -# 10 -ge 20: x 小于 y -# 10 -le 20: x 小于或等于 y -``` - -### 6.3. 布尔运算符 - -下表列出了常用的布尔运算符,假定变量 x 为 10,变量 y 为 20: - -| 运算符 | 说明 | 举例 | -| ------ | --------------------------------------------------- | ------------------------------------------ | -| `!` | 非运算,表达式为 true 则返回 false,否则返回 true。 | `[ ! false ]` 返回 true。 | -| `-o` | 或运算,有一个表达式为 true 则返回 true。 | `[ $a -lt 20 -o $b -gt 100 ]` 返回 true。 | -| `-a` | 与运算,两个表达式都为 true 才返回 true。 | `[ $a -lt 20 -a $b -gt 100 ]` 返回 false。 | - -**⌨️ 『示例源码』** - -```bash -x=10 -y=20 - -echo "x=${x}, y=${y}" - -if [[ ${x} != ${y} ]]; then - echo "${x} != ${y} : x 不等于 y" -else - echo "${x} != ${y}: x 等于 y" -fi - -if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then - echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" -else - echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" -fi - -if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then - echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" -else - echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" -fi - -if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then - echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" -else - echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" -fi - -# Output: -# x=10, y=20 -# 10 != 20 : x 不等于 y -# 10 小于 100 且 20 大于 15 : 返回 true -# 10 小于 100 或 20 大于 100 : 返回 true -# 10 小于 5 或 20 大于 100 : 返回 false -``` - -### 6.4. 逻辑运算符 - -以下介绍 Shell 的逻辑运算符,假定变量 x 为 10,变量 y 为 20: - -| 运算符 | 说明 | 举例 | -| ------ | ---------- | ----------------------------------------------- | -| `&&` | 逻辑的 AND | `[[ ${x} -lt 100 && ${y} -gt 100 ]]` 返回 false | -| `||` | 逻辑的 OR | `[[ ${x} -lt 100 || ${y} -gt 100 ]]` 返回 true | - -**⌨️ 『示例源码』** - -```bash -x=10 -y=20 - -echo "x=${x}, y=${y}" - -if [[ ${x} -lt 100 && ${y} -gt 100 ]] -then - echo "${x} -lt 100 && ${y} -gt 100 返回 true" -else - echo "${x} -lt 100 && ${y} -gt 100 返回 false" -fi - -if [[ ${x} -lt 100 || ${y} -gt 100 ]] -then - echo "${x} -lt 100 || ${y} -gt 100 返回 true" -else - echo "${x} -lt 100 || ${y} -gt 100 返回 false" -fi - -# Output: -# x=10, y=20 -# 10 -lt 100 && 20 -gt 100 返回 false -# 10 -lt 100 || 20 -gt 100 返回 true -``` - -### 6.5. 字符串运算符 - -下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg": - -| 运算符 | 说明 | 举例 | -| ------ | ------------------------------------------ | -------------------------- | -| `=` | 检测两个字符串是否相等,相等返回 true。 | `[ $a = $b ]` 返回 false。 | -| `!=` | 检测两个字符串是否相等,不相等返回 true。 | `[ $a != $b ]` 返回 true。 | -| `-z` | 检测字符串长度是否为 0,为 0 返回 true。 | `[ -z $a ]` 返回 false。 | -| `-n` | 检测字符串长度是否为 0,不为 0 返回 true。 | `[ -n $a ]` 返回 true。 | -| `str` | 检测字符串是否为空,不为空返回 true。 | `[ $a ]` 返回 true。 | - -**⌨️ 『示例源码』** - -```bash -x="abc" -y="xyz" - - -echo "x=${x}, y=${y}" - -if [[ ${x} = ${y} ]]; then - echo "${x} = ${y} : x 等于 y" -else - echo "${x} = ${y}: x 不等于 y" -fi - -if [[ ${x} != ${y} ]]; then - echo "${x} != ${y} : x 不等于 y" -else - echo "${x} != ${y}: x 等于 y" -fi - -if [[ -z ${x} ]]; then - echo "-z ${x} : 字符串长度为 0" -else - echo "-z ${x} : 字符串长度不为 0" -fi - -if [[ -n "${x}" ]]; then - echo "-n ${x} : 字符串长度不为 0" -else - echo "-n ${x} : 字符串长度为 0" -fi - -if [[ ${x} ]]; then - echo "${x} : 字符串不为空" -else - echo "${x} : 字符串为空" -fi - -# Output: -# x=abc, y=xyz -# abc = xyz: x 不等于 y -# abc != xyz : x 不等于 y -# -z abc : 字符串长度不为 0 -# -n abc : 字符串长度不为 0 -# abc : 字符串不为空 -``` - -### 6.6. 文件测试运算符 - -文件测试运算符用于检测 Unix 文件的各种属性。 - -属性检测描述如下: - -| 操作符 | 说明 | 举例 | -| ------- | --------------------------------------------------------------------------- | --------------------------- | -| -b file | 检测文件是否是块设备文件,如果是,则返回 true。 | `[ -b $file ]` 返回 false。 | -| -c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | `[ -c $file ]` 返回 false。 | -| -d file | 检测文件是否是目录,如果是,则返回 true。 | `[ -d $file ]` 返回 false。 | -| -f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | `[ -f $file ]` 返回 true。 | -| -g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | `[ -g $file ]` 返回 false。 | -| -k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | `[ -k $file ]`返回 false。 | -| -p file | 检测文件是否是有名管道,如果是,则返回 true。 | `[ -p $file ]` 返回 false。 | -| -u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | `[ -u $file ]` 返回 false。 | -| -r file | 检测文件是否可读,如果是,则返回 true。 | `[ -r $file ]` 返回 true。 | -| -w file | 检测文件是否可写,如果是,则返回 true。 | `[ -w $file ]` 返回 true。 | -| -x file | 检测文件是否可执行,如果是,则返回 true。 | `[ -x $file ]` 返回 true。 | -| -s file | 检测文件是否为空(文件大小是否大于 0),不为空返回 true。 | `[ -s $file ]` 返回 true。 | -| -e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | `[ -e $file ]` 返回 true。 | - -**⌨️ 『示例源码』** - -```bash -file="/etc/hosts" - -if [[ -r ${file} ]]; then - echo "${file} 文件可读" -else - echo "${file} 文件不可读" -fi -if [[ -w ${file} ]]; then - echo "${file} 文件可写" -else - echo "${file} 文件不可写" -fi -if [[ -x ${file} ]]; then - echo "${file} 文件可执行" -else - echo "${file} 文件不可执行" -fi -if [[ -f ${file} ]]; then - echo "${file} 文件为普通文件" -else - echo "${file} 文件为特殊文件" -fi -if [[ -d ${file} ]]; then - echo "${file} 文件是个目录" -else - echo "${file} 文件不是个目录" -fi -if [[ -s ${file} ]]; then - echo "${file} 文件不为空" -else - echo "${file} 文件为空" -fi -if [[ -e ${file} ]]; then - echo "${file} 文件存在" -else - echo "${file} 文件不存在" -fi - -# Output:(根据文件的实际情况,输出结果可能不同) -# /etc/hosts 文件可读 -# /etc/hosts 文件可写 -# /etc/hosts 文件不可执行 -# /etc/hosts 文件为普通文件 -# /etc/hosts 文件不是个目录 -# /etc/hosts 文件不为空 -# /etc/hosts 文件存在 -``` - -## 7. 控制语句 - -### 7.1. 条件语句 - -跟其它程序设计语言一样,Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在`[[ ]]`里的表达式。 - -由`[[ ]]`(`sh`中是`[ ]`)包起来的表达式被称作 **检测命令** 或 **基元**。这些表达式帮助我们检测一个条件的结果。这里可以找到有关[bash 中单双中括号区别](http://serverfault.com/a/52050)的答案。 - -共有两个不同的条件表达式:`if`和`case`。 - -#### `if` - -(1)`if` 语句 - -`if`在使用上跟其它语言相同。如果中括号里的表达式为真,那么`then`和`fi`之间的代码会被执行。`fi`标志着条件代码块的结束。 - -```bash -# 写成一行 -if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi -# Output: 1 -eq 1 result is: true - -# 写成多行 -if [[ "abc" -eq "abc" ]] -then - echo ""abc" -eq "abc" result is: true" -fi -# Output: abc -eq abc result is: true -``` - -(2)`if else` 语句 - -同样,我们可以使用`if..else`语句,例如: - -```bash -if [[ 2 -ne 1 ]]; then - echo "true" -else - echo "false" -fi -# Output: true -``` - -(3)`if elif else` 语句 - -有些时候,`if..else`不能满足我们的要求。别忘了`if..elif..else`,使用起来也很方便。 - -**⌨️ 『示例源码』** - -```bash -x=10 -y=20 -if [[ ${x} > ${y} ]]; then - echo "${x} > ${y}" -elif [[ ${x} < ${y} ]]; then - echo "${x} < ${y}" -else - echo "${x} = ${y}" -fi -# Output: 10 < 20 -``` - -#### `case` - -如果你需要面对很多情况,分别要采取不同的措施,那么使用`case`会比嵌套的`if`更有用。使用`case`来解决复杂的条件判断,看起来像下面这样: - -**⌨️ 『示例源码』** - -```bash -exec -case ${oper} in - "+") - val=`expr ${x} + ${y}` - echo "${x} + ${y} = ${val}" - ;; - "-") - val=`expr ${x} - ${y}` - echo "${x} - ${y} = ${val}" - ;; - "*") - val=`expr ${x} \* ${y}` - echo "${x} * ${y} = ${val}" - ;; - "/") - val=`expr ${x} / ${y}` - echo "${x} / ${y} = ${val}" - ;; - *) - echo "Unknown oper!" - ;; -esac -``` - -每种情况都是匹配了某个模式的表达式。`|`用来分割多个模式,`)`用来结束一个模式序列。第一个匹配上的模式对应的命令将会被执行。`*`代表任何不匹配以上给定模式的模式。命令块儿之间要用`;;`分隔。 - -### 7.2. 循环语句 - -循环其实不足为奇。跟其它程序设计语言一样,bash 中的循环也是只要控制条件为真就一直迭代执行的代码块。 - -Bash 中有四种循环:`for`,`while`,`until`和`select`。 - -#### `for`循环 - -`for`与它在 C 语言中的姊妹非常像。看起来是这样: - -```bash -for arg in elem1 elem2 ... elemN -do - ### 语句 -done -``` - -在每次循环的过程中,`arg`依次被赋值为从`elem1`到`elemN`。这些值还可以是通配符或者[大括号扩展](https://github.com/denysdovhan/bash-handbook/blob/master/translations/zh-CN/README.md#%E5%A4%A7%E6%8B%AC%E5%8F%B7%E6%89%A9%E5%B1%95)。 - -当然,我们还可以把`for`循环写在一行,但这要求`do`之前要有一个分号,就像下面这样: - -```bash -for i in {1..5}; do echo $i; done -``` - -还有,如果你觉得`for..in..do`对你来说有点奇怪,那么你也可以像 C 语言那样使用`for`,比如: - -```bash -for (( i = 0; i < 10; i++ )); do - echo $i -done -``` - -当我们想对一个目录下的所有文件做同样的操作时,`for`就很方便了。举个例子,如果我们想把所有的`.bash`文件移动到`script`文件夹中,并给它们可执行权限,我们的脚本可以这样写: - -**⌨️ 『示例源码』** - -```bash -DIR=/home/zp -for FILE in ${DIR}/*.sh; do - mv "$FILE" "${DIR}/scripts" -done -# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts -``` - -#### `while`循环 - -`while`循环检测一个条件,只要这个条件为 _真_,就执行一段命令。被检测的条件跟`if..then`中使用的[基元](https://github.com/denysdovhan/bash-handbook/blob/master/translations/zh-CN/README.md#%E5%9F%BA%E5%85%83%E5%92%8C%E7%BB%84%E5%90%88%E8%A1%A8%E8%BE%BE%E5%BC%8F)并无二异。因此一个`while`循环看起来会是这样: - -```bash -while [[ condition ]] -do - ### 语句 -done -``` - -跟`for`循环一样,如果我们把`do`和被检测的条件写到一行,那么必须要在`do`之前加一个分号。 - -**⌨️ 『示例源码』** - -```bash -### 0到9之间每个数的平方 -x=0 -while [[ ${x} -lt 10 ]]; do - echo $((x * x)) - x=$((x + 1)) -done -# Output: -# 0 -# 1 -# 4 -# 9 -# 16 -# 25 -# 36 -# 49 -# 64 -# 81 -``` - -#### `until`循环 - -`until`循环跟`while`循环正好相反。它跟`while`一样也需要检测一个测试条件,但不同的是,只要该条件为 _假_ 就一直执行循环: - -**⌨️ 『示例源码』** - -```bash -x=0 -until [[ ${x} -ge 5 ]]; do - echo ${x} - x=`expr ${x} + 1` -done -# Output: -# 0 -# 1 -# 2 -# 3 -# 4 -``` - -#### `select`循环 - -`select`循环帮助我们组织一个用户菜单。它的语法几乎跟`for`循环一致: - -```bash -select answer in elem1 elem2 ... elemN -do - ### 语句 -done -``` - -`select`会打印`elem1..elemN`以及它们的序列号到屏幕上,之后会提示用户输入。通常看到的是`$?`(`PS3`变量)。用户的选择结果会被保存到`answer`中。如果`answer`是一个在`1..N`之间的数字,那么`语句`会被执行,紧接着会进行下一次迭代 —— 如果不想这样的话我们可以使用`break`语句。 - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -PS3="Choose the package manager: " -select ITEM in bower npm gem pip -do -echo -n "Enter the package name: " && read PACKAGE -case ${ITEM} in - bower) bower install ${PACKAGE} ;; - npm) npm install ${PACKAGE} ;; - gem) gem install ${PACKAGE} ;; - pip) pip install ${PACKAGE} ;; -esac -break # 避免无限循环 -done -``` - -这个例子,先询问用户他想使用什么包管理器。接着,又询问了想安装什么包,最后执行安装操作。 - -运行这个脚本,会得到如下输出: - -```bash -$ ./my_script -1) bower -2) npm -3) gem -4) pip -Choose the package manager: 2 -Enter the package name: gitbook-cli -``` - -#### `break` 和 `continue` - -如果想提前结束一个循环或跳过某次循环执行,可以使用 shell 的`break`和`continue`语句来实现。它们可以在任何循环中使用。 - -> `break`语句用来提前结束当前循环。 -> -> `continue`语句用来跳过某次迭代。 - -**⌨️ 『示例源码』** - -```bash -# 查找 10 以内第一个能整除 2 和 3 的正整数 -i=1 -while [[ ${i} -lt 10 ]]; do - if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then - echo ${i} - break; - fi - i=`expr ${i} + 1` -done -# Output: 6 -``` - -**⌨️ 『示例源码』** - -```bash -# 打印10以内的奇数 -for (( i = 0; i < 10; i ++ )); do - if [[ $((i % 2)) -eq 0 ]]; then - continue; - fi - echo ${i} -done -# Output: -# 1 -# 3 -# 5 -# 7 -# 9 -``` - -## 8. 函数 - -bash 函数定义语法如下: - -```bash -[ function ] funname [()] { - action; - [return int;] -} -``` - -> 💡 说明: -> -> 1. 函数定义时,`function` 关键字可有可无。 -> 2. 函数返回值 - return 返回函数返回值,返回值类型只能为整数(0-255)。如果不加 return 语句,shell 默认将以最后一条命令的运行结果,作为函数返回值。 -> 3. 函数返回值在调用该函数后通过 `$?` 来获得。 -> 4. 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。 - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -calc(){ - PS3="choose the oper: " - select oper in + - \* / # 生成操作符选择菜单 - do - echo -n "enter first num: " && read x # 读取输入参数 - echo -n "enter second num: " && read y # 读取输入参数 - exec - case ${oper} in - "+") - return $((${x} + ${y})) - ;; - "-") - return $((${x} - ${y})) - ;; - "*") - return $((${x} * ${y})) - ;; - "/") - return $((${x} / ${y})) - ;; - *) - echo "${oper} is not support!" - return 0 - ;; - esac - break - done -} -calc -echo "the result is: $?" # $? 获取 calc 函数返回值 -``` - -执行结果: - -```bash -$ ./function-demo.sh -1) + -2) - -3) * -4) / -choose the oper: 3 -enter first num: 10 -enter second num: 10 -the result is: 100 -``` - -### 8.1. 位置参数 - -**位置参数**是在调用一个函数并传给它参数时创建的变量。 - -位置参数变量表: - -| 变量 | 描述 | -| -------------- | ------------------------------ | -| `$0` | 脚本名称 | -| `$1 … $9` | 第 1 个到第 9 个参数列表 | -| `${10} … ${N}` | 第 10 个到 N 个参数列表 | -| `$*` or `$@` | 除了`$0`外的所有位置参数 | -| `$#` | 不包括`$0`在内的位置参数的个数 | -| `$FUNCNAME` | 函数名称(仅在函数内部有值) | - -**⌨️ 『示例源码』** - -```bash -#!/usr/bin/env bash - -x=0 -if [[ -n $1 ]]; then - echo "第一个参数为:$1" - x=$1 -else - echo "第一个参数为空" -fi - -y=0 -if [[ -n $2 ]]; then - echo "第二个参数为:$2" - y=$2 -else - echo "第二个参数为空" -fi - -paramsFunction(){ - echo "函数第一个入参:$1" - echo "函数第二个入参:$2" -} -paramsFunction ${x} ${y} -``` - -执行结果: - -```bash -$ ./function-demo2.sh -第一个参数为空 -第二个参数为空 -函数第一个入参:0 -函数第二个入参:0 - -$ ./function-demo2.sh 10 20 -第一个参数为:10 -第二个参数为:20 -函数第一个入参:10 -函数第二个入参:20 -``` - -执行 `./variable-demo4.sh hello world` ,然后在脚本中通过 `$1`、`$2` ... 读取第 1 个参数、第 2 个参数。。。 - -### 8.2. 函数处理参数 - -另外,还有几个特殊字符用来处理参数: - -| 参数处理 | 说明 | -| -------- | ------------------------------------------------ | -| `$#` | 返回参数个数 | -| `$*` | 返回所有参数 | -| `$$` | 脚本运行的当前进程 ID 号 | -| `$!` | 后台运行的最后一个进程的 ID 号 | -| `$@` | 返回所有参数 | -| `$-` | 返回 Shell 使用的当前选项,与 set 命令功能相同。 | -| `$?` | 函数返回值 | - -**⌨️ 『示例源码』** - -```bash -runner() { - return 0 -} - -name=zp -paramsFunction(){ - echo "函数第一个入参:$1" - echo "函数第二个入参:$2" - echo "传递到脚本的参数个数:$#" - echo "所有参数:" - printf "+ %s\n" "$*" - echo "脚本运行的当前进程 ID 号:$$" - echo "后台运行的最后一个进程的 ID 号:$!" - echo "所有参数:" - printf "+ %s\n" "$@" - echo "Shell 使用的当前选项:$-" - runner - echo "runner 函数的返回值:$?" -} -paramsFunction 1 "abc" "hello, \"zp\"" -# Output: -# 函数第一个入参:1 -# 函数第二个入参:abc -# 传递到脚本的参数个数:3 -# 所有参数: -# + 1 abc hello, "zp" -# 脚本运行的当前进程 ID 号:26400 -# 后台运行的最后一个进程的 ID 号: -# 所有参数: -# + 1 -# + abc -# + hello, "zp" -# Shell 使用的当前选项:hB -# runner 函数的返回值:0 -``` - -## 9. Shell 扩展 - -_扩展_ 发生在一行命令被分成一个个的 _记号(tokens)_ 之后。换言之,扩展是一种执行数学运算的机制,还可以用来保存命令的执行结果,等等。 - -感兴趣的话可以阅读[关于 shell 扩展的更多细节](https://www.gnu.org/software/bash/manual/bash.html###Shell-Expansions)。 - -#### 大括号扩展 - -大括号扩展让生成任意的字符串成为可能。它跟 _文件名扩展_ 很类似,举个例子: - -```bash -echo beg{i,a,u}n ### begin began begun -``` - -大括号扩展还可以用来创建一个可被循环迭代的区间。 - -```bash -echo {0..5} ### 0 1 2 3 4 5 -echo {00..8..2} ### 00 02 04 06 08 -``` - -#### 命令置换 - -命令置换允许我们对一个命令求值,并将其值置换到另一个命令或者变量赋值表达式中。当一个命令被``或`$()`包围时,命令置换将会执行。举个例子: - -```bash -now=`date +%T` -### or -now=$(date +%T) - -echo $now ### 19:08:26 -``` - -#### 算数扩展 - -在 bash 中,执行算数运算是非常方便的。算数表达式必须包在`$(( ))`中。算数扩展的格式为: - -```bash -result=$(( ((10 + 5*3) - 7) / 2 )) -echo $result ### 9 -``` - -在算数表达式中,使用变量无需带上`$`前缀: - -```bash -x=4 -y=7 -echo $(( x + y )) ### 11 -echo $(( ++x + y++ )) ### 12 -echo $(( x + y )) ### 13 -``` - -#### 单引号和双引号 - -单引号和双引号之间有很重要的区别。在双引号中,变量引用或者命令置换是会被展开的。在单引号中是不会的。举个例子: - -```bash -echo "Your home: $HOME" ### Your home: /Users/ -echo 'Your home: $HOME' ### Your home: $HOME -``` - -当局部变量和环境变量包含空格时,它们在引号中的扩展要格外注意。随便举个例子,假如我们用`echo`来输出用户的输入: - -```bash -INPUT="A string with strange whitespace." -echo $INPUT ### A string with strange whitespace. -echo "$INPUT" ### A string with strange whitespace. -``` - -调用第一个`echo`时给了它 5 个单独的参数 —— `$INPUT` 被分成了单独的词,`echo`在每个词之间打印了一个空格。第二种情况,调用`echo`时只给了它一个参数(整个\$INPUT 的值,包括其中的空格)。 - -来看一个更严肃的例子: - -```bash -FILE="Favorite Things.txt" -cat $FILE ### 尝试输出两个文件: `Favorite` 和 `Things.txt` -cat "$FILE" ### 输出一个文件: `Favorite Things.txt` -``` - -尽管这个问题可以通过把 FILE 重命名成`Favorite-Things.txt`来解决,但是,假如这个值来自某个环境变量,来自一个位置参数,或者来自其它命令(`find`, `cat`, 等等)呢。因此,如果输入 _可能_ 包含空格,务必要用引号把表达式包起来。 - -## 10. 流和重定向 - -Bash 有很强大的工具来处理程序之间的协同工作。使用流,我们能将一个程序的输出发送到另一个程序或文件,因此,我们能方便地记录日志或做一些其它我们想做的事。 - -管道给了我们创建传送带的机会,控制程序的执行成为可能。 - -学习如何使用这些强大的、高级的工具是非常非常重要的。 - -### 10.1. 输入、输出流 - -Bash 接收输入,并以字符序列或 **字符流** 的形式产生输出。这些流能被重定向到文件或另一个流中。 - -有三个文件描述符: - -| 代码 | 描述符 | 描述 | -| ---- | -------- | ------------ | -| `0` | `stdin` | 标准输入 | -| `1` | `stdout` | 标准输出 | -| `2` | `stderr` | 标准错误输出 | - -### 10.2. 重定向 - -重定向让我们可以控制一个命令的输入来自哪里,输出结果到什么地方。这些运算符在控制流的重定向时会被用到: - -| Operator | Description | -| -------- | ------------------------------------------------------------- | -| `>` | 重定向输出 | -| `&>` | 重定向输出和错误输出 | -| `&>>` | 以附加的形式重定向输出和错误输出 | -| `<` | 重定向输入 | -| `<<` | [Here 文档](http://tldp.org/LDP/abs/html/here-docs.html) 语法 | -| `<<<` | [Here 字符串](http://www.tldp.org/LDP/abs/html/x17837.html) | - -以下是一些使用重定向的例子: - -```bash -### ls的结果将会被写到list.txt中 -ls -l > list.txt - -### 将输出附加到list.txt中 -ls -a >> list.txt - -### 所有的错误信息会被写到errors.txt中 -grep da * 2> errors.txt - -### 从errors.txt中读取输入 -less < errors.txt -``` - -### 10.3. `/dev/null` 文件 - -如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null: - -```bash -$ command > /dev/null -``` - -/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。 - -如果希望屏蔽 stdout 和 stderr,可以这样写: - -```bash -$ command > /dev/null 2>&1 -``` - -## 11. Debug - -shell 提供了用于 debug 脚本的工具。 - -如果想采用 debug 模式运行某脚本,可以在其 shebang 中使用一个特殊的选项: - -``` -#!/bin/bash options -``` - -options 是一些可以改变 shell 行为的选项。下表是一些可能对你有用的选项: - -| Short | Name | Description | -| ----- | ----------- | ---------------------------------------------------------- | -| `-f` | noglob | 禁止文件名展开(globbing) | -| `-i` | interactive | 让脚本以 _交互_ 模式运行 | -| `-n` | noexec | 读取命令,但不执行(语法检查) | -| `-t` | — | 执行完第一条命令后退出 | -| `-v` | verbose | 在执行每条命令前,向`stderr`输出该命令 | -| `-x` | xtrace | 在执行每条命令前,向`stderr`输出该命令以及该命令的扩展参数 | - -举个例子,如果我们在脚本中指定了`-x`例如: - -```bash -#!/bin/bash -x - -for (( i = 0; i < 3; i++ )); do - echo $i -done -``` - -这会向`stdout`打印出变量的值和一些其它有用的信息: - -```bash -$ ./my_script -+ (( i = 0 )) -+ (( i < 3 )) -+ echo 0 -0 -+ (( i++ )) -+ (( i < 3 )) -+ echo 1 -1 -+ (( i++ )) -+ (( i < 3 )) -+ echo 2 -2 -+ (( i++ )) -+ (( i < 3 )) -``` - -有时我们值需要 debug 脚本的一部分。这种情况下,使用`set`命令会很方便。这个命令可以启用或禁用选项。使用`-`启用选项,`+`禁用选项: - -**⌨️ 『示例源码』** - -```bash -# 开启 debug -set -x -for (( i = 0; i < 3; i++ )); do - printf ${i} -done -# 关闭 debug -set +x -# Output: -# + (( i = 0 )) -# + (( i < 3 )) -# + printf 0 -# 0+ (( i++ )) -# + (( i < 3 )) -# + printf 1 -# 1+ (( i++ )) -# + (( i < 3 )) -# + printf 2 -# 2+ (( i++ )) -# + (( i < 3 )) -# + set +x - -for i in {1..5}; do printf ${i}; done -printf "\n" -# Output: 12345 -``` - -## 12. 更多内容 - -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog)」 - -- [awesome-shell](https://github.com/alebcay/awesome-shell),shell 资源列表 -- [awesome-bash](https://github.com/awesome-lists/awesome-bash),bash 资源列表 -- [bash-handbook](https://github.com/denysdovhan/bash-handbook) -- [bash-guide](https://github.com/vuuihc/bash-guide) ,bash 基本用法指南 -- [bash-it](https://github.com/Bash-it/bash-it),为你日常使用,开发以及维护 shell 脚本和自定义命令提供了一个可靠的框架 -- [dotfiles.github.io](http://dotfiles.github.io/),上面有 bash 和其它 shell 的各种 dotfiles 集合以及 shell 框架的链接 -- [Runoob Shell 教程](http://www.runoob.com/linux/linux-shell.html) -- [shellcheck](https://github.com/koalaman/shellcheck) 一个静态 shell 脚本分析工具,本质上是 bash/sh/zsh 的 lint。 - -最后,Stack Overflow 上 [bash 标签下](https://stackoverflow.com/questions/tagged/bash)有很多你可以学习的问题,当你遇到问题时,也是一个提问的好地方。 diff --git a/docs/linux/soft/README.md b/docs/linux/soft/README.md index f9232db0..2792f088 100644 --- a/docs/linux/soft/README.md +++ b/docs/linux/soft/README.md @@ -11,5 +11,5 @@ - [RocketMQ 安装](rocketmq-install.md) - [Svn 安装](svn-ops.md) - [Tomcat 安装](tomcat-install.md) -- [Zookeeper 安装](zookeeper-install.md) +- [Zookeeper 安装](zookeeper-ops.md) - [Nacos 安装](nacos-install.md) diff --git a/docs/linux/soft/mysql-ops.md b/docs/linux/soft/mysql-ops.md deleted file mode 100644 index d4d48213..00000000 --- a/docs/linux/soft/mysql-ops.md +++ /dev/null @@ -1,493 +0,0 @@ -# Mysql 维护 - -## 安装配置 - -通过 rpm 包安装 - -centos 的 yum 源中默认是没有 mysql 的,所以我们需要先去官网下载 mysql 的 repo 源并安装。 - -### 安装 mysql yum 源 - -官方下载地址:https://dev.mysql.com/downloads/repo/yum/ - -(1)下载 yum 源 - -```bash -$ wget https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm -``` - -(2)安装 yum repo 文件并更新 yum 缓存 - -```bash -$ rpm -ivh mysql80-community-release-el7-1.noarch.rpm -``` - -执行结果: - -会在 /etc/yum.repos.d/ 目录下生成两个 repo 文件 - -```bash -$ ls | grep mysql -mysql-community.repo -mysql-community-source.repo -``` - -更新 yum: - -```bash -$ yum clean all -$ yum makecache -``` - -(3)查看 rpm 安装状态 - -```bash -$ yum search mysql | grep server -mysql-community-common.i686 : MySQL database common files for server and client -mysql-community-common.x86_64 : MySQL database common files for server and -mysql-community-test.x86_64 : Test suite for the MySQL database server - : administering MySQL servers -mysql-community-server.x86_64 : A very fast and reliable SQL database server -``` - -通过 yum 安装 mysql 有几个重要目录: - -``` -# 数据库目录 -/var/lib/mysql/ -# 配置文件 -/usr/share/mysql(mysql.server命令及配置文件) -# 相关命令 -/usr/bin(mysqladmin mysqldump等命令) -# 启动脚本 -/etc/rc.d/init.d/(启动脚本文件mysql的目录) -# 配置文件 -/etc/my.cnf -``` - -### 安装 mysql 服务器 - -```bash -$ yum install mysql-community-server -``` - -### 启动 mysql 服务 - -```bash -# 启动 mysql 服务 -systemctl start mysqld.service - -# 查看运行状态 -systemctl status mysqld.service - -# 开机启动 -systemctl enable mysqld -systemctl daemon-reload -``` - -### 初始化数据库密码 - -查看一下初始密码 - -```bash -$ grep "password" /var/log/mysqld.log -2018-09-30T03:13:41.727736Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: %:lt+srWu4k1 -``` - -执行命令: - -```bash -mysql -uroot -p<临时密码> -``` - -输入临时密码,进入 mysql,如果要修改密码,执行以下指令: - -```bash -ALTER user 'root'@'localhost' IDENTIFIED BY '你的密码'; -``` - -注:密码强度默认为中等,大小写字母、数字、特殊符号,只有修改成功后才能修改配置再设置更简单的密码 - -### 配置远程访问 - -```sql -mysql> CREATE USER 'root'@'%' IDENTIFIED BY '你的密码'; -mysql> GRANT ALL ON *.* TO 'root'@'%'; -mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码'; -mysql> FLUSH PRIVILEGES; -``` - -### 跳过登录认证 - -``` -vim /etc/my.cnf -``` - -在 [mysqld] 下面加上 skip-grant-tables - -作用是登录时跳过登录认证,换句话说就是 root 什么密码都可以登录进去。 - -执行 `service mysqld restart`,重启 mysql - -## 部署 - -### 主从节点部署 - -假设需要配置一个主从 Mysql 服务器环境 - -- master 节点:192.168.8.10 -- slave 节点:192.168.8.11 - -#### 配置主从同步 - -(1)主节点配置 - -执行 `vi /etc/my.cnf` ,添加如下配置: - -```ini -[mysqld] -server-id=1 -log-bin=mysql-bin -``` - -- `server-id` - 服务器 ID 号; -- `log-bin` - 同步的日志路径及文件名,一定注意这个目录要是mysql有权限写入的; - -(2)从节点配置 - -执行 `vi /etc/my.cnf` ,添加如下配置: - -```ini -[mysqld] -server-id=2 -log-bin=mysql-bin -``` - -(3)创建用于复制操作的用户 - -```sql -mysql> CREATE USER 'sync'@'192.168.8.11' IDENTIFIED WITH mysql_native_password BY '密码'; -- 创建用户 -mysql> GRANT REPLICATION SLAVE ON *.* TO 'sync'@'192.168.8.11'; -- 授权 -mysql> FLUSH PRIVILEGES; -- 刷新授权表信息 -``` - -(4)查看主节点状态 - -```sql -mysql> show master status; -+------------------+----------+--------------+---------------------------------------------+-------------------+ -| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | -+------------------+----------+--------------+---------------------------------------------+-------------------+ -| mysql-bin.000001 | 4202 | | mysql,information_schema,performance_schema | | -+------------------+----------+--------------+---------------------------------------------+-------------------+ -1 row in set (0.00 sec) -``` - -(5)在Slave节点上设置主节点参数 - -`MASTER_LOG_FILE` 和 `MASTER_LOG_POS` 参数要分别与 `show master status` 指令获得的 `File` 和 `Position` 属性值对应。 - -```sql -mysql> CHANGE MASTER TO -MASTER_HOST='192.168.199.149', -MASTER_USER='sync', -MASTER_PASSWORD='密码', -MASTER_LOG_FILE='binlog.000001', -MASTER_LOG_POS=4202; - -``` - -(6)查看主从同步状态 - -``` -mysql> show slave status\G; -``` - -说明:如果以下两项参数均为 YES,说明配置正确。 - -- `Slave_IO_Running` -- `Slave_SQL_Running` - -(7)启动 slave 进程 - -``` -mysql> start slave; -``` - -#### 同步主节点已有数据到从节点 - -主库操作: - -(1)停止主库的数据更新操作 - -```sql -mysql> flush tables with read lock; -``` - -(2)新开终端,生成主数据库的备份(导出数据库) - -```bash -$ mysqldump -uroot -p<密码> test > test.sql -``` - -(3)将备份文件传到从库 - -```bash -$ scp test.sql root@192.168.8.11:/root/ -``` - -(4)主库解锁 - -```mysql -mysql> unlock tables; -``` - - 从库操作: - -(1)停止从库slave - -```mysql -mysql> stop slave; -``` - -(2)新建数据库test - -```mysql -mysql> create database test default charset utf8; -``` - -(3)导入数据 - -```bash -$ mysql -uroot -ptest123 cmdb show databases; -+--------------------+ -| Database | -+--------------------+ -| information_schema | -| cmdb | -| mysql | -| performance_schema | -| test | -+--------------------+ -``` - -## 运维 - -### 创建用户 - -``` -CREATE USER 'username'@'host' IDENTIFIED BY 'password'; -``` - -说明: - -- username:你将创建的用户名 -- host:指定该用户在哪个主机上可以登陆,如果是本地用户可用 localhost,如果想让该用户可以**从任意远程主机登陆**,可以使用通配符`%` -- password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器 - -示例: - -```sql -CREATE USER 'dog'@'localhost' IDENTIFIED BY '123456'; -CREATE USER 'pig'@'192.168.1.101_' IDENDIFIED BY '123456'; -CREATE USER 'pig'@'%' IDENTIFIED BY '123456'; -CREATE USER 'pig'@'%' IDENTIFIED BY ''; -CREATE USER 'pig'@'%'; -``` - -### 授权 - -命令: - -```sql -GRANT privileges ON databasename.tablename TO 'username'@'host' -``` - -说明: - -- privileges:用户的操作权限,如`SELECT`,`INSERT`,`UPDATE`等,如果要授予所的权限则使用`ALL` -- databasename:数据库名 -- tablename:表名,如果要授予该用户对所有数据库和表的相应操作权限则可用`*`表示,如`*.*` - -示例: - -```sql -GRANT SELECT, INSERT ON test.user TO 'pig'@'%'; -GRANT ALL ON *.* TO 'pig'@'%'; -GRANT ALL ON maindataplus.* TO 'pig'@'%'; -``` - -注意: - -用以上命令授权的用户不能给其它用户授权,如果想让该用户可以授权,用以下命令: - -```sql -GRANT privileges ON databasename.tablename TO 'username'@'host' WITH GRANT OPTION; -``` - -### 撤销授权 - -命令: - -``` -REVOKE privilege ON databasename.tablename FROM 'username'@'host'; -``` - -说明: - -privilege, databasename, tablename:同授权部分 - -例子: - -``` -REVOKE SELECT ON *.* FROM 'pig'@'%'; -``` - -注意: - -假如你在给用户`'pig'@'%'`授权的时候是这样的(或类似的):`GRANT SELECT ON test.user TO 'pig'@'%'`,则在使用`REVOKE SELECT ON *.* FROM 'pig'@'%';`命令并不能撤销该用户对 test 数据库中 user 表的`SELECT` 操作。相反,如果授权使用的是`GRANT SELECT ON *.* TO 'pig'@'%';`则`REVOKE SELECT ON test.user FROM 'pig'@'%';`命令也不能撤销该用户对 test 数据库中 user 表的`Select`权限。 - -具体信息可以用命令`SHOW GRANTS FOR 'pig'@'%';` 查看。 - -### 更改用户密码 - -```sql -SET PASSWORD FOR 'username'@'host' = PASSWORD('newpassword'); -``` - -如果是当前登陆用户用: - -```sql -SET PASSWORD = PASSWORD("newpassword"); -``` - -示例: - -```sql -SET PASSWORD FOR 'pig'@'%' = PASSWORD("123456"); -``` - -### 备份与恢复 - -Mysql 备份数据使用 mysqldump 命令。 - -mysqldump 将数据库中的数据备份成一个文本文件,表的结构和表中的数据将存储在生成的文本文件中。 - -备份: - -(1)备份一个数据库 - -语法: - -``` -mysqldump -u -p [ ...] > backup.sql -``` - -- username 数据库用户 -- dbname 数据库名称 -- table1 和 table2 参数表示需要备份的表的名称,为空则整个数据库备份; -- BackupName.sql 参数表设计备份文件的名称,文件名前面可以加上一个绝对路径。通常将数据库被分成一个后缀名为 sql 的文件 - -(2)备份多个数据库 - -``` -mysqldump -u -p --databases ... > backup.sql -``` - -(3)备份所有数据库 - -``` -mysqldump -u -p -all-databases > backup.sql -``` - -恢复: - -Mysql 恢复数据使用 mysqldump 命令。 - -语法: - -``` -mysql -u -p < backup.sql -``` - -### 卸载 - -(1)查看已安装的 mysql - -```bash -$ rpm -qa | grep -i mysql -perl-DBD-MySQL-4.023-6.el7.x86_64 -mysql80-community-release-el7-1.noarch -mysql-community-common-8.0.12-1.el7.x86_64 -mysql-community-client-8.0.12-1.el7.x86_64 -mysql-community-libs-compat-8.0.12-1.el7.x86_64 -mysql-community-libs-8.0.12-1.el7.x86_64 -``` - -(2)卸载 mysql - -```bash -$ yum remove mysql-community-server.x86_64 -``` - -## 问题 - -### JDBC 与 Mysql 因 CST 时区协商无解导致偏差了 14 或 13 小时 - -**现象** - -数据库中存储的 Timestamp 字段值比真实值少了 13 个小时。 - -**原因** - -- 当 JDBC 与 MySQL 开始建立连接时,会获取服务器参数。 -- 当 MySQL 的 `time_zone` 值为 `SYSTEM` 时,会取 `system_time_zone` 值作为协调时区,若得到的是 `CST` 那么 Java 会误以为这是 `CST -0500` ,因此会给出错误的时区信息(国内一般是`CST +0800`,即东八区)。 - -> 查看时区方法: -> -> 通过 `show variables like '%time_zone%';` 命令查看 Mysql 时区配置: -> -> ``` -> mysql> show variables like '%time_zone%'; -> +------------------+--------+ -> | Variable_name | Value | -> +------------------+--------+ -> | system_time_zone | CST | -> | time_zone | SYSTEM | -> +------------------+--------+ -> ``` - -**解决方案** - -方案一 - -``` -mysql> set global time_zone = '+08:00'; -Query OK, 0 rows affected (0.00 sec) - -mysql> set time_zone = '+08:00'; -Query OK, 0 rows affected (0.00 sec) -``` - -方案二 - -修改 `my.cnf` 文件,在 `[mysqld]` 节下增加 `default-time-zone = '+08:00'` ,然后重启。 - -## 参考资料 - -- https://www.cnblogs.com/xiaopotian/p/8196464.html -- https://www.cnblogs.com/bigbrotherer/p/7241845.html -- https://blog.csdn.net/managementandjava/article/details/80039650 -- http://www.manongjc.com/article/6996.html -- https://www.cnblogs.com/xyabk/p/8967990.html -- [MySQL 8.0主从(Master-Slave)配置](https://blog.csdn.net/zyhlwzy/article/details/80569422) - -## :door: 传送门 - -| [我的 Github 博客](https://github.com/dunwu/blog) | [db-tutorial 首页](https://github.com/dunwu/db-tutorial) | diff --git a/docs/linux/soft/redis-ops.md b/docs/linux/soft/redis-ops.md deleted file mode 100644 index 40519944..00000000 --- a/docs/linux/soft/redis-ops.md +++ /dev/null @@ -1,475 +0,0 @@ -# Redis 运维 - -> **Redis** 是一个高性能的 key-value 数据库。 -> -> SET 操作每秒钟 110000 次;GET 操作每秒钟 81000 次。 - - - -- [安装](#安装) - - [Window 下安装](#window-下安装) - - [Linux 下安装](#linux-下安装) - - [Ubuntu 下安装](#ubuntu-下安装) - - [开机启动](#开机启动) - - [开放防火墙端口](#开放防火墙端口) -- [Redis 使用和配置](#redis-使用和配置) - - [启动](#启动) - - [常见配置](#常见配置) - - [设为守护进程](#设为守护进程) - - [远程访问](#远程访问) - - [设置密码](#设置密码) - - [配置参数表](#配置参数表) -- [Redis 集群使用和配置](#redis-集群使用和配置) - - [集群规划](#集群规划) - - [部署](#部署) -- [Redis 命令](#redis-命令) -- [压力测试](#压力测试) -- [客户端](#客户端) -- [脚本](#脚本) -- [参考资料](#参考资料) - - - -## 安装 - -### Window 下安装 - -**下载地址:**。 - -Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 **Redis-x64-xxx.zip**压缩包到 C 盘,解压后,将文件夹重新命名为 **redis**。 - -打开一个 **cmd** 窗口 使用 cd 命令切换目录到 **C:\redis** 运行 **redis-server.exe redis.windows.conf** 。 - -如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。 - -这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。 - -切换到 redis 目录下运行 **redis-cli.exe -h 127.0.0.1 -p 6379** 。 - -### Linux 下安装 - -**下载地址:** http://redis.io/download,下载最新文档版本。 - -下载、解压、编译 Redis - -``` -$ wget http://download.redis.io/releases/redis-5.0.4.tar.gz -$ tar xzf redis-5.0.4.tar.gz -$ cd redis-5.0.4 -$ make -``` - -为了编译 Redis 源码,你需要 gcc-c++和 tcl。如果你的系统是 CentOS,可以直接执行命令:`yum install -y gcc-c++ tcl` 来安装。 - -进入到解压后的 `src` 目录,通过如下命令启动 Redis: - -``` -$ src/redis-server -``` - -您可以使用内置的客户端与 Redis 进行交互: - -``` -$ src/redis-cli -redis> set foo bar -OK -redis> get foo -"bar" -``` - -### Ubuntu 下安装 - -在 Ubuntu 系统安装 Redi 可以使用以下命令: - -``` -$sudo apt-get update -$sudo apt-get install redis-server -``` - -### 开机启动 - -- 开机启动配置:`echo "/usr/local/bin/redis-server /etc/redis.conf" >> /etc/rc.local` - -### 开放防火墙端口 - -- 添加规则:`iptables -I INPUT -p tcp -m tcp --dport 6379 -j ACCEPT` -- 保存规则:`service iptables save` -- 重启 iptables:`service iptables restart` - -## Redis 使用和配置 - -### 启动 - -**启动 redis 服务** - -``` -cd /usr/local/redis/src -./redis-server -``` - -**启动 redis 客户端** - -``` -cd /usr/local/redis/src -./redis-cli -``` - -**查看 redis 是否启动** - -``` -$ redis-cli -``` - -以上命令将打开以下终端: - -``` -redis 127.0.0.1:6379> -``` - -127.0.0.1 是本机 IP ,6379 是 redis 服务端口。现在我们输入 PING 命令。 - -``` -redis 127.0.0.1:6379> ping -PONG -``` - -以上说明我们已经成功启动了 redis。 - -### 常见配置 - -> Redis 默认的配置文件是根目录下的 `redis.conf` 文件。 -> -> 如果需要指定特定文件作为配置文件,需要使用命令: `./redis-server -c xxx.conf` -> -> 每次修改配置后,需要重启才能生效。 -> -> Redis 官方默认配置: -> -> - 自描述文档 [redis.conf for Redis 2.8](https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf) -> - 自描述文档 [redis.conf for Redis 2.6](https://raw.githubusercontent.com/antirez/redis/2.6/redis.conf). -> - 自描述文档 [redis.conf for Redis 2.4](https://raw.githubusercontent.com/antirez/redis/2.4/redis.conf). -> -> 自 Redis2.6 起就可以直接通过命令行传递 Redis 配置参数。这种方法可以用于测试。自 Redis2.6 起就可以直接通过命令行传递 Redis 配置参数。这种方法可以用于测试。 - -### 设为守护进程 - -Redis 默认以非守护进程方式启动,而通常我们会将 Redis 设为守护进程启动方式,配置:`daemonize yes` - -#### 远程访问 - -Redis 默认绑定 127.0.0.1,这样就只能本机才能访问,若要 Redis 允许远程访问,需要配置:`bind 0.0.0.0` - -#### 设置密码 - -Redis 默认访问不需要密码,如果需要设置密码,需要如下配置: - -- `protected-mode yes` -- `requirepass <密码>` - -#### 配置参数表 - -| 配置项 | 说明 | -| :-- | :-- | -| `daemonize no` | Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no ) | -| `pidfile /var/run/redis.pid` | 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定 | -| `port 6379` | 指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上 MERZ 对应的号码,而 MERZ 取自意大利歌女 Alessia Merz 的名字 | -| `bind 127.0.0.1` | 绑定的主机地址 | -| `timeout 300` | 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能 | -| `loglevel notice` | 指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice | -| `logfile stdout` | 日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null | -| `databases 16` | 设置数据库的数量,默认数据库为 0,可以使用 SELECT 命令在连接上指定数据库 id | -| `save ` Redis 默认配置文件中提供了三个条件:**save 900 1**、**save 300 10**、**save 60 10000** 分别表示 900 秒(15 分钟)内有 1 个更改,300 秒(5 分钟)内有 10 个更改以及 60 秒内有 10000 个更改。 | 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合 | -| `rdbcompression yes` | 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大 | -| `dbfilename dump.rdb` | 指定本地数据库文件名,默认值为 dump.rdb | -| `dir ./` | 指定本地数据库存放目录 | -| `slaveof ` | 设置当本机为 slav 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步 | -| `masterauth ` | 当 master 服务设置了密码保护时,slav 服务连接 master 的密码 | -| `requirepass foobared` | 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH 命令提供密码,默认关闭 | -| `maxclients 128` | 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息 | -| `maxmemory ` | 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区 | -| `appendonly no` | 指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为 no | -| `appendfilename appendonly.aof` | 指定更新日志文件名,默认为 appendonly.aof | -| `appendfsync everysec` | 指定更新日志条件,共有 3 个可选值:**no**:表示等操作系统进行数据缓存同步到磁盘(快)**always**:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)**everysec**:表示每秒同步一次(折中,默认值) | -| `vm-enabled no` | 指定是否启用虚拟内存机制,默认值为 no,简单的介绍一下,VM 机制将数据分页存放,由 Redis 将访问量较少的页即冷数据 swap 到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析 Redis 的 VM 机制) | -| `vm-swap-file /tmp/redis.swap` | 虚拟内存文件路径,默认值为 /tmp/redis.swap,不可多个 Redis 实例共享 | -| `vm-max-memory 0` | 将所有大于 vm-max-memory 的数据存入虚拟内存,无论 vm-max-memory 设置多小,所有索引数据都是内存存储的(Redis 的索引数据 就是 keys),也就是说,当 vm-max-memory 设置为 0 的时候,其实是所有 value 都存在于磁盘。默认值为 0 | -| `vm-page-size 32` | Redis swap 文件分成了很多的 page,一个对象可以保存在多个 page 上面,但一个 page 上不能被多个对象共享,vm-page-size 是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为 32 或者 64bytes;如果存储很大大对象,则可以使用更大的 page,如果不确定,就使用默认值 | -| `vm-pages 134217728` | 设置 swap 文件中的 page 数量,由于页表(一种表示页面空闲或使用的 bitmap)是在放在内存中的,,在磁盘上每 8 个 pages 将消耗 1byte 的内存。 | -| `vm-max-threads 4` | 设置访问 swap 文件的线程数,最好不要超过机器的核数,如果设置为 0,那么所有对 swap 文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为 4 | -| `glueoutputbuf yes` | 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启 | -| `hash-max-zipmap-entries 64 hash-max-zipmap-value 512` | 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法 | -| `activerehashing yes` | 指定是否激活重置哈希,默认为开启(后面在介绍 Redis 的哈希算法时具体介绍) | -| `include /path/to/local.conf` | 指定包含其它的配置文件,可以在同一主机上多个 Redis 实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件 | - -## Redis 集群使用和配置 - -Redis 3.0 后支持集群模式。 - -### 集群规划 - -`Redis` 集群一般由 **多个节点** 组成,节点数量至少为 `6` 个,才能保证组成 **完整高可用** 的集群。 - -
-理想情况当然是所有节点各自在不同的机器上,首先于资源,本人在部署 Redis 集群时,只得到 3 台服务器。所以,我计划每台服务器部署 2 个 Redis 节点。 - -### 部署 - -Redis 集群节点的安装与单节点服务相同,差异仅在于部署方式。 - -假设三台服务器地址如下: - -- 服务 A:127.0.0.1 -- 服务 B:127.0.0.2 -- 服务 C:127.0.0.3 - -分配如下: - -| 127.0.0.1 | 127.0.0.2 | 127.0.0.3 | -| -------------- | -------------- | -------------- | -| 127.0.0.1:6381 | 127.0.0.2:6383 | 127.0.0.3:6385 | -| 127.0.0.1:6382 | 127.0.0.2:6384 | 127.0.0.3:6386 | - -#### (1)创建节点目录 - -我个人偏好将软件放在 `/opt` 目录下,在我的机器中,Redis 都安装在 `/usr/local/redis` 目录下。所以,下面的命令和配置都假设 Redis 安装目录为 `/usr/local/redis` 。 - -确保机器上已经安装了 Redis 后,执行以下命令,创建 Redis 集群节点实例目录: - -- 127.0.0.1 - -```bash -sudo mkdir -p /usr/local/redis/cluster/6381 -sudo mkdir -p /usr/local/redis/cluster/6382 -``` - -- 127.0.0.2 - -```bash -sudo mkdir -p /usr/local/redis/cluster/6383 -sudo mkdir -p /usr/local/redis/cluster/6384 -``` - -- 127.0.0.3 - -```bash -sudo mkdir -p /usr/local/redis/cluster/6385 -sudo mkdir -p /usr/local/redis/cluster/6386 -``` - - -#### (2)集群节点实例配置 - -每个实例目录下,新建 `redis.conf` 配置文件。 - -实例配置模板以 6381 节点为例(其他节点,完全替换配置中的端口号 6381 即可),如下: - -``` -# 端口号 -port 6381 -# 绑定的主机端口(0.0.0.0 表示允许远程访问) -bind 0.0.0.0 -# 以守护进程方式启动 -daemonize yes - -# 开启集群模式 -cluster-enabled yes -# 集群的配置,配置文件首次启动自动生成 -cluster-config-file /usr/local/redis/cluster/6381/6381.conf -# 请求超时时间,设置 10 秒 -cluster-node-timeout 10000 - -# 开启 AOF 持久化 -appendonly yes -# 数据存放目录 -dir /usr/local/redis/cluster/6381 -# 进程文件 -pidfile /var/run/redis/redis-6381.pid -# 日志文件 -logfile /usr/local/redis/cluster/6381/6381.log -``` - -#### (3)启动 Redis 节点 - -Redis 的 utils/create-cluster 目录下自带了一个名为 create-cluster 的脚本工具,可以利用它来新建、启动、停止、重启 Redis 节点。 - -脚本中有几个关键参数: - -- `PORT`=30000 - 初始端口号 -- `TIMEOUT`=2000 - 超时时间 -- `NODES`=6 - 节点数 -- `REPLICAS`=1 - 备份数 - -脚本中的每个命令项会根据初始端口号,以及设置的节点数,遍历的去执行操作。 - -由于前面的规划中,节点端口是从 6381 ~ 6386,所以需要将 PORT 变量设为 6380。 - -脚本中启动每个 Redis 节点是通过指定命令行参数来配置属性。所以,我们需要改一下: - -```bash -if [ "$1" == "start" ] -then - while [ $((PORT < ENDPORT)) != "0" ]; do - PORT=$((PORT+1)) - echo "Starting $PORT" - /usr/local/redis/src/redis-server /usr/local/redis/cluster/${PORT}/redis.conf - done - exit 0 -fi -``` - -好了,在每台服务器上,都执行 `./create-cluster start` 来启动节点。 - -然后,通过 ps 命令来确认 Redis 进程是否已经工作: - -``` -$ ps -ef | grep redis -root 12036 1 12 16:26 ? 00:08:28 /usr/local/redis/src/redis-server 0.0.0.0:6381 [cluster] -root 12038 1 0 16:26 ? 00:00:03 /usr/local/redis/src/redis-server 0.0.0.0:6382 [cluster] -``` - -#### (4)启动集群 - -通过 `redis-cli --cluster create` 命令可以自动配置集群,如下: - -```bash -$ /usr/local/redis/src/redis-cli --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.2:6383 127.0.0.2:6384 127.0.0.3:6385 127.0.0.3:6386 --cluster-replicas 1 -``` - -如果启动成功,可以看到如下信息: - -``` ->>> Performing hash slots allocation on 6 nodes... -Master[0] -> Slots 0 - 5460 -Master[1] -> Slots 5461 - 10922 -Master[2] -> Slots 10923 - 16383 -Adding replica 127.0.0.2:6384 to 127.0.0.1:6381 -Adding replica 127.0.0.3:6386 to 127.0.0.2:6383 -Adding replica 127.0.0.1:6382 to 127.0.0.3:6385 -M: 75527b790e46530ea271a5b78f9e0fd9030f68e0 127.0.0.1:6381 - slots:[0-5460] (5461 slots) master -S: 031dd0fd5ad90fa26fcf45d49ad906d063611a6d 127.0.0.1:6382 - replicates 53012ebdd25005840da9ecbe07d937296a264206 -M: 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 127.0.0.2:6383 - slots:[5461-10922] (5462 slots) master -S: 016ae9624202891cc6f2b480ff0634de478197fb 127.0.0.2:6384 - replicates 75527b790e46530ea271a5b78f9e0fd9030f68e0 -M: 53012ebdd25005840da9ecbe07d937296a264206 127.0.0.3:6385 - slots:[10923-16383] (5461 slots) master -S: b6d70f2ed78922b1dcb7967ebe1d05ad9157fca8 127.0.0.3:6386 - replicates 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 -Can I set the above configuration? (type 'yes' to accept): yes ->>> Nodes configuration updated ->>> Assign a different config epoch to each node ->>> Sending CLUSTER MEET messages to join the cluster -Waiting for the cluster to join -.... ->>> Performing Cluster Check (using node 127.0.0.1:6381) -M: 75527b790e46530ea271a5b78f9e0fd9030f68e0 127.0.0.1:6381 - slots:[0-5460] (5461 slots) master - 1 additional replica(s) -M: 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 127.0.0.2:6383 - slots:[5461-10922] (5462 slots) master - 1 additional replica(s) -S: 016ae9624202891cc6f2b480ff0634de478197fb 127.0.0.2:6384 - slots: (0 slots) slave - replicates 75527b790e46530ea271a5b78f9e0fd9030f68e0 -M: 53012ebdd25005840da9ecbe07d937296a264206 127.0.0.3:6385 - slots:[10923-16383] (5461 slots) master - 1 additional replica(s) -S: 031dd0fd5ad90fa26fcf45d49ad906d063611a6d 127.0.0.1:6382 - slots: (0 slots) slave - replicates 53012ebdd25005840da9ecbe07d937296a264206 -S: b6d70f2ed78922b1dcb7967ebe1d05ad9157fca8 127.0.0.3:6386 - slots: (0 slots) slave - replicates 0cfbceec272b6ff70e1dfb5c5346a5cb2c20c884 -[OK] All nodes agree about slots configuration. ->>> Check for open slots... ->>> Check slots coverage... -[OK] All 16384 slots covered. -``` - -#### (5)日常维护操作 - -- 关闭集群 - `./create-cluster stop` -- 检查集群是否健康(指定任意节点即可):`./redis-cli --cluster check ` -- 尝试修复集群节点:`./redis-cli --cluster fix ` - -## Redis 命令 - -> 命令详细用法,请参考 [**Redis 命令官方文档**](https://redis.io/commands) -> -> 搬迁两张 cheat sheet 图,原址:https://www.cheatography.com/tasjaevan/cheat-sheets/redis/ - -
-
-## 压力测试 - -> 参考官方文档:[How fast is Redis?](https://redis.io/topics/benchmarks) - -Redis 自带了一个性能测试工具:`redis-benchmark` - -**(1)基本测试** - -``` -$ redis-benchmark -q -n 100000 -``` - -- `-q` 表示静默(quiet)执行 -- `-n 100000` 请求 10 万次 - -**(2)测试指定读写指令** - -``` -$ redis-benchmark -t set,lpush -n 100000 -q -SET: 74239.05 requests per second -LPUSH: 79239.30 requests per second -``` - -**(3)测试 pipeline 模式下指定读写指令** - -``` -redis-benchmark -n 1000000 -t set,get -P 16 -q -SET: 403063.28 requests per second -GET: 508388.41 requests per second -``` - -## 客户端 - -推荐使用 [**RedisDesktopManager**](https://github.com/uglide/RedisDesktopManager) - -## 脚本 - -> CentOS7 环境安装脚本:[软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - -**安装说明** - -- 采用编译方式安装 Redis, 并将其注册为 systemd 服务 -- 安装路径为:`/usr/local/redis` -- 默认下载安装 `5.0.4` 版本,端口号为:`6379`,密码为空 - -**使用方法** - -- 默认安装 - 执行以下任意命令即可: - -```sh -curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash -wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash -``` - -- 自定义安装 - 下载脚本到本地,并按照以下格式执行: - - -```sh -sh redis-install.sh [version] [port] [password] -``` - -参数说明: - -- `version` - redis 版本号 -- `port` - redis 服务端口号 -- `password` - 访问密码 - -## 参考资料 - -- [Redis 官方文档](https://redis.io) -- [深入剖析 Redis 系列(三) - Redis 集群模式搭建与原理详解](https://juejin.im/post/5b8fc5536fb9a05d2d01fb11) diff --git a/docs/linux/soft/zookeeper-install.md b/docs/linux/soft/zookeeper-install.md deleted file mode 100644 index ffb12795..00000000 --- a/docs/linux/soft/zookeeper-install.md +++ /dev/null @@ -1,96 +0,0 @@ -# ZooKeeper 安装部署 - -> 环境要求:JDK6+ - - - -- [下载解压 ZooKeeper](#下载解压-zookeeper) -- [创建配置文件](#创建配置文件) -- [启动 ZooKeeper 服务器](#启动-zookeeper-服务器) -- [启动 CLI](#启动-cli) -- [停止 ZooKeeper 服务器](#停止-zookeeper-服务器) - - - -在安装 ZooKeeper 之前,请确保你的系统是在以下任一操作系统上运行: - -- **任意 Linux OS** - 支持开发和部署。适合演示应用程序。 -- **Windows OS** - 仅支持开发。 -- **Mac OS** - 仅支持开发。 - -安装步骤如下: - -## 下载解压 ZooKeeper - -进入官方下载地址:http://zookeeper.apache.org/releases.html#download ,选择合适版本。 - -解压到本地: - -``` -$ tar -zxf zookeeper-3.4.6.tar.gz -$ cd zookeeper-3.4.6 -``` - -## 创建配置文件 - -你必须创建 `conf/zoo.cfg` 文件,否则启动时会提示你没有此文件。 - -初次尝试,不妨直接使用 Kafka 提供的模板配置文件 `conf/zoo_sample.cfg`: - -``` -$ cp conf/zoo_sample.cfg conf/zoo.cfg -``` - -## 启动 ZooKeeper 服务器 - -执行以下命令 - -``` -$ bin/zkServer.sh start -``` - -执行此命令后,你将收到以下响应 - -``` -$ JMX enabled by default -$ Using config: /Users/../zookeeper-3.4.6/bin/../conf/zoo.cfg -$ Starting zookeeper ... STARTED -``` - -## 启动 CLI - -键入以下命令 - -``` -$ bin/zkCli.sh -``` - -键入上述命令后,将连接到 ZooKeeper 服务器,你应该得到以下响应。 - -``` -Connecting to localhost:2181 -................ -................ -................ -Welcome to ZooKeeper! -................ -................ -WATCHER:: -WatchedEvent state:SyncConnected type: None path:null -[zk: localhost:2181(CONNECTED) 0] -``` - -## 停止 ZooKeeper 服务器 - -连接服务器并执行所有操作后,可以使用以下命令停止 zookeeper 服务器。 - -``` -$ bin/zkServer.sh stop -``` - -> 本节安装内容参考:[Zookeeper 安装](https://www.w3cschool.cn/zookeeper/zookeeper_installation.html) - -## 更多内容 - -- **引申** - - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) diff --git a/docs/mac.md b/docs/mac.md deleted file mode 100644 index 146d4751..00000000 --- a/docs/mac.md +++ /dev/null @@ -1,324 +0,0 @@ ---- -title: Mac -date: 2019-03-06 ---- - -# Mac - -## 基本操作 - -### 软件管理 - -dmg 格式:双击安装包,然后拖到 applications 文件夹下即可。 - -### 浏览器 - -#### 更改默认搜索引擎 - -选择「偏好设置--\>搜索--\>搜索引擎--\>Google」。 - -#### 导入 chrome 浏览器的书签 - -选择「文件-->导入自--> Google Chrome」,然后选择要导入的项目。 - -#### 快捷键 - -Command + R 刷新 - -#### 上方显示书签栏/收藏栏 - -选择「显示--> 显示个人收藏栏」。 - -#### 关闭软件的右上角通知 - -在 Mac 系统中有对通知的设置,打开系统偏好设置 — 通知 找到 QQ,然后将 QQ 提示样式设置成无即可。 - -#### 复制文件/文件夹路径 - -- OS X 10.11 系统,选中文件夹,「cmd +Option +c」 复制文件夹路径,cmd+v 粘贴。 - 之前的系统,利用 Administrator 创建一个到右键菜单,然后到设置里面设置快捷键。具体操作请百度。 - -#### 打开来自身份不明的开发者的应用程序 - -在应用程序文件夹,按住 control 键的同时打开应用程序。 - -#### 复制文件路径 - -- 选择文件/文件夹按 Command+C 复制,在终端中 Command+V 粘贴即可。 - -- 如果只是想在 Finder 中看到文件的路径, 并方便切换层级, Finder 内置了「显示路径栏」的功能, 并配置了快捷键(Option+Cmd+P). 如下图所示: - -20161124-184148.png - -参考链接: - -- [https://www.zhihu.com/question/22883229] - -### 隐藏和取消隐藏 Mac App Store 中的已购项目 - -### Mac 同时登陆两个 QQ - -在已经打开的 QQ 中,按住「command + N」即可。 - -## 系统便好设置 - -### 语音播报 - -打开「系统便好设置-->辅助功能-->语音」,即可设置不同国家的语言。 - -勾选上图中的红框部分,可以设置全局快捷键。这样的话,在任何一个软件当中,按下「 option+esc」时,就会朗读选中的文本。 - -### 调整字体大小 - -Mac 调整字体大小:「系统偏好设置 -> 显示器 -> 缩放」。如下图: - -### 如何分别设置 Mac 的鼠标和触控板的滚动方向 - -很多人习惯鼠标使用相反的滚动方向,而触控板类似 iPad 那样的自然滚动,问如何设置,当时我的回答是不知道,因为目前 OS X 的系统设置里,鼠标和触控板的设置是统一 -的。今天发现了一个免费的软件 Scroll Reverser,可以实现鼠标和触控板的分别设置。下载地址: -启动后程序显示在顶部菜单栏,设置简单明了,有需要的用户体验一下吧。 - -### Touch Bar 自定义 - -打开「系统偏好设置-键盘」,下面有个自定义控制条。 - -### 色温调节:夜间模式 - -iOS9.3 的最明显变化,莫过于苹果在发布会上特意提到的 Night Shift 夜间护眼模式。 - -### iCloud 邮箱 - -如果您用于设置 iCloud 的 Apple ID 不以“@icloud.com”、“@me.com”或“@mac.com”结尾,您必须先设置一个“@icloud.com”电子邮件地址,然后才能使用 iCloud“邮件”。 - -如果您拥有以“@mac.com”或“@me.com”结尾的电子邮件地址,则您已经拥有了名称相同但以“@icloud.com”结尾的等效地址。如果您使用的电子邮件别名以“@mac.com”或“@me.com”结尾,您也将拥有以“@icloud.com”结尾的等效地址。 - -**操作如下:** - -- 在 iOS 设备上,前往“设置”>“iCloud”,开启“邮件”,然后按照屏幕上的说明操作。 - -- 在 Mac 上,选取 Apple 菜单 >“系统偏好设置”,点按“iCloud”,再选择“邮件”,然后按照屏幕上的说明操作。 - -PS:创建 iCloud 电子邮件地址后,您无法对其进行更改。 - -设置 @icloud.com 电子邮件地址后即可用其登录 iCloud。您也可以用创建 iCloud 帐户时所用的 Apple ID 登录。 - -您可以从以下任意地址发送 iCloud 电子邮件: - -您的 iCloud 电子邮件地址(您的帐号名称@icloud.com) - -别名 - -参考链接: - -**直接注册以@icloud.com 结尾的 Apple ID:** - -参考链接: - -## PodCast - -PodCast 中文翻译为播客,是一种特殊的音频 or 视频节目。PodCast 这个单词是由 iPod+Broadcast 这两个单词组成的。 - -PodCast 可以在 iTunes 中收听。 - -## others - -### 词典 - -系统有一个自带应用「词典」,可以进行单词的查询。 - -### 如何解决 MAC 软件(dmg,akp,app)出现程序已损坏的提示 - -「xxx.app 已损坏,打不开.你应该将它移到废纸篓」,并非你安装的软件已损坏,而是 Mac 系统的安全设置问题,因为这些应用都是破解或者汉化的,那么解决方法就是临时改变 Mac 系统安全设置。 - -出现这个问题的解决方法:修改系统配置:系统偏好设置... -> 安全性与隐私。修改为任何来源。 - -如果没有这个选项的话(macOS Sierra 10.12),打开终端,执行: - -```bash -sudo spctl --master-disable -``` - -即可。 - -参考链接: - -- [Max OS-[xxx.app 已损坏,打不开.你应该将它移到废纸篓]](http://www.jianshu.com/p/379b49b88df9) - -- [如何解决 MAC 软件(dmg,akp,app)出现程序已损坏的提示](http://www.yunrui.co/25693.html) - -备注:这个链接里的各种资源都很不错啊。 - -#### 终端 - -#### 在 Finder 的当前目录打开终端 - -在 Finder 打开 terminal 终端这个功能其实是有的,但是系统默认没有打开。我们可以通过如下方法将其打开: - -进入系统偏好设置->键盘->快捷键->服务。 - -在右边新建位于文件夹位置的终端窗口上打勾。 - -如此设置后,在 Finder 中右击某文件,在出现的菜单中找到服务,然后点击新建位于文件夹位置的终端窗口即可! - -## Mac 常用快捷键 - -### Finder - -| 快捷键 | 作用 | 备注 | -| :------------------ | :------------------- | :----------------- | -| Shift + Command + G | 前往指定路径的文件夹 | 包括隐藏文件夹 | -| Shift + Command + . | 显示隐藏文件、文件夹 | 再按一次,恢复隐藏 | -| Command + ↑ | 返回上一层 | | -| Command + ↓ | 进入当前文件夹 | | - -### 编辑 - -**删除文字**: - -| 快捷键 | 作用 | 备注 | -| :----------------------- | :--------------------- | :---------------------------- | -| delete | 删除光标的前一个字符 | 相当于 Windows 键盘上的退格键 | -| fn + delete | 删除光标的后一个字符 | | -| option + delete | 删除光标之前的一个单词 | 英文有效 | -| **command + delete** | 删除光标之前的整行内容 | 【荐】 | -| command + delete | 在 finder 中删掉该文件 | | -| shift + command + delete | 清空回收站 | | - -**剪切文件**: - -首先选中文件,按 Command+C 复制文件;然后按「Command + Option + V」剪切文件。 - -备注:Command+X 只能剪切文字文本,不要混淆了。 - -## Mac 用户必须知道的 15 组快捷键 - -> 参考链接:[《轻松玩 Mac》第 6 期:Mac 用户必须知道的 15 组快捷键](http://v.youku.com/v_show/id_XNDE4MzM0NDgw.html) - -### 「space」键:快速预览 - -选中文件后, 不需要启动任何应用程序,使用「space」空格键可进行快速预览,再次按下「space」空格键取消预览。 - -可以预览 mp3、视频、pdf 等文件。 - -我们还可以**选中多张图片**, 然后按「space」键,就可以同时对比预览多张图片。这一点,很赞。 - -### 改名 - -选中文件/文件夹后,按 enter 键,就可以改名了。 - -### 「command + I」键:查看文件属性 - -- 选中文件后,按「command + I」键,可以查看文件的各种属性。 - -- 选中**文件夹**后,按「command + I」键,可以查看文件夹的大小。【荐】 - -### 切换输入法 - -「control + space」 - -### 打开 spotlight 搜索框 - -spotlight 是系统自带的软件,搜索功能不是很强大。我们一般都会用第三方的 Alfred 软件。 - -### 编辑相关 - -Cmd+C、Cmd+V、Cmd+X、Cmd+A、Cmd+Z。 - -### 翻页和光标 - -- 「control + ↑」:将光标定位到文章的最开头(翻页到文档的最上方) - -- 「control + ↓」:将光标定位到文章的最末尾(翻页到文档的最下方) - -- 「control + ←」:将光标定位到当前行的最左侧 - -- 「control + →」:将光标定位到当前行的最右侧 - -### 「command + shift + Y」:将文字快速保存到便笺 - -选中你想要的内容(例如文字、链接等),然后按下 command + shift + Y」,那么你选中的内容就会快速保存到系统自带的「便笺」软件中。 - -如果你想临时性的保存一段内容,这个操作很实用。 - -### 程序相关 - -- 「command + Q」:快速退出程序 - -- 「command + tab」:切换程序 - -- 「command + H」:隐藏当前应用程序。这是一个有趣的快捷键。 - -- 「command + ,」:打开当前应用程序的「偏好设置」。 - -### 窗口相关 - -- 「command + N」:新建一个当前应用程序的窗口 - -- 「command + `」:在当前应用程序的不同窗口之间切换【很实用】 - -我们知道,「command + tab」是在不同的软件之间切换。但你不知道的是,「command + `」是在同一个软件的不同窗口之间切换。 - -- 「command + M」:将当前窗口最小化 - -- 「command + W」:关闭当前窗口 - -### 浏览器相关 - -- 「command + T」:浏览器中,新建一个标签 - -- 「command + W」:关闭当前标签 - -* 「command + R」:强制刷新。 - -- 「command + L」:定位到地址栏。【重要】 - -### 截图相关 - -- 「command + shift + 3」:截全屏(对整个屏幕截图)。 - -### 声音相关 - -选中文字后,按住「ctrl + esc」键,会将文字进行朗读。(我发现,在触控条版的 mac 上,并没有生效) - -### Dock 栏相关 - -- 「option + command + D」:隐藏 dock 栏 - -### 强制推出 - -> 强制退出的快捷键非常重要 - -- 「option + command + esc」:打开强制退出的窗口 - -### option 相关 - -> 强烈推荐 - -- 「option + command + H」:隐藏除当前应用程序之外的其他应用程序 - -- 在文本中,按住「option」键,配合鼠标的选中,可以进行块状文字选取。 - -- 「option + command + W」:快速关闭当前应用程序的所有窗口。【很实用】 - -比如说,你一次性打开了很多文件的详情,然后就可以通过此快捷键,将这些窗口一次性关闭。 - -- 「option + command + I」:查看多个文件的总的属性。 - -* 打开 launchpad,按住「option」键,可以快速卸载应用程序。 - -* 在 dock 栏,右键点击软件图标,同时按住「option」键,就可以**强制退出**该软件。【重要】 - -- 在 Safari 浏览器中,按住「option + command + Q」退出 Safari。等下次进入 Safari 的时候,上次退出时的网址会自动被打开。【实用】 - -### 推荐一个软件:CheatSheet - -打开 CheatSheet 后,长按 command 键,会弹出当前应用程序的所有快捷键。我们还可以对这些快捷键进行保存。 - -## 📚 学习资源 - -- [Awesome Mac](https://github.com/jaywcjlove/awesome-mac) -- [awesome-macos-command-line](https://github.com/herrbischoff/awesome-macos-command-line) - -## :door: 传送门 - -| [回首頁](https://github.com/dunwu/blog) | diff --git a/docs/sidebar.md b/docs/sidebar.md new file mode 100644 index 00000000..44cc8154 --- /dev/null +++ b/docs/sidebar.md @@ -0,0 +1,53 @@ +## 文章 + +- [**Linux 命令**](linux/cli/README.md) + - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) + - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) + - [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) + - [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) + - [Linux 用户管理](linux/cli/Linux用户管理.md) + - [Linux 系统管理](linux/cli/Linux系统管理.md) + - [Linux 网络管理](linux/cli/Linux网络管理.md) + - [Linux 硬件管理](linux/cli/Linux硬件管理.md) + - [Linux 软件管理](linux/cli/Linux硬件管理.md) +- [**Linux 系统运维**](linux/ops/README.md) + - [linux 典型运维应用](linux/ops/linux典型运维应用.md) + - [samba 使用详解](linux/ops/samba使用详解.md) + - [Systemd 教程](linux/ops/systemd.md) + - [Vim 应用指南](linux/ops/vim.md) + - [Zsh 应用指南](linux/ops/zsh.md) +- [**软件运维**](linux/soft/README.md) + - 开发环境 + - [JDK 安装](linux/soft/jdk-install.md) + - [Maven 安装](linux/soft/maven-install.md) + - [Nodejs 安装](linux/soft/nodejs-install.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 运维](linux/soft/elastic) + - [Kafka 运维](linux/soft/kafka-install.md) + - [RocketMQ 运维](linux/soft/rocketmq-install.md) + - [Nacos 运维](linux/soft/nacos-install.md) + - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) + - 服务器 + - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) + - [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) + - [Docker 应用指南](docker/docker.md) + - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) + - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) + - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) + - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) + +## 脚本 + +- [**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) +- [**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) diff --git a/docs/windows.md b/docs/windows.md deleted file mode 100644 index 7ab5bf9e..00000000 --- a/docs/windows.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: 程序员玩转 Windows -categories: ['os'] -tags: ['os', 'windows'] -date: 2019-03-22 15:53 ---- - -# 程序员玩转 Windows - - - -- [软件](#软件) - - [视频音频](#视频音频) - - [压缩](#压缩) - - [文件管理](#文件管理) - - [开发](#开发) - - [编辑器](#编辑器) - - [文档](#文档) - - [效率提升](#效率提升) - - [办公](#办公) - - [个性化](#个性化) -- [参考资料](#参考资料) - - - -## 软件 - -> 扩展阅读: -> -> - [Awesome Windows](https://github.com/Awesome-Windows/Awesome/blob/master/README-cn.md) -> - [best-windows-apps](https://github.com/stackia/best-windows-apps) - -### 视频音频 - -- [Musicbee](http://getmusicbee.com/) - 类似 iTunes,但比 iTunes 更好用。 -- [ScreenToGif](http://www.screentogif.com/) - 它允许你录制屏幕的一部分区域并保存为 gif 或视频。 -- [PotPlayer](http://potplayer.daum.net/) - 多媒体播放器,具有广泛的编解码器集合,它还为用户提供大量配置选项。 -- [射手影音播放器](http://www.splayer.org/) - 来自射手网,小巧开源,首创自动匹配字幕功能。 - -### 压缩 - -- [7-Zip](http://www.7-zip.org/) - 用于处理压缩包的开源 Windows 实用程序。完美支持 7z,ZIP,GZIP,BZIP2 和 TAR 的全部特性,其他格式也可解压缩。 -- [WinRAR](http://www.rarlab.com/) - 强大的归档管理器。 它可以备份您的数据并减小电子邮件附件的大小,解压缩 RAR,ZIP 和其他文件。 - -### 文件管理 - -- [Clover](http://en.ejie.me/) - 为资源管理器加上多标签功能。 -- [Total Commander](http://www.ghisler.com/) - 老牌、功能异常强大的文件管理增强软件。 -- [Q-Dir](http://www.softwareok.com/?seite=Freeware/Q-Dir) - 轻量级的文件管理器,各种布局视图切换灵活,默认四个小窗口组成一个大窗口,操作快捷。软件虽小,粉丝忠诚。 -- [WoX](https://github.com/Wox-launcher/Wox) - 新一代文件定位工具,堪称 Windows 上的 Alfred。 -- [Everything](http://www.voidtools.com/) - 最快的文件/文件夹搜索工具, 通过名称搜索。 -- [Listary](http://www.listary.com/) - 非常优秀的 Windows 文件浏览和搜索增强工具。 -- Beyond Compare - 好用又万能的文件对比工具。 -- [CCleaner](https://www.piriform.com/ccleaner/download) - 如果你有系统洁癖,那一定要选择一款干净、良心、老牌的清洁软件。 -- [chocolatey](https://chocolatey.org/) - 包管理器 -- [Ninite](https://ninite.com/) - 最简单,最快速的更新或安装软件的方式。 -- [Recuva](http://www.piriform.com/RECUVA) - 来自 piriform 梨子公司产品,免费的数据恢复工具。 -- [Launchy](http://www.launchy.net/):自由的跨平台工具,帮助你忘记开始菜单、桌面图标甚至文件管理器。 - -### 开发 - -- [Fiddler](http://www.telerik.com/fiddler) - web 调试代理工具。 -- [Postman](https://www.getpostman.com/postman) - 适合 API 开发的完整工具链,最常用的 REST 客户端。 -- [SourceTree](https://www.sourcetreeapp.com/) - 一个免费的 Git & Mercurial 客户端。 -- [TortoiseSVN](https://tortoisesvn.net/) - Subversion(SVN)的图形客户端 -- [Wireshark](https://www.wireshark.org/) - 一个网络协议分析工具。 -- Switchhosts -- [Cmder](https://github.com/cmderdev/cmder) - 控制台模拟器包。扩展阅读:[Win 下必备神器之 Cmder](https://www.jeffjade.com/2016/01/13/2016-01-13-windows-software-cmder/) -- [Babun](http://babun.github.io/) - 基于 Cygwin,用于替代 Windows shell。 - -### 编辑器 - -- [JetBrain IDE 系列](http://www.jetbrains.com/) - 真香! -- [Visual Studio Code](https://code.visualstudio.com/) - 用于构建和调试现代 Web 和云应用程序。 -- [Eclipse](https://eclipse.org/downloads/) - 一款功能强大的 IDE。 -- [Visual Studio](https://www.visualstudio.com/vs/) - 微软官方的 IDE,通过插件可支持大量编程语言。 -- [NetBeans IDE](https://netbeans.org/) - 免费开源的 IDE。 -- [Typora](https://www.typora.io/) - 个人觉得最好用的 Markdown 编辑器。 -- [Cmd Markdown](https://www.zybuluo.com/cmd/) - 跨平台优秀 Markdown 编辑器,本文即用其所写。 -- [Notepad++](https://notepad-plus-plus.org/) - 一款支持多种编程语言的源码编辑器。 -- [Notepad2](http://www.flos-freeware.ch/notepad2.html) - 用于替代默认文本编辑器的轻量快速的编辑器,拥有众多有用的功能。 -- [Sublime Text 3](http://www.sublimetext.com/3) - 高级文本编辑器。 -- [Atom](https://atom.io/) - 面向 21 世纪的极客文本编辑器。 - -### 文档 - -- [Microsoft Office](http://www.office.com/) - 微软办公软件。 -- [WPS Office](https://www.wps.com/office-free) - 金山免费办公软件。 -- [Calibre](http://calibre-ebook.com/) - 用于电子书管理和转换的强大软件。 -- [福昕阅读器](http://www.foxitsoftware.cn/products/reader/) - 在全球拥有大量用户,最优秀的国产软件之一。Ribbon 界面,支持手写签名、插入印章等。 - -### 效率提升 - -**【笔记】** - -- [XMind](http://www.xmind.net/) - 优秀的思维导图。 -- [OneNote](https://www.onenote.com/) - Windows 下综合评价非常高的笔记应用。 -- [印象笔记](http://www.yinxiang.com/) - 老牌跨平台笔记工具,国际版 Evernote。一家立志于做百年公司的企业,安全、可靠。 -- [为知笔记](http://www.wiz.cn/index.html) - 越来越好的笔记应用,记录、查阅一切有价值的信息,同样跨平台支持。 -- [有道云笔记](http://note.youdao.com/) - 网易旗下笔记工具,同样跨主流平台支持,文字、手写、录音、拍照多种记录方式,支持任意附件格式。 -- [ShareX](https://getsharex.com/) - 你要的所有与截图、录屏相关的功能,这里都有了。 - -【快捷键】 - -- [AutoHotkey](https://autohotkey.com/) - Windows 平台的终极自动化脚本语言。 - -> 技巧: -> -> - https://www.jeffjade.com/2016/03/11/2016-03-11-autohotkey/ -> - https://www.autohotkey.com/boards/viewtopic.php?f=29&t=4296 - -### 办公 - -- [有道词典](http://cidian.youdao.com/index.html) - 最好用的免费全能翻译软件。 -- [Outlook](http://office.microsoft.com/zh-cn/outlook/) - 大名鼎鼎的 Microsoft Office 组件之一,除了电子邮件,还包含了日历、任务管理、联系人、记事本等功能。 -- [Gmail](http://www.gmail.com/) - 功能上可以称为业界标杆,用户数量世界第一,或许你真的找不到比它更好的邮件系统。 -- [Chrome](https://www.google.com/intl/zh-CN/chrome/browser/) - 最好的浏览器。 -- [Teamviewer](http://www.teamviewer.com/Zhcn/index.aspx) - 专业、功能强大的远程控制软件。使用简单,对个人用户免费。 - -### 个性化 - -- [TranslucentTB](https://github.com/TranslucentTB/TranslucentTB) - 透明化你的 Windows 任务栏。 -- [QTTabBar](http://qttabbar.wikidot.com/) - 通过多标签和额外的文件夹视图扩展资源管理器的功能。 -- [Fences](https://www.stardock.com/products/fences/) - 管理桌面快捷方式。 - -## 参考资料 - -- https://github.com/Awesome-Windows/Awesome/blob/master/README-cn.md -- https://love.appinn.com/ -- https://github.com/stackia/best-windows-apps diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 00000000..eb6bb1f5 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,7 @@ +/** + * @see https://prettier.io/docs/en/options.html + * @see https://prettier.io/docs/en/configuration.html + */ +module.exports = { + tabWidth: 2, semi: false, singleQuote: true +} From 52f0a889db475795fad5b9d69533936361fc143c Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 25 Nov 2019 11:55:01 +0800 Subject: [PATCH 19/64] update docs --- ...45\347\234\213\347\274\226\350\276\221.md" | 48 ++------ ...51\345\222\214\350\247\243\345\216\213.md" | 33 +----- ...56\345\275\225\347\256\241\347\220\206.md" | 104 ++++++------------ ...50\346\210\267\347\256\241\347\220\206.md" | 56 +++------- ...54\344\273\266\347\256\241\347\220\206.md" | 36 ++---- ...73\347\273\237\347\256\241\347\220\206.md" | 57 +++------- ...21\347\273\234\347\256\241\347\220\206.md" | 80 ++++---------- ...57\344\273\266\347\256\241\347\220\206.md" | 29 +---- ...14\347\232\204\350\211\272\346\234\257.md" | 71 ++++++------ ...56\345\212\251\344\277\241\346\201\257.md" | 43 ++------ ...20\347\273\264\345\272\224\347\224\250.md" | 27 ++--- ...77\347\224\250\350\257\246\350\247\243.md" | 32 ------ 12 files changed, 172 insertions(+), 444 deletions(-) diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" index 5fc4409b..a2714212 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" @@ -1,34 +1,8 @@ ---- -title: Linux 文件内容查看编辑 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 文件内容查看编辑 > 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` - - -- [Linux 文件内容查看编辑要点](#linux-文件内容查看编辑要点) -- [命令常见用法](#命令常见用法) - - [cat](#cat) - - [head](#head) - - [tail](#tail) - - [more](#more) - - [less](#less) - - [sed](#sed) - - [vi](#vi) - - [grep](#grep) -- [参考资料](#参考资料) - - - -## Linux 文件内容查看编辑要点 +## 1. Linux 文件内容查看编辑要点 - 连接文件并打印到标准输出设备 - 使用 [cat](#cat) - 显示指定文件的开头若干行 - 使用 [head](#head) @@ -39,9 +13,9 @@ tags: - 文本编辑器 - 使用 [vi](#vi) - 使用正则表达式搜索文本,并把匹配的行打印出来 - 使用 [grep](#grep) -## 命令常见用法 +## 2. 命令常见用法 -### cat +### 2.1. cat > cat 命令用于连接文件并打印到标准输出设备上。 > @@ -55,13 +29,13 @@ cat m1 m2 # 同时显示文件 ml 和 m2 的内容 cat m1 m2 > file # 将文件 ml 和 m2 合并后放入文件 file 中 ``` -### head +### 2.2. head > head 命令用于显示文件的开头内容。在默认情况下,head 命令显示文件的头部 10 行内容。 > > 参考:http://man.linuxde.net/head -### tail +### 2.3. tail > tail 命令用于显示文件的尾部内容。在默认情况下,tail 命令显示文件的尾部 10 行内容。如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题。如果没有指定文件或者文件名为“-”,则读取标准输入。 > @@ -75,7 +49,7 @@ tail -n +20 file # 显示文件file的内容,从第20行至文件末尾 tail -c 10 file # 显示文件file的最后10个字符 ``` -### more +### 2.4. more > more 命令是一个基于 vi 编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持 vi 中的关键字定位操作。more 名单中内置了若干快捷键,常用的有 H(获得帮助信息),Enter(向下翻滚一行),空格(向下滚动一屏),Q(退出命令)。 > @@ -100,7 +74,7 @@ more -dc file more -c -10 file ``` -### less +### 2.5. less less 命令的作用与 more 十分相似,都可以用来浏览文字档案的内容,不同的是 less 命令允许用户向前或向后浏览文件,而 more 命令只能向前浏览。用 less 命令显示文件时,用 PageUp 键向上翻页,用 PageDown 键向下翻页。要退出 less 程序,应按 Q 键。 @@ -110,7 +84,7 @@ less 命令的作用与 more 十分相似,都可以用来浏览文字档案的 less /var/log/shadowsocks.log ``` -### sed +### 2.6. sed > sed 是一种流编辑器,它是文本处理工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed 主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。 > @@ -147,7 +121,7 @@ sed '$d' file sed '/^test/'d file ``` -### vi +### 2.7. vi > vi 命令是 UNIX 操作系统和类 UNIX 操作系统中最通用的全屏幕纯文本编辑器。Linux 中的 vi 编辑器叫 vim,它是 vi 的增强版(vi Improved),与 vi 编辑器完全兼容,而且实现了很多增强功能。 > @@ -155,7 +129,7 @@ sed '/^test/'d file > > 引申阅读:[Vim 快速指南](https://github.com/dunwu/OS/blob/master/docs/vim.md) -### grep +### 2.8. grep > grep(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。 > @@ -183,6 +157,6 @@ $ grep "main()" . -r --exclude "README" $ grep "main()" . -r --exclude-from filelist ``` -## 参考资料 +## 3. 参考资料 - [Linux 命令大全](http://man.linuxde.net/) diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" index 1185ff30..ba3fad16 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" @@ -1,37 +1,16 @@ ---- -title: Linux 文件压缩和解压 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 文件压缩和解压 > 关键词:`tar`, `gzip`, `zip`, `unzip` - - -- [Linux 文件压缩和解压要点](#linux-文件压缩和解压要点) -- [命令常见用法](#命令常见用法) - - [tar](#tar) - - [gzip](#gzip) - - [zip](#zip) - - [unzip](#unzip) - - - -## Linux 文件压缩和解压要点 +## 1. Linux 文件压缩和解压要点 - 压缩和解压 tar 文件 - 使用 [tar](#tar) - 压缩和解压 gz 文件 - 使用 [gzip](#gzip) - 压缩和解压 zip 文件 - 分别使用 [zip](#zip)、[unzip](#unzip) -## 命令常见用法 +## 2. 命令常见用法 -### tar +### 2.1. tar > tar 命令可以为 linux 的文件和目录创建档案。利用 tar,可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件。tar 最初被用来在磁带上创建档案,现在,用户可以在任何设备上创建档案。利用 tar 命令,可以把一大堆的文件和目录全部打包成一个文件,这对于备份文件或将几个文件组合成为一个文件以便于网络传输是非常有用的。 > @@ -49,7 +28,7 @@ tar -zxvf log.tar.gz # 将 tar 包解压缩 tar -zxvf log30.tar.gz log2013.log # 只将 tar 内的部分文件解压出来 ``` -### gzip +### 2.2. gzip > gzip 命令用来压缩文件。gzip 是个使用广泛的压缩程序,文件经它压缩过后,其名称后面会多出“.gz”扩展名。 > @@ -68,7 +47,7 @@ gzip -rv test/ # 递归的压缩目录 gzip -dr test/ # 递归地解压目录 ``` -### zip +### 2.3. zip > zip 命令可以用来解压缩文件,或者对文件进行打包操作。zip 是个使用广泛的压缩程序,文件经它压缩后会另外产生具有“.zip”扩展名的压缩文件。 > @@ -81,7 +60,7 @@ gzip -dr test/ # 递归地解压目录 zip -q -r html.zip /home/Blinux/html ``` -### unzip +### 2.4. unzip > unzip 命令用于解压缩由 zip 命令压缩的“.zip”压缩包。 > diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" index 6e062ad8..56b5d648 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" @@ -1,58 +1,16 @@ ---- -title: Linux 文件目录管理命令 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 文件目录管理 > 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `scp`, `mv`, `rm` - - -- [Linux 文件目录工作机制](#linux-文件目录工作机制) - - [Linux 目录结构](#linux-目录结构) - - [Linux 文件属性](#linux-文件属性) -- [Linux 文件目录管理要点](#linux-文件目录管理要点) - - [目录管理](#目录管理) - - [文件管理](#文件管理) - - [文件和目录通用管理](#文件和目录通用管理) -- [命令常见用法](#命令常见用法) - - [cd](#cd) - - [ls](#ls) - - [pwd](#pwd) - - [mkdir](#mkdir) - - [rmdir](#rmdir) - - [tree](#tree) - - [touch](#touch) - - [ln](#ln) - - [rename](#rename) - - [stat](#stat) - - [file](#file) - - [chmod](#chmod) - - [chown](#chown) - - [locate](#locate) - - [find](#find) - - [cp](#cp) - - [scp](#scp) - - [mv](#mv) - - [rm](#rm) - - - -## Linux 文件目录工作机制 - -### Linux 目录结构 +## 1. Linux 文件目录工作机制 + +### 1.1. Linux 目录结构 linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用:

-### Linux 文件属性 +### 1.2. Linux 文件属性 Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限。为了保护系统的安全性,Linux 系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定。 在 Linux 中我们可以使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组,如: @@ -89,7 +47,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot - 第 2、5、8 位表示写权限,如果用"w"字符表示,则有写权限,如果用"-"字符表示没有写权限。 - 第 3、6、9 位表示可执行权限,如果用"x"字符表示,则有执行权限,如果用"-"字符表示,则没有执行权限。 -#### Linux 文件属主和属组 +#### 1.2.1. Linux 文件属主和属组 ```bash $ ls -l @@ -104,9 +62,9 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot - 因此,Linux 系统按文件拥有者、文件拥有者同组用户和其他用户来规定了不同的文件访问权限。 - 在以上实例中,bin 文件是一个目录文件,属主和属组都为 root,属主有可读、可写、可执行的权限;与属主同组的其他用户有可读和可执行的权限;其他用户也有可读和可执行的权限。 -## Linux 文件目录管理要点 +## 2. Linux 文件目录管理要点 -### 目录管理 +### 2.1. 目录管理 - 切换目录 - 使用 [cd](#cd) - 查看目录信息 - 使用 [ls](#ls) @@ -115,7 +73,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot - 创建目录 - 使用 [mkdir](#mkdir) - 删除目录 - 使用 [rmdir](#rmdir) -### 文件管理 +### 2.2. 文件管理 - 创建空文件 - 使用 [touch](#touch) - 为文件创建连接 - 使用 [ln](#ln) @@ -129,16 +87,16 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot - 查找命令的绝对路径 - 使用 [which](#which) - 查找命令的程序、源代码等相关文件 - 使用 [whereis](#whereis) -### 文件和目录通用管理 +### 2.3. 文件和目录通用管理 - 复制文件或目录 - 使用 [cp](#cp) - 复制文件或目录到远程服务器 - 使用 [scp](#scp) - 移动文件或目录 - 使用 [mv](#mv) - 删除文件或目录 - 使用 [rm](#rm) -## 命令常见用法 +## 3. 命令常见用法 -### cd +### 3.1. cd > cd 命令用来切换工作目录。 > @@ -154,7 +112,7 @@ cd .. # 切换到上级目录 cd ../.. # 切换到上两级目录 ``` -### ls +### 3.2. ls > ls 命令用来显示目录信息。 > @@ -172,13 +130,13 @@ ls -ltr # 按修改时间列出文件和文件夹详细信息 ls --color=auto # 列出文件并标记颜色分类 ``` -### pwd +### 3.3. pwd > pwd 命令用来显示当前目录的绝对路径。 > > 参考:http://man.linuxde.net/pwd -### mkdir +### 3.4. mkdir > mkdir 命令用来创建目录。 > @@ -194,7 +152,7 @@ mkdir -p zp/test mkdir -p -m 750 zp/test ``` -### rmdir +### 3.5. rmdir > rmdir 命令用来删除空目录。 > @@ -207,7 +165,7 @@ mkdir -p -m 750 zp/test rmdir -p zp/test ``` -### tree +### 3.6. tree > tree 命令以树状显示目录的内。 > @@ -234,7 +192,7 @@ tree -L 2 > /home/www/tree.txt # 当前目录结果存到 tree.txt 文件中 tree -I 'node_modules|icon|font' -L 2 ``` -### touch +### 3.7. touch > touch 命令有两个功能:一是用于把已存在文件的时间标签更新为系统当前的时间(默认方式),它们的数据将原封不动地保留下来;二是用来创建空文件。 > @@ -246,7 +204,7 @@ tree -I 'node_modules|icon|font' -L 2 touch ex2 ``` -### ln +### 3.8. ln > ln 命令用来为文件创建连接,连接类型分为硬连接和符号连接两种,默认的连接类型是硬连接。如果要创建符号连接必须使用"-s"选项。 > @@ -266,7 +224,7 @@ ln /mub1/m2.c /usr/liu/a2.c ln -s /usr/mengqc/mub1 /usr/liu/abc ``` -### rename +### 3.9. rename > rename 命令用字符串替换的方式批量重命名。 > @@ -284,7 +242,7 @@ rename "s/$//.txt/" * # 把所有的文件名都以 txt 结尾 rename "s//.txt//" * # 把所有以 .txt 结尾的文件名的.txt 删掉 ``` -### stat +### 3.10. stat > stat 命令用于显示文件的状态信息。stat 命令的输出信息比 ls 命令的输出信息要更详细。 > @@ -296,7 +254,7 @@ rename "s//.txt//" * # 把所有以 .txt 结尾的文件名的.txt stat myfile ``` -### file +### 3.11. file > file 命令用来探测给定文件的类型。file 命令对文件的检查分为文件系统、魔法幻数检查和语言检查 3 个过程。 > @@ -311,7 +269,7 @@ file -i install.log # 显示 MIME 类型 file -L /var/spool/mail # 显示符号链接的文件类型 ``` -### chmod +### 3.12. chmod > chmod 命令用来变更文件或目录的权限。在 UNIX 系统家族里,文件或目录权限的控制分别以读取、写入、执行 3 种一般权限来区分,另有 3 种特殊权限可供运用。用户可以使用 chmod 指令去变更文件与目录的权限,设置方式采用文字或数字代号皆可。符号连接的权限无法变更,如果用户对符号连接修改权限,其改变会作用在被连接的原始文件。 > @@ -350,7 +308,7 @@ chmod a+x f01   # 对文件f01的u,g,o都设置可执行属性 chmod -R 755 /home/wwwroot/* ``` -### chown +### 3.13. chown > chown 命令改变某个文件或目录的所有者和所属的组,该命令可以向某个用户授权,使该用户变成指定文件的所有者或者改变文件所属的组。用户可以是用户或者是用户 D,用户组可以是组名或组 id。文件名可以使由空格分开的文件列表,在文件名中可以包含通配符。 > @@ -365,7 +323,7 @@ chmod -R 755 /home/wwwroot/* chown -R liu /usr/meng ``` -### locate +### 3.14. locate > locate 命令和 slocate 命令都用来查找文件或目录。 > @@ -380,7 +338,7 @@ locate pwd # 查找和 pwd 相关的所有文件 locate /etc/sh # 搜索 etc 目录下所有以 sh 开头的文件 ``` -### find +### 3.15. find > find 命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则 find 命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。 > @@ -413,7 +371,7 @@ find . -iregex ".*\(\.txt\|\.pdf\)$" find /home ! -name "*.txt" ``` -### cp +### 3.16. cp > cp 命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录。它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下。cp 命令还支持同时复制多个文件,当一次复制多个文件时,目标文件参数必须是一个已经存在的目录,否则将出现错误。 > @@ -421,7 +379,7 @@ find /home ! -name "*.txt" 示例: -#### 参数 +#### 3.16.1. 参数 - 源文件:制定源文件列表。默认情况下,cp 命令不能复制目录,如果要复制目录,则必须使用`-R`选项; - 目标文件:指定目标文件。当“源文件”为多个文件时,要求“目标文件”为指定的目录。 @@ -442,7 +400,7 @@ cp -rf /usr/men/* /usr/zh cp -i /usr/men m*.c /usr/zh ``` -### scp +### 3.17. scp > scp 命令用于在 Linux 下进行远程拷贝文件的命令,和它类似的命令有 cp,不过 cp 只是在本机进行拷贝不能跨服务器,而且 scp 传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system 时,用 scp 可以帮你把文件移出来。另外,scp 还非常不占资源,不会提高多少系统负荷,在这一点上,rsync 就远远不及它了。虽然 rsync 比 scp 会快一点,但当小文件众多的情况下,rsync 会导致硬盘 I/O 非常高,而 scp 基本不影响系统正常使用。 @@ -458,7 +416,7 @@ scp -r @: scp -r test root@192.168.0.1:/opt ``` -#### 免密码传输 +#### 3.17.1. 免密码传输 (1)生成 ssh 公私钥对 @@ -477,7 +435,7 @@ cat ~/.ssh/id_rsa.pub.tmp >> ~/.ssh/authorized_keys rm ~/.ssh/id_rsa.pub.tmp ``` -### mv +### 3.18. mv > mv 命令用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中。source 表示源文件或目录,target 表示目标文件或目录。如果将一个文件移到一个已经存在的目标文件中,则目标文件的内容将被覆盖。 > @@ -503,7 +461,7 @@ mv -f *.txt /home/office # 无条件覆盖已经存在的文件 mv -bv *.txt /home/office # 复制时创建备份 ``` -### rm +### 3.19. rm > rm 命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉。对于链接文件,只是删除整个链接文件,而原有文件保持不变。 > diff --git "a/docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" index 5b050f45..91254414 100644 --- "a/docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" @@ -1,34 +1,8 @@ ---- -title: Linux 用户管理 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 用户管理 > 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` - - -- [Linux 用户管理要点](#linux-用户管理要点) -- [命令常见用法](#命令常见用法) - - [groupadd](#groupadd) - - [groupdel](#groupdel) - - [groupmod](#groupmod) - - [useradd](#useradd) - - [userdel](#userdel) - - [usermod](#usermod) - - [passwd](#passwd) - - [su](#su) - - [sudo](#sudo) - - - -## Linux 用户管理要点 +## 1. Linux 用户管理要点 - 创建用户组 - 使用 [groupadd](#groupadd) - 删除用户组 - 使用 [groupdel](#groupdel) @@ -40,9 +14,9 @@ tags: - 切换用户 - 使用 [su](#su) - 当前用户想执行没有权限执行的命令时,使用其他用户身份去执行 - 使用 [sudo](#sudo) -## 命令常见用法 +## 2. 命令常见用法 -### groupadd +### 2.1. groupadd > groupadd 命令用于创建一个新的用户组,新用户组的信息将被添加到系统文件中。 > @@ -55,7 +29,7 @@ tags: $ groupadd -g 344 jsdigname ``` -### groupdel +### 2.2. groupdel > groupdel 命令用于删除指定的用户组,本命令要修改的系统文件包括 `/ect/group` 和 `/ect/gshadow`。若该群组中仍包括某些用户,则必须先删除这些用户后,方能删除群组。 > @@ -68,13 +42,13 @@ $ groupadd damon # 创建damon用户组 $ groupdel damon # 删除这个用户组 ``` -### groupmod +### 2.3. groupmod > groupmod 命令更改群组识别码或名称。需要更改群组的识别码或名称时,可用 groupmod 指令来完成这项工作。 > > 参考:http://man.linuxde.net/groupmod -### useradd +### 2.4. useradd > useradd 命令用于 Linux 中创建的新的系统用户。useradd 可用来建立用户帐号。帐号建好之后,再用 passwd 设定帐号的密码.而可用 userdel 删除帐号。使用 useradd 指令所建立的帐号,实际上是保存在 `/etc/passwd` 文本文件中。 > @@ -90,7 +64,7 @@ $ useradd –g sales jack –G company,employees # -g:加入主要组、-G $ useradd caojh -u 544 ``` -### userdel +### 2.5. userdel > userdel 命令用于删除给定的用户,以及与用户相关的文件。若不加选项,则仅删除用户帐号,而不删除相关文件。 > @@ -105,7 +79,7 @@ $ userdel linuxde # 删除用户linuxde,但不删除其家目录及文 $ userdel -r linuxde # 删除用户linuxde,其 home 目录及文件一并删除; ``` -### usermod +### 2.6. usermod > usermod 命令用于修改用户的基本信息。usermod 命令不允许你改变正在线上的使用者帐号名称。当 usermod 命令用来改变 user id,必须确认这名 user 没在电脑上执行任何程序。你需手动更改使用者的 crontab 档。也需手动更改使用者的 at 工作档。采用 NIS server 须在 server 上更动相关的 NIS 设定。 > @@ -127,7 +101,7 @@ $ usermod -L newuser1 $ usermod -U newuser1 ``` -### passwd +### 2.7. passwd > passwd 命令用于设置用户的认证信息,包括用户密码、密码过期时间等。系统管理者则能用它管理系统用户的密码。只有管理者可以指定用户名称,一般用户只能变更自己的密码。 > @@ -172,7 +146,7 @@ $ passwd -S linuxde # 查询linuxde用户密码状态; Empty password. # 空密码,也就是没有密码; ``` -### su +### 2.8. su > su 命令用于切换当前用户身份到其他用户身份,变更时须输入所要变更的用户帐号与密码。 > @@ -191,7 +165,7 @@ $ su root -f $ su -test ``` -### sudo +### 2.9. sudo > sudo 命令用来以其他身份来执行命令,预设的身份为 root。在 `/etc/sudoers` 中设置了可执行 sudo 指令的用户。若其未经授权的用户企图使用 sudo,则会发出警告的邮件给管理员。用户使用 sudo 时,必须先输入密码,之后有 5 分钟的有效期限,超过期限则必须重新输入密码。 > @@ -208,14 +182,14 @@ $ sudo -l $ sudo -L ``` -#### 给普通用户授权 sudo +#### 2.9.1. 给普通用户授权 sudo 假设要给普通用户 mary 配置 sudo 权限: 1. `/etc/sudoers` 文件存放了 sudo 的相关用户,但是默认是没有写权限的,所以需要设为可写:`chmod u+w /etc/sudoers` -2. 在该文件中添加 `mary ALL=(ALL) ALL` ,保存并退出,让 mary 具有 sudo 的所有权限 +2. 在该文件中添加 `mary ALL=(ALL) ALL` ,保存并退出,让 mary 具有 sudo 的所有权限 3. 再将 `/etc/sudoers` 的权限恢复到默认状态:`chmod u-w /etc/sudoers` -#### 免密码授权 sudo +#### 2.9.2. 免密码授权 sudo -与给普通用户授权 sudo 类似,区别仅在于第 2 步:`mary ALL=(ALL) NOPASSWD: ALL`。 +与给普通用户授权 sudo 类似,区别仅在于第 2 步:`mary ALL=(ALL) NOPASSWD: ALL`。 diff --git "a/docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" index bae5e857..7a66969b 100644 --- "a/docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" @@ -1,30 +1,8 @@ ---- -title: Linux 硬件管理 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 硬件管理 > 关键词:`df`, `du`, `top`, `free`, `iotop` - - -- [Linux 硬件管理要点](#linux-硬件管理要点) -- [命令常见用法](#命令常见用法) - - [df](#df) - - [du](#du) - - [top](#top) - - [free](#free) - - [iotop](#iotop) - - - -## Linux 硬件管理要点 +## 1. Linux 硬件管理要点 - 查看磁盘空间 - 使用 [df](#df) - 查看文件或目录的磁盘空间 - 使用 [du](#du) @@ -32,9 +10,9 @@ tags: - 查看已使用和未使用的内存 - 使用 [free](#free) - 查看磁盘 I/O 使用状况 - 使用 [iotop](#iotop) -## 命令常见用法 +## 2. 命令常见用法 -### df +### 2.1. df > df 命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为 KB。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。 > @@ -72,7 +50,7 @@ tmpfs 1032204 0 1032204 0% /dev/shm none 0 0 0 - /proc/sys/fs/binfmt_misc ``` -### du +### 2.2. du > du 命令也是查看使用空间的,但是与 df 命令不同的是:du 命令是对文件和目录磁盘使用的空间的查看,还是和 df 命令有一些区别的。 > @@ -124,13 +102,13 @@ root@localhost [test]# du 32 scf ``` -### top +### 2.3. top > top 命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过 top 命令所提供的互动式界面,用热键可以管理。 > > 参考:http://man.linuxde.net/top -### free +### 2.4. free > free 命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。 > @@ -151,7 +129,7 @@ Mem: 2016 1973 42 0 163 1497 Swap: 4094 0 4094 ``` -### iotop +### 2.5. iotop > iotop 命令是一个用来监视磁盘 I/O 使用状况的 top 类工具。iotop 具有与 top 相似的 UI,其中包括 PID、用户、I/O、进程等相关信息。Linux 下的 IO 统计工具如 iostat,nmon 等大多数是只能统计到 per 设备的读写情况,如果你想知道每个进程是如何使用 IO 的就比较麻烦,使用 iotop 命令可以很方便的查看。 > diff --git "a/docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" index 3e3a1852..5a1b4ea2 100644 --- "a/docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" @@ -1,37 +1,8 @@ ---- -title: Linux 系统管理 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 系统管理 > 关键词:`lsb_release`, `reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` - - -- [Linux 系统管理要点](#linux-系统管理要点) -- [命令常见用法](#命令常见用法) - - [lsb_release](#lsb_release) - - [reboot](#reboot) - - [exit](#exit) - - [shutdown](#shutdown) - - [date](#date) - - [mount](#mount) - - [umount](#umount) - - [ps](#ps) - - [kill](#kill) - - [systemctl](#systemctl) - - [service](#service) - - [crontab](#crontab) - - - -## Linux 系统管理要点 +## 1. Linux 系统管理要点 - 查看 Linux 系统发行版本 - 使用 [lsb_release](#lsb_release)(此命令适用于所有的 Linux 发行版本) @@ -49,9 +20,9 @@ tags: - 启动、停止、重启、关闭、显示系统服务(Centos7 以前),使用 [service](#service) - 管理需要周期性执行的任务,使用 [crontab](#crontab) -## 命令常见用法 +## 2. 命令常见用法 -### lsb_release +### 2.1. lsb_release lsb_release 不是 bash 默认命令,如果要使用,需要先安装。 @@ -60,7 +31,7 @@ lsb_release 不是 bash 默认命令,如果要使用,需要先安装。 1. 执行 `yum provides lsb_release`,查看支持 lsb_release 命令的包。 2. 选择合适版本,执行类似这样的安装命令:`yum install -y redhat-lsb-core-4.1-27.el7.centos.1.x86_64` -### reboot +### 2.2. reboot > reboot 命令用来重新启动正在运行的 Linux 操作系统。 > @@ -73,7 +44,7 @@ reboot # 重开机。 reboot -w # 做个重开机的模拟(只有纪录并不会真的重开机)。 ``` -### exit +### 2.3. exit > exit 命令同于退出 shell,并返回给定值。在 shell 脚本中可以终止当前脚本执行。执行 exit 可使 shell 以指定的状态值退出。若不设置状态值参数,则 shell 以预设值退出。状态值 0 代表执行成功,其他值代表执行失败。 > @@ -106,7 +77,7 @@ if [ "$EXCODE" == "0" ]; then fi ``` -### shutdown +### 2.4. shutdown > shutdown 命令用来系统关机命令。shutdown 指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作。 > @@ -122,7 +93,7 @@ shutdown -h now shutdown +5 "System will shutdown after 5 minutes" ``` -### date +### 2.5. date > date 命令是显示或设置系统时间与日期。 > @@ -188,7 +159,7 @@ difference=$(( end - start )) echo $difference seconds. ``` -### mount +### 2.6. mount > mount 命令用于挂载文件系统到指定的挂载点。此命令的最常用于挂载 cdrom,使我们可以访问 cdrom 中的数据,因为你将光盘插入 cdrom 中,Linux 并不会自动挂载,必须使用 Linux mount 命令来手动完成挂载。 > @@ -208,7 +179,7 @@ mount -o ro /dev/hda1 /mnt mount -o loop /tmp/image.iso /mnt/cdrom ``` -### umount +### 2.7. umount > umount 命令用于卸载已经挂载的文件系统。利用设备名或挂载点都能 umount 文件系统,不过最好还是通过挂载点卸载,以免使用绑定挂载(一个设备,多个挂载点)时产生混乱。 > @@ -226,7 +197,7 @@ umount -v /mnt/mymount/ /tmp/diskboot.img umounted ``` -### ps +### 2.8. ps > ps 命令用于报告当前系统的进程状态。可以搭配 kill 指令随时中断、删除不必要的程序。ps 命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等,总之大部分信息都是可以通过执行该命令得到的。 > @@ -242,7 +213,7 @@ ps aux | sort -rnk 4 ps aux | sort -nk 3 ``` -### kill +### 2.9. kill > kill 命令用来删除执行中的程序或工作。kill 可将指定的信息送至程序。预设的信息为 SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9) 信息尝试强制删除程序。程序或工作的编号可利用 ps 指令或 job 指令查看。 > @@ -280,7 +251,7 @@ kill 3268 -bash: kill: (3268) - 没有那个进程 ``` -### systemctl +### 2.10. systemctl > systemctl 命令是系统服务管理器指令,它实际上将 service 和 chkconfig 这两个命令组合到一起。 > @@ -316,7 +287,7 @@ sudo systemctl stop firewalld.service sudo systemctl disable firewalld.service ``` -### service +### 2.11. service > service 命令是 Redhat Linux 兼容的发行版中用来控制系统服务的实用工具,它以启动、停止、重新启动和关闭系统服务,还可以显示所有系统服务的当前状态。 > @@ -339,7 +310,7 @@ service network restart 弹出界面 eth0: [ 确定 ] ``` -### crontab +### 2.12. crontab > crontab 命令被用来提交和管理用户的需要周期性执行的任务,与 windows 下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动 crond 进程,crond 进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。 > diff --git "a/docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" index ea01a90a..a3b7c142 100644 --- "a/docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" @@ -1,42 +1,8 @@ ---- -title: Linux 网络管理 -date: 2018-02-27 -categories: - - linux -tags: - - linux - - command ---- - # Linux 网络管理 > 关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` - - -- [Linux 网络应用要点](#linux-网络应用要点) -- [命令常见用法](#命令常见用法) - - [curl](#curl) - - [wget](#wget) - - [telnet](#telnet) - - [ip](#ip) - - [hostname](#hostname) - - [ifconfig](#ifconfig) - - [route](#route) - - [ssh](#ssh) - - [ssh-keygen](#ssh-keygen) - - [firewalld](#firewalld) - - [iptables](#iptables) - - [host](#host) - - [nslookup](#nslookup) - - [nc/netcat](#ncnetcat) - - [ping](#ping) - - [traceroute](#traceroute) - - [netstat](#netstat) - - - -## Linux 网络应用要点 +## 1. Linux 网络应用要点 - 下载文件 - 使用 [curl](#curl)、[wget](#wget) - telnet 方式登录远程主机,对远程主机进行管理 - 使用 [telnet](#telnet) @@ -54,9 +20,9 @@ tags: - 追踪数据在网络上的传输时的全部路径 - 使用 [traceroute](#traceroute) - 查看当前工作的端口信息 - 使用 [netstat](#netstat) -## 命令常见用法 +## 2. 命令常见用法 -### curl +### 2.1. curl > curl 命令是一个利用 URL 规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称 curl 为下载工具。作为一款强力工具,curl 支持包括 HTTP、HTTPS、ftp 等众多协议,还支持 POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl 可以祝一臂之力。 > @@ -73,7 +39,7 @@ $ curl http://man.linuxde.net/test.iso -o filename.iso --progress ########################################## 100.0% ``` -### wget +### 2.2. wget > wget 命令用来从指定的 URL 下载文件。 > @@ -86,7 +52,7 @@ $ curl http://man.linuxde.net/test.iso -o filename.iso --progress $ wget http://www.linuxde.net/testfile.zip ``` -### telnet +### 2.3. telnet > telnet 命令用于登录远程主机,对远程主机进行管理。 > @@ -107,7 +73,7 @@ Password: Login incorrect ``` -### ip +### 2.4. ip > ip 命令用来查看或操纵 Linux 主机的路由、网络设备、策略路由和隧道,是 Linux 下较新的功能强大的网络配置工具。 > @@ -137,7 +103,7 @@ $ ip route del default # 删除默认路由 $ ip route delete 192.168.1.0/24 dev eth0 # 删除路由 ``` -### hostname +### 2.5. hostname > hostname 命令用于查看和设置系统的主机名称。环境变量 HOSTNAME 也保存了当前的主机名。在使用 hostname 命令设置主机名后,系统并不会永久保存新的主机名,重新启动机器之后还是原来的主机名。如果需要永久修改主机名,需要同时修改 `/etc/hosts` 和 `/etc/sysconfig/network` 的相关内容。 > @@ -150,7 +116,7 @@ $ hostname AY1307311912260196fcZ ``` -### ifconfig +### 2.6. ifconfig > ifconfig 命令被用于查看和配置 Linux 内核中网络接口的网络参数。用 ifconfig 命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在。要想将上述的配置信息永远的存的电脑里,那就要修改网卡的配置文件了。 > @@ -178,7 +144,7 @@ lo Link encap:Local Loopback RX bytes:5079451 (4.8 MiB) TX bytes:5079451 (4.8 MiB) ``` -### route +### 2.7. route > route 命令用来查看和设置 Linux 内核中的网络路由表,route 命令设置的路由主要是静态路由。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。 > @@ -205,7 +171,7 @@ route add default gw 192.168.120.240 # 添加默认网关 route del default gw 192.168.120.240 # 删除默认网关 ``` -### ssh +### 2.8. ssh > ssh 命令是 openssh 套件中的客户端连接工具,可以给予 ssh 加密协议实现安全的远程登录服务器。 > @@ -222,19 +188,19 @@ ssh -p 2211 root@140.206.185.170 引申阅读:[ssh 背后的故事](https://linux.cn/article-8476-1.html) -### ssh-keygen +### 2.9. ssh-keygen > ssh-keygen 命令用于为 ssh 生成、管理和转换认证密钥,它支持 RSA 和 DSA 两种认证密钥。 > > 参考:http://man.linuxde.net/ssh-keygen -### firewalld +### 2.10. firewalld > firewalld 命令是 Linux 上的防火墙软件(Centos7 默认防火墙)。 > > 参考:https://www.cnblogs.com/moxiaoan/p/5683743.html -#### firewalld 的基本使用 +#### 2.10.1. firewalld 的基本使用 - 启动 - systemctl start firewalld - 关闭 - systemctl stop firewalld @@ -242,7 +208,7 @@ ssh -p 2211 root@140.206.185.170 - 开机禁用 - systemctl disable firewalld - 开机启用 - systemctl enable firewalld -#### 使用 systemctl 管理 firewalld 服务 +#### 2.10.2. 使用 systemctl 管理 firewalld 服务 systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 service 和 chkconfig 的功能于一体。 @@ -256,7 +222,7 @@ systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 - 查看已启动的服务列表 - systemctl list-unit-files|grep enabled - 查看启动失败的服务列表 - systemctl --failed -#### 配置 firewalld-cmd +#### 2.10.3. 配置 firewalld-cmd - 查看版本 - firewall-cmd --version - 查看帮助 - firewall-cmd --help @@ -269,14 +235,14 @@ systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 - 取消拒绝状态 - firewall-cmd --panic-off - 查看是否拒绝 - firewall-cmd --query-panic -#### 在防火墙中开放一个端口 +#### 2.10.4. 在防火墙中开放一个端口 - 添加(--permanent 永久生效,没有此参数重启后失效) - firewall-cmd --zone=public --add-port=80/tcp --permanent - 重新载入 - firewall-cmd --reload - 查看 - firewall-cmd --zone= public --query-port=80/tcp - 删除 - firewall-cmd --zone= public --remove-port=80/tcp --permanent -### iptables +### 2.11. iptables > iptables 命令是 Linux 上常用的防火墙软件,是 netfilter 项目的一部分。可以直接配置,也可以通过许多前端和图形界面配置。 > @@ -320,7 +286,7 @@ Chain OUTPUT (policy ACCEPT 3382K packets, 1819M bytes) 5075 589K ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 ``` -### host +### 2.12. host > host 命令是常用的分析域名查询工具,可以用来测试域名系统工作是否正常。 > @@ -347,7 +313,7 @@ www.jsdig.com. 463 IN CNAME host.1.jsdig.com. Received 54 bytes from 202.96.104.15#53 in 0 ms ``` -### nslookup +### 2.13. nslookup > nslookup 命令是常用域名查询工具,就是查 DNS 信息用的命令。 > @@ -366,7 +332,7 @@ Name: host.1.jsdig.com Address: 100.42.212.8 ``` -### nc/netcat +### 2.14. nc/netcat > nc 命令是 netcat 命令的简称,都是用来设置路由器。 > @@ -386,7 +352,7 @@ Address: 100.42.212.8 [root@localhost ~]# nc -u -z -w2 192.168.0.1 1-1000 # 扫描192.168.0.3 的端口 范围是 1-1000 ``` -### ping +### 2.15. ping > ping 命令用来测试主机之间网络的连通性。执行 ping 指令会使用 ICMP 传输协议,发出要求回应的信息,若远端主机的网络功能没有问题,就会回应该信息,因而得知该主机运作正常。 > @@ -408,7 +374,7 @@ PING host.1.jsdig.com (100.42.212.8) 56(84) bytes of data. rtt min/avg/max/mdev = 174.068/176.916/178.182/1.683 ms ``` -### traceroute +### 2.16. traceroute > traceroute 命令用于追踪数据包在网络上的传输时的全部路径,它默认发送的数据包大小是 40 字节。 > @@ -433,7 +399,7 @@ traceroute to www.58.com (211.151.111.30), 30 hops max, 40 byte packets 12 211.151.111.30 (211.151.111.30) 35.161 ms 35.938 ms 36.005 ms ``` -### netstat +### 2.17. netstat > netstat 命令用来打印 Linux 中网络系统的状态信息,可让你得知整个 Linux 系统的网络情况。 > diff --git "a/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" index 7292493c..64b1487e 100644 --- "a/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" @@ -1,27 +1,8 @@ ---- -title: Linux 软件管理 -date: 2018-02-26 -categories: - - linux -tags: - - linux ---- - # Linux 软件管理 > 关键词:`rpm`, `yum`, `apt-get` - - -- [rpm](#rpm) -- [yum](#yum) - - [yum 源](#yum-源) -- [apt-get](#apt-get) -- [参考资料](#参考资料) - - - -## rpm +## 1. rpm > rpm 命令是 RPM 软件包的管理工具。rpm 原本是 Red Hat Linux 发行版专门用来管理 Linux 各项套件的程序,由于它遵循 GPL 规则且功能强大方便,因而广受欢迎。逐渐受到其他发行版的采用。RPM 套件管理方式的出现,让 Linux 易于安装,升级,间接提升了 Linux 的适用度。 > @@ -83,7 +64,7 @@ rpm -e proftpd-1 rpm -qa # 列出所有安装过的包 ``` -## yum +## 2. yum > yum 命令是在 Fedora 和 RedHat 以及 SUSE 中基于 rpm 的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理 RPM 软件包,能够从指定的服务器自动下载 RPM 包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。 > @@ -141,7 +122,7 @@ yum clean headers #清除缓存目录下的 headers yum clean oldheaders #清除缓存目录下旧的 headers ``` -### yum 源 +### 2.1. yum 源 yum 的默认源是国外的,下载速度比较慢,所以最好替换为一个国内的 yum 源。 @@ -161,7 +142,7 @@ yum clean all yum makecache ``` -## apt-get +## 3. apt-get > apt-get 命令是 Debian Linux 发行版中的 APT 软件包管理工具。所有基于 Debian 的发行都使用这个包管理系统。deb 包可以把一个应用的文件包在一起,大体就如同 Windows 上的安装文件。 > @@ -202,7 +183,7 @@ apt-get upgrade apt-get dist-upgrade ``` -## 参考资料 +## 4. 参考资料 - http://man.linuxde.net/rpm - http://man.linuxde.net/yum diff --git "a/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" "b/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" index 8072d2af..16272b98 100644 --- "a/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" +++ "b/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" @@ -1,7 +1,6 @@ -> 转载自 https://github.com/jlevy/the-art-of-command-line - -*[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](README-el.md) ∙ [English](../README.md) ∙ [Español](README-es.md) ∙ [Français](README-fr.md) ∙ [Indonesia](README-id.md) ∙ [Italiano](README-it.md) ∙ [日本語](README-ja.md) ∙ [한국어](README-ko.md) ∙ [Português](README-pt.md) ∙ [Română](README-ro.md) ∙ [Русский](README-ru.md) ∙ [Slovenščina](README-sl.md) ∙ [Українська](README-uk.md) ∙ [简体中文](README-zh.md) ∙ [繁體中文](README-zh-Hant.md)* +> 转载自 https://github.com/jlevy/the-art-of-command-line +_[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](README-el.md) ∙ [English](../README.md) ∙ [Español](README-es.md) ∙ [Français](README-fr.md) ∙ [Indonesia](README-id.md) ∙ [Italiano](README-it.md) ∙ [日本語](README-ja.md) ∙ [한국어](README-ko.md) ∙ [Português](README-pt.md) ∙ [Română](README-ro.md) ∙ [Русский](README-ru.md) ∙ [Slovenščina](README-sl.md) ∙ [Українська](README-uk.md) ∙ [简体中文](README-zh.md) ∙ [繁體中文](README-zh-Hant.md)_ # 命令行的艺术 @@ -19,7 +18,6 @@ - [更多资源](#更多资源) - [免责声明](#免责声明) -

熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际上,它会提高你作为工程师的灵活性以及生产力。本文是一份我在 Linux 上工作时,发现的一些命令行使用技巧的摘要。有些技巧非常基础,而另一些则相当复杂,甚至晦涩难懂。这篇文章并不长,但当你能够熟练掌握这里列出的所有技巧时,你就学会了很多关于命令行的东西了。 @@ -36,7 +34,7 @@ 涵盖范围: -- 这篇文章不仅能帮助刚接触命令行的新手,而且对具有经验的人也大有裨益。本文致力于做到*覆盖面广*(涉及所有重要的内容),*具体*(给出具体的最常用的例子),以及*简洁*(避免冗余的内容,或是可以在其他地方轻松查到的细枝末节)。在特定应用场景下,本文的内容属于基本功或者能帮助您节约大量的时间。 +- 这篇文章不仅能帮助刚接触命令行的新手,而且对具有经验的人也大有裨益。本文致力于做到*覆盖面广*(涉及所有重要的内容),_具体_(给出具体的最常用的例子),以及*简洁*(避免冗余的内容,或是可以在其他地方轻松查到的细枝末节)。在特定应用场景下,本文的内容属于基本功或者能帮助您节约大量的时间。 - 本文主要为 Linux 所写,但在[仅限 OS X 系统](#仅限-os-x-系统)章节和[仅限 Windows 系统](#仅限-windows-系统)章节中也包含有对应操作系统的内容。除去这两个章节外,其它的内容大部分均可在其他类 Unix 系统或 OS X,甚至 Cygwin 中得到应用。 - 本文主要关注于交互式 Bash,但也有很多技巧可以应用于其他 shell 和 Bash 脚本当中。 - 除去“标准的”Unix 命令,本文还包括了一些依赖于特定软件包的命令(前提是它们具有足够的价值)。 @@ -47,7 +45,6 @@ `pip` 或 `brew`(以及其它合适的包管理器)来安装依赖的程序。 - 遇到问题的话,请尝试使用 [Explainshell](http://explainshell.com/) 去获取相关命令、参数、管道等内容的解释。 - ## 基础 - 学习 Bash 的基础知识。具体地,在命令行中输入 `man bash` 并至少全文浏览一遍; 它理解起来很简单并且不冗长。其他的 shell 可能很好用,但 Bash 的功能已经足够强大并且到几乎总是可用的( 如果你*只*学习 zsh,fish 或其他的 shell 的话,在你自己的设备上会显得很方便,但过度依赖这些功能会给您带来不便,例如当你需要在服务器上工作时)。 @@ -74,33 +71,31 @@ - 学会使用 `apt-get`,`yum`,`dnf` 或 `pacman` (具体使用哪个取决于你使用的 Linux 发行版)来查找和安装软件包。并确保你的环境中有 `pip` 来安装基于 Python 的命令行工具 (接下来提到的部分程序使用 `pip` 来安装会很方便)。 - ## 日常使用 - 在 Bash 中,可以通过按 **Tab** 键实现自动补全参数,使用 **ctrl-r** 搜索命令行历史记录(按下按键之后,输入关键字便可以搜索,重复按下 **ctrl-r** 会向后查找匹配项,按下 **Enter** 键会执行当前匹配的命令,而按下右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改)。 -- 在 Bash 中,可以按下 **ctrl-w** 删除你键入的最后一个单词,**ctrl-u** 可以删除行内光标所在位置之前的内容,**alt-b** 和 **alt-f** 可以以单词为单位移动光标,**ctrl-a** 可以将光标移至行首,**ctrl-e** 可以将光标移至行尾,**ctrl-k** 可以删除光标至行尾的所有内容,**ctrl-l** 可以清屏。键入 `man readline` 可以查看 Bash 中的默认快捷键。内容有很多,例如 **alt-.** 循环地移向前一个参数,而 **alt-*** 可以展开通配符。 +- 在 Bash 中,可以按下 **ctrl-w** 删除你键入的最后一个单词,**ctrl-u** 可以删除行内光标所在位置之前的内容,**alt-b** 和 **alt-f** 可以以单词为单位移动光标,**ctrl-a** 可以将光标移至行首,**ctrl-e** 可以将光标移至行尾,**ctrl-k** 可以删除光标至行尾的所有内容,**ctrl-l** 可以清屏。键入 `man readline` 可以查看 Bash 中的默认快捷键。内容有很多,例如 **alt-.** 循环地移向前一个参数,而 **alt-\*** 可以展开通配符。 +* 你喜欢的话,可以执行 `set -o vi` 来使用 vi 风格的快捷键,而执行 `set -o emacs` 可以把它改回来。 -- 你喜欢的话,可以执行 `set -o vi` 来使用 vi 风格的快捷键,而执行 `set -o emacs` 可以把它改回来。 +* 为了便于编辑长命令,在设置你的默认编辑器后(例如 `export EDITOR=vim`),**ctrl-x** **ctrl-e** 会打开一个编辑器来编辑当前输入的命令。在 vi 风格下快捷键则是 **escape-v**。 -- 为了便于编辑长命令,在设置你的默认编辑器后(例如 `export EDITOR=vim`),**ctrl-x** **ctrl-e** 会打开一个编辑器来编辑当前输入的命令。在 vi 风格下快捷键则是 **escape-v**。 +* 键入 `history` 查看命令行历史记录,再用 `!n`(`n` 是命令编号)就可以再次执行。其中有许多缩写,最有用的大概就是 `!$`, 它用于指代上次键入的参数,而 `!!` 可以指代上次键入的命令了(参考 man 页面中的“HISTORY EXPANSION”)。不过这些功能,你也可以通过快捷键 **ctrl-r** 和 **alt-.** 来实现。 -- 键入 `history` 查看命令行历史记录,再用 `!n`(`n` 是命令编号)就可以再次执行。其中有许多缩写,最有用的大概就是 `!$`, 它用于指代上次键入的参数,而 `!!` 可以指代上次键入的命令了(参考 man 页面中的“HISTORY EXPANSION”)。不过这些功能,你也可以通过快捷键 **ctrl-r** 和 **alt-.** 来实现。 +* `cd` 命令可以切换工作路径,输入 `cd \~` 可以进入 home 目录。要访问你的 home 目录中的文件,可以使用前缀 `\~`(例如 `\~/.bashrc`)。在 `sh` 脚本里则用环境变量 `$HOME` 指代 home 目录的路径。 -- `cd` 命令可以切换工作路径,输入 `cd \~` 可以进入 home 目录。要访问你的 home 目录中的文件,可以使用前缀 `\~`(例如 `\~/.bashrc`)。在 `sh` 脚本里则用环境变量 `$HOME` 指代 home 目录的路径。 +* 回到前一个工作路径:`cd -`。 -- 回到前一个工作路径:`cd -`。 +* 如果你输入命令的时候中途改了主意,按下 **alt-#** 在行首添加 `#` 把它当做注释再按下回车执行(或者依次按下 **ctrl-a**, **#**, **enter**)。这样做的话,之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令。 -- 如果你输入命令的时候中途改了主意,按下 **alt-#** 在行首添加 `#` 把它当做注释再按下回车执行(或者依次按下 **ctrl-a**, **#**, **enter**)。这样做的话,之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令。 +* 使用 `xargs` ( 或 `parallel`)。他们非常给力。注意到你可以控制每行参数个数(`-L`)和最大并行数(`-P`)。如果你不确定它们是否会按你想的那样工作,先使用 `xargs echo` 查看一下。此外,使用 `-I{}` 会很方便。例如: -- 使用 `xargs` ( 或 `parallel`)。他们非常给力。注意到你可以控制每行参数个数(`-L`)和最大并行数(`-P`)。如果你不确定它们是否会按你想的那样工作,先使用 `xargs echo` 查看一下。此外,使用 `-I{}` 会很方便。例如: ```bash find . -name '*.py' | xargs grep some_function cat hosts | xargs -I{} ssh root@{} hostname ``` - - `pstree -p` 以一种优雅的方式展示进程树。 - 使用 `pgrep` 和 `pkill` 根据名字查找进程或发送信号(`-f` 参数通常有用)。 @@ -126,28 +121,32 @@ - 当变量和文件名中包含空格的时候要格外小心。Bash 变量要用引号括起来,比如 `"$FOO"`。尽量使用 `-0` 或 `-print0` 选项以便用 NULL 来分隔文件名,例如 `locate -0 pattern | xargs -0 ls -al` 或 `find / -print0 -type d | xargs -0 ls -al`。如果 for 循环中循环访问的文件名含有空字符(空格、tab 等字符),只需用 `IFS=$'\n'` 把内部字段分隔符设为换行符。 - 在 Bash 脚本中,使用 `set -x` 去调试输出(或者使用它的变体 `set -v`,它会记录原始输入,包括多余的参数和注释)。尽可能地使用严格模式:使用 `set -e` 令脚本在发生错误时退出而不是继续运行;使用 `set -u` 来检查是否使用了未赋值的变量;试试 `set -o pipefail`,它可以监测管道中的错误。当牵扯到很多脚本时,使用 `trap` 来检测 ERR 和 EXIT。一个好的习惯是在脚本文件开头这样写,这会使它能够检测一些错误,并在错误发生时中断程序并输出信息: + ```bash set -euo pipefail trap "echo 'error: Script failed: see failed command above'" ERR ``` - 在 Bash 脚本中,子 shell(使用括号 `(...)`)是一种组织参数的便捷方式。一个常见的例子是临时地移动工作路径,代码如下: + ```bash # do something in current dir (cd /some/other/dir && other-command) # continue in original dir ``` -- 在 Bash 中,变量有许多的扩展方式。`${name:?error message}` 用于检查变量是否存在。此外,当 Bash 脚本只需要一个参数时,可以使用这样的代码 `input_file=${1:?usage: $0 input_file}`。在变量为空时使用默认值:`${name:-default}`。如果你要在之前的例子中再加一个(可选的)参数,可以使用类似这样的代码 `output_file=${2:-logfile}`,如果省略了 $2,它的值就为空,于是 `output_file` 就会被设为 `logfile`。数学表达式:`i=$(( (i + 1) % 5 ))`。序列:`{1..10}`。截断字符串:`${var%suffix}` 和 `${var#prefix}`。例如,假设 `var=foo.pdf`,那么 `echo ${var%.pdf}.txt` 将输出 `foo.txt`。 +- 在 Bash 中,变量有许多的扩展方式。`${name:?error message}` 用于检查变量是否存在。此外,当 Bash 脚本只需要一个参数时,可以使用这样的代码 `input_file=${1:?usage: $0 input_file}`。在变量为空时使用默认值:`${name:-default}`。如果你要在之前的例子中再加一个(可选的)参数,可以使用类似这样的代码 `output_file=${2:-logfile}`,如果省略了 \$2,它的值就为空,于是 `output_file` 就会被设为 `logfile`。数学表达式:`i=$(( (i + 1) % 5 ))`。序列:`{1..10}`。截断字符串:`${var%suffix}` 和 `${var#prefix}`。例如,假设 `var=foo.pdf`,那么 `echo ${var%.pdf}.txt` 将输出 `foo.txt`。 - 使用括号扩展(`{`...`}`)来减少输入相似文本,并自动化文本组合。这在某些情况下会很有用,例如 `mv foo.{txt,pdf} some-dir`(同时移动两个文件),`cp somefile{,.bak}`(会被扩展成 `cp somefile somefile.bak`)或者 `mkdir -p test-{a,b,c}/subtest-{1,2,3}`(会被扩展成所有可能的组合,并创建一个目录树)。 - 通过使用 `<(some command)` 可以将输出视为文件。例如,对比本地文件 `/etc/hosts` 和一个远程文件: + ```bash diff /etc/hosts <(ssh somehost cat /etc/hosts) ``` - 编写脚本时,你可能会想要把代码都放在大括号里。缺少右括号的话,代码就会因为语法错误而无法执行。如果你的脚本是要放在网上分享供他人使用的,这样的写法就体现出它的好处了,因为这样可以防止下载不完全代码被执行。 + ```bash { # 在这里写代码 @@ -158,13 +157,14 @@ - 在 Bash 中,同时重定向标准输出和标准错误:`some-command >logfile 2>&1` 或者 `some-command &>logfile`。通常,为了保证命令不会在标准输入里残留一个未关闭的文件句柄捆绑在你当前所在的终端上,在命令后添加 `>> 2+3 5 ``` - ## 文件及数据处理 - 在当前目录下通过文件名查找一个文件,使用类似于这样的命令:`find . -iname '*something*'`。在所有路径下通过文件名查找文件,使用 `locate something` (但注意到 `updatedb` 可能没有对最近新建的文件建立索引,所以你可能无法定位到这些未被索引的文件)。 @@ -222,7 +223,7 @@ - 要处理 Excel 或 CSV 文件的话,[csvkit](https://github.com/onyxfish/csvkit) 提供了 `in2csv`,`csvcut`,`csvjoin`,`csvgrep` 等方便易用的工具。 -- 当你要处理 Amazon S3 相关的工作的时候,[`s3cmd`](https://github.com/s3tools/s3cmd) 是一个很方便的工具而 [`s4cmd`](https://github.com/bloomreach/s4cmd) 的效率更高。Amazon 官方提供的 [`aws`](https://github.com/aws/aws-cli) 以及 [`saws`](https://github.com/donnemartin/saws) 是其他 AWS 相关工作的基础,值得学习。 +- 当你要处理 Amazon S3 相关的工作的时候,[`s3cmd`](https://github.com/s3tools/s3cmd) 是一个很方便的工具而 [`s4cmd`](https://github.com/bloomreach/s4cmd) 的效率更高。Amazon 官方提供的 [`aws`](https://github.com/aws/aws-cli) 以及 [`saws`](https://github.com/donnemartin/saws) 是其他 AWS 相关工作的基础,值得学习。 - 了解如何使用 `sort` 和 `uniq`,包括 uniq 的 `-u` 参数和 `-d` 参数,具体内容在后文单行脚本节中。另外可以了解一下 `comm`。 @@ -241,11 +242,13 @@ - 了解如何使用 `awk` 和 `sed` 来进行简单的数据处理。 参阅 [One-liners](#one-liners) 获取示例。 - 替换一个或多个文件中出现的字符串: + ```bash perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt ``` - 使用 [`repren`](https://github.com/jlevy/repren) 来批量重命名文件,或是在多个文件中搜索替换内容。(有些时候 `rename` 命令也可以批量重命名,但要注意,它在不同 Linux 发行版中的功能并不完全一样。) + ```bash # 将文件、目录和内容全部重命名 foo -> bar: repren --full --preserve-case --from foo --to bar . @@ -256,11 +259,12 @@ ``` - 根据 man 页面的描述,`rsync` 是一个快速且非常灵活的文件复制工具。它闻名于设备之间的文件同步,但其实它在本地情况下也同样有用。在安全设置允许下,用 `rsync` 代替 `scp` 可以实现文件续传,而不用重新从头开始。它同时也是删除大量文件的[最快方法](https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html)之一: + ```bash mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir ``` -- 若要在复制文件时获取当前进度,可使用 `pv`,[`pycp`](https://github.com/dmerejkowsky/pycp),[`progress`](https://github.com/Xfennec/progress),`rsync --progress`。若所执行的复制为block块拷贝,可以使用 `dd status=progress`。 +- 若要在复制文件时获取当前进度,可使用 `pv`,[`pycp`](https://github.com/dmerejkowsky/pycp),[`progress`](https://github.com/Xfennec/progress),`rsync --progress`。若所执行的复制为 block 块拷贝,可以使用 `dd status=progress`。 - 使用 `shuf` 可以以行为单位来打乱文件的内容或从一个文件中随机选取多行。 @@ -277,6 +281,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 制作二进制差分文件(Delta 压缩),使用 `xdelta3`。 - 使用 `iconv` 更改文本编码。需要更高级的功能,可以使用 `uconv`,它支持一些高级的 Unicode 功能。例如,这条命令移除了所有重音符号: + ```bash uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt ``` @@ -290,6 +295,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 文件属性可以通过 `chattr` 进行设置,它比文件权限更加底层。例如,为了保护文件不被意外删除,可以使用不可修改标记:`sudo chattr +i /critical/directory/or/file` - 使用 `getfacl` 和 `setfacl` 以保存和恢复文件权限。例如: + ```bash getfacl -R /some/path > permissions.txt setfacl --restore=permissions.txt @@ -315,7 +321,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 用 [`ncdu`](https://dev.yorhel.nl/ncdu) 来查看磁盘使用情况,它比寻常的命令,如 `du -sh *`,更节省时间。 -- 查找正在使用带宽的套接字连接或进程,使用 [`iftop`](http://www.ex-parrot.com/\~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs)。 +- 查找正在使用带宽的套接字连接或进程,使用 [`iftop`](http://www.ex-parrot.com/~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs)。 - `ab` 工具(Apache 中自带)可以简单粗暴地检查 web 服务器的性能。对于更复杂的负载测试,使用 `siege`。 @@ -331,7 +337,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 当调试一些之前出现的问题的时候,[`sar`](http://sebastien.godard.pagesperso-orange.fr/) 非常有用。它展示了 cpu、内存以及网络等的历史数据。 -- 关于更深层次的系统分析以及性能分析,看看 `stap`([SystemTap](https://sourceware.org/systemtap/wiki)),[`perf`](https://en.wikipedia.org/wiki/Perf_(Linux)),以及[`sysdig`](https://github.com/draios/sysdig)。 +- 关于更深层次的系统分析以及性能分析,看看 `stap`([SystemTap](https://sourceware.org/systemtap/wiki)),[`perf`](),以及[`sysdig`](https://github.com/draios/sysdig)。 - 查看你当前使用的系统,使用 `uname`,`uname -a`(Unix/kernel 信息)或者 `lsb_release -a`(Linux 发行版信息)。 @@ -340,12 +346,12 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 如果你删除了一个文件,但通过 `du` 发现没有释放预期的磁盘空间,请检查文件是否被进程占用: `lsof | grep deleted | grep "filename-of-my-big-file"` - ## 单行脚本 一些命令组合的例子: - 当你需要对文本文件做集合交、并、差运算时,`sort` 和 `uniq` 会是你的好帮手。具体例子请参照代码后面的,此处假设 `a` 与 `b` 是两内容不同的文件。这种方式效率很高,并且在小文件和上 G 的文件上都能运用(注意尽管在 `/tmp` 在一个小的根分区上时你可能需要 `-T` 参数,但是实际上 `sort` 并不被内存大小约束),参阅前文中关于 `LC_ALL` 和 `sort` 的 `-u` 参数的部分。 + ```bash sort a b | uniq > c # c 是 a 并 b sort a b | uniq -d > c # c 是 a 交 b @@ -354,18 +360,20 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 使用 `grep . *`(每行都会附上文件名)或者 `head -100 *`(每个文件有一个标题)来阅读检查目录下所有文件的内容。这在检查一个充满配置文件的目录(如 `/sys`、`/proc`、`/etc`)时特别好用。 +* 计算文本文件第三列中所有数的和(可能比同等作用的 Python 代码快三倍且代码量少三倍): -- 计算文本文件第三列中所有数的和(可能比同等作用的 Python 代码快三倍且代码量少三倍): ```bash awk '{ x += $3 } END { print x }' myfile ``` - 如果你想在文件树上查看大小/日期,这可能看起来像递归版的 `ls -l` 但比 `ls -lR` 更易于理解: + ```bash find . -type f -ls ``` - 假设你有一个类似于 web 服务器日志文件的文本文件,并且一个确定的值只会出现在某些行上,假设一个 `acct_id` 参数在 URI 中。如果你想计算出每个 `acct_id` 值有多少次请求,使用如下代码: + ```bash egrep -o 'acct_id=[0-9]+' access.log | cut -d= -f2 | sort | uniq -c | sort -rn ``` @@ -373,6 +381,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - 要持续监测文件改动,可以使用 `watch`,例如检查某个文件夹中文件的改变,可以用 `watch -d -n 2 'ls -rtlh | tail'`;或者在排查 WiFi 设置故障时要监测网络设置的更改,可以用 `watch -d -n 2 ifconfig`。 - 运行这个函数从这篇文档中随机获取一条技巧(解析 Markdown 文件并抽取项目): + ```bash function taocl() { curl -s https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/README-zh.md| @@ -514,7 +523,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - [`sar`](http://sebastien.godard.pagesperso-orange.fr/):系统历史数据 -- [`iftop`](http://www.ex-parrot.com/\~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs):套接字及进程的网络利用情况 +- [`iftop`](http://www.ex-parrot.com/~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs):套接字及进程的网络利用情况 - `ss`:套接字数据 @@ -526,13 +535,12 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir - `lsblk`:列出块设备信息:以树形展示你的磁盘以及磁盘分区信息 -- `lshw`,`lscpu`,`lspci`,`lsusb` 和 `dmidecode`:查看硬件信息,包括 CPU、BIOS、RAID、显卡、USB设备等 +- `lshw`,`lscpu`,`lspci`,`lsusb` 和 `dmidecode`:查看硬件信息,包括 CPU、BIOS、RAID、显卡、USB 设备等 - `lsmod` 和 `modinfo`:列出内核模块,并显示其细节 - `fortune`,`ddate` 和 `sl`:额,这主要取决于你是否认为蒸汽火车和莫名其妙的名人名言是否“有用” - ## 仅限 OS X 系统 以下是*仅限于* OS X 系统的技巧。 @@ -604,8 +612,7 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir ## 免责声明 -除去特别小的工作,你编写的代码应当方便他人阅读。能力往往伴随着责任,你 *有能力* 在 Bash 中玩一些奇技淫巧并不意味着你应该去做!;) - +除去特别小的工作,你编写的代码应当方便他人阅读。能力往往伴随着责任,你 _有能力_ 在 Bash 中玩一些奇技淫巧并不意味着你应该去做!;) ## 授权条款 diff --git "a/docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" "b/docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" index e0e60782..83d08447 100644 --- "a/docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" +++ "b/docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" @@ -1,34 +1,11 @@ ---- -title: 查看 Linux 命令帮助信息 -date: 2018-09-26 -categories: - - linux -tags: - - linux - - command ---- - # 查看 Linux 命令帮助信息 > Linux 中有非常多的命令,想全部背下来是很困难的事。所以,我认为学习 Linux 的第一步,就是了解如何快速检索命令说明。 > > 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - - -- [查看 Linux 命令帮助信息的要点](#查看-linux-命令帮助信息的要点) -- [命令常见用法](#命令常见用法) - - [help](#help) - - [whatis](#whatis) - - [info](#info) - - [which](#which) - - [whereis](#whereis) - - [man](#man) -- [参考资料](#参考资料) - - -## 查看 Linux 命令帮助信息的要点 +## 1. 查看 Linux 命令帮助信息的要点 - 查看 Shell 内部命令的帮助信息 - 使用 [help](#help) - 查看命令的简要说明 - 使用 [whatis](#whatis) @@ -43,15 +20,15 @@ tags: > - [Linux 命令大全](http://man.linuxde.net/) > - [linux-command](https://github.com/jaywcjlove/linux-command) -## 命令常见用法 +## 2. 命令常见用法 -### help +### 2.1. help > help 命令用于查看 Shell 内部命令的帮助信息。而对于外部命令的帮助信息只能使用 man 或者 info 命令查看。 > > 参考:http://man.linuxde.net/help -### whatis +### 2.2. whatis > whatis 用于查询一个命令执行什么功能。 > @@ -67,7 +44,7 @@ $ whatis man $ whatis -w "loca*" ``` -### info +### 2.3. info > info 是 Linux 下 info 格式的帮助指令。 > @@ -80,7 +57,7 @@ $ whatis -w "loca*" $ info man ``` -### which +### 2.4. which > which 命令用于查找并显示给定命令的绝对路径,环境变量 PATH 中保存了查找命令时需要遍历的目录。which 指令会在环境变量$PATH 设置的目录里查找符合条件的文件。也就是说,使用 which 命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。 > @@ -101,7 +78,7 @@ cd: shell built-in command cd 这个常用的命令竟然找不到啊!为什么呢?这是因为 cd 是 bash 内建的命令!但是 which 默认是找 PATH 内所规范的目录,所以当然一定找不到的! -### whereis +### 2.5. whereis > whereis 命令用来定位指令的二进制程序、源代码文件和 man 手册页等相关文件的路径。 > @@ -115,7 +92,7 @@ cd 这个常用的命令竟然找不到啊!为什么呢?这是因为 cd 是 whereis git # 将相关的文件都查找出来 ``` -### man +### 2.6. man > man 命令是 Linux 下的帮助指令,通过 man 指令可以查看 Linux 中的指令帮助、配置文件帮助和编程帮助等信息。 > @@ -129,7 +106,7 @@ $ man 3 printf # 查看 printf 命令的帮助手册中的第 3 类 $ man -k keyword # 根据命令中部分关键字来查询命令 ``` -#### man 要点 +#### 2.6.1. man 要点 在 man 的帮助手册中,可以使用 page up 和 page down 来上下翻页。 @@ -164,6 +141,6 @@ printf [builtins](1) - bash built-in commands, see bash(1) $ man 3 printf ``` -## 参考资料 +## 3. 参考资料 https://linuxtools-rst.readthedocs.io/zh_CN/latest/base/01_use_man.html diff --git "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" "b/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" index f8ed21f4..5c337274 100644 --- "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" +++ "b/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" @@ -1,15 +1,10 @@ ---- -title: Linux 典型运维应用 -date: 2019-03-06 ---- - # Linux 典型运维应用 > 💡 如果没有特殊说明,本文的案例都是针对 Centos 发行版本。 -## 网络操作 +## 1. 网络操作 -### 无法访问外网域名 +### 1.1. 无法访问外网域名 (1)在 hosts 中添加本机实际 IP 和本机实际域名的映射 @@ -36,7 +31,7 @@ nameserver 8.8.8.8 (3)测试一下能否 ping 通 www.baidu.com -### 开启、关闭防火墙 +### 1.2. 开启、关闭防火墙 firewalld 的基本使用 @@ -88,9 +83,9 @@ systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 > :point_right: 参考:[CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) -## 系统维护 +## 2. 系统维护 -### 使用 NTP 进行时间同步 +### 2.1. 使用 NTP 进行时间同步 (1)先安装时钟同步工具 ntp @@ -133,9 +128,9 @@ systemctl restart crond.service > :point_right: 参考:https://www.cnblogs.com/quchunhui/p/7658853.html -## 自动化脚本 +## 3. 自动化脚本 -### Linux 开机自启动脚本 +### 3.1. Linux 开机自启动脚本 (1)在 `/etc/rc.local` 文件中添加命令 @@ -221,7 +216,7 @@ $ update-rc.d mysql start 98 2 . > - https://blog.csdn.net/linuxshine/article/details/50717272 > - https://www.cnblogs.com/ssooking/p/6094740.html -### 定时执行脚本 +### 3.2. 定时执行脚本 (1)安装 crontab @@ -278,9 +273,9 @@ MAILTO=root > :point_right: 参考:[linux 定时执行脚本](https://blog.csdn.net/z_yong_cool/article/details/79288397) -## 配置 +## 4. 配置 -### 设置 Linux 启动模式 +### 4.1. 设置 Linux 启动模式 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) 2. 单用户模式,就像 Win9X 下的安全模式 @@ -296,7 +291,7 @@ MAILTO=root $ sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab ``` -## 参考资料 +## 5. 参考资料 - [CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) diff --git "a/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" "b/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" index f8ce05ca..e7319eaf 100644 --- "a/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" +++ "b/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" @@ -1,13 +1,3 @@ ---- -title: samba 使用详解 -date: 2018-09-28 -categories: - - linux -tags: - - linux - - windows ---- - # samba 使用详解 > samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的一个免费软件。 @@ -16,28 +6,6 @@ tags: > > 关键词:`samba`, `selinux` - - -- [1. 安装配置 samba](#1-安装配置-samba) - - [1.1. 查看是否已经安装 samba](#11-查看是否已经安装-samba) - - [1.2. 安装 samba 工具](#12-安装-samba-工具) - - [1.3. 配置 samba](#13-配置-samba) - - [1.4. 创建 samba 用户](#14-创建-samba-用户) - - [1.5. 启动 samba 服务](#15-启动-samba-服务) - - [1.6. 为 samba 添加防火墙规则](#16-为-samba-添加防火墙规则) - - [1.7. 测试 samba 服务](#17-测试-samba-服务) - - [1.8. 访问 samba 服务共享的目录](#18-访问-samba-服务共享的目录) -- [2. 配置详解](#2-配置详解) - - [2.1. samba 默认配置](#21-samba-默认配置) - - [2.2. 全局参数 [global]](#22-全局参数-global) - - [2.3. 共享参数 [共享名]](#23-共享参数-共享名) -- [3. 常见问题](#3-常见问题) - - [3.1. 你可能没有权限访问网络资源](#31-你可能没有权限访问网络资源) - - [3.2. window 下对 samba 的清理操作](#32-window-下对-samba-的清理操作) -- [4. 参考资料](#4-参考资料) - - - ## 1. 安装配置 samba 本文将以一个完整的示例来展示如何配置 samba 来实现 Linux 和 Windows 的文件共享。 From ed0bc02055402b6e85b784c84a9380bd33ae2a88 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 25 Nov 2019 13:45:01 +0800 Subject: [PATCH 20/64] Change licenses --- LICENSE | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 427 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index 6a8657f1..3b7b82d0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,427 @@ -MIT License - -Copyright (c) 2018 Zhang Peng - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. From 2970e91cc1cc91d06784afb1b50eca9ca04e2ee2 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 25 Nov 2019 17:25:03 +0800 Subject: [PATCH 21/64] update docs --- README.md | 47 +-- docs/README.md | 39 ++- docs/index.html | 1 - ...56\345\275\225\347\256\241\347\220\206.md" | 2 +- docs/linux/cli/README.md | 13 +- docs/linux/ops/README.md | 2 +- docs/linux/ops/iptables.md | 279 ++++++++++++++++++ ...20\347\273\264\345\272\224\347\224\250.md" | 31 ++ .../linux/ops/samba.md | 2 +- docs/linux/ops/systemd.md | 98 +++--- docs/linux/ops/vim.md | 64 ++-- docs/linux/ops/zsh.md | 23 +- docs/sidebar.md | 103 ++++--- 13 files changed, 498 insertions(+), 206 deletions(-) create mode 100644 docs/linux/ops/iptables.md rename "docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" => docs/linux/ops/samba.md (99%) diff --git a/README.md b/README.md index 2477f2ce..440082c7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ -
-

Linux 教程

-
+

+ + logo + +

+

+ license +

+ +

Linux Tutorial

> 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) @@ -16,25 +23,26 @@ > 学习 Linux 的第一步:当然是从 [Linux 命令](docs/linux/cli/README.md) 入手了。 -- [查看 Linux 命令帮助信息](docs/linux/cli/查看Linux命令帮助信息.md) -- [Linux 文件目录管理](docs/linux/cli/Linux文件目录管理.md) -- [Linux 文件内容查看命令](docs/linux/cli/Linux文件内容查看编辑.md) -- [Linux 文件压缩和解压](docs/linux/cli/Linux文件压缩和解压.md) -- [Linux 用户管理](docs/linux/cli/Linux用户管理.md) -- [Linux 系统管理](docs/linux/cli/Linux系统管理.md) -- [Linux 网络管理](docs/linux/cli/Linux网络管理.md) -- [Linux 硬件管理](docs/linux/cli/Linux硬件管理.md) -- [Linux 软件管理](docs/linux/cli/Linux硬件管理.md) +- [查看 Linux 命令帮助信息](docs/linux/cli/查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` +- [Linux 文件目录管理](docs/linux/cli/Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` +- [Linux 文件内容查看命令](docs/linux/cli/Linux文件内容查看编辑.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` +- [Linux 文件压缩和解压](docs/linux/cli/Linux文件压缩和解压.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` +- [Linux 用户管理](docs/linux/cli/Linux用户管理.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` +- [Linux 系统管理](docs/linux/cli/Linux系统管理.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` +- [Linux 网络管理](docs/linux/cli/Linux网络管理.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硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` +- [Linux 软件管理](docs/linux/cli/Linux硬件管理.md) - 关键词:`rpm`, `yum`, `apt-get` ### Linux 系统运维 > Linux 系统的常见运维工作。 -- [linux 典型运维应用](linux/ops/linux典型运维应用.md) -- [samba 使用详解](linux/ops/samba使用详解.md) -- [Systemd 教程](linux/ops/systemd.md) -- [Vim 应用指南](linux/ops/vim.md) -- [Zsh 应用指南](linux/ops/zsh.md) +- [linux 典型运维应用](docs/linux/ops/linux典型运维应用.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +- [Samba 应用](docs/linux/ops/samba.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) ### 软件运维 @@ -91,8 +99,9 @@ ## 资源 - **Linux 命令** - - [Linux 命令大全](http://man.linuxde.net/) - - [linux-command](https://jaywcjlove.gitee.io/linux-command/) + - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) + - [Linux 命令大全](https://man.linuxde.net/) + - [linux-command](https://github.com/jaywcjlove/linux-command) - **社区网站** - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 - [实验楼](https://www.shiyanlou.com/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 diff --git a/docs/README.md b/docs/README.md index c3bec264..ff8f6a5d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,14 @@ -
-

Linux 教程

-
+

+ + logo + +

+ +

+ license +

+ +

Linux Tutorial

> 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > @@ -16,22 +24,22 @@ > 学习 Linux 的第一步:当然是从 [Linux 命令](linux/cli/README.md) 入手了。 -- [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) -- [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) -- [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) -- [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) -- [Linux 用户管理](linux/cli/Linux用户管理.md) -- [Linux 系统管理](linux/cli/Linux系统管理.md) -- [Linux 网络管理](linux/cli/Linux网络管理.md) -- [Linux 硬件管理](linux/cli/Linux硬件管理.md) -- [Linux 软件管理](linux/cli/Linux硬件管理.md) +- [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` +- [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` +- [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` +- [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` +- [Linux 用户管理](linux/cli/Linux用户管理.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` +- [Linux 系统管理](linux/cli/Linux系统管理.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` +- [Linux 网络管理](linux/cli/Linux网络管理.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` +- [Linux 硬件管理](linux/cli/Linux硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` +- [Linux 软件管理](linux/cli/Linux硬件管理.md) - 关键词:`rpm`, `yum`, `apt-get` ### Linux 系统运维 > Linux 系统的常见运维工作。 - [linux 典型运维应用](linux/ops/linux典型运维应用.md) -- [samba 使用详解](linux/ops/samba使用详解.md) +- [samba 使用详解](linux/ops/samba.md) - [Systemd 教程](linux/ops/systemd.md) - [Vim 应用指南](linux/ops/vim.md) - [Zsh 应用指南](linux/ops/zsh.md) @@ -91,8 +99,9 @@ ## 资源 - **Linux 命令** - - [Linux 命令大全](http://man.linuxde.net/) - - [linux-command](https://jaywcjlove.gitee.io/linux-command/) + - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) + - [Linux命令大全](https://man.linuxde.net/) + - [linux-command](https://github.com/jaywcjlove/linux-command) - **社区网站** - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 - [实验楼](https://www.shiyanlou.com/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 diff --git a/docs/index.html b/docs/index.html index 77b43deb..66adae37 100644 --- a/docs/index.html +++ b/docs/index.html @@ -169,7 +169,6 @@ } .markdown-section p { - line-height: 1rem; word-spacing: 0.05rem; } diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" index 56b5d648..2ff7f159 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" @@ -8,7 +8,7 @@ linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -

+

### 1.2. Linux 文件属性 diff --git a/docs/linux/cli/README.md b/docs/linux/cli/README.md index d3a19730..41442c4c 100644 --- a/docs/linux/cli/README.md +++ b/docs/linux/cli/README.md @@ -1,10 +1,6 @@ # Linux 命令行 -## :memo: 知识点 - -> 根据应用场景,将常见 Linux 命令分门别类的一一介绍。 -> -> 如果想快速学习,推荐参考这篇文章:[命令行的艺术(转载)](命令行的艺术.md) +## 常用命令 - [查看 Linux 命令帮助信息](查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - [Linux 文件目录管理](Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` @@ -15,3 +11,10 @@ - [Linux 网络管理](Linux网络管理.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` - [Linux 硬件管理](Linux硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` - [Linux 软件管理](Linux硬件管理.md) - 关键词:`rpm`, `yum`, `apt-get` + +## 资料 + +- [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) +- [Linux命令大全](https://man.linuxde.net/) +- [linux-command](https://github.com/jaywcjlove/linux-command) + diff --git a/docs/linux/ops/README.md b/docs/linux/ops/README.md index ea47db12..e28138a4 100644 --- a/docs/linux/ops/README.md +++ b/docs/linux/ops/README.md @@ -1,3 +1,3 @@ - [linux 典型运维应用](linux典型运维应用.md) -- [samba 使用详解](samba使用详解.md) +- [samba 使用详解](samba.md) diff --git a/docs/linux/ops/iptables.md b/docs/linux/ops/iptables.md new file mode 100644 index 00000000..4863c05b --- /dev/null +++ b/docs/linux/ops/iptables.md @@ -0,0 +1,279 @@ +# Iptables 应用 + +> _iptables_ 是一个配置 Linux 内核 [防火墙](https://wiki.archlinux.org/index.php/Firewall) 的命令行工具,是 [netfilter](https://en.wikipedia.org/wiki/Netfilter) 项目的一部分。 可以直接配置,也可以通过许多前端和图形界面配置。 +> +> iptables 也经常代指该内核级防火墙。iptables 用于 [ipv4](https://en.wikipedia.org/wiki/Ipv4),_ip6tables_ 用于 [ipv6](https://en.wikipedia.org/wiki/Ipv6)。 +> +> [nftables](https://wiki.archlinux.org/index.php/Nftables) 已经包含在 [Linux kernel 3.13](http://www.phoronix.com/scan.php?page=news_item&px=MTQ5MDU) 中,以后会取代 iptables 成为主要的 Linux 防火墙工具。 +> +> 环境:CentOS7 + +## 1. 简介 + +**iptables 可以检测、修改、转发、重定向和丢弃 IPv4 数据包**。 + +过滤 IPv4 数据包的代码已经内置于内核中,并且按照不同的目的被组织成 **表** 的集合。**表** 由一组预先定义的 **链** 组成,**链**包含遍历顺序规则。每一条规则包含一个谓词的潜在匹配和相应的动作(称为 **目标**),如果谓词为真,该动作会被执行。也就是说条件匹配。 + +## 2. 安装 iptables + +(1)禁用 firewalld + +CentOS 7 上默认安装了 firewalld 作为防火墙,使用 iptables 建议关闭并禁用 firewalld。 + +```bash +systemctl stop firewalld +systemctl disable firewalld +``` + +(2)安装 iptables + +``` +yum install -y iptables-services +``` + +(3)服务管理 + +- 查看服务状态:`systemctl status iptables` +- 启用服务:`systemctl enable iptables` +- 禁用服务:`systemctl disable iptables` +- 启动服务:`systemctl start iptables` +- 重启服务:`systemctl restart iptables` +- 关闭服务: `systemctl stop iptables` + +## 3. 命令 + +基本语法: + +``` +iptables(选项)(参数) +``` + +基本选项说明: + +| 参数 | 作用 | +| ----------- | ------------------------------------------------- | +| -P | 设置默认策略:iptables -P INPUT (DROP | +| -F | 清空规则链 | +| -L | 查看规则链 | +| -A | 在规则链的末尾加入新规则 | +| -I | num 在规则链的头部加入新规则 | +| -D | num 删除某一条规则 | +| -s | 匹配来源地址 IP/MASK,加叹号"!"表示除这个 IP 外。 | +| -d | 匹配目标地址 | +| -i | 网卡名称 匹配从这块网卡流入的数据 | +| -o | 网卡名称 匹配从这块网卡流出的数据 | +| -p | 匹配协议,如 tcp,udp,icmp | +| --dport num | 匹配目标端口号 | +| --sport num | 匹配来源端口号 | + +顺序: + +``` +iptables -t 表名 <-A/I/D/R> 规则链名 [规则号] <-i/o 网卡名> -p 协议名 <-s 源IP/源子网> --sport 源端口 <-d 目标IP/目标子网> --dport 目标端口 -j 动作 +``` + +## 4. iptables 示例 + +### 4.1. 清空当前的所有规则和计数 + +```shell +iptables -F # 清空所有的防火墙规则 +iptables -X # 删除用户自定义的空链 +iptables -Z # 清空计数 +``` + +### 4.2. 配置允许 ssh 端口连接 + +```shell +iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT +# 22为你的ssh端口, -s 192.168.1.0/24表示允许这个网段的机器来连接,其它网段的ip地址是登陆不了你的机器的。 -j ACCEPT表示接受这样的请求 +``` + +### 4.3. 允许本地回环地址可以正常使用 + +```shell +iptables -A INPUT -i lo -j ACCEPT +#本地圆环地址就是那个127.0.0.1,是本机上使用的,它进与出都设置为允许 +iptables -A OUTPUT -o lo -j ACCEPT +``` + +### 4.4. 设置默认的规则 + +```shell +iptables -P INPUT DROP # 配置默认的不让进 +iptables -P FORWARD DROP # 默认的不允许转发 +iptables -P OUTPUT ACCEPT # 默认的可以出去 +``` + +### 4.5. 配置白名单 + +```shell +iptables -A INPUT -p all -s 192.168.1.0/24 -j ACCEPT # 允许机房内网机器可以访问 +iptables -A INPUT -p all -s 192.168.140.0/24 -j ACCEPT # 允许机房内网机器可以访问 +iptables -A INPUT -p tcp -s 183.121.3.7 --dport 3380 -j ACCEPT # 允许183.121.3.7访问本机的3380端口 +``` + +### 4.6. 开启相应的服务端口 + +```shell +iptables -A INPUT -p tcp --dport 80 -j ACCEPT # 开启80端口,因为web对外都是这个端口 +iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT # 允许被ping +iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 已经建立的连接得让它进来 +``` + +### 4.7. 保存规则到配置文件中 + +```shell +cp /etc/sysconfig/iptables /etc/sysconfig/iptables.bak # 任何改动之前先备份,请保持这一优秀的习惯 +iptables-save > /etc/sysconfig/iptables +cat /etc/sysconfig/iptables +``` + +### 4.8. 列出已设置的规则 + +> iptables -L [-t 表名][链名] + +- 四个表名 `raw`,`nat`,`filter`,`mangle` +- 五个规则链名 `INPUT`、`OUTPUT`、`FORWARD`、`PREROUTING`、`POSTROUTING` +- filter 表包含`INPUT`、`OUTPUT`、`FORWARD`三个规则链 + +```shell +iptables -L -t nat # 列出 nat 上面的所有规则 +# ^ -t 参数指定,必须是 raw, nat,filter,mangle 中的一个 +iptables -L -t nat --line-numbers # 规则带编号 +iptables -L INPUT + +iptables -L -nv # 查看,这个列表看起来更详细 +``` + +### 4.9. 清除已有规则 + +```shell +iptables -F INPUT # 清空指定链 INPUT 上面的所有规则 +iptables -X INPUT # 删除指定的链,这个链必须没有被其它任何规则引用,而且这条上必须没有任何规则。 + # 如果没有指定链名,则会删除该表中所有非内置的链。 +iptables -Z INPUT # 把指定链,或者表中的所有链上的所有计数器清零。 +``` + +### 4.10. 删除已添加的规则 + +```shell +# 添加一条规则 +iptables -A INPUT -s 192.168.1.5 -j DROP +``` + +将所有 iptables 以序号标记显示,执行: + +```shell +iptables -L -n --line-numbers +``` + +比如要删除 INPUT 里序号为 8 的规则,执行: + +```shell +iptables -D INPUT 8 +``` + +### 4.11. 开放指定的端口 + +```shell +iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT #允许本地回环接口(即运行本机访问本机) +iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #允许已建立的或相关连的通行 +iptables -A OUTPUT -j ACCEPT #允许所有本机向外的访问 +iptables -A INPUT -p tcp --dport 22 -j ACCEPT #允许访问22端口 +iptables -A INPUT -p tcp --dport 80 -j ACCEPT #允许访问80端口 +iptables -A INPUT -p tcp --dport 21 -j ACCEPT #允许ftp服务的21端口 +iptables -A INPUT -p tcp --dport 20 -j ACCEPT #允许FTP服务的20端口 +iptables -A INPUT -j reject #禁止其他未允许的规则访问 +iptables -A FORWARD -j REJECT #禁止其他未允许的规则访问 +``` + +### 4.12. 屏蔽 IP + +```shell +iptables -A INPUT -p tcp -m tcp -s 192.168.0.8 -j DROP # 屏蔽恶意主机(比如,192.168.0.8 +iptables -I INPUT -s 123.45.6.7 -j DROP #屏蔽单个IP的命令 +iptables -I INPUT -s 123.0.0.0/8 -j DROP #封整个段即从123.0.0.1到123.255.255.254的命令 +iptables -I INPUT -s 124.45.0.0/16 -j DROP #封IP段即从123.45.0.1到123.45.255.254的命令 +iptables -I INPUT -s 123.45.6.0/24 -j DROP #封IP段即从123.45.6.1到123.45.6.254的命令是 +``` + +### 4.13. 指定数据包出去的网络接口 + +只对 OUTPUT,FORWARD,POSTROUTING 三个链起作用。 + +```shell +iptables -A FORWARD -o eth0 +``` + +### 4.14. 查看已添加的规则 + +```shell +iptables -L -n -v +Chain INPUT (policy DROP 48106 packets, 2690K bytes) + pkts bytes target prot opt in out source destination + 5075 589K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 + 191K 90M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 +1499K 133M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 +4364K 6351M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED + 6256 327K ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 + +Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination + +Chain OUTPUT (policy ACCEPT 3382K packets, 1819M bytes) + pkts bytes target prot opt in out source destination + 5075 589K ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 +``` + +### 4.15. 启动网络转发规则 + +公网`210.14.67.7`让内网`192.168.188.0/24`上网 + +```shell +iptables -t nat -A POSTROUTING -s 192.168.188.0/24 -j SNAT --to-source 210.14.67.127 +``` + +### 4.16. 端口映射 + +本机的 2222 端口映射到内网 虚拟机的 22 端口 + +```shell +iptables -t nat -A PREROUTING -d 210.14.67.127 -p tcp --dport 2222 -j DNAT --to-dest 192.168.188.115:22 +``` + +### 4.17. 字符串匹配 + +比如,我们要过滤所有 TCP 连接中的字符串`test`,一旦出现它我们就终止这个连接,我们可以这么做: + +```shell +iptables -A INPUT -p tcp -m string --algo kmp --string "test" -j REJECT --reject-with tcp-reset +iptables -L + +# Chain INPUT (policy ACCEPT) +# target prot opt source destination +# REJECT tcp -- anywhere anywhere STRING match "test" ALGO name kmp TO 65535 reject-with tcp-reset +# +# Chain FORWARD (policy ACCEPT) +# target prot opt source destination +# +# Chain OUTPUT (policy ACCEPT) +# target prot opt source destination +``` + +### 4.18. 阻止 Windows 蠕虫的攻击 + +```shell +iptables -I INPUT -j DROP -p tcp -s 0.0.0.0/0 -m string --algo kmp --string "cmd.exe" +``` + +### 4.19. 防止 SYN 洪水攻击 + +```shell +iptables -A INPUT -p tcp --syn -m limit --limit 5/second -j ACCEPT +``` + +## 5. 参考资料 + +- https://wiki.archlinux.org/index.php/iptables_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87) +- https://wangchujiang.com/linux-command/c/iptables.html diff --git "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" "b/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" index 5c337274..d4596d90 100644 --- "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" +++ "b/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" @@ -83,6 +83,37 @@ systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 > :point_right: 参考:[CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) +### 1.3. 配置网卡 + +使用 root 权限编辑 `/etc/sysconfig/network-scripts/ifcfg-eno16777736X` 文件 + +参考以下进行配置: + +```properties +TYPE=Ethernet                        # 网络类型:Ethernet以太网 +BOOTPROTO=none                       # 引导协议:自动获取、static静态、none不指定 +DEFROUTE=yes                         # 启动默认路由 +IPV4_FAILURE_FATAL=no                # 不启用IPV4错误检测功能 +IPV6INIT=yes                         # 启用IPV6协议 +IPV6_AUTOCONF=yes                    # 自动配置IPV6地址 +IPV6_DEFROUTE=yes                    # 启用IPV6默认路由 +IPV6_FAILURE_FATAL=no                # 不启用IPV6错误检测功能 +IPV6_PEERDNS=yes +IPV6_PEERROUTES=yes +IPV6_PRIVACY="no" + +NAME=eno16777736                     # 网卡设备的别名(需要和文件名同名) +UUID=90528772-9967-46da-b401-f82b64b4acbc # 网卡设备的UUID唯一标识号 +DEVICE=eno16777736                   # 网卡的设备名称 +ONBOOT=yes                           # 开机自动激活网卡 +IPADDR=192.168.1.199                 # 网卡的固定IP地址 +PREFIX=24                            # 子网掩码 +GATEWAY=192.168.1.1                  # 默认网关IP地址 +DNS1=8.8.8.8                         # DNS域名解析服务器的IP地址 +``` + +修改完后,执行 `systemctl restart network.service` 重启网卡服务。 + ## 2. 系统维护 ### 2.1. 使用 NTP 进行时间同步 diff --git "a/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" b/docs/linux/ops/samba.md similarity index 99% rename from "docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" rename to docs/linux/ops/samba.md index e7319eaf..116ddc1f 100644 --- "a/docs/linux/ops/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" +++ b/docs/linux/ops/samba.md @@ -1,4 +1,4 @@ -# samba 使用详解 +# Samba 应用 > samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的一个免费软件。 > diff --git a/docs/linux/ops/systemd.md b/docs/linux/ops/systemd.md index fa95e913..a7f9d73d 100644 --- a/docs/linux/ops/systemd.md +++ b/docs/linux/ops/systemd.md @@ -1,4 +1,4 @@ -# Systemd 教程 +# Systemd 应用 > 搬运自:[Systemd 入门教程:命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html)、[Systemd 入门教程:实战篇](hhttp://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) @@ -6,7 +6,7 @@ Systemd 是 Linux 系统工具,用来启动[守护进程](http://www.ruanyifen 本文介绍它的基本用法,分为上下两篇。今天介绍它的主要命令,[下一篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html)介绍如何用于实战。 -## 由来 +## 1. 由来 历史上,[Linux 的启动](http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html)一直采用[`init`](https://en.wikipedia.org/wiki/Init)进程。 @@ -25,7 +25,7 @@ $ service apache2 start 二是启动脚本复杂。`init`进程只是执行启动脚本,不管其他事情。脚本需要自己处理各种 情况,这往往使得脚本变得很长。 -## Systemd 概述 +## 2. Systemd 概述 Systemd 就是为了解决这些问题而诞生的。它的设计目标是,为系统的启动和管理提供一套 完整的解决方案。 @@ -51,11 +51,11 @@ simple, keep stupid" (上图为 Systemd 架构图) -## 系统管理 +## 3. 系统管理 Systemd 并不是一个命令,而是一组命令,涉及到系统管理的方方面面。 -### systemctl +### 3.1. systemctl `systemctl`是 Systemd 的主命令,用于管理系统。 @@ -82,7 +82,7 @@ $ sudo systemctl hybrid-sleep $ sudo systemctl rescue ``` -### systemd-analyze +### 3.2. systemd-analyze `systemd-analyze`命令用于查看启动耗时。 @@ -100,7 +100,7 @@ $ systemd-analyze critical-chain $ systemd-analyze critical-chain atd.service ``` -### hostnamectl +### 3.3. hostnamectl `hostnamectl`命令用于查看当前主机的信息。 @@ -112,7 +112,7 @@ $ hostnamectl $ sudo hostnamectl set-hostname rhel7 ``` -### localectl +### 3.4. localectl `localectl`命令用于查看本地化设置。 @@ -125,7 +125,7 @@ $ sudo localectl set-locale LANG=en_GB.utf8 $ sudo localectl set-keymap en_GB ``` -### timedatectl +### 3.5. timedatectl `timedatectl`命令用于查看当前时区设置。 @@ -142,7 +142,7 @@ $ sudo timedatectl set-time YYYY-MM-DD $ sudo timedatectl set-time HH:MM:SS ``` -### loginctl +### 3.6. loginctl `loginctl`命令用于查看当前登录的用户。 @@ -157,9 +157,9 @@ $ loginctl list-users $ loginctl show-user ruanyf ``` -## Unit +## 4. Unit -### 含义 +### 4.1. 含义 Systemd 可以管理所有系统资源。不同的资源统称为 Unit(单位)。 @@ -197,7 +197,7 @@ $ systemctl list-units --failed $ systemctl list-units --type=service ``` -### Unit 的状态 +### 4.2. Unit 的状态 `systemctl status`命令用于查看系统状态和单个 Unit 的状态。 @@ -226,7 +226,7 @@ $ systemctl is-failed application.service $ systemctl is-enabled application.service ``` -### Unit 管理 +### 4.3. Unit 管理 对于用户来说,最常用的是下面这些命令,用于启动和停止 Unit(主要是 service)。 @@ -259,7 +259,7 @@ $ systemctl show -p CPUShares httpd.service $ sudo systemctl set-property httpd.service CPUShares=500 ``` -### 依赖关系 +### 4.4. 依赖关系 Unit 之间存在依赖关系:A 依赖于 B,就意味着 Systemd 在启动 A 的时候,同时会去启 动 B。 @@ -277,9 +277,9 @@ $ systemctl list-dependencies nginx.service $ systemctl list-dependencies --all nginx.service ``` -## Unit 的配置文件 +## 5. Unit 的配置文件 -### 概述 +### 5.1. 概述 每一个 Unit 都有一个配置文件,告诉 Systemd 怎么启动这个 Unit 。 @@ -306,7 +306,7 @@ $ sudo systemctl disable clamd@scan.service 配置文件的后缀名,就是该 Unit 的种类,比如`sshd.socket`。如果省略,Systemd 默认 后缀名为`.service`,所以`sshd`会被理解成`sshd.service`。 -### 配置文件的状态 +### 5.2. 配置文件的状态 `systemctl list-unit-files`命令用于列出所有配置文件。 @@ -351,7 +351,7 @@ $ sudo systemctl daemon-reload $ sudo systemctl restart httpd.service ``` -### 配置文件的格式 +### 5.3. 配置文件的格式 配置文件就是普通的文本文件,可以用文本编辑器打开。 @@ -386,7 +386,7 @@ Directive2=value 注意,键值对的等号两侧不能有空格。 -### 配置文件的区块 +### 5.4. 配置文件的区块 `[Unit]`区块通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系。它的主要字段如下。 @@ -437,7 +437,7 @@ Unit 的关系。它的主要字段如下。 Unit 配置文件的完整字段清单,请参 考[官方文档](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)。 -## Target +## 6. Target 启动计算机的时候,需要启动大量的 Unit。如果每一次启动,都要一一写明本次启动需要 哪些 Unit,显然非常不方便。Systemd 的解决方案就是 Target。 @@ -497,7 +497,7 @@ Runlevel 6 | runlevel6.target -> reboot.target 配置文件存放在`/etc/sysconfig`目录。现在的配置文件主要存放在`/lib/systemd`目录 ,在`/etc/systemd`目录里面的修改可以覆盖原始设置。 -## 日志管理 +## 7. 日志管理 Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用`journalctl`一个命 令,查看所有日志(内核日志和应用日志)。日志的配置文件 @@ -588,16 +588,16 @@ $ sudo journalctl --vacuum-size=1G $ sudo journalctl --vacuum-time=1years ``` -## 实战 +## 8. 实战 -### 开机启动 +### 8.1. 开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在`/usr/lib/systemd/system`目录添 加一个配置文件。 如果你想让该软件开机启动,就执行下面的命令(以`httpd.service`为例)。 -``` +```bash $ sudo systemctl enable httpd ``` @@ -607,19 +607,19 @@ $ sudo systemctl enable httpd 这是因为开机时,`Systemd`只执行`/etc/systemd/system`目录里面的配置文件。这也意味 着,如果把修改后的配置文件放在该目录,就可以达到覆盖原始配置的效果。 -### 启动服务 +### 8.2. 启动服务 设置开机启动以后,软件并不会立即启动,必须等到下一次开机。如果想现在就运行该软件 ,那么要执行`systemctl start`命令。 -``` +```bash $ sudo systemctl start httpd ``` 执行上面的命令以后,有可能启动失败,因此要用`systemctl status`命令查看一下该服务 的状态。 -``` +```bash $ sudo systemctl status httpd httpd.service - The Apache HTTP Server @@ -649,28 +649,28 @@ CGroup: /system.slice/httpd.service - `CGroup`块:应用的所有子进程 - 日志块:应用的日志 -### 停止服务 +### 8.3. 停止服务 终止正在运行的服务,需要执行`systemctl stop`命令。 -``` +```bash $ sudo systemctl stop httpd.service ``` 有时候,该命令可能没有响应,服务停不下来。这时候就不得不"杀进程"了,向正在运行的 进程发出`kill`信号。 -``` +```bash $ sudo systemctl kill httpd.service ``` 此外,重启服务要执行`systemctl restart`命令。 -``` +```bash $ sudo systemctl restart httpd.service ``` -### 读懂配置文件 +### 8.4. 读懂配置文件 一个服务怎么启动,完全由它的配置文件决定。下面就来看,配置文件有些什么内容。 @@ -706,7 +706,7 @@ WantedBy=multi-user.target 下面依次解释每个区块的内容。 -### [Unit] 区块:启动顺序与依赖关系。 +### 8.5. [Unit] 区块:启动顺序与依赖关系。 `Unit`区块的`Description`字段给出当前服务的简单描述,`Documentation`字段给出文档 位置。 @@ -735,11 +735,11 @@ postgresql 之后启动,而没有定义依赖 postgresql 。上线后,由于 注意,`Wants`字段与`Requires`字段只涉及依赖关系,与启动顺序无关,默认情况下是同 时启动的。 -### [Service] 区块:启动行为 +### 8.6. [Service] 区块:启动行为 `Service`区块定义如何启动当前服务。 -#### 启动命令 +#### 8.6.1. 启动命令 许多软件都有自己的环境参数文件,该文件可以用`EnvironmentFile`字段读取。 @@ -787,19 +787,19 @@ post2 候,不影响其他命令的执行。比如,`EnvironmentFile=-/etc/sysconfig/sshd`(注意等号 后面的那个连词号),就表示即使`/etc/sysconfig/sshd`文件不存在,也不会抛出错误。 -#### 启动类型 +#### 8.6.2. 启动类型 `Type`字段定义启动类型。它可以设置的值如下。 - simple(默认值):`ExecStart`字段启动的进程为主进程 - forking:`ExecStart`字段将以`fork()`方式启动,此时父进程将会退出,子进程将成 -为主进程 + 为主进程 - oneshot:类似于`simple`,但只执行一次,Systemd 会等它执行完,才启动其他服务 - dbus:类似于`simple`,但会等待 D-Bus 信号后启动 - notify:类似于`simple`,启动结束后会发出通知信号,然后 Systemd 再启动其他服 -务 + 务 - idle:类似于`simple`,但是要等到其他任务都执行完,才会启动该服务。一种使用场 -合是为让该服务的输出,不与其他服务的输出相混合 + 合是为让该服务的输出,不与其他服务的输出相混合 下面是一个`oneshot`的例子,笔记本电脑启动时,要把触摸板关掉,配置文件可以这样写 。 @@ -839,7 +839,7 @@ WantedBy=multi-user.target 行。这样的话,一旦使用`systemctl stop`命令停止服务,`ExecStop`指定的命令就会执行 ,从而重新开启触摸板。 -#### 重启行为 +#### 8.6.3. 重启行为 `Service`区块有一些字段,定义了重启行为。 @@ -881,7 +881,7 @@ sshd 正常停止(比如执行`systemctl stop`命令),它就不会重启 > `RestartSec`字段:表示 Systemd 重启服务之前,需要等待的秒数。上面的例子设为等 > 待 42 秒。 -### [Install] 区块 +### 8.7. [Install] 区块 `Install`区块,定义如何安装这个配置文件,即怎样做到开机启动。 @@ -896,7 +896,7 @@ sshd 正常停止(比如执行`systemctl stop`命令),它就不会重启 Systemd 有默认的启动 Target。 -``` +```bash $ systemctl get-default multi-user.target ``` @@ -907,7 +907,7 @@ multi-user.target 使用 Target 的时候,`systemctl list-dependencies`命令和`systemctl isolate`命令也 很有用。 -``` +```bash # 查看 multi-user.target 包含的所有服务 $ systemctl list-dependencies multi-user.target @@ -921,11 +921,11 @@ $ sudo systemctl isolate shutdown.target 方文档有一张非常清晰的 [Target 依赖关系图](https://www.freedesktop.org/software/systemd/man/bootup.html#System%20Manager%20Bootup)。 -### Target 的配置文件 +### 8.8. Target 的配置文件 Target 也有自己的配置文件。 -``` +```bash $ systemctl cat multi-user.target [Unit] @@ -948,11 +948,11 @@ AllowIsolate=yes `rescue.target`之后启动,如果它们有启动的话。 - `AllowIsolate`:允许使用`systemctl isolate`命令切换到`multi-user.target`。 -### 修改配置文件后重启 +### 8.9. 修改配置文件后重启 修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。 -``` +```bash # 重新加载配置文件 $ sudo systemctl daemon-reload @@ -960,7 +960,7 @@ $ sudo systemctl daemon-reload $ sudo systemctl restart foobar ``` -## 参考资料 +## 9. 参考资料 - [Systemd 入门教程:命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html) - [Systemd 入门教程:实战篇](hhttp://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 046fc472..3c8eae98 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -1,24 +1,4 @@ -# Vim 应用指南 - - - -- [1. 概念](#1-概念) - - [1.1. 什么是 vim](#11-什么是-vim) - - [1.2. Vim 的模式](#12-vim-的模式) -- [2. Vim 渐进学习](#2-vim-渐进学习) - - [2.1. 存活](#21-存活) - - [2.2. 感觉良好](#22-感觉良好) - - [2.3. 更好,更强,更快](#23-更好更强更快) - - [2.4. Vim 超能力](#24-vim-超能力) -- [3. Vim Cheat Sheet](#3-vim-cheat-sheet) - - [3.1. 经典版](#31-经典版) - - [3.2. 入门版](#32-入门版) - - [3.3. 进阶版](#33-进阶版) - - [3.4. 增强版](#34-增强版) - - [3.5. 文字版](#35-文字版) -- [4. 资料](#4-资料) - - +# Vim 应用 ## 1. 概念 @@ -30,19 +10,19 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 基本上 vi/vim 共分为三种模式,分别是**命令模式(Command mode)**,**插入模式(Insert mode)**和**底线命令模式(Last line mode)**。 -#### 命令模式 +#### 1.2.1. 命令模式 **用户刚刚启动 vi/vim,便进入了命令模式。** 此状态下敲击键盘动作会被 Vim 识别为命令,而非输入字符。 -#### 插入模式 +#### 1.2.2. 插入模式 **在命令模式下按下 `i` 就进入了输入模式。** 在输入模式下,你可以输入文本内容。 -#### 底线命令模式 +#### 1.2.3. 底线命令模式 **在命令模式下按下 `:`(英文冒号)就进入了底线命令模式。** @@ -133,7 +113,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 先恭喜你!你干的很不错。我们可以开始一些更为有趣的事了。在第三级,我们只谈那些和 vi 可以兼容的命令。 -#### 更好 +#### 2.3.1. 更好 下面,让我们看一下 vim 是怎么重复自己的:1515G @@ -148,7 +128,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `.` → 重复上一个命令—— 100 “desu “. > - `3.` → 重复 3 次 “desu” (注意:不是 300,你看,VIM 多聪明啊). -#### 更强 +#### 2.3.2. 更强 你要让你的光标移动更有效率,你一定要了解下面的这些命令,**千万别跳过**。 @@ -167,7 +147,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > > \> 如果你认为单词是由 blank 字符分隔符,那么你需要使用大写的 E 和 W。(注:程序语句) > -

+ >

下面,让我来说说最强的光标移动: @@ -176,7 +156,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 相信我,上面这三个命令对程序员来说是相当强大的。 -#### 更快 +#### 2.3.3. 更快 你一定要记住光标的移动,因为很多命令都可以和这些移动光标的命令连动。很多命令都可以如下来干: @@ -206,7 +186,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 你只需要掌握前面的命令,你就可以很舒服的使用 VIM 了。但是,现在,我们向你介绍的是 VIM 杀手级的功能。下面这些功能是我只用 vim 的原因。 -#### 在当前行上移动光标: `0` `^` `####`f`F`t`T`,``;` +#### 2.4.1. 在当前行上移动光标: `0` `^` `####`f`F`t`T`,``;` > - `0` → 到行头 > - `^` → 到本行的第一个非 blank 字符 @@ -216,11 +196,11 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。 > - `3fa` → 在当前行查找第三个出现的 a。 > - `F` 和 `T` → 和 `f` 和 `t` 一样,只不过是相反方向。 -

+>

还有一个很有用的命令是 `dt"` → 删除所有的内容,直到遇到双引号—— `"。` -#### 区域选择 `a` 或 `i` +#### 2.4.2. 区域选择 `a` 或 `i` 在 visual 模式下,这些命令很强大,其命令格式为 @@ -240,7 +220,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错

-#### 块操作: `` +#### 2.4.3. 块操作: `` 块操作,典型的操作: `0 I-- [ESC]` @@ -253,13 +233,13 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 在 Windows 下的 vim,你需要使用 `` 而不是 `` ,`` 是拷贝剪贴板。 -#### 自动提示: `` 和 `` +#### 2.4.4. 自动提示: `` 和 `` 在 Insert 模式下,你可以输入一个词的开头,然后按 `或是,自动补齐功能就出现了……`

-#### 宏录制: `qa` 操作序列 `q`, `@a`, `@@` +#### 2.4.5. 宏录制: `qa` 操作序列 `q`, `@a`, `@@` - `qa` 把你的操作记录在寄存器 `a。` - 于是 `@a` 会 replay 被录制的宏。 @@ -288,7 +268,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错

-#### 可视化选择: `v`,`V`,`` +#### 2.4.6. 可视化选择: `v`,`V`,`` 前面,我们看到了 ``的示例 (在 Windows 下应该是),我们可以使用 `v` 和 `V`。一但被选好了,你可以做下面的事: @@ -307,7 +287,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错

-#### 分屏: `:split` 和 `vsplit`. +#### 2.4.7. 分屏: `:split` 和 `vsplit`. 下面是主要的命令,你可以使用 VIM 的帮助 `:help split`. 你可以参考本站以前的一篇文章[VIM 分屏](https://coolshell.cn/articles/1679.html)。 @@ -328,33 +308,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -

+

### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -

+

### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -

+

### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -

+

### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -

+

-

+

## 4. 资料 diff --git a/docs/linux/ops/zsh.md b/docs/linux/ops/zsh.md index a6b54a69..90343f8a 100644 --- a/docs/linux/ops/zsh.md +++ b/docs/linux/ops/zsh.md @@ -1,22 +1,8 @@ -# Zsh 应用指南 - - - -- [1. Zsh 简介](#1-zsh-简介) - - [1.1. Zsh 是什么?](#11-zsh-是什么) -- [2. Zsh 安装](#2-zsh-安装) - - [2.1. 环境要求](#21-环境要求) - - [2.2. 安装 zsh](#22-安装-zsh) - - [2.3. 安装 oh-my-zsh](#23-安装-oh-my-zsh) - - [2.4. 配置 oh-my-zsh](#24-配置-oh-my-zsh) -- [3. 快捷键](#3-快捷键) -- [4. 参考资料](#4-参考资料) - - +# oh-my-zsh 应用 ## 1. Zsh 简介 -### 1.1. Zsh 是什么? +### 1.1. Zsh 是什么 使用 Linux 的人都知道:**_Shell_ 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。_Shell_ 既是一种命令语言,又是一种程序设计语言**。 @@ -61,7 +47,7 @@ wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - ### 2.4. 配置 oh-my-zsh -#### 插件 +#### 2.4.1. 插件 > oh-my-zsh 插件太多,不一一列举,请参考:[oh-my-zsh 插件列表](https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins) @@ -94,7 +80,7 @@ wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - - 再执行下这个:`source /etc/profile.d/autojump.sh` - 编辑配置文件,添加上 autojump 的名字:`vim /root/.zshrc` -#### 主题 +#### 2.4.2. 主题 > oh-my-zsh 主题太多,不一一列举,请参考:[oh-my-zsh 主题列表](https://github.com/robbyrussell/oh-my-zsh/wiki/Themes) @@ -110,7 +96,6 @@ wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - zsh 效果如下:
- ## 3. 快捷键 - 呃,这个其实可以不用讲的,你自己用的时候你自己会发现的,各种便捷,特别是用 Tab 多的人一定会有各种惊喜的。 diff --git a/docs/sidebar.md b/docs/sidebar.md index 44cc8154..7df9e0d0 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -1,53 +1,50 @@ -## 文章 - -- [**Linux 命令**](linux/cli/README.md) - - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) - - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) - - [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) - - [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) - - [Linux 用户管理](linux/cli/Linux用户管理.md) - - [Linux 系统管理](linux/cli/Linux系统管理.md) - - [Linux 网络管理](linux/cli/Linux网络管理.md) - - [Linux 硬件管理](linux/cli/Linux硬件管理.md) - - [Linux 软件管理](linux/cli/Linux硬件管理.md) -- [**Linux 系统运维**](linux/ops/README.md) - - [linux 典型运维应用](linux/ops/linux典型运维应用.md) - - [samba 使用详解](linux/ops/samba使用详解.md) - - [Systemd 教程](linux/ops/systemd.md) - - [Vim 应用指南](linux/ops/vim.md) - - [Zsh 应用指南](linux/ops/zsh.md) -- [**软件运维**](linux/soft/README.md) - - 开发环境 - - [JDK 安装](linux/soft/jdk-install.md) - - [Maven 安装](linux/soft/maven-install.md) - - [Nodejs 安装](linux/soft/nodejs-install.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 运维](linux/soft/elastic) - - [Kafka 运维](linux/soft/kafka-install.md) - - [RocketMQ 运维](linux/soft/rocketmq-install.md) - - [Nacos 运维](linux/soft/nacos-install.md) - - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) - - 服务器 - - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) - - [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) - - [Docker 应用指南](docker/docker.md) - - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) - -## 脚本 - -- [**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) -- [**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) +- **文章** + - [**Linux 命令**](linux/cli/README.md) + - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) + - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) + - [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) + - [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) + - [Linux 用户管理](linux/cli/Linux用户管理.md) + - [Linux 系统管理](linux/cli/Linux系统管理.md) + - [Linux 网络管理](linux/cli/Linux网络管理.md) + - [Linux 硬件管理](linux/cli/Linux硬件管理.md) + - [Linux 软件管理](linux/cli/Linux硬件管理.md) + - [**Linux 系统运维**](linux/ops/README.md) + - [linux 典型运维应用](linux/ops/linux典型运维应用.md) + - [samba 使用详解](linux/ops/samba.md) + - [Systemd 教程](linux/ops/systemd.md) + - [Vim 应用指南](linux/ops/vim.md) + - [Zsh 应用指南](linux/ops/zsh.md) + - [**软件运维**](linux/soft/README.md) + - 开发环境 + - [JDK 安装](linux/soft/jdk-install.md) + - [Maven 安装](linux/soft/maven-install.md) + - [Nodejs 安装](linux/soft/nodejs-install.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 运维](linux/soft/elastic) + - [Kafka 运维](linux/soft/kafka-install.md) + - [RocketMQ 运维](linux/soft/rocketmq-install.md) + - [Nacos 运维](linux/soft/nacos-install.md) + - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) + - 服务器 + - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) + - [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) + - [Docker 应用指南](docker/docker.md) + - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) + - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) + - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) + - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) +- **脚本** + - [**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) + - [**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) From 98c2ec02d3aab0b5e578f8af9f4c14488ac2735e Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 26 Nov 2019 17:11:53 +0800 Subject: [PATCH 22/64] update scripts --- codes/linux/soft/config/mysql/my.cnf | 45 ++++++++++++++++++++++++ codes/linux/soft/mysql-install.sh | 10 ++++++ codes/linux/tool/Autoinstall_ELK_V1.3.sh | 2 +- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 codes/linux/soft/config/mysql/my.cnf diff --git a/codes/linux/soft/config/mysql/my.cnf b/codes/linux/soft/config/mysql/my.cnf new file mode 100644 index 00000000..5372ab87 --- /dev/null +++ b/codes/linux/soft/config/mysql/my.cnf @@ -0,0 +1,45 @@ +[mysqld] +# mysql 的数据目录 +datadir = /var/lib/mysql +# binlog 目录 +log_bin = /var/lib/mysql/binlog +# socket 文件 +socket = /var/lib/mysql/mysql.sock +# 设置时区 +default-time_zone = '+8:00' +# 数据库默认字符集 +character-set-server = utf8mb4 +# 数据库字符集对应一些排序等规则,注意要和 character-set-server 对应 +collation-server = utf8mb4_0900_ai_ci + + +# 数据库连接相关设置 +# ------------------------------------------------------------------------------- +# 最大连接数 +max_connections = 10000 +# 最大错误连接数 +max_connect_errors = 10000 +# MySQL打开的文件描述符限制 +open_files_limit = 65535 + + +# 日志文件相关设置,一般只开启三种日志,错误日志,慢查询日志,二进制日志。普通查询日志不开启。 +# ------------------------------------------------------------------------------- +# 错误日志记录的信息 +log_error_verbosity = 2 +# 错误日志文件地址 +log-error = /var/log/mysqld.log +# 开启慢查询 +slow_query_log = 1 +# 开启慢查询时间,此处为3秒,达到此值才记录数据 +long_query_time = 3 +# 检索行数达到此数值,才记录慢查询日志中 +min_examined_row_limit = 10 +# 慢查询日志文件地址 +slow-query-log-file = /var/log/mysqld-slow.log +# 开启记录没有使用索引查询语句 +log_queries_not_using_indexes = 1 +# mysql清除过期日志的时间,默认值0,不自动清理,而是使用滚动循环的方式。这里设置为30天 +binlog_expire_logs_seconds = 2592000 +# 如果二进制日志写入的内容超出给定值,日志就会发生滚动。你不能将该变量设置为大于1GB或小于4096字节。 默认值是1GB。 +max_binlog_size = 1000M diff --git a/codes/linux/soft/mysql-install.sh b/codes/linux/soft/mysql-install.sh index 2f96450c..3d88e627 100644 --- a/codes/linux/soft/mysql-install.sh +++ b/codes/linux/soft/mysql-install.sh @@ -40,12 +40,22 @@ command -v yum > /dev/null 2>&1 || { } # 使用 rpm 安装 mysql +printf "${CYAN}>>>> yum install mysql${RESET}\n" wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm sudo rpm -Uvh mysql80-community-release-el7-3.noarch.rpm sudo yum install mysql-community-server +printf "${CYAN}>>>> replace settings${RESET}\n" +cp /etc/my.cnf /etc/my.cnf.bak +wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/mysql/my.cnf -O /etc/my.cnf +# 创建空的慢查询日志文件 +touch /var/log/mysqld-slow.log +chmod 777 /var/log/mysqld-slow.log + # 设置开机启动 +printf "${CYAN}>>>> start mysqld${RESET}\n" systemctl enable mysqld +systemctl start mysqld systemctl daemon-reload password=$(grep "password" /var/log/mysqld.log | awk '{print $NF}') diff --git a/codes/linux/tool/Autoinstall_ELK_V1.3.sh b/codes/linux/tool/Autoinstall_ELK_V1.3.sh index f1ad94e0..b673c571 100644 --- a/codes/linux/tool/Autoinstall_ELK_V1.3.sh +++ b/codes/linux/tool/Autoinstall_ELK_V1.3.sh @@ -48,7 +48,7 @@ init_sys() { cat >> /etc/security/limits.conf << EOF * soft nofile 65536 * hard nofile 65536 -* soft nGproc 65536 +* soft nproc 65536 * hard nproc 65536 EOF } From b24c6a928a6f65f82485bc1c6b5a391b921363ff Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 26 Nov 2019 21:13:05 +0800 Subject: [PATCH 23/64] update scripts --- codes/linux/soft/mysql-backup.sh | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 codes/linux/soft/mysql-backup.sh diff --git a/codes/linux/soft/mysql-backup.sh b/codes/linux/soft/mysql-backup.sh new file mode 100644 index 00000000..79aba5ab --- /dev/null +++ b/codes/linux/soft/mysql-backup.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +#数据库IP +dbServer="127.0.0.1" +#数据库用户名 +dbUser="root" +#数据密码 +dbPassword="Tw#123456" +# 备份模式:备份所有数据库(ALL)|备份指定数据库列表(CUSTOM) +backupMode="ALL" +#backupMode="CUSTOM" +#数据库,如有多个库用空格分开 +databaseList="mysql sys" +#备份日期 +backupDate=`date +"%Y%m%d"` +#备份路径 +backupPath="/var/lib/mysql/backup" +#备份日志路径 +logPath="${backupPath}/mysql-backup.log" + +#日志记录头部 +mkdir -p ${backupPath} +touch ${logPath} +echo "------------------------------------------------------------------" >> ${logPath} +beginTime=`date +"%Y-%m-%d %H:%M:%S"` +echo "备份数据库开始,时间:${beginTime}" >> ${logPath} + +#正式备份数据库 +if [[ ${backupMode} == "ALL" ]];then + filename="all-${backupDate}" + #备份所有数据库 + source=`mysqldump -h ${dbServer} -u ${dbUser} -p${dbPassword} --all-databases > ${backupPath}/${filename}.sql` + 2>> ${logPath}; + #备份成功以下操作 + if [[ "$?" == 0 ]];then + cd ${backupPath} + #为节约硬盘空间,将数据库压缩 + tar zcf ${filename}.tar.gz ${filename}.sql > /dev/null + #删除原始文件,只留压缩后文件 + rm -f ${backupPath}/${filename}.sql + #删除七天前备份,也就是只保存7天内的备份 + find ${backupPath} -name "*.tar.gz" -type f -mtime +7 -exec rm -rf {} \; > /dev/null 2>&1 + echo ">>>> 备份所有数据库成功!" >> ${logPath} + else + #备份失败则进行以下操作 + echo ">>>> 备份所有数据库失败!" >> ${logPath} + fi +else + #备份指定数据库列表 + for database in ${databaseList}; do + filename="${database}-${backupDate}" + source=`mysqldump -h ${dbServer} -u ${dbUser} -p${dbPassword} ${database} > ${backupPath}/${filename}.sql` 2>> ${logPath}; + #备份成功以下操作 + if [[ "$?" == 0 ]];then + cd ${backupPath} + #为节约硬盘空间,将数据库压缩 + tar zcf ${filename}.tar.gz ${filename}.sql > /dev/null + #删除原始文件,只留压缩后文件 + rm -f ${backupPath}/${filename}.sql + #删除七天前备份,也就是只保存7天内的备份 + find ${backupPath} -name "*.tar.gz" -type f -mtime +7 -exec rm -rf {} \; > /dev/null 2>&1 + echo ">>>> 备份数据库 ${database} 成功!" >> ${logPath} + else + #备份失败则进行以下操作 + echo ">>>> 备份数据库 ${database} 失败!" >> ${logPath} + fi + done +fi +endTime=`date +"%Y-%m-%d %H:%M:%S"` +echo "备份数据库结束,时间:${endTime}" >> ${logPath} From b526a167c414f50978eac65ac4eff733f3ab8152 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 27 Nov 2019 13:34:49 +0800 Subject: [PATCH 24/64] update mysql's script and configuration --- codes/linux/soft/config/mysql/my.cnf | 75 +++++++++++++++------------- codes/linux/soft/mysql-install.sh | 11 +++- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/codes/linux/soft/config/mysql/my.cnf b/codes/linux/soft/config/mysql/my.cnf index 5372ab87..8b1b765c 100644 --- a/codes/linux/soft/config/mysql/my.cnf +++ b/codes/linux/soft/config/mysql/my.cnf @@ -1,45 +1,48 @@ +# ------------------------------------------------------------------------------- +# Mysql 基本配置模板 +# ------------------------------------------------------------------------------- + [mysqld] -# mysql 的数据目录 +# GENERAL +# ------------------------------------------------------------------------------- +server_id = 2 datadir = /var/lib/mysql -# binlog 目录 -log_bin = /var/lib/mysql/binlog -# socket 文件 socket = /var/lib/mysql/mysql.sock -# 设置时区 -default-time_zone = '+8:00' -# 数据库默认字符集 -character-set-server = utf8mb4 -# 数据库字符集对应一些排序等规则,注意要和 character-set-server 对应 -collation-server = utf8mb4_0900_ai_ci +pid_file = /var/lib/mysql/mysql.pid +user = mysql +port = 3306 +default_storage_engine = InnoDB +default_time_zone = '+8:00' +character_set_server = utf8mb4 +collation_server = utf8mb4_0900_ai_ci +# LOG +# ------------------------------------------------------------------------------- +log_error = /var/log/mysql/mysql-error.log +slow_query_log = 1 +slow_query_log_file = /var/log/mysql/mysql-slow.log +long_query_time = 3 +min_examined_row_limit = 100 +expire_logs_days = 7 -# 数据库连接相关设置 +# InnoDB # ------------------------------------------------------------------------------- -# 最大连接数 -max_connections = 10000 -# 最大错误连接数 -max_connect_errors = 10000 -# MySQL打开的文件描述符限制 -open_files_limit = 65535 +innodb_buffer_pool_size = 4G +innodb_log_file_size = 128M +innodb_file_per_table = 1 +innodb_flush_method = O_DIRECT +# MyIsam +# ------------------------------------------------------------------------------- +key_buffer_size = 256M -# 日志文件相关设置,一般只开启三种日志,错误日志,慢查询日志,二进制日志。普通查询日志不开启。 +# OTHER # ------------------------------------------------------------------------------- -# 错误日志记录的信息 -log_error_verbosity = 2 -# 错误日志文件地址 -log-error = /var/log/mysqld.log -# 开启慢查询 -slow_query_log = 1 -# 开启慢查询时间,此处为3秒,达到此值才记录数据 -long_query_time = 3 -# 检索行数达到此数值,才记录慢查询日志中 -min_examined_row_limit = 10 -# 慢查询日志文件地址 -slow-query-log-file = /var/log/mysqld-slow.log -# 开启记录没有使用索引查询语句 -log_queries_not_using_indexes = 1 -# mysql清除过期日志的时间,默认值0,不自动清理,而是使用滚动循环的方式。这里设置为30天 -binlog_expire_logs_seconds = 2592000 -# 如果二进制日志写入的内容超出给定值,日志就会发生滚动。你不能将该变量设置为大于1GB或小于4096字节。 默认值是1GB。 -max_binlog_size = 1000M +tmp_table_size = 32M +max_heap_table_size = 32M +max_connections = 10000 +open_files_limit = 65535 + +[client] +socket = /var/lib/mysql/mysql.sock +port = 3306 diff --git a/codes/linux/soft/mysql-install.sh b/codes/linux/soft/mysql-install.sh index 3d88e627..24e39a0b 100644 --- a/codes/linux/soft/mysql-install.sh +++ b/codes/linux/soft/mysql-install.sh @@ -49,8 +49,10 @@ printf "${CYAN}>>>> replace settings${RESET}\n" cp /etc/my.cnf /etc/my.cnf.bak wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/mysql/my.cnf -O /etc/my.cnf # 创建空的慢查询日志文件 -touch /var/log/mysqld-slow.log -chmod 777 /var/log/mysqld-slow.log +mkdir -p /var/log/mysql +touch /var/log/mysql/mysql-slow.log +chmod 777 /var/log/mysql/mysql-slow.log +chown -R mysql:mysql /var/log/mysql # 设置开机启动 printf "${CYAN}>>>> start mysqld${RESET}\n" @@ -58,6 +60,11 @@ systemctl enable mysqld systemctl start mysqld systemctl daemon-reload +cat >> /etc/security/limits.conf << EOF +mysql soft nofile 65536 +mysql hard nofile 65536 +EOF + password=$(grep "password" /var/log/mysqld.log | awk '{print $NF}') printf "临时密码为:${PURPLE}${password}${RESET},请登录 mysql 后重置新密码\n" From eff3b9299018c8a7d9042550cc449ac6875d4ec9 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Fri, 6 Dec 2019 11:20:49 +0800 Subject: [PATCH 25/64] update --- docs/index.html | 1 + docs/linux/soft/nexus-ops.md | 64 ++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/docs/index.html b/docs/index.html index 66adae37..15198d09 100644 --- a/docs/index.html +++ b/docs/index.html @@ -278,6 +278,7 @@ } + diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index fa65f4fe..992d0367 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -12,22 +12,24 @@ -- [安装 Nexus](#安装-nexus) -- [启动/停止 Nexus](#启动停止-nexus) -- [使用 Nexus 搭建 Maven 私服](#使用-nexus-搭建-maven-私服) - - [配置仓库](#配置仓库) - - [配置 settings.xml](#配置-settingsxml) - - [配置 pom.xml](#配置-pomxml) - - [执行 maven 构建](#执行-maven-构建) -- [将 Nexus 设置为服务](#将-nexus-设置为服务) -- [Nexus 备份和迁移](#nexus-备份和迁移) - - [备份](#备份) - - [迁移](#迁移) -- [参考资料](#参考资料) +- [1. 安装 Nexus](#1-安装-nexus) +- [2. 启动/停止 Nexus](#2-启动停止-nexus) +- [3. 搭建 Maven 私服](#3-搭建-maven-私服) + - [3.1. 配置仓库](#31-配置仓库) + - [3.2. 配置 settings.xml](#32-配置-settingsxml) + - [3.3. 配置 pom.xml](#33-配置-pomxml) + - [3.4. 执行 maven 构建](#34-执行-maven-构建) +- [4. 开机自启动](#4-开机自启动) +- [5. Nexus 备份和迁移](#5-nexus-备份和迁移) + - [5.1. 备份](#51-备份) + - [5.2. 迁移](#52-迁移) +- [6. FAQ](#6-faq) + - [6.1. 配置 INSTALL4J_JAVA_HOME](#61-配置-install4j_java_home) +- [7. 参考资料](#7-参考资料) -## 安装 Nexus +## 1. 安装 Nexus 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 @@ -47,7 +49,7 @@ tar -zxf nexus-unix.tar.gz - nexus-3.13.0-01 - 包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 - sonatype-work - 包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 -## 启动/停止 Nexus +## 2. 启动/停止 Nexus 进入 nexus-3.13.0-01/bin 目录,有一个可执行脚本 nexus。 @@ -68,9 +70,9 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} 点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。 -## 使用 Nexus 搭建 Maven 私服 +## 3. 搭建 Maven 私服 -### 配置仓库 +### 3.1. 配置仓库 Nexus 中的仓库有以下类型: @@ -96,7 +98,7 @@ Nexus 中的仓库有以下类型:
-### 配置 settings.xml +### 3.2. 配置 settings.xml 如果要使用 Nexus,还必须在 settings.xml 和 pom.xml 中配置认证信息。 @@ -171,7 +173,7 @@ Nexus 中的仓库有以下类型: ``` -### 配置 pom.xml +### 3.3. 配置 pom.xml 在 pom.xml 中添加如下配置: @@ -195,7 +197,7 @@ Nexus 中的仓库有以下类型: > - `` 和 `` 的 id 必须和 `settings.xml` 配置文件中的 `` 标签中的 id 匹配。 > - `` 标签的地址需要和 maven 私服的地址匹配。 -### 执行 maven 构建 +### 3.4. 执行 maven 构建 如果要使用 settings.xml 中的私服配置,必须通过指定 `-P zp` 来激活 profile。 @@ -209,9 +211,9 @@ $ mvn clean package -Dmaven.skip.test=true -P zp $ mvn clean deploy -Dmaven.skip.test=true -P zp ``` -## 将 Nexus 设置为服务 +## 4. 开机自启动 -将 Nexus 添加为服务,以便开机自启动。 +将 Nexus 设置为 systemd 服务,以便开机自启动。 在 `/lib/systemd/system` 目录下创建 `nexus.service` 文件,内容如下: @@ -242,7 +244,7 @@ WantedBy=multi-user.target - `systemctl stop nexus` - 停止 nexus 服务 - `systemctl restart nexus` - 重启 nexus 服务 -## Nexus 备份和迁移 +## 5. Nexus 备份和迁移 Nexus 三个重要目录: @@ -252,15 +254,27 @@ Nexus 三个重要目录: | sonatype-work 目录 | sonatype-work | nexus/conf/nexus.xml 里面有 storage 的地址 | | storage 目录 | storage | 里面主要是各种程序的 jar 包等 | -### 备份 +### 5.1. 备份 Nexus 的数据都存储在 sonatype-work 目录,备份 Nexus 数据只需要将其打包即可。 -### 迁移 +### 5.2. 迁移 将原 Nexus 服务器中的 sonatype-work 目录迁移到新 Nexus 服务器的 sonatype-work 目录下。 -## 参考资料 +## 6. FAQ + +### 6.1. 配置 INSTALL4J_JAVA_HOME + +我在工作中遇到 nexus systemctl 服务无法自启动的问题,通过查看状态,发现以下报错: + +``` +Please define INSTALL4J_JAVA_HOME to point to a suitable JVM +``` + +通过排查,找到原因:即使环境上已安装 JDK,且配置了 JAVA_HOME,但 nexus 仍然无法正确找到 JDK,需要在 `/bin/nexus` 中指定 `INSTALL4J_JAVA_HOME_OVERRIDE=` + +## 7. 参考资料 - [maven 私库 nexus3 安装及使用](https://blog.csdn.net/clj198606061111/article/details/52200928) - [Nexus 安装 使用说明](https://www.cnblogs.com/jtlgb/p/7473837.html) From 6ab987a746eab60df8261bc465daf56b56235679 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 30 Dec 2019 16:30:18 +0800 Subject: [PATCH 26/64] update --- .editorconfig | 4 +- .gitignore | 2 + README.md | 10 +- codes/linux/soft/maven-install.sh | 4 +- codes/shell/README.md | 4 + docs/README.md | 30 +- docs/book.json | 71 +++ docs/docker/README.md | 27 + docs/docker/advanced/docker-design.md | 97 ---- docs/docker/basics/docker-container.md | 261 --------- docs/docker/basics/docker-helloworld.md | 71 --- docs/docker/basics/docker-image.md | 491 ----------------- docs/docker/basics/docker-repository.md | 100 ---- docs/docker/basics/docker-services.md | 36 -- docs/docker/docker-cheat-sheet.md | 116 +++- docs/docker/{basics => }/docker-dockerfile.md | 26 +- docs/docker/docker-quickstart.md | 356 ++++++++++++ docs/docker/docker.md | 154 ------ docs/{kubernetes => docker}/kubernetes.md | 2 +- .../docker-install-mysql.md | 0 .../docker-install-nginx.md | 0 docs/index.html | 515 +++++++++--------- ...56\345\275\225\347\256\241\347\220\206.md" | 4 +- ...14\347\232\204\350\211\272\346\234\257.md" | 6 +- docs/linux/ops/samba.md | 2 +- docs/linux/ops/vim.md | 30 +- docs/linux/ops/zsh.md | 2 +- docs/linux/soft/elastic/elastic-beats.md | 4 +- docs/linux/soft/elastic/elastic-kibana.md | 4 +- docs/linux/soft/elastic/elastic-logstash.md | 2 +- docs/linux/soft/elastic/elastic-quickstart.md | 4 +- docs/linux/soft/fastdfs.md | 2 +- docs/linux/soft/gitlab-install.md | 30 +- docs/linux/soft/jdk-install.md | 10 +- docs/linux/soft/nexus-ops.md | 8 +- docs/linux/soft/svn-ops.md | 2 +- docs/linux/soft/yapi-ops.md | 2 +- docs/package.json | 36 +- docs/sidebar.md | 99 ++-- 39 files changed, 987 insertions(+), 1637 deletions(-) create mode 100644 docs/book.json delete mode 100644 docs/docker/advanced/docker-design.md delete mode 100644 docs/docker/basics/docker-container.md delete mode 100644 docs/docker/basics/docker-helloworld.md delete mode 100644 docs/docker/basics/docker-image.md delete mode 100644 docs/docker/basics/docker-repository.md delete mode 100644 docs/docker/basics/docker-services.md rename docs/docker/{basics => }/docker-dockerfile.md (98%) create mode 100644 docs/docker/docker-quickstart.md delete mode 100644 docs/docker/docker.md rename docs/{kubernetes => docker}/kubernetes.md (97%) rename docs/docker/{action => service}/docker-install-mysql.md (100%) rename docs/docker/{action => service}/docker-install-nginx.md (100%) diff --git a/.editorconfig b/.editorconfig index c7ca29b2..d72a75ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,7 @@ root = true [*] end_of_line = lf indent_size = 2 -indent_style = tab +indent_style = space max_line_length = 120 charset = utf-8 trim_trailing_whitespace = true @@ -19,7 +19,7 @@ insert_final_newline = true [*.{bat, cmd}] end_of_line = crlf -[*.{java, groovy, kt, sh}] +[*.{java, gradle, groovy, kt, sh}] indent_size = 4 [*.md] diff --git a/.gitignore b/.gitignore index b8875768..4a99239d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ hs_err_pid* # maven plugin temp files .flattened-pom.xml +package-lock.json # ------------------------------- javascript ------------------------------- @@ -47,6 +48,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* bundle*.js +book.pdf # ------------------------------- intellij ------------------------------- diff --git a/README.md b/README.md index 440082c7..340f0197 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ license

-

Linux Tutorial

+

linux-tutorial

+ > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) @@ -68,15 +69,16 @@ - [Nacos 运维](docs/linux/soft/nacos-install.md) - 服务器 - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) - - [Tomcat 运维](linux/soft/tomcat-install.md) + - [Tomcat 运维](docs/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 教程](docs/docker) - - [Docker 应用指南](docs/docker/docker.md) +- [Docker 教程](docs/docker/README.md) + - [Docker 快速入门](docs/docker/docker-quickstart.md) + - [Dockerfile 最佳实践](docs/docker/docker-dockerfile.md) - [Docker Cheat Sheet](docs/docker/docker-cheat-sheet.md) - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) diff --git a/codes/linux/soft/maven-install.sh b/codes/linux/soft/maven-install.sh index 821bcbef..47e62662 100644 --- a/codes/linux/soft/maven-install.sh +++ b/codes/linux/soft/maven-install.sh @@ -40,7 +40,7 @@ if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${RESET}\n" fi -version=3.6.2 +version=3.5.4 if [[ -n $1 ]]; then version=$1 fi @@ -58,7 +58,7 @@ printf "${RESET}\n" # download and decompression mkdir -p ${path} -wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -O ${path}/apache-maven-${version}-bin.tar.gz http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/${version}/binaries/apache-maven-${version}-bin.tar.gz +wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -O ${path}/apache-maven-${version}-bin.tar.gz http://apache.01link.hk/maven/maven-3/${version}/binaries/apache-maven-${version}-bin.tar.gz tar -zxvf ${path}/apache-maven-${version}-bin.tar.gz -C ${path} # setting env diff --git a/codes/shell/README.md b/codes/shell/README.md index 541bda19..0d5b2063 100644 --- a/codes/shell/README.md +++ b/codes/shell/README.md @@ -6,3 +6,7 @@ > > - 你想要执行某个操作,却不知 Shell 命令如何写 > - 你想要快速掌握 Shell 基本用法 + +## 参考资料 + +- [pure-bash-bible](https://github.com/dylanaraps/pure-bash-bible#change-a-string-to-lowercase) diff --git a/docs/README.md b/docs/README.md index ff8f6a5d..a0da52bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,14 +1,4 @@ -

- - logo - -

- -

- license -

- -

Linux Tutorial

+# linux-tutorial > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > @@ -38,11 +28,12 @@ > Linux 系统的常见运维工作。 -- [linux 典型运维应用](linux/ops/linux典型运维应用.md) -- [samba 使用详解](linux/ops/samba.md) -- [Systemd 教程](linux/ops/systemd.md) -- [Vim 应用指南](linux/ops/vim.md) -- [Zsh 应用指南](linux/ops/zsh.md) +- [linux 典型运维应用](linux/ops/linux典型运维应用.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +- [Samba 应用](linux/ops/samba.md) +- [Systemd 应用](linux/ops/systemd.md) +- [Vim 应用](linux/ops/vim.md) +- [Iptables 应用](linux/ops/iptables.md) +- [oh-my-zsh 应用](linux/ops/zsh.md) ### 软件运维 @@ -75,8 +66,9 @@ ### 扩展 -- [Docker 教程](docker) - - [Docker 应用指南](docker/docker.md) +- [Docker 教程](docker/README.md) + - [Docker 快速入门](docker/docker-quickstart.md) + - [Dockerfile 最佳实践](docker/docker-dockerfile.md) - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) @@ -100,7 +92,7 @@ - **Linux 命令** - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - - [Linux命令大全](https://man.linuxde.net/) + - [Linux 命令大全](https://man.linuxde.net/) - [linux-command](https://github.com/jaywcjlove/linux-command) - **社区网站** - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 diff --git a/docs/book.json b/docs/book.json new file mode 100644 index 00000000..17705e65 --- /dev/null +++ b/docs/book.json @@ -0,0 +1,71 @@ +{ + "gitbook": "3.2.2", + "title": "linux-tutorial", + "language": "zh-hans", + "root": "./", + "structure": { + "summary": "sidebar.md" + }, + "links": { + "sidebar": { + "linux-tutorial": "https://github.com/dunwu/linux-tutorial" + } + }, + "plugins": [ + "-lunr", + "-search", + "advanced-emoji@^0.2.2", + "anchor-navigation-ex@1.0.10", + "anchors@^0.7.1", + "edit-link@^2.0.2", + "expandable-chapters-small@^0.1.7", + "github@^2.0.0", + "search-plus@^0.0.11", + "simple-page-toc@^0.1.1", + "splitter@^0.0.8", + "tbfed-pagefooter@^0.0.1" + ], + "pluginsConfig": { + "anchor-navigation-ex": { + "showLevel": false, + "associatedWithSummary": true, + "multipleH1": true, + "mode": "float", + "isRewritePageTitle": false, + "float": { + "showLevelIcon": false, + "level1Icon": "fa fa-hand-o-right", + "level2Icon": "fa fa-hand-o-right", + "level3Icon": "fa fa-hand-o-right" + }, + "pageTop": { + "showLevelIcon": false, + "level1Icon": "fa fa-hand-o-right", + "level2Icon": "fa fa-hand-o-right", + "level3Icon": "fa fa-hand-o-right" + } + }, + "edit-link": { + "base": "https://github.com/dunwu/linux-tutorial/blob/master/docs", + "label": "编辑此页面" + }, + "github": { + "url": "https://github.com/dunwu" + }, + "simple-page-toc": { + "maxDepth": 4, + "skipFirstH1": true + }, + "sharing": { + "weibo": true, + "all": [ + "weibo" + ] + }, + "tbfed-pagefooter": { + "copyright": "Copyright © Zhang Peng 2017", + "modify_label": "该文件上次修订时间:", + "modify_format": "YYYY-MM-DD HH:mm:ss" + } + } +} diff --git a/docs/docker/README.md b/docs/docker/README.md index 1eb4ac7a..7715d4eb 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -1 +1,28 @@ # Docker 教程 + +- [Docker 快速入门](docker-quickstart.md) +- [Dockerfile 最佳实践](docker-dockerfile.md) +- [Docker Cheat Sheet](docker-cheat-sheet.md) + +## 资源 + +- **官方** + - [Docker 官网](http://www.docker.com) + - [Docker Github](https://github.com/moby/moby) + - [Docker 官方文档](https://docs.docker.com/) + - [Docker Hub](https://hub.docker.com/) + - [Docker 开源](https://www.docker.com/community/open-source) +- **资源整理** + - [Awesome Docker](https://github.com/veggiemonk/awesome-docker) +- **教程** + - [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) - 非常详尽的 Docker 中文教程 + - [Docker 中文网站](https://www.docker-cn.com/) + - [Docker 安装手册](https://docs.docker-cn.com/engine/installation/) +- **镜像** + - [时速云镜像仓库](https://hub.tenxcloud.com/) + - [网易云镜像服务](https://c.163.com/hub#/m/library/) + - [DaoCloud 镜像市场](https://hub.daocloud.io/) + - [阿里云镜像库](https://cr.console.aliyun.com/) +- **文章** + - [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) + - [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn) diff --git a/docs/docker/advanced/docker-design.md b/docs/docker/advanced/docker-design.md deleted file mode 100644 index 47148d59..00000000 --- a/docs/docker/advanced/docker-design.md +++ /dev/null @@ -1,97 +0,0 @@ - - -- [Docker 的设计](#docker-%E7%9A%84%E8%AE%BE%E8%AE%A1) - - [Docker 架构](#docker-%E6%9E%B6%E6%9E%84) - - [Docker 守护进程(docker daemon)](#docker-%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%EF%BC%88docker-daemon%EF%BC%89) - - [Docker 客户端](#docker-%E5%AE%A2%E6%88%B7%E7%AB%AF) - - [Docker 注册中心](#docker-%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83) - - [Docker 对象](#docker-%E5%AF%B9%E8%B1%A1) - - [镜像](#%E9%95%9C%E5%83%8F) - - [容器](#%E5%AE%B9%E5%99%A8) - - [服务](#%E6%9C%8D%E5%8A%A1) - - [底层技术](#%E5%BA%95%E5%B1%82%E6%8A%80%E6%9C%AF) - - [命名空间](#%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4) - - [控制组](#%E6%8E%A7%E5%88%B6%E7%BB%84) - - [联合文件系统](#%E8%81%94%E5%90%88%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F) - - [容器格式](#%E5%AE%B9%E5%99%A8%E6%A0%BC%E5%BC%8F) - - [资料](#%E8%B5%84%E6%96%99) - - - -# Docker 的设计 - -## Docker 架构 - -Docker 使用 C/S 体系结构。Docker 守护进程,负责构建、运行和分发 Docker 容器;Docker 客户端与 Docker 守护进程通信。Docker 客户端和守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程使用 REST API,并通过 UNIX 套接字或网络接口进行通信。 - -

- -### Docker 守护进程(docker daemon) - -Docker 守护进程(`dockerd`)监听 Docker API 请求并管理 Docker 对象(如镜像,容器,网络和卷)。守护进程还可以与其他守护进程通信来管理 Docker 服务。 - -### Docker 客户端 - -Docker 客户端(`docker`)是许多 Docker 用户与 Docker 进行交互的主要方式。当你使用诸如 `docker run` 之类的命令时,客户端将这些命令发送到 `dockerd`,`dockerd` 执行这些命令。 `docker` 命令使用 Docker API。 Docker 客户端可以与多个守护进程进行通信。 - -### Docker 注册中心 - -Docker 注册中心存储 Docker 镜像。Docker Hub 和 Docker Cloud 是任何人都可以使用的公共注册中心,并且 Docker 默认配置为在 Docker Hub 上查找镜像。你甚至可以运行你自己的私人注册中心。如果您使用 Docker Datacenter(DDC),它包括 Docker Trusted Registry(DTR)。 - -当您使用 `docker pull` 或 `docker run` 命令时,所需的镜像将从配置的注册中心中提取。当您使用 `docker push` 命令时,您的镜像将被推送到您配置的注册中心。 - -[Docker 商店](http://store.docker.com/) 允许您购买和销售 Docker 镜像或免费发布。例如,您可以购买包含来自软件供应商的应用程序或服务的 Docker 镜像,并使用该镜像将应用程序部署到您的测试,临时和生产环境中。您可以通过拉取新版本的镜像并重新部署容器来升级应用程序。 - -### Docker 对象 - -#### 镜像 - -镜像是一个只读模板,带有创建 Docker 容器的说明。通常,镜像基于另一个镜像,并具有一些额外的自定义功能。例如,您可以构建基于 ubuntu 镜像的镜像,但会安装 Apache Web 服务器和应用程序,以及使应用程序运行所需的配置细节。 - -您可能会创建自己的镜像,或者您可能只能使用其他人创建并在注册中心中发布的镜像。为了构建您自己的镜像,您可以使用简单的语法创建 `Dockerfile`,以定义创建镜像并运行所需的步骤。 `Dockerfile` 中的每条指令都会在镜像中创建一个图层。当您更改 `Dockerfile` 并重建镜像时,只重建那些已更改的图层。与其他虚拟化技术相比,这是使镜像轻量,小巧,快速的一部分。 - -#### 容器 - -容器是镜像的可运行实例。您可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。您可以将容器连接到一个或多个网络,将存储器连接到它,甚至可以根据其当前状态创建新镜像。 - -默认情况下,容器与其他容器及其主机相对隔离。您可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。 - -容器由其镜像以及您在创建或启动时提供给它的任何配置选项来定义。当一个容器被移除时,其未被存储在永久存储器中的状态将消失。 - -#### 服务 - -通过服务,您可以跨多个 Docker 守护进程扩展容器,这些守护进程可以作为一个群组与多个管理人员、工作人员一起工作。集群中的每个成员都是 Docker 守护进程,守护进程都使用 Docker API 进行通信。服务允许您定义所需的状态,例如在任何给定时间必须可用的服务的副本数量。默认情况下,该服务在所有工作节点之间进行负载平衡。对于消费者来说,Docker 服务似乎是一个单一的应用程序。Docker 引擎在 Docker 1.12 及更高版本中支持集群模式。 - -## 底层技术 - -Docker 使用 Go 编写,利用 Linux 内核的几个特性来提供其功能。 - -### 命名空间 - -Docker 使用名为 `namespaces` 的技术来提供独立工作空间(即容器)。当你运行一个容器时,Docker 会为该容器创建一组命名空间。 - -这些命名空间提供了一个隔离层。容器的每个方面都在单独的命名空间中运行,并且其访问权限限于该命名空间。 - -Docker 引擎在 Linux 上使用如下的命名空间: - -* `pid` 命名空间:进程隔离(PID:进程ID)。 -* `net` 命名空间:管理网络接口(NET:网络)。 -* `ipc` 命名空间:管理对IPC资源的访问(IPC:InterProcess Communication)。 -* `mnt` 命名空间:管理文件系统挂载点(MNT:挂载)。 -* `uts` 命名空间:隔离内核和版本标识符。 (UTS:Unix分时系统)。 - -### 控制组 - -Linux 上的 Docker Engine 也依赖于另一种称为控制组(`cgroups`)的技术。 cgroup 将应用程序限制为一组特定的资源。控制组允许 Docker 引擎将可用硬件资源共享给容器,并可选地强制实施限制和约束。例如,您可以限制可用于特定容器的内存。 - -### 联合文件系统 - -联合文件系统(UnionFS)是通过创建图层进行操作的文件系统,这使它们非常轻巧和快速。 Docker 引擎使用 UnionFS 为容器提供构建块。Docker 引擎可以使用多种 UnionFS 变体,包括 AUFS,btrfs,vfs 和 DeviceMapper。 - -### 容器格式 - -Docker 引擎将命名空间,控制组和 UnionFS 组合成一个名为容器格式的包装器。默认的容器格式是`libcontainer`。将来,Docker 可以通过与诸如 BSD Jails 或 Solaris Zones 等技术集成来支持其他容器格式。 - -## 资料 - -* https://docs.docker.com/engine/docker-overview/ diff --git a/docs/docker/basics/docker-container.md b/docs/docker/basics/docker-container.md deleted file mode 100644 index c993a7be..00000000 --- a/docs/docker/basics/docker-container.md +++ /dev/null @@ -1,261 +0,0 @@ -# Docker 容器 - -容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。 - - - -- [启动容器](#启动容器) - - [新建并启动](#新建并启动) - - [启动已终止容器](#启动已终止容器) -- [后台运行](#后台运行) -- [终止容器](#终止容器) -- [进入容器](#进入容器) - - [`attach` 命令](#attach-命令) - - [`exec` 命令](#exec-命令) -- [导出和导入容器](#导出和导入容器) - - [导出容器](#导出容器) - - [导入容器快照](#导入容器快照) -- [删除容器](#删除容器) -- [清理所有处于终止状态的容器](#清理所有处于终止状态的容器) - - - -## 启动容器 - -启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(`stopped`)的容器重新启动。 - -因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。 - -### 新建并启动 - -所需要的命令主要为 `docker run`。 - -例如,下面的命令输出一个 “Hello World”,之后终止容器。 - -```bash -$ docker run ubuntu:14.04 /bin/echo 'Hello world' -Hello world -``` - -这跟在本地直接执行 `/bin/echo 'hello world'` 几乎感觉不出任何区别。 - -下面的命令则启动一个 bash 终端,允许用户进行交互。 - -```bash -$ docker run -t -i ubuntu:14.04 /bin/bash -root@af8bae53bdd3:/# -``` - -其中,`-t` 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, `-i` 则让容器的标准输入保持打开。 - -在交互模式下,用户可以通过所创建的终端来输入命令,例如 - -```bash -root@af8bae53bdd3:/# pwd -/ -root@af8bae53bdd3:/# ls -bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var -``` - -当利用 `docker run` 来创建容器时,Docker 在后台运行的标准操作包括: - -- 检查本地是否存在指定的镜像,不存在就从公有仓库下载 -- 利用镜像创建并启动一个容器 -- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层 -- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去 -- 从地址池配置一个 ip 地址给容器 -- 执行用户指定的应用程序 -- 执行完毕后容器被终止 - -### 启动已终止容器 - -可以利用 `docker container start` 命令,直接将一个已经终止的容器启动运行。 - -容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 `ps` 或 `top` 来查看进程信息。 - -```bash -root@ba267838cc1b:/# ps - PID TTY TIME CMD - 1 ? 00:00:00 bash - 11 ? 00:00:00 ps -``` - -可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。 - -## 后台运行 - -更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 `-d` 参数来实现。 - -下面举两个例子来说明一下。 - -如果不使用 `-d` 参数运行容器。 - -```bash -$ docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" -hello world -hello world -hello world -hello world -``` - -容器会把输出的结果 (STDOUT) 打印到宿主机上面 - -如果使用了 `-d` 参数运行容器。 - -```bash -$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" -77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a -``` - -此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 `docker logs` 查看)。 - -**注:** 容器是否会长久运行,是和 `docker run` 指定的命令有关,和 `-d` 参数无关。 - -使用 `-d` 参数启动后会返回一个唯一的 id,也可以通过 `docker container ls` 命令来查看容器信息。 - -``` -$ docker container ls -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -77b2dc01fe0f ubuntu:18.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright -``` - -要获取容器的输出信息,可以通过 `docker container logs` 命令。 - -```bash -$ docker container logs [container ID or NAMES] -hello world -hello world -hello world -. . . -``` - -## 终止容器 - -可以使用 `docker container stop` 来终止一个运行中的容器。 - -此外,当 Docker 容器中指定的应用终结时,容器也自动终止。 - -例如对于上一章节中只启动了一个终端的容器,用户通过 `exit` 命令或 `Ctrl+d` 来退出终端时,所创建的容器立刻终止。 - -终止状态的容器可以用 `docker container ls -a` 命令看到。例如 - -```bash -docker container ls -a -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton -98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike -``` - -处于终止状态的容器,可以通过 `docker container start` 命令来重新启动。 - -此外,`docker container restart` 命令会将一个运行态的容器终止,然后再重新启动它。 - -## 进入容器 - -在使用 `-d` 参数时,容器启动后会进入后台。 - -某些时候需要进入容器进行操作,包括使用 `docker attach` 命令或 `docker exec` 命令,推荐大家使用 `docker exec` 命令,原因会在下面说明。 - -### `attach` 命令 - -`docker attach` 是 Docker 自带的命令。下面示例如何使用该命令。 - -```bash -$ docker run -dit ubuntu -243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550 - -$ docker container ls -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia - -$ docker attach 243c -root@243c32535da7:/# -``` - -_注意:_ 如果从这个 stdin 中 exit,会导致容器的停止。 - -### `exec` 命令 - -#### -i -t 参数 - -`docker exec` 后边可以跟多个参数,这里主要说明 `-i` `-t` 参数。 - -只用 `-i` 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。 - -当 `-i` `-t` 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。 - -```bash -$ docker run -dit ubuntu -69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6 - -$ docker container ls -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles - -$ docker exec -i 69d1 bash -ls -bin -boot -dev -... - -$ docker exec -it 69d1 bash -root@69d137adef7a:/# -``` - -如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 `docker exec` 的原因。 - -更多参数说明请使用 `docker exec --help` 查看。 - -## 导出和导入容器 - -### 导出容器 - -如果要导出本地某个容器,可以使用 `docker export` 命令。 - -```bash -$ docker container ls -a -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test -$ docker export 7691a814370e > ubuntu.tar -``` - -这样将导出容器快照到本地文件。 - -### 导入容器快照 - -可以使用 `docker import` 从容器快照文件中再导入为镜像,例如 - -```bash -$ cat ubuntu.tar | docker import - test/ubuntu:v1.0 -$ docker image ls -REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE -test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB -``` - -此外,也可以通过指定 URL 或者某个目录来导入,例如 - -```bash -$ docker import http://example.com/exampleimage.tgz example/imagerepo -``` - -_注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。_ - -## 删除容器 - -可以使用 `docker container rm` 来删除一个处于终止状态的容器。例如 - -```bash -$ docker container rm trusting_newton -trusting_newton -``` - -如果要删除一个运行中的容器,可以添加 `-f` 参数。Docker 会发送 `SIGKILL` 信号给容器。 - -## 清理所有处于终止状态的容器 - -用 `docker container ls -a` 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。 - -```bash -$ docker container prune -``` diff --git a/docs/docker/basics/docker-helloworld.md b/docs/docker/basics/docker-helloworld.md deleted file mode 100644 index 47134f77..00000000 --- a/docs/docker/basics/docker-helloworld.md +++ /dev/null @@ -1,71 +0,0 @@ -# Docker 之 Hello World - -## 前提 - -确保你的环境上已经成功安装 Docker。 - -## Hello World 实例 - -1. 使用 `docker version` 命令确保你的环境已成功安装 Docker。 - -``` -# docker version -Client: - Version: 1.13.1 - API version: 1.26 - Package version: - Go version: go1.8.3 - Git commit: 774336d/1.13.1 - Built: Wed Mar 7 17:06:16 2018 - OS/Arch: linux/amd64 - -Server: - Version: 1.13.1 - API version: 1.26 (minimum version 1.12) - Package version: - Go version: go1.8.3 - Git commit: 774336d/1.13.1 - Built: Wed Mar 7 17:06:16 2018 - OS/Arch: linux/amd64 - Experimental: false -``` - -2. 使用 `docker run` 命令运行 Hello World 镜像。 - -``` -docker run hello-world - -Unable to find image 'hello-world:latest' locally -latest: Pulling from library/hello-world -ca4f61b1923c: Pull complete -Digest: sha256:ca0eeb6fb05351dfc8759c20733c91def84cb8007aa89a5bf606bc8b315b9fc7 -Status: Downloaded newer image for hello-world:latest - -Hello from Docker! -This message shows that your installation appears to be working correctly. -... -``` - -3. 使用 `docker image ls`命令查看镜像 - -``` -docker image ls -REPOSITORY TAG IMAGE ID CREATED SIZE -docker.io/maven latest 76c9ab5df55b 7 days ago 737 MB -docker.io/python 2.7-slim 5541369755c4 13 days ago 139 MB -docker.io/hello-world latest f2a91732366c 4 months ago 1.85 kB -docker.io/java 8-jre e44d62cf8862 14 months ago 311 MB -docker.io/training/webapp latest 6fae60ef3446 2 years ago 349 MB -``` - -4. 使用 `docker container ls --all` 命令查看容器 - -如果查看正在运行的容器,不需要添加 `--all` 参数。 - -``` -docker container ls --all -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -a661d957c6fa hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago mystifying_swartz -3098f24a1064 docker.io/hello-world "/hello" 6 minutes ago Exited (0) 6 minutes ago sad_yonath -4c98c4f18a39 hello-world "/hello" 8 minutes ago Exited (0) 8 minutes ago admiring_banach -``` diff --git a/docs/docker/basics/docker-image.md b/docs/docker/basics/docker-image.md deleted file mode 100644 index ac6ca425..00000000 --- a/docs/docker/basics/docker-image.md +++ /dev/null @@ -1,491 +0,0 @@ -# Docker 镜像 - - - -- [获取镜像](#获取镜像) - - [运行](#运行) -- [列出镜像](#列出镜像) - - [镜像体积](#镜像体积) - - [虚悬镜像](#虚悬镜像) - - [中间层镜像](#中间层镜像) - - [列出部分镜像](#列出部分镜像) - - [以特定格式显示](#以特定格式显示) -- [删除本地镜像](#删除本地镜像) - - [用 ID、镜像名、摘要删除镜像](#用-id镜像名摘要删除镜像) - - [Untagged 和 Deleted](#untagged-和-deleted) - - [用 docker image ls 命令来配合](#用-docker-image-ls-命令来配合) - - [CentOS/RHEL 的用户需要注意的事项](#centosrhel-的用户需要注意的事项) -- [使用 Dockerfile 定制镜像](#使用-dockerfile-定制镜像) - - [构建镜像](#构建镜像) - - [镜像构建上下文(Context)](#镜像构建上下文context) - - [其它 `docker build` 的用法](#其它-docker-build-的用法) - - - -## 获取镜像 - -之前提到过,[Docker Hub](https://hub.docker.com/explore/) 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。 - -从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为: - -```bash -docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] -``` - -具体的选项可以通过 `docker pull --help` 命令看到,这里我们说一下镜像名称的格式。 - -- Docker 镜像仓库地址:地址的格式一般是 `<域名/IP>[:端口号]`。默认地址是 Docker Hub。 -- 仓库名:如之前所说,这里的仓库名是两段式名称,即 `<用户名>/<软件名>`。对于 Docker Hub,如果不给出用户名,则默认为 `library`,也就是官方镜像。 - -比如: - -```bash -$ docker pull ubuntu:18.04 -18.04: Pulling from library/ubuntu -bf5d46315322: Pull complete -9f13e0ac480c: Pull complete -e8988b5b3097: Pull complete -40af181810e7: Pull complete -e6f7c7e5c03e: Pull complete -Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe -Status: Downloaded newer image for ubuntu:18.04 -``` - -上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 `ubuntu:18.04`,因此将会获取官方镜像 `library/ubuntu` 仓库中标签为 `18.04` 的镜像。 - -从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 `sha256` 的摘要,以确保下载一致性。 - -在使用上面命令的时候,你可能会发现,你所看到的层 ID 以及 `sha256` 的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的 bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像。 - -*如果从 Docker Hub 下载镜像非常缓慢,可以参照 镜像加速器 一节配置加速器。* - -### 运行 - -有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 `ubuntu:18.04` 为例,如果我们打算启动里面的 `bash` 并且进行交互式操作的话,可以执行下面的命令。 - -```bash -$ docker run -it --rm \ - ubuntu:18.04 \ - bash - -root@e7009c6ce357:/# cat /etc/os-release -NAME="Ubuntu" -VERSION="18.04.1 LTS (Bionic Beaver)" -ID=ubuntu -ID_LIKE=debian -PRETTY_NAME="Ubuntu 18.04.1 LTS" -VERSION_ID="18.04" -HOME_URL="https://www.ubuntu.com/" -SUPPORT_URL="https://help.ubuntu.com/" -BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" -PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" -VERSION_CODENAME=bionic -UBUNTU_CODENAME=bionic -``` - -`docker run` 就是运行容器的命令,具体格式我们会在 [容器](https://yeasy.gitbooks.io/docker_practice/content/container) 一节进行详细讲解,我们这里简要的说明一下上面用到的参数。 - -- `-it`:这是两个参数,一个是 `-i`:交互式操作,一个是 `-t` 终端。我们这里打算进入 `bash` 执行一些命令并查看返回结果,因此我们需要交互式终端。 -- `--rm`:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 `docker rm`。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 `--rm` 可以避免浪费空间。 -- `ubuntu:18.04`:这是指用 `ubuntu:18.04` 镜像为基础来启动容器。 -- `bash`:放在镜像名后的是**命令**,这里我们希望有个交互式 Shell,因此用的是 `bash`。 - -进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 `cat /etc/os-release`,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 `Ubuntu 18.04.1 LTS` 系统。 - -最后我们通过 `exit` 退出了这个容器。 - -## 列出镜像 - -要想列出已经下载下来的镜像,可以使用 `docker image ls` 命令。 - -```bash -$ docker image ls -REPOSITORY TAG IMAGE ID CREATED SIZE -redis latest 5f515359c7f8 5 days ago 183 MB -nginx latest 05a60462f8ba 5 days ago 181 MB -mongo 3.2 fe9198c04d62 5 days ago 342 MB - 00285df0df87 5 days ago 342 MB -ubuntu 18.04 f753707788c5 4 weeks ago 127 MB -ubuntu latest f753707788c5 4 weeks ago 127 MB -ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB -``` - -列表包含了 `仓库名`、`标签`、`镜像 ID`、`创建时间` 以及 `所占用的空间`。 - -其中仓库名、标签在之前的基础概念章节已经介绍过了。**镜像 ID** 则是镜像的唯一标识,一个镜像可以对应多个**标签**。因此,在上面的例子中,我们可以看到 `ubuntu:18.04` 和 `ubuntu:latest` 拥有相同的 ID,因为它们对应的是同一个镜像。 - -### 镜像体积 - -如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,`ubuntu:18.04` 镜像大小,在这里是 `127 MB`,但是在 [Docker Hub](https://hub.docker.com/r/library/ubuntu/tags/) 显示的却是 `50 MB`。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 `docker image ls` 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小。 - -另外一个需要注意的问题是,`docker image ls` 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。 - -你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。 - -```bash -$ docker system df - -TYPE TOTAL ACTIVE SIZE RECLAIMABLE -Images 24 0 1.992GB 1.992GB (100%) -Containers 1 0 62.82MB 62.82MB (100%) -Local Volumes 9 0 652.2MB 652.2MB (100%) -Build Cache 0B 0B -``` - -### 虚悬镜像 - -上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 ``。: - -```bash - 00285df0df87 5 days ago 342 MB -``` - -这个镜像原本是有镜像名和标签的,原来为 `mongo:3.2`,随着官方镜像维护,发布了新版本后,重新 `docker pull mongo:3.2` 时,`mongo:3.2` 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 ``。除了 `docker pull` 可能导致这种情况,`docker build` 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 `` 的镜像。这类无标签镜像也被称为 **虚悬镜像(dangling image)** ,可以用下面的命令专门显示这类镜像: - -```bash -$ docker image ls -f dangling=true -REPOSITORY TAG IMAGE ID CREATED SIZE - 00285df0df87 5 days ago 342 MB -``` - -一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。 - -```bash -$ docker image prune -``` - -### 中间层镜像 - -为了加速镜像构建、重复利用资源,Docker 会利用 **中间层镜像**。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 `docker image ls` 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 `-a` 参数。 - -```bash -$ docker image ls -a -``` - -这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。实际上,这些镜像也没必要删除,因为之前说过,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。 - -### 列出部分镜像 - -不加任何参数的情况下,`docker image ls` 会列出所有顶级镜像,但是有时候我们只希望列出部分镜像。`docker image ls` 有好几个参数可以帮助做到这个事情。 - -根据仓库名列出镜像 - -```bash -$ docker image ls ubuntu -REPOSITORY TAG IMAGE ID CREATED SIZE -ubuntu 18.04 f753707788c5 4 weeks ago 127 MB -ubuntu latest f753707788c5 4 weeks ago 127 MB -ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB -``` - -列出特定的某个镜像,也就是说指定仓库名和标签 - -```bash -$ docker image ls ubuntu:18.04 -REPOSITORY TAG IMAGE ID CREATED SIZE -ubuntu 18.04 f753707788c5 4 weeks ago 127 MB -``` - -除此以外,`docker image ls` 还支持强大的过滤器参数 `--filter`,或者简写 `-f`。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在 `mongo:3.2` 之后建立的镜像,可以用下面的命令: - -```bash -$ docker image ls -f since=mongo:3.2 -REPOSITORY TAG IMAGE ID CREATED SIZE -redis latest 5f515359c7f8 5 days ago 183 MB -nginx latest 05a60462f8ba 5 days ago 181 MB -``` - -想查看某个位置之前的镜像也可以,只需要把 `since` 换成 `before` 即可。 - -此外,如果镜像构建时,定义了 `LABEL`,还可以通过 `LABEL` 来过滤。 - -```bash -$ docker image ls -f label=com.example.version=0.1 -... -``` - -### 以特定格式显示 - -默认情况下,`docker image ls` 会输出一个完整的表格,但是我们并非所有时候都会需要这些内容。比如,刚才删除虚悬镜像的时候,我们需要利用 `docker image ls` 把所有的虚悬镜像的 ID 列出来,然后才可以交给 `docker image rm` 命令作为参数来删除指定的这些镜像,这个时候就用到了 `-q` 参数。 - -```bash -$ docker image ls -q -5f515359c7f8 -05a60462f8ba -fe9198c04d62 -00285df0df87 -f753707788c5 -f753707788c5 -1e0c3dd64ccd -``` - -`--filter` 配合 `-q` 产生出指定范围的 ID 列表,然后送给另一个 `docker` 命令作为参数,从而针对这组实体成批的进行某种操作的做法在 Docker 命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成很强大的功能。因此每次在文档看到过滤器后,可以多注意一下它们的用法。 - -另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 [Go 的模板语法](https://gohugo.io/templates/go-templates/)。 - -比如,下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名: - -```bash -$ docker image ls --format "{{.ID}}: {{.Repository}}" -5f515359c7f8: redis -05a60462f8ba: nginx -fe9198c04d62: mongo -00285df0df87: -f753707788c5: ubuntu -f753707788c5: ubuntu -1e0c3dd64ccd: ubuntu -``` - -或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列: - -```bash -$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" -IMAGE ID REPOSITORY TAG -5f515359c7f8 redis latest -05a60462f8ba nginx latest -fe9198c04d62 mongo 3.2 -00285df0df87 -f753707788c5 ubuntu 18.04 -f753707788c5 ubuntu latest -1e0c3dd64ccd ubuntu 14.04 -``` - -## 删除本地镜像 - -如果要删除本地的镜像,可以使用 `docker image rm` 命令,其格式为: - -``` -$ docker image rm [选项] <镜像1> [<镜像2> ...] -``` - -### 用 ID、镜像名、摘要删除镜像 - -其中,`<镜像>` 可以是 `镜像短 ID`、`镜像长 ID`、`镜像名` 或者 `镜像摘要`。 - -比如我们有这么一些镜像: - -```bash -$ docker image ls -REPOSITORY TAG IMAGE ID CREATED SIZE -centos latest 0584b3d2cf6d 3 weeks ago 196.5 MB -redis alpine 501ad78535f0 3 weeks ago 21.03 MB -docker latest cf693ec9b5c7 3 weeks ago 105.1 MB -nginx latest e43d811ce2f4 5 weeks ago 181.5 MB -``` - -我们可以用镜像的完整 ID,也称为 `长 ID`,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 `短 ID` 来删除镜像。`docker image ls` 默认列出的就已经是短 ID 了,一般取前3个字符以上,只要足够区分于别的镜像就可以了。 - -比如这里,如果我们要删除 `redis:alpine` 镜像,可以执行: - -```bash -$ docker image rm 501 -Untagged: redis:alpine -Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d -Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7 -Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b -Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23 -Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2fa -Deleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3 -Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7 -``` - -我们也可以用`镜像名`,也就是 `<仓库名>:<标签>`,来删除镜像。 - -```bash -$ docker image rm centos -Untagged: centos:latest -Untagged: centos@sha256:b2f9d1c0ff5f87a4743104d099a3d561002ac500db1b9bfa02a783a46e0d366c -Deleted: sha256:0584b3d2cf6d235ee310cf14b54667d889887b838d3f3d3033acd70fc3c48b8a -Deleted: sha256:97ca462ad9eeae25941546209454496e1d66749d53dfa2ee32bf1faabd239d38 -``` - -当然,更精确的是使用 `镜像摘要` 删除镜像。 - -```bash -$ docker image ls --digests -REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE -node slim sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 6e0c4c8e3913 3 weeks ago 214 MB - -$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 -Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 -``` - -### Untagged 和 Deleted - -如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是 `Untagged`,另一类是 `Deleted`。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。 - -因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 `Untagged` 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 `Delete` 行为就不会发生。所以并非所有的 `docker image rm`都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。 - -当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 `docker pull` 看到的层数不一样的源。 - -除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。 - -### 用 docker image ls 命令来配合 - -像其它可以承接多个实体的命令一样,可以使用 `docker image ls -q` 来配合使用 `docker image rm`,这样可以成批的删除希望删除的镜像。我们在“镜像列表”章节介绍过很多过滤镜像列表的方式都可以拿过来使用。 - -比如,我们需要删除所有仓库名为 `redis` 的镜像: - -```bash -$ docker image rm $(docker image ls -q redis) -``` - -或者删除所有在 `mongo:3.2` 之前的镜像: - -```bash -$ docker image rm $(docker image ls -q -f before=mongo:3.2) -``` - -充分利用你的想象力和 Linux 命令行的强大,你可以完成很多非常赞的功能。 - -### CentOS/RHEL 的用户需要注意的事项 - -在 Ubuntu/Debian 上有 `UnionFS` 可以使用,如 `aufs` 或者 `overlay2`,而 CentOS 和 RHEL 的内核中没有相关驱动。因此对于这类系统,一般使用 `devicemapper` 驱动利用 LVM 的一些机制来模拟分层存储。这样的做法除了性能比较差外,稳定性一般也不好,而且配置相对复杂。Docker 安装在 CentOS/RHEL 上后,会默认选择 `devicemapper`,但是为了简化配置,其 `devicemapper`是跑在一个稀疏文件模拟的块设备上,也被称为 `loop-lvm`。这样的选择是因为不需要额外配置就可以运行 Docker,这是自动配置唯一能做到的事情。但是 `loop-lvm` 的做法非常不好,其稳定性、性能更差,无论是日志还是 `docker info` 中都会看到警告信息。官方文档有明确的文章讲解了如何配置块设备给 `devicemapper` 驱动做存储层的做法,这类做法也被称为配置 `direct-lvm`。 - -除了前面说到的问题外,`devicemapper` + `loop-lvm` 还有一个缺陷,因为它是稀疏文件,所以它会不断增长。用户在使用过程中会注意到 `/var/lib/docker/devicemapper/devicemapper/data` 不断增长,而且无法控制。很多人会希望删除镜像或者可以解决这个问题,结果发现效果并不明显。原因就是这个稀疏文件的空间释放后基本不进行垃圾回收的问题。因此往往会出现即使删除了文件内容,空间却无法回收,随着使用这个稀疏文件一直在不断增长。 - -所以对于 CentOS/RHEL 的用户来说,在没有办法使用 `UnionFS` 的情况下,一定要配置 `direct-lvm` 给 `devicemapper`,无论是为了性能、稳定性还是空间利用率。 - -*或许有人注意到了 CentOS 7 中存在被 backports 回来的 overlay 驱动,不过 CentOS 里的这个驱动达不到生产环境使用的稳定程度,所以不推荐使用。* - -## 使用 Dockerfile 定制镜像 - -从刚才的 `docker commit` 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。 - -Dockerfile 是一个文本文件,其内包含了一条条的**指令(Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。 - -还以之前定制 `nginx` 镜像为例,这次我们使用 Dockerfile 来定制。 - -在一个空白目录中,建立一个文本文件,并命名为 `Dockerfile`: - -```bash -$ mkdir mynginx -$ cd mynginx -$ touch Dockerfile -``` - -其内容为: - -```dockerfile -FROM nginx -RUN echo '

Hello, Docker!

' > /usr/share/nginx/html/index.html -``` - -这个 Dockerfile 很简单,一共就两行。涉及到了两条指令,`FROM` 和 `RUN`。 - -### 构建镜像 - -好了,让我们再回到之前定制的 nginx 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。 - -在 `Dockerfile` 文件所在目录执行: - -```bash -$ docker build -t nginx:v3 . -Sending build context to Docker daemon 2.048 kB -Step 1 : FROM nginx - ---> e43d811ce2f4 -Step 2 : RUN echo '

Hello, Docker!

' > /usr/share/nginx/html/index.html - ---> Running in 9cdc27646c7b - ---> 44aa4490ce2c -Removing intermediate container 9cdc27646c7b -Successfully built 44aa4490ce2c -``` - -从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 `Step 2` 中,如同我们之前所说的那样,`RUN` 指令启动了一个容器 `9cdc27646c7b`,执行了所要求的命令,并最后提交了这一层 `44aa4490ce2c`,随后删除了所用到的这个容器 `9cdc27646c7b`。 - -这里我们使用了 `docker build` 命令进行镜像构建。其格式为: - -```bash -docker build [选项] <上下文路径/URL/-> -``` - -在这里我们指定了最终镜像的名称 `-t nginx:v3`,构建成功后,我们可以像之前运行 `nginx:v2` 那样来运行这个镜像,其结果会和 `nginx:v2` 一样。 - -### 镜像构建上下文(Context) - -如果注意,会看到 `docker build` 命令最后有一个 `.`。`.` 表示当前目录,而 `Dockerfile` 就在当前目录,因此不少初学者以为这个路径是在指定 `Dockerfile` 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定**上下文路径**。那么什么是上下文呢? - -首先我们要理解 `docker build` 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 [Docker Remote API](https://docs.docker.com/engine/reference/api/docker_remote_api/),而如 `docker` 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 `docker` 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。 - -当我们进行镜像构建的时候,并非所有定制都会通过 `RUN` 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 `COPY` 指令、`ADD` 指令等。而 `docker build` 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢? - -这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,`docker build` 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。 - -如果在 `Dockerfile` 中这么写: - -```Dockerfile -COPY ./package.json /app/ -``` - -这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`,也不是复制 `Dockerfile` 所在目录下的 `package.json`,而是复制 **上下文(context)** 目录下的 `package.json`。 - -因此,`COPY` 这类指令中的源文件的路径都是*相对路径*。这也是初学者经常会问的为什么 `COPY ../package.json /app` 或者 `COPY /opt/xxxx /app` 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。 - -现在就可以理解刚才的命令 `docker build -t nginx:v3 .` 中的这个 `.`,实际上是在指定上下文的目录,`docker build` 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。 - -如果观察 `docker build` 输出,我们其实已经看到了这个发送上下文的过程: - -```bash -$ docker build -t nginx:v3 . -Sending build context to Docker daemon 2.048 kB -... -``` - -理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 `COPY /opt/xxxx /app` 不工作后,于是干脆将 `Dockerfile` 放到了硬盘根目录去构建,结果发现 `docker build` 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 `docker build`打包整个硬盘,这显然是使用错误。 - -一般来说,应该会将 `Dockerfile` 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 `.gitignore` 一样的语法写一个 `.dockerignore`,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。 - -那么为什么会有人误以为 `.` 是指定 `Dockerfile` 所在目录呢?这是因为在默认情况下,如果不额外指定 `Dockerfile` 的话,会将上下文目录下的名为 `Dockerfile` 的文件作为 Dockerfile。 - -这只是默认行为,实际上 `Dockerfile` 的文件名并不要求必须为 `Dockerfile`,而且并不要求必须位于上下文目录中,比如可以用 `-f ../Dockerfile.php` 参数指定某个文件作为 `Dockerfile`。 - -当然,一般大家习惯性的会使用默认的文件名 `Dockerfile`,以及会将其置于镜像构建上下文目录中。 - -### 其它 `docker build` 的用法 - -#### 直接用 Git repo 进行构建 - -或许你已经注意到了,`docker build` 还支持从 URL 构建,比如可以直接从 Git repo 中构建: - -```bash -$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14 -docker build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14 -Sending build context to Docker daemon 2.048 kB -Step 1 : FROM gitlab/gitlab-ce:8.14.0-ce.0 -8.14.0-ce.0: Pulling from gitlab/gitlab-ce -aed15891ba52: Already exists -773ae8583d14: Already exists -... -``` - -这行命令指定了构建所需的 Git repo,并且指定默认的 `master` 分支,构建目录为 `/8.14/`,然后 Docker 就会自己去 `git clone` 这个项目、切换到指定分支、并进入到指定目录后开始构建。 - -#### 用给定的 tar 压缩包构建 - -```bash -$ docker build http://server/context.tar.gz -``` - -如果所给出的 URL 不是个 Git repo,而是个 `tar` 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。 - -#### 从标准输入中读取 Dockerfile 进行构建 - -```bash -docker build - < Dockerfile -``` - -或 - -```bash -cat Dockerfile | docker build - -``` - -如果标准输入传入的是文本文件,则将其视为 `Dockerfile`,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 `COPY` 进镜像之类的事情。 - -#### 从标准输入中读取上下文压缩包进行构建 - -```bash -$ docker build - < context.tar.gz -``` - -如果发现标准输入的文件格式是 `gzip`、`bzip2` 以及 `xz` 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。 diff --git a/docs/docker/basics/docker-repository.md b/docs/docker/basics/docker-repository.md deleted file mode 100644 index 5c6a7bdf..00000000 --- a/docs/docker/basics/docker-repository.md +++ /dev/null @@ -1,100 +0,0 @@ -# Docker 仓库 - -仓库(Repository)是集中存放镜像的地方。 - -一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 dl.dockerpool.com/ubuntu 来说,dl.dockerpool.com 是注册服务器地址,ubuntu 是仓库名。 - -## Docker Hub - -目前 Docker 官方维护了一个公共仓库 [Docker Hub](https://hub.docker.com/),其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。 - -### 注册 - -你可以在 [https://cloud.docker.com](https://cloud.docker.com/) 免费注册一个 Docker 账号。 - -### 登录 - -可以通过执行 `docker login` 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。 - -你可以通过 `docker logout` 退出登录。 - -### 拉取镜像 - -你可以通过 `docker search` 命令来查找官方仓库中的镜像,并利用 `docker pull` 命令来将它下载到本地。 - -例如以 `centos` 为关键词进行搜索: - -```bash -$ docker search centos -NAME DESCRIPTION STARS OFFICIAL AUTOMATED -centos The official build of CentOS. 465 [OK] -tianon/centos CentOS 5 and 6, created using rinse instea... 28 -blalor/centos Bare-bones base CentOS 6.5 image 6 [OK] -saltstack/centos-6-minimal 6 [OK] -tutum/centos-6.4 DEPRECATED. Use tutum/centos:6.4 instead. ... 5 [OK] -``` - -可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度)、是否官方创建、是否自动创建。 - -官方的镜像说明是官方项目组创建和维护的,automated 资源允许用户验证镜像的来源和内容。 - -根据是否是官方提供,可将镜像资源分为两类。 - -一种是类似 `centos` 这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。 - -还有一种类型,比如 `tianon/centos` 镜像,它是由 Docker 的用户创建并维护的,往往带有用户名称前缀。可以通过前缀 `username/` 来指定使用某个用户提供的镜像,比如 tianon 用户。 - -另外,在查找的时候通过 `--filter=stars=N` 参数可以指定仅显示收藏数量为 `N` 以上的镜像。 - -下载官方 `centos` 镜像到本地。 - -```bash -$ docker pull centos -Pulling repository centos -0b443ba03958: Download complete -539c0211cd76: Download complete -511136ea3c5a: Download complete -7064731afe90: Download complete -``` - -### 推送镜像 - -用户也可以在登录后通过 `docker push` 命令来将自己的镜像推送到 Docker Hub。 - -以下命令中的 `username` 请替换为你的 Docker 账号用户名。 - -```bash -$ docker tag ubuntu:18.04 username/ubuntu:18.04 - -$ docker image ls - -REPOSITORY TAG IMAGE ID CREATED SIZE -ubuntu 18.04 275d79972a86 6 days ago 94.6MB -username/ubuntu 18.04 275d79972a86 6 days ago 94.6MB - -$ docker push username/ubuntu:18.04 - -$ docker search username - -NAME DESCRIPTION STARS OFFICIAL AUTOMATED -username/ubuntu -``` - -### 自动创建 - -自动创建(Automated Builds)功能对于需要经常升级镜像内程序来说,十分方便。 - -有时候,用户创建了镜像,安装了某个软件,如果软件发布新版本则需要手动更新镜像。 - -而自动创建允许用户通过 Docker Hub 指定跟踪一个目标网站(目前支持 [GitHub](https://github.com/) 或 [BitBucket](https://bitbucket.org/))上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。 - -要配置自动创建,包括如下的步骤: - -- 创建并登录 Docker Hub,以及目标网站; -- 在目标网站中连接帐户到 Docker Hub; -- 在 Docker Hub 中 [配置一个自动创建](https://registry.hub.docker.com/builds/add/); -- 选取一个目标网站中的项目(需要含 `Dockerfile`)和分支; -- 指定 `Dockerfile` 的位置,并提交创建。 - -之后,可以在 Docker Hub 的 [自动创建页面](https://registry.hub.docker.com/builds/) 中跟踪每次创建的状态。 - diff --git a/docs/docker/basics/docker-services.md b/docs/docker/basics/docker-services.md deleted file mode 100644 index 392d3268..00000000 --- a/docs/docker/basics/docker-services.md +++ /dev/null @@ -1,36 +0,0 @@ -# Docker 服务 - -## 关于服务 - -在分布式应用程序中,应用程序的不同部分被称为“服务”。例如,如果您想象一个视频共享网站,它可能包含用于将应用程序数据存储在数据库中的服务,用户上传文件后在后台传输的服务,前端应用服务等等。 - -服务实际上只是“生产环境中的容器”。一个服务只运行一个镜像,但它需要制定镜像的运行方式 - 应该使用哪个端口,容器应该运行多少副本,以便服务具有所需的容量,以及等等。缩放服务会更改运行该软件的容器实例的数量,从而为流程中的服务分配更多计算资源。 - -幸运的是,使用 Docker 平台定义,运行和扩展服务非常简单 - 只需编写一个 docker-compose.yml 文件即可。 - -## docker-compose.yml 文件 - -它是一个YAML文件,它定义了Docker容器在生产中的行为方式。 - - -``` -version: "3" -services: - web: - # replace username/repo:tag with your name and image details - image: username/repo:tag - deploy: - replicas: 5 - resources: - limits: - cpus: "0.1" - memory: 50M - restart_policy: - condition: on-failure - ports: - - "80:80" - networks: - - webnet -networks: - webnet: -``` diff --git a/docs/docker/docker-cheat-sheet.md b/docs/docker/docker-cheat-sheet.md index 58f27ade..d5765fd9 100644 --- a/docs/docker/docker-cheat-sheet.md +++ b/docs/docker/docker-cheat-sheet.md @@ -4,6 +4,8 @@ +- [为何使用 Docker](#为何使用-docker) +- [运维](#运维) - [容器(Container)](#容器container) - [镜像(Images)](#镜像images) - [网络(Networks)](#网络networks) @@ -20,9 +22,89 @@ +## 为何使用 Docker + +「通过 Docker,开发者可以使用任何语言任何工具创建任何应用。“Dockerized” 的应用是完全可移植的,能在任何地方运行 - 不管是同事的 OS X 和 Windows 笔记本,或是在云端运行的 Ubuntu QA 服务,还是在虚拟机运行的 Red Hat 产品数据中心。 + +Docker Hub 上有 13000+ 的应用,开发者可以从中选取一个进行快速扩展开发。Docker 跟踪管理变更和依赖关系,让系统管理员能更容易理解开发人员是如何让应用运转起来的。而开发者可以通过 Docker Hub 的共有/私有仓库,构建他们的自动化编译,与其他合作者共享成果。 + +Docker 帮助开发者更快地构建和发布高质量的应用。」—— [什么是 Docker](https://www.docker.com/what-docker/#copy1) + +## 运维 + +### 安装 + +Docker 是一个开源的商业产品,有两个版本:社区版(Community Edition,缩写为 CE)和企业版(Enterprise Edition,缩写为 EE)。企业版包含了一些收费服务,个人开发者一般用不到。 + +Docker CE 的安装请参考官方文档。 + +- [Mac](https://docs.docker.com/docker-for-mac/install/) +- [Windows](https://docs.docker.com/docker-for-windows/install/) +- [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/) +- [Debian](https://docs.docker.com/install/linux/docker-ce/debian/) +- [CentOS](https://docs.docker.com/install/linux/docker-ce/centos/) +- [Fedora](https://docs.docker.com/install/linux/docker-ce/fedora/) +- [其他 Linux 发行版](https://docs.docker.com/install/linux/docker-ce/binaries/) + +### 检查版本 + +[`docker version`](https://docs.docker.com/engine/reference/commandline/version/) 查看你正在运行的 Docker 版本。 + +获取 Docker 服务版本: + +``` +docker version --format '{{.Server.Version}}' +``` + +你也可以输出原始的 JSON 数据: + +``` +docker version --format '{{json .}}' +``` + +### Docker 加速 + +国内访问 Docker Hub 很慢,所以,推荐配置 Docker 镜像仓库来提速。 + +镜像仓库清单: + +| 镜像仓库 | 镜像仓库地址 | 说明 | +| --------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [DaoCloud 镜像站](https://daocloud.io/mirror) | `http://f1361db2.m.daocloud.io` | 开发者需要开通 DaoCloud 账户,然后可以得到专属加速器。 | +| [阿里云](https://cr.console.aliyun.com) | `https://yourcode.mirror.aliyuncs.com` | 开发者需要开通阿里开发者帐户,再使用阿里的加速服务。登录后阿里开发者帐户后,`https://cr.console.aliyun.com/undefined/instances/mirrors` 中查看你的您的专属加速器地址。 | +| [网易云](https://c.163yun.com/hub) | `https://hub-mirror.c.163.com` | 直接配置即可,亲测较为稳定。 | + +配置镜像仓库方法(以 CentOS 为例): + +> 下面的示例为在 CentOS 环境中,指定镜像仓库为 `https://hub-mirror.c.163.com` + +(1)修改配置文件 + +修改 `/etc/docker/daemon.json` ,如果不存在则新建。执行以下 Shell: + +```bash +sudo mkdir -p /etc/docker +cat >> /etc/docker/daemon.json << EOF +{ + "registry-mirrors": [ + "https://hub-mirror.c.163.com" + ] +} +EOF +``` + +重启 docker 以生效: + +```bash +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +执行 `docker info` 命令,查看 `Registry Mirrors` 是否已被改为 `https://hub-mirror.c.163.com` ,如果是,则表示配置成功。 + ## 容器(Container) -[关于 Docker 进程隔离的基础](http://etherealmind.com/basics-docker-containers-hypervisors-coreos/)。容器 (Container) 之于虚拟机 (Virtual Machine) 就好比线程之于进程。 +[关于 Docker 进程隔离的基础](http://etherealmind.com/basics-docker-containers-hypervisors-coreos/)。容器 (Container) 之于虚拟机 (Virtual Machine) 就好比线程之于进程。或者你可以把他们想成是「吃了类固醇的 chroots」。 ### 生命周期 @@ -38,7 +120,7 @@ 如果你需要一个临时容器,可使用 `docker run --rm` 会在容器停止之后删除它。 -如果你需要映射宿主机 (host) 的目录到 Docker 容器内,可使用 `docker run -v $HOSTDIR:$DOCKERDIR`。详见 [卷标(Volumes)](#卷标volumes) 一节。 +如果你需要映射宿主机 (host) 的目录到 Docker 容器内,可使用 `docker run -v $HOSTDIR:$DOCKERDIR`。详见 [卷标(Volumes)](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#卷标volumes) 一节。 如果你想同时删除与容器相关联的卷标,那么在删除容器的时候必须包含 `-v` 选项,像这样 `docker rm -v`。 @@ -55,7 +137,7 @@ - [`docker kill`](https://docs.docker.com/engine/reference/commandline/kill) 向运行中的容器发送 SIGKILL 指令。 - [`docker attach`](https://docs.docker.com/engine/reference/commandline/attach) 连接到运行中的容器。 -如果你想将容器的端口 (ports) 暴露至宿主机,请见 [暴露端口](#暴露端口exposing-ports) 一节。 +如果你想将容器的端口 (ports) 暴露至宿主机,请见 [暴露端口](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#暴露端口exposing-ports) 一节。 关于 Docker 实例崩溃后的重启策略,详见 [本文](http://container42.com/2014/09/30/docker-restart-policies/)。 @@ -69,7 +151,7 @@ docker run -ti --c 512 agileek/cpuset-test ``` -更多信息请参阅 。 +更多信息请参阅 https://goldmann.pl/blog/2014/09/11/resource-management-in-docker/#_cpu。 通过 [`cpuset-cpus`](https://docs.docker.com/engine/reference/run/#/cpuset-constraint) 可使用特定 CPU 内核。 @@ -77,9 +159,9 @@ docker run -ti --c 512 agileek/cpuset-test docker run -ti --cpuset-cpus=0,4,6 agileek/cpuset-test ``` -请参阅 获取更多细节以及一些不错的视频。 +请参阅 https://agileek.github.io/docker/2014/08/06/docker-cpuset/ 获取更多细节以及一些不错的视频。 -注意,Docker 在容器内仍然能够 **看到** 全部 CPU -- 它仅仅是不使用全部而已。请参阅 获取更多细节。 +注意,Docker 在容器内仍然能够 **看到** 全部 CPU -- 它仅仅是不使用全部而已。请参阅 https://github.com/docker/docker/issues/20770 获取更多细节。 #### 内存限制 @@ -91,7 +173,7 @@ docker run -it -m 300M ubuntu:14.04 /bin/bash #### 能力(Capabilities) -Linux 的 Capability 可以通过使用 `cap-add` 和 `cap-drop` 设置。请参阅 获取更多细节。这有助于提高安全性。 +Linux 的 Capability 可以通过使用 `cap-add` 和 `cap-drop` 设置。请参阅 https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities 获取更多细节。这有助于提高安全性。 如需要挂载基于 FUSE 的文件系统,你需要结合 `--cap-add` 和 `--device` 使用: @@ -136,7 +218,8 @@ docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash ### 执行命令 - [`docker exec`](https://docs.docker.com/engine/reference/commandline/exec) 在容器内执行命令。 - - 例如,进入正在运行的 `foo` 容器,并连接 (attach) 到一个新的 Shell 进程:`docker exec -it foo /bin/bash`。 + +例如,进入正在运行的 `foo` 容器,并连接 (attach) 到一个新的 Shell 进程:`docker exec -it foo /bin/bash`。 ## 镜像(Images) @@ -159,7 +242,7 @@ docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash ### 清理 -虽然你可以用 `docker rmi` 命令来删除指定的镜像,不过有个名为 [docker-gc](https://github.com/spotify/docker-gc) 的工具,它可以以一种安全的方式,清理掉那些不再被任何容器使用的镜像。Docker 1.13 起,使用 `docker image prune` 亦可删除未使用的镜像。参见 [清理](#清理)。 +虽然你可以用 `docker rmi` 命令来删除指定的镜像,不过有个名为 [docker-gc](https://github.com/spotify/docker-gc) 的工具,它可以以一种安全的方式,清理掉那些不再被任何容器使用的镜像。Docker 1.13 起,使用 `docker image prune` 亦可删除未使用的镜像。参见 [清理](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#清理)。 ### 加载 / 保存镜像 @@ -191,8 +274,7 @@ docker export my_container | gzip > my_container.tar.gz ### 加载已保存的镜像 与 导入已导出为镜像的容器 的不同 -通过 `load` 命令来加载镜像,会创建一个新的镜像,并继承原镜像的所有历史。 -通过 `import` 将容器作为镜像导入,也会创建一个新的镜像,但并不包含原镜像的历史,因此会比使用 `load` 方式生成的镜像更小。 +通过 `load` 命令来加载镜像,会创建一个新的镜像,并继承原镜像的所有历史。 通过 `import` 将容器作为镜像导入,也会创建一个新的镜像,但并不包含原镜像的历史,因此会比使用 `load` 方式生成的镜像更小。 ## 网络(Networks) @@ -252,8 +334,8 @@ Docker 官方托管着自己的 [仓管中心](https://hub.docker.com/),包含 以下是一些编写 Dockerfile 的常用编辑器,并链接到适配的语法高亮模块︰ -- 如果你在使用 [jEdit](http://jedit.org),你可以使用我开发的 Dockerfile [语法高亮模块](https://github.com/wsargent/jedit-docker-mode)。 -- [Sublime Text 2](https://packagecontrol.io/packages/Dockerfile%20Syntax%20Highlighting) +- 如果你在使用 [jEdit](http://jedit.org/),你可以使用我开发的 Dockerfile [语法高亮模块](https://github.com/wsargent/jedit-docker-mode)。 +- [Sublime Text 2](https://packagecontrol.io/packages/Dockerfile Syntax Highlighting) - [Atom](https://atom.io/packages/language-docker) - [Vim](https://github.com/ekalinin/Dockerfile.vim) - [Emacs](https://github.com/spotify/dockerfile-mode) @@ -361,7 +443,7 @@ docker run -v /Users/wsargent/myapp/src:/src 还可以考虑运行一个纯数据容器,像 [这里](http://container42.com/2013/12/16/persistent-volumes-with-docker-container-as-volume-pattern/) 所说的那样,提供可移植数据。 -记得,[文件也可以被挂载为卷标](#将文件挂载为卷标)。 +记得,[文件也可以被挂载为卷标](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#将文件挂载为卷标)。 ## 暴露端口(Exposing ports) @@ -498,8 +580,7 @@ USER user ### 安全路线图 -Docker 的路线图提到关于 [seccomp 的支持](https://github.com/docker/docker/blob/master/ROADMAP.md#11-security)。 -一个名为 [bane](https://github.com/jfrazelle/bane) 的 AppArmor 策略生成器正在实现 [安全配置文件](https://github.com/docker/docker/issues/17142)。 +Docker 的路线图提到关于 [seccomp 的支持](https://github.com/docker/docker/blob/master/ROADMAP.md#11-security)。 一个名为 [bane](https://github.com/jfrazelle/bane) 的 AppArmor 策略生成器正在实现 [安全配置文件](https://github.com/docker/docker/issues/17142)。 ## 小贴士 @@ -654,8 +735,7 @@ docker images -viz | dot -Tpng -o docker.png - 在某层 (RUN layer) 清理 APT -这应当和其他 apt 命令在同一层中完成。 -否则,前面的层将会保持原有信息,而你的镜像则依旧臃肿。 +这应当和其他 apt 命令在同一层中完成。 否则,前面的层将会保持原有信息,而你的镜像则依旧臃肿。 ``` RUN {apt commands} \ diff --git a/docs/docker/basics/docker-dockerfile.md b/docs/docker/docker-dockerfile.md similarity index 98% rename from docs/docker/basics/docker-dockerfile.md rename to docs/docker/docker-dockerfile.md index 4c221540..d13d7d03 100644 --- a/docs/docker/basics/docker-dockerfile.md +++ b/docs/docker/docker-dockerfile.md @@ -1,4 +1,4 @@ -# Dockerfile +# Dockerfile 最佳实践 @@ -25,9 +25,8 @@ ### FROM(指定基础镜像) -> 作用: +> 作用:**`FROM` 指令用于指定基础镜像**。 > -> `FROM` 指令用于指定基础镜像。 所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 `nginx` 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 `FROM` 就是指定**基础镜像**,因此一个 `Dockerfile` 中 `FROM` 是必备的指令,并且必须是第一条指令。 @@ -48,15 +47,16 @@ FROM scratch ### RUN(执行命令) -`RUN` 指令是用来执行命令行命令的。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种: - -- _shell_ 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。 - -```dockerfile -RUN echo '

Hello, Docker!

' > /usr/share/nginx/html/index.html -``` - -- _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。 +> **`RUN` 指令是用来执行命令行命令的**。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种: +> +> - _shell_ 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。 +> +> ```dockerfile +> RUN echo '

Hello, Docker!

' > /usr/share/nginx/html/index.html +> ``` +> +> - _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。 +> 既然 `RUN` 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样: @@ -641,7 +641,7 @@ FROM my-node 是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。 -## 引用和引申 +## 参考资料 - [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/) - [Dockerfile 最佳实践文档](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) diff --git a/docs/docker/docker-quickstart.md b/docs/docker/docker-quickstart.md new file mode 100644 index 00000000..ff00c0eb --- /dev/null +++ b/docs/docker/docker-quickstart.md @@ -0,0 +1,356 @@ +# Docker 快速入门 + + + +- [一、Docker 的简介](#一docker-的简介) +- [二、Docker 的运维](#二docker-的运维) +- [三、hello world 实例](#三hello-world-实例) +- [四、制作 Docker 容器](#四制作-docker-容器) +- [参考资料](#参考资料) + + + +## 一、Docker 的简介 + +### 什么是 Docker + +> **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。** + +它是目前最流行的 Linux 容器解决方案。 + +Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。 + +总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。 + +### 为什么需要 Docker + +- **更高效的利用系统资源** - 由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,`Docker` 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。 +- **更快速的启动时间** - 传统的虚拟机技术启动应用服务往往需要数分钟,而 `Docker` 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。 +- **一致的运行环境** - 开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 `Docker` 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 _「这段代码在我机器上没问题啊」_ 这类问题。 +- **持续交付和部署** - 对开发和运维([DevOps](https://zh.wikipedia.org/wiki/DevOps))人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。使用 `Docker` 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 [Dockerfile](https://yeasy.gitbooks.io/docker_practice/image/dockerfile) 来进行镜像构建,并结合 [持续集成(Continuous Integration)](https://en.wikipedia.org/wiki/Continuous_integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 [持续部署(Continuous Delivery/Deployment)](https://en.wikipedia.org/wiki/Continuous_delivery) 系统进行自动部署。而且使用 [`Dockerfile`](https://yeasy.gitbooks.io/docker_practice/image/build.html) 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。 +- **更轻松的迁移** - 由于 `Docker` 确保了执行环境的一致性,使得应用的迁移更加容易。`Docker` 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。 +- **更轻松的维护和扩展** - `Docker` 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,`Docker` 团队同各个开源项目团队一起维护了一大批高质量的 [官方镜像](https://hub.docker.com/search/?type=image&image_filter=official),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。 + +![img](http://dunwu.test.upcdn.net/cs/os/docker/containers-and-vm.png) + +### Docker 的主要用途 + +Docker 提供了被称为容器的松散隔离环境,在环境中可以打包和运行应用程序。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机的内核中运行。这意味着您可以在给定的硬件组合上运行更多容器,而不是使用虚拟机。你甚至可以在实际上是虚拟机的主机中运行 Docker 容器! + +Docker 的主要用途,目前有三大类。 + +- **提供一次性的环境。**比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。 +- **提供弹性的云服务。**因为 Docker 容器可以随开随关,很适合动态扩容和缩容。 +- **组建微服务架构。**通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。 + +### Docker 的核心概念 + +#### 镜像 + +Docker 把应用程序及其依赖,打包在镜像(Image)文件里面。 + +我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。 + +Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。 + +**分层存储** + +因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。 + +镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。 + +分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。 + +#### 容器 + +镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。 + +容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。 + +前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为**容器存储层**。 + +容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。 + +按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。 + +数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。 + +#### 仓库 + +镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,[Docker Registry](https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html) 就是这样的服务。 + +一个 **Docker Registry** 中可以包含多个**仓库**(`Repository`);每个仓库可以包含多个**标签**(`Tag`);每个标签对应一个镜像。 + +通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 `latest` 作为默认标签。 + +以 [Ubuntu 镜像](https://store.docker.com/images/ubuntu) 为例,`ubuntu` 是仓库的名字,其内包含有不同的版本标签,如,`16.04`, `18.04`。我们可以通过 `ubuntu:14.04`,或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 `ubuntu`,那将视为 `ubuntu:latest`。 + +仓库名经常以 _两段式路径_ 形式出现,比如 `jwilder/nginx-proxy`,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。 + +## 二、Docker 的运维 + +不同操作系统环境下安装 Docker 的方式有所不同,详情可以参: + +- [Docker 官方安装指南](https://docs.docker.com/install/) +- [安装 Docker(中文)](https://docker_practice.gitee.io/install/) + +国内访问 Docker 比较慢,如果需要提速,可以参考 [镜像加速器](https://docker_practice.gitee.io/install/mirror.html) + +安装完成后,运行下面的命令,验证是否安装成功。 + +- `docker version` +- `docker info` + +Docker 需要用户具有 sudo 权限,为了避免每次命令都输入`sudo`,可以把用户加入 Docker 用户组([官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user))。 + +```bash +$ sudo usermod -aG docker $USER +``` + +Docker 是服务器----客户端架构。命令行运行`docker`命令的时候,需要本机有 Docker 服务。如果这项服务没有启动,可以用下面的命令启动([官方文档](https://docs.docker.com/config/daemon/systemd/))。 + +```bash +# service 命令的用法 +$ sudo service docker start + +# systemctl 命令的用法 +$ sudo systemctl start docker +``` + +## 三、hello world 实例 + +下面,我们通过最简单的 image 文件"[hello world"](https://hub.docker.com/r/library/hello-world/),感受一下 Docker。 + +需要说明的是,国内连接 Docker 的官方仓库很慢,还会断线,需要将默认仓库改成国内的镜像网站,具体的修改方法在[下一篇文章](http://www.ruanyifeng.com/blog/2018/02/docker-wordpress-tutorial.html)的第一节。有需要的朋友,可以先看一下。 + +首先,运行下面的命令,将 image 文件从仓库抓取到本地。 + +> ```bash +> $ docker image pull library/hello-world +> ``` + +上面代码中,`docker image pull`是抓取 image 文件的命令。`library/hello-world`是 image 文件在仓库里面的位置,其中`library`是 image 文件所在的组,`hello-world`是 image 文件的名字。 + +由于 Docker 官方提供的 image 文件,都放在[`library`](https://hub.docker.com/r/library/)组里面,所以它的是默认组,可以省略。因此,上面的命令可以写成下面这样。 + +> ```bash +> $ docker image pull hello-world +> ``` + +抓取成功以后,就可以在本机看到这个 image 文件了。 + +> ```bash +> $ docker image ls +> ``` + +现在,运行这个 image 文件。 + +> ```bash +> $ docker container run hello-world +> ``` + +`docker container run`命令会从 image 文件,生成一个正在运行的容器实例。 + +注意,`docker container run`命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的`docker image pull`命令并不是必需的步骤。 + +如果运行成功,你会在屏幕上读到下面的输出。 + +> ```bash +> $ docker container run hello-world +> +> Hello from Docker! +> This message shows that your installation appears to be working correctly. +> +> ... ... +> ``` + +输出这段提示以后,`hello world`就会停止运行,容器自动终止。 + +有些容器不会自动终止,因为提供的是服务。比如,安装运行 Ubuntu 的 image,就可以在命令行体验 Ubuntu 系统。 + +> ```bash +> $ docker container run -it ubuntu bash +> ``` + +对于那些不会自动终止的容器,必须使用[`docker container kill`](https://docs.docker.com/engine/reference/commandline/container_kill/) 命令手动终止。 + +> ```bash +> $ docker container kill [containID] +> ``` + +## 四、制作 Docker 容器 + +下面我以 [koa-demos](http://www.ruanyifeng.com/blog/2017/08/koa.html) 项目为例,介绍怎么写 Dockerfile 文件,实现让用户在 Docker 容器里面运行 Koa 框架。 + +作为准备工作,请先[下载源码](https://github.com/ruanyf/koa-demos/archive/master.zip)。 + +> ```bash +> $ git clone https://github.com/ruanyf/koa-demos.git +> $ cd koa-demos +> ``` + +### 编写 Dockerfile 文件 + +首先,在项目的根目录下,新建一个文本文件`.dockerignore`,写入下面的[内容](https://github.com/ruanyf/koa-demos/blob/master/.dockerignore)。 + +> ```bash +> .git +> node_modules +> npm-debug.log +> ``` + +上面代码表示,这三个路径要排除,不要打包进入 image 文件。如果你没有路径要排除,这个文件可以不新建。 + +然后,在项目的根目录下,新建一个文本文件 Dockerfile,写入下面的[内容](https://github.com/ruanyf/koa-demos/blob/master/Dockerfile)。 + +> ```bash +> FROM node:8.4 +> COPY . /app +> WORKDIR /app +> RUN npm install --registry=https://registry.npm.taobao.org +> EXPOSE 3000 +> ``` + +上面代码一共五行,含义如下。 + +> - `FROM node:8.4`:该 image 文件继承官方的 node image,冒号表示标签,这里标签是`8.4`,即 8.4 版本的 node。 +> - `COPY . /app`:将当前目录下的所有文件(除了`.dockerignore`排除的路径),都拷贝进入 image 文件的`/app`目录。 +> - `WORKDIR /app`:指定接下来的工作路径为`/app`。 +> - `RUN npm install`:在`/app`目录下,运行`npm install`命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。 +> - `EXPOSE 3000`:将容器 3000 端口暴露出来, 允许外部连接这个端口。 + +### 创建 image 文件 + +有了 Dockerfile 文件以后,就可以使用`docker image build`命令创建 image 文件了。 + +> ```bash +> $ docker image build -t koa-demo . +> # 或者 +> $ docker image build -t koa-demo:0.0.1 . +> ``` + +上面代码中,`-t`参数用来指定 image 文件的名字,后面还可以用冒号指定标签。如果不指定,默认的标签就是`latest`。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。 + +如果运行成功,就可以看到新生成的 image 文件`koa-demo`了。 + +> ```bash +> $ docker image ls +> ``` + +### 生成容器 + +`docker container run`命令会从 image 文件生成容器。 + +> ```bash +> $ docker container run -p 8000:3000 -it koa-demo /bin/bash +> # 或者 +> $ docker container run -p 8000:3000 -it koa-demo:0.0.1 /bin/bash +> ``` + +上面命令的各个参数含义如下: + +> - `-p`参数:容器的 3000 端口映射到本机的 8000 端口。 +> - `-it`参数:容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。 +> - `koa-demo:0.0.1`:image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)。 +> - `/bin/bash`:容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。 + +如果一切正常,运行上面的命令以后,就会返回一个命令行提示符。 + +> ```bash +> root@66d80f4aaf1e:/app# +> ``` + +这表示你已经在容器里面了,返回的提示符就是容器内部的 Shell 提示符。执行下面的命令。 + +> ```bash +> root@66d80f4aaf1e:/app# node demos/01.js +> ``` + +这时,Koa 框架已经运行起来了。打开本机的浏览器,访问 http://127.0.0.1:8000,网页显示"Not Found",这是因为这个 [demo](https://github.com/ruanyf/koa-demos/blob/master/demos/01.js) 没有写路由。 + +这个例子中,Node 进程运行在 Docker 容器的虚拟环境里面,进程接触到的文件系统和网络接口都是虚拟的,与本机的文件系统和网络接口是隔离的,因此需要定义容器与物理机的端口映射(map)。 + +现在,在容器的命令行,按下 Ctrl + c 停止 Node 进程,然后按下 Ctrl + d (或者输入 exit)退出容器。此外,也可以用`docker container kill`终止容器运行。 + +> ```bash +> # 在本机的另一个终端窗口,查出容器的 ID +> $ docker container ls +> +> # 停止指定的容器运行 +> $ docker container kill [containerID] +> ``` + +容器停止运行之后,并不会消失,用下面的命令删除容器文件。 + +> ```bash +> # 查出容器的 ID +> $ docker container ls --all +> +> # 删除指定的容器文件 +> $ docker container rm [containerID] +> ``` + +也可以使用`docker container run`命令的`--rm`参数,在容器终止运行后自动删除容器文件。 + +> ```bash +> $ docker container run --rm -p 8000:3000 -it koa-demo /bin/bash +> ``` + +### CMD 命令 + +上一节的例子里面,容器启动以后,需要手动输入命令`node demos/01.js`。我们可以把这个命令写在 Dockerfile 里面,这样容器启动以后,这个命令就已经执行了,不用再手动输入了。 + +> ```bash +> FROM node:8.4 +> COPY . /app +> WORKDIR /app +> RUN npm install --registry=https://registry.npm.taobao.org +> EXPOSE 3000 +> CMD node demos/01.js +> ``` + +上面的 Dockerfile 里面,多了最后一行`CMD node demos/01.js`,它表示容器启动后自动执行`node demos/01.js`。 + +你可能会问,`RUN`命令与`CMD`命令的区别在哪里?简单说,`RUN`命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;`CMD`命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个`RUN`命令,但是只能有一个`CMD`命令。 + +注意,指定了`CMD`命令以后,`docker container run`命令就不能附加命令了(比如前面的`/bin/bash`),否则它会覆盖`CMD`命令。现在,启动容器可以使用下面的命令。 + +> ```bash +> $ docker container run --rm -p 8000:3000 -it koa-demo:0.0.1 +> ``` + +### 发布 image 文件 + +容器运行成功后,就确认了 image 文件的有效性。这时,我们就可以考虑把 image 文件分享到网上,让其他人使用。 + +首先,去 [hub.docker.com](https://hub.docker.com/) 或 [cloud.docker.com](https://cloud.docker.com/) 注册一个账户。然后,用下面的命令登录。 + +> ```bash +> $ docker login +> ``` + +接着,为本地的 image 标注用户名和版本。 + +> ```bash +> $ docker image tag [imageName] [username]/[repository]:[tag] +> # 实例 +> $ docker image tag koa-demos:0.0.1 ruanyf/koa-demos:0.0.1 +> ``` + +也可以不标注用户名,重新构建一下 image 文件。 + +> ```bash +> $ docker image build -t [username]/[repository]:[tag] . +> ``` + +最后,发布 image 文件。 + +> ```bash +> $ docker image push [username]/[repository]:[tag] +> ``` + +发布成功以后,登录 hub.docker.com,就可以看到已经发布的 image 文件。 + +## 参考资料 + +- [Docker 入门教程](https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) diff --git a/docs/docker/docker.md b/docs/docker/docker.md deleted file mode 100644 index 36d5f53d..00000000 --- a/docs/docker/docker.md +++ /dev/null @@ -1,154 +0,0 @@ -# Docker 应用指南 - -## 简介 - -### Docker 是什么 - -> **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。** - -Docker 是目前最流行的 Linux 容器解决方案。 - -Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。 - -总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。 - -### Docker 的用途 - -Docker 的主要用途,目前有三大类。 - -- **提供一次性的环境** - 比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。 -- **提供弹性的云服务** - 因为 Docker 容器可以随开随关,很适合动态扩容和缩容。 -- **组建微服务架构** - 通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。 - -### 虚拟机和 Docker - -虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。 - -**资源占用多** - 虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。 - -**冗余步骤多** - 虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。 - -**启动慢** - 启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。 - -

- -### Docker 平台 - -Docker 提供了被称为容器的松散隔离环境,在环境中可以打包和运行应用程序。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机的内核中运行。这意味着您可以在给定的硬件组合上运行更多容器,而不是使用虚拟机。你甚至可以在实际上是虚拟机的主机中运行 Docker 容器! - -Docker 提供工具和平台来管理容器的生命周期: - -- 使用容器开发您的应用程序及其支持组件。 -- 容器成为分发和测试你的应用程序的单元。 -- 准备好后,将您的应用程序部署到生产环境中,作为容器或协调服务。无论您的生产环境是本地数据中心,云提供商还是两者的混合,这都是一样的。 - -## 核心概念 - -### 引擎 - -Docker 引擎是一个 C/S 架构的应用,它有这些主要的组件: - -服务器是一个长期运行的程序,被称为守护进程。 - -REST API 指定程序可用于与守护进程进行通信并指示其执行操作的接口。 - -命令行客户端。 - -

- -CLI 使用 Docker REST API 通过脚本或直接 CLI 命令来控制 Docker 守护进程或与其进行交互。许多其他 Docker 应用程序使用底层的 API 和 CLI。 - -守护进程创建并管理 Docker 对象,如镜像,容器,网络和卷。 - -### 镜像 - -我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。 - -Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。 - -**分层存储** - -因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。 - -镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。 - -分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。 - -### 容器 - -镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。 - -容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。 - -前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为**容器存储层**。 - -容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。 - -按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。 - -数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。 - -### 仓库 - -镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,[Docker Registry](https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html) 就是这样的服务。 - -一个 **Docker Registry** 中可以包含多个**仓库**(`Repository`);每个仓库可以包含多个**标签**(`Tag`);每个标签对应一个镜像。 - -通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 `latest` 作为默认标签。 - -以 [Ubuntu 镜像](https://store.docker.com/images/ubuntu) 为例,`ubuntu` 是仓库的名字,其内包含有不同的版本标签,如,`16.04`, `18.04`。我们可以通过 `ubuntu:14.04`,或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 `ubuntu`,那将视为 `ubuntu:latest`。 - -仓库名经常以 _两段式路径_ 形式出现,比如 `jwilder/nginx-proxy`,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。 - -## 安装配置 - -不同操作系统环境下安装 Docker 的方式有所不同,详情可以参: - -- [Docker 官方安装指南](https://docs.docker.com/install/) -- [安装 Docker(中文)](https://docker_practice.gitee.io/install/) - -国内访问 Docker 比较慢,如果需要提速,可以参考 [镜像加速器](https://docker_practice.gitee.io/install/mirror.html) - -安装完成后,运行下面的命令,验证是否安装成功。 - -- `docker version` -- `docker info` - -Docker 需要用户具有 sudo 权限,为了避免每次命令都输入`sudo`,可以把用户加入 Docker 用户组([官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user))。 - -```bash -$ sudo usermod -aG docker $USER -``` - -Docker 是服务器----客户端架构。命令行运行`docker`命令的时候,需要本机有 Docker 服务。如果这项服务没有启动,可以用下面的命令启动([官方文档](https://docs.docker.com/config/daemon/systemd/))。 - -```bash -# service 命令的用法 -$ sudo service docker start - -# systemctl 命令的用法 -$ sudo systemctl start docker -``` - -## 参考资料 - -- Docker 官方资源 - - [Docker 官网](http://www.docker.com) - - [Docker Github](https://github.com/moby/moby) - - [Docker 官方文档](https://docs.docker.com/) - - [Docker Hub](https://hub.docker.com/) - - [Docker 开源](https://www.docker.com/community/open-source) -- 资源整理 - - [Awesome Docker](https://github.com/veggiemonk/awesome-docker) -- Docker 中文资源 - - [Docker 中文网站](https://www.docker-cn.com/) - - [Docker 安装手册](https://docs.docker-cn.com/engine/installation/) - - [Docker — 从入门到实践](https://docker_practice.gitee.io/) -- Docker 国内镜像 - - [时速云镜像仓库](https://hub.tenxcloud.com/) - - [网易云镜像服务](https://c.163.com/hub#/m/library/) - - [DaoCloud 镜像市场](https://hub.daocloud.io/) - - [阿里云镜像库](https://cr.console.aliyun.com/) -- 文章 - - [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) - - [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn) diff --git a/docs/kubernetes/kubernetes.md b/docs/docker/kubernetes.md similarity index 97% rename from docs/kubernetes/kubernetes.md rename to docs/docker/kubernetes.md index c1191702..c2a780de 100644 --- a/docs/kubernetes/kubernetes.md +++ b/docs/docker/kubernetes.md @@ -44,7 +44,7 @@ Kubernetes 包含若干抽象用来表示系统状态,包括:已部署的容 - Volume - Namespace -

+
![img](http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp)
高级对象 diff --git a/docs/docker/action/docker-install-mysql.md b/docs/docker/service/docker-install-mysql.md similarity index 100% rename from docs/docker/action/docker-install-mysql.md rename to docs/docker/service/docker-install-mysql.md diff --git a/docs/docker/action/docker-install-nginx.md b/docs/docker/service/docker-install-nginx.md similarity index 100% rename from docs/docker/action/docker-install-nginx.md rename to docs/docker/service/docker-install-nginx.md diff --git a/docs/index.html b/docs/index.html index 15198d09..633d6c7d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,290 +1,291 @@ - - - Linux Tutorial - - - - - - - + - + - + - + - - -
正在加载...
- - - - - - + @media (max-width: 600px) { + pre { + padding-left: 0 !important; + padding-right: 0 !important; + } + } + + + +
正在加载...
+ + + + + + - - - - - - + + + + + + diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" index 2ff7f159..6969934e 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" +++ "b/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" @@ -8,7 +8,7 @@ linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/linux-folders.png!zp)
### 1.2. Linux 文件属性 @@ -35,7 +35,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot 每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 -

+
![img](http://dunwu.test.upcdn.net/snap/20180920180927171909.png!zp)
从左至右用 0-9 这些数字来表示。 diff --git "a/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" "b/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" index 16272b98..1210a346 100644 --- "a/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" +++ "b/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" @@ -4,7 +4,7 @@ _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](RE # 命令行的艺术 -

+
![img](https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- [前言](#前言) - [基础](#基础) @@ -18,7 +18,7 @@ _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](RE - [更多资源](#更多资源) - [免责声明](#免责声明) -

+
![img](https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/cowsay.png)
熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际上,它会提高你作为工程师的灵活性以及生产力。本文是一份我在 Linux 上工作时,发现的一些命令行使用技巧的摘要。有些技巧非常基础,而另一些则相当复杂,甚至晦涩难懂。这篇文章并不长,但当你能够熟练掌握这里列出的所有技巧时,你就学会了很多关于命令行的东西了。 @@ -616,6 +616,6 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir ## 授权条款 -

+
![img](http://creativecommons.org/licenses/by-sa/4.0/)
本文使用授权协议 [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)。 diff --git a/docs/linux/ops/samba.md b/docs/linux/ops/samba.md index 116ddc1f..e3187e7c 100644 --- a/docs/linux/ops/samba.md +++ b/docs/linux/ops/samba.md @@ -149,7 +149,7 @@ Windows: 访问:`\\<你的ip>\<你的共享路径>` : -

+
![img](http://dunwu.test.upcdn.net/snap/20180920180928161334.png!zp)
Mac: diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 3c8eae98..95e78782 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -147,7 +147,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > > \> 如果你认为单词是由 blank 字符分隔符,那么你需要使用大写的 E 和 W。(注:程序语句) > - >

+ >
![img](http://upload-images.jianshu.io/upload_images/3101171-46f752c581d79057.jpg)
下面,让我来说说最强的光标移动: @@ -196,7 +196,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。 > - `3fa` → 在当前行查找第三个出现的 a。 > - `F` 和 `T` → 和 `f` 和 `t` 一样,只不过是相反方向。 ->

+>
![img](http://upload-images.jianshu.io/upload_images/3101171-00835b8316330c58.jpg)
还有一个很有用的命令是 `dt"` → 删除所有的内容,直到遇到双引号—— `"。` @@ -218,7 +218,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `v2i)` → 会选择 `map (+) ("foo")` > - `v2a)` → 会选择 `(map (+) ("foo"))` -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-0b109d66a6111c83.png)
#### 2.4.3. 块操作: `` @@ -229,7 +229,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 - `` → 向下移动 (你也可以使用 hjkl 来移动光标,或是使用%,或是别的) - `I-- [ESC]` → I 是插入,插入“`--`”,按 ESC 键来为每一行生效。 -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-8b093a0f65707949.gif?imageMogr2/auto-orient/strip)
在 Windows 下的 vim,你需要使用 `` 而不是 `` ,`` 是拷贝剪贴板。 @@ -237,7 +237,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 在 Insert 模式下,你可以输入一个词的开头,然后按 `或是,自动补齐功能就出现了……` -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-e2ae877e67880ff7.gif?imageMogr2/auto-orient/strip)
#### 2.4.5. 宏录制: `qa` 操作序列 `q`, `@a`, `@@` @@ -266,7 +266,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > > - 现在做 `100@@` 会创建新的 100 行,并把数据增加到 103. -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-f1889f8bca723964.gif?imageMogr2/auto-orient/strip)
#### 2.4.6. 可视化选择: `v`,`V`,`` @@ -276,7 +276,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 - `<` 或 `>` → 左右缩进 - `=` → 自动给缩进 (注:这个功能相当强大,我太喜欢了) -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-fe1e19983fca213f.gif?imageMogr2/auto-orient/strip)
在所有被选择的行后加上点东西: @@ -285,7 +285,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 - `$` 到行最后 - `A`, 输入字符串,按 `ESC。` -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-b192601247334c4e.gif?imageMogr2/auto-orient/strip)
#### 2.4.7. 分屏: `:split` 和 `vsplit`. @@ -296,7 +296,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `_` (或 `|`) : 最大化尺寸 (| 垂直分屏) > - `+` (或 `-`) : 增加尺寸 -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-f329d01e299cb366.gif?imageMogr2/auto-orient/strip)
## 3. Vim Cheat Sheet @@ -308,33 +308,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet.png!zp)
### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/basic-vim-cheat-sheet.png!zp)
### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png!zp)
### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-02.png!zp)
### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-01.png!zp)
-

+
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-02.png!zp)
## 4. 资料 diff --git a/docs/linux/ops/zsh.md b/docs/linux/ops/zsh.md index 90343f8a..3c0943be 100644 --- a/docs/linux/ops/zsh.md +++ b/docs/linux/ops/zsh.md @@ -95,7 +95,7 @@ wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - zsh 效果如下: -
+![img](https://cloud.githubusercontent.com/assets/2618447/6316862/70f58fb6-ba03-11e4-82c9-c083bf9a6574.png) ## 3. 快捷键 - 呃,这个其实可以不用讲的,你自己用的时候你自己会发现的,各种便捷,特别是用 Tab 多的人一定会有各种惊喜的。 diff --git a/docs/linux/soft/elastic/elastic-beats.md b/docs/linux/soft/elastic/elastic-beats.md index a94b8691..b6bce288 100644 --- a/docs/linux/soft/elastic/elastic-beats.md +++ b/docs/linux/soft/elastic/elastic-beats.md @@ -18,7 +18,7 @@ Beats 是安装在服务器上的数据中转代理。 Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。 -

+
![img](https://www.elastic.co/guide/en/beats/libbeat/current/images/beats-platform.png)
Beats 有多种类型,可以根据实际应用需要选择合适的类型。 @@ -47,7 +47,7 @@ Filebeat带有内部模块(auditd,Apache,Nginx,System和MySQL),可 FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。 -

+
![img](https://www.elastic.co/guide/en/beats/filebeat/current/images/filebeat.png)
## 安装 diff --git a/docs/linux/soft/elastic/elastic-kibana.md b/docs/linux/soft/elastic/elastic-kibana.md index 66d4ffb9..41a43a44 100644 --- a/docs/linux/soft/elastic/elastic-kibana.md +++ b/docs/linux/soft/elastic/elastic-kibana.md @@ -4,7 +4,7 @@ 单击侧面导航栏中的 `Discover` ,可以显示 `Kibana` 的数据查询功能功能。 -

+
![img](https://www.elastic.co/guide/en/kibana/current/images/tutorial-discover.png)
在搜索栏中,您可以输入Elasticsearch查询条件来搜索您的数据。您可以在 `Discover` 页面中浏览结果并在 `Visualize` 页面中创建已保存搜索条件的可视化。 @@ -14,7 +14,7 @@ 默认情况下,每个匹配文档都显示所有字段。要选择要显示的文档字段,请将鼠标悬停在“可用字段”列表上,然后单击要包含的每个字段旁边的添加按钮。例如,如果只添加account_number,则显示将更改为包含五个帐号的简单列表: -

+
![img](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-discover-3.png)
### 查询语义 diff --git a/docs/linux/soft/elastic/elastic-logstash.md b/docs/linux/soft/elastic/elastic-logstash.md index aa93feca..5c4abb2c 100644 --- a/docs/linux/soft/elastic/elastic-logstash.md +++ b/docs/linux/soft/elastic/elastic-logstash.md @@ -32,7 +32,7 @@ Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`f 这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。 -

+
![img](https://www.elastic.co/guide/en/logstash/current/static/images/basic_logstash_pipeline.png)
- input 负责从数据源采集数据。 - filter 将数据修改为你指定的格式或内容。 diff --git a/docs/linux/soft/elastic/elastic-quickstart.md b/docs/linux/soft/elastic/elastic-quickstart.md index 35999496..d3d12814 100644 --- a/docs/linux/soft/elastic/elastic-quickstart.md +++ b/docs/linux/soft/elastic/elastic-quickstart.md @@ -32,7 +32,7 @@ ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/ ### Elastic 架构 -

+
![img](https://www.elastic.co/guide/en/logstash/current/static/images/deploy3.png)
> **说明** > @@ -274,7 +274,7 @@ output { 大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。 -

+
![img](http://upload-images.jianshu.io/upload_images/3101171-cd876d79a14955b0.png)
## 资料 diff --git a/docs/linux/soft/fastdfs.md b/docs/linux/soft/fastdfs.md index 751c9131..d773b186 100644 --- a/docs/linux/soft/fastdfs.md +++ b/docs/linux/soft/fastdfs.md @@ -33,7 +33,7 @@ storage 接受到写文件请求时,会根据配置好的规则(后面会介 **meta data** :文件相关属性,键值对( Key Value Pair) 方式,如:width=1024,heigth=768 。 -
+![img](http://www.ityouknow.com/assets/images/2018/fastdfs/fastdfs_arch.png) diff --git a/docs/linux/soft/gitlab-install.md b/docs/linux/soft/gitlab-install.md index ef0ee848..3ba93bc5 100644 --- a/docs/linux/soft/gitlab-install.md +++ b/docs/linux/soft/gitlab-install.md @@ -25,7 +25,7 @@ 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 -

+
![img](http://dunwu.test.upcdn.net/snap/20190129155838.png!zp)
以 CentOS7 为例: @@ -87,7 +87,7 @@ docker run -d \ gitlab/gitlab-ce ``` -

+
![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp)
## 安装 gitlab-ci-multi-runner @@ -138,7 +138,7 @@ sudo gitlab-runner register URL 和令牌信息在 Gitlab 的 Runner 管理页面获取: -

+
![img](http://dunwu.test.upcdn.net/snap/20190129163100.png!zp)
``` Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) @@ -302,31 +302,31 @@ sudo gitlab-ctl restart 3. 打开 **Profile settings**. -

+
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings.png)
4. 跳转到 **SSH keys** tab 页 -

+
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys.png)
5. 黏贴你的 SSH 公钥内容到 Key 文本框 -

+
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png)
6. 为了便于识别,你可以为其命名 -

+
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_title.png)
7. 点击 **Add key** 将 SSH 公钥添加到 GitLab -

+
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_single_key.png)
### 创建项目 -

+
![img](http://dunwu.test.upcdn.net/snap/20190131150658.png!zp)
输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。 -

+
![img](http://dunwu.test.upcdn.net/snap/20190131150759.png!zp)
### 克隆项目到本地 @@ -338,23 +338,23 @@ sudo gitlab-ctl restart 依次点击 **Project’s Dashboard** > **Issues** > **New Issue** 可以新建 Issue -

+
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_tracker_list.png)
在项目中直接添加 issue -

+
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue.png)
在未关闭 issue 中,点击 **New Issue** 添加 issue -

+
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_open_issue.png)
通过项目面板添加 issue -

+
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_projects_dashboard.png)
通过 issue 面板添加 issue -

+
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png)
## 更多内容 diff --git a/docs/linux/soft/jdk-install.md b/docs/linux/soft/jdk-install.md index b881d0f7..6dcb3f40 100644 --- a/docs/linux/soft/jdk-install.md +++ b/docs/linux/soft/jdk-install.md @@ -22,13 +22,13 @@ a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/java b. 选择需要的版本: -

+
![img](http://dunwu.test.upcdn.net/snap/20180920181010164121.png!zp)
c. 选择对应操作系统的安装包: Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。 -

+
![img](http://dunwu.test.upcdn.net/snap/20180920181010164308.png!zp)
(2)运行安装包,按提示逐步安装 @@ -51,11 +51,11 @@ Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系 a. 安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置"; -

+
![img](https://www.runoob.com/wp-content/uploads/2013/12/win-java1.png)
b. 选择"高级"选项卡,点击"环境变量"; -

+
![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win2.png)
然后就会出现如下图所示的画面: @@ -78,7 +78,7 @@ a. "开始"->"运行",键入"cmd"; b. 键入命令: **java -version**、**java**、**javac** 几个命令,出现以下信息,说明环境变量配置成功; -

+
![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win9.png)
## Linux 系统安装方法 diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 992d0367..d404d55f 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -33,7 +33,7 @@ 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 -
+![img](http://dunwu.test.upcdn.net/snap/20181127203029.png!zp) 本人希望将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 @@ -66,7 +66,7 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} 启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: -
+![img](http://dunwu.test.upcdn.net/snap/20181127203131.png!zp) 点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。 @@ -81,7 +81,7 @@ Nexus 中的仓库有以下类型: - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -
+![img](http://dunwu.test.upcdn.net/cs/java/javalib/maven/nexus.png!zp) > **最佳实践** > @@ -96,7 +96,7 @@ Nexus 中的仓库有以下类型: > - group 仓库 > - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 -
+![img](http://dunwu.test.upcdn.net/snap/20181127203156.png!zp) ### 3.2. 配置 settings.xml diff --git a/docs/linux/soft/svn-ops.md b/docs/linux/soft/svn-ops.md index 726971b3..66d863fb 100644 --- a/docs/linux/soft/svn-ops.md +++ b/docs/linux/soft/svn-ops.md @@ -145,7 +145,7 @@ $ vi /etc/sysconfig/svnserve 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: -

+
![img](http://dunwu.test.upcdn.net/snap/20190129175443.png!zp)
## 2. 参考资料 diff --git a/docs/linux/soft/yapi-ops.md b/docs/linux/soft/yapi-ops.md index 6da62055..13802443 100644 --- a/docs/linux/soft/yapi-ops.md +++ b/docs/linux/soft/yapi-ops.md @@ -4,7 +4,7 @@ > > 本文目的在于记录 svn 的安装、配置、使用。 -
+![img](http://dunwu.test.upcdn.net/snap/1562814562978.png!zp) diff --git a/docs/package.json b/docs/package.json index 2ca3fc06..55269c87 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,33 @@ { - "name": "linux-tutorial", - "version": "1.0.0", - "scripts": { - "start": "docsify serve ./ --port 4000" - } + "name": "linux-tutorial", + "author": "Zhang Peng", + "homepage": "http://dunwu.github.io/linux-tutorial", + "repository": { + "type": "git", + "url": "git@github.com:dunwu/linux-tutorial.git" + }, + "scripts": { + "start": "docsify serve ./ --port 4000", + "clean": "rimraf _book", + "install": "gitbook install", + "serve": "gitbook serve", + "build": "npm run clean & gitbook build", + "pdf": "gitbook pdf ." + }, + "dependencies": { + "gitbook-plugin-advanced-emoji": "^0.2.2", + "gitbook-plugin-anchor-navigation-ex": "^1.0.10", + "gitbook-plugin-anchors": "^0.7.1", + "gitbook-plugin-edit-link": "^2.0.2", + "gitbook-plugin-expandable-chapters-small": "^0.1.7", + "gitbook-plugin-github": "^2.0.0", + "gitbook-plugin-search-plus": "0.0.11", + "gitbook-plugin-simple-page-toc": "^0.1.2", + "gitbook-plugin-splitter": "0.0.8", + "gitbook-plugin-tbfed-pagefooter": "0.0.1" + }, + "devDependencies": { + "gh-pages": "^2.1.1", + "rimraf": "^3.0.0" + } } diff --git a/docs/sidebar.md b/docs/sidebar.md index 7df9e0d0..5e19d9cc 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -1,50 +1,49 @@ -- **文章** - - [**Linux 命令**](linux/cli/README.md) - - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) - - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) - - [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) - - [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) - - [Linux 用户管理](linux/cli/Linux用户管理.md) - - [Linux 系统管理](linux/cli/Linux系统管理.md) - - [Linux 网络管理](linux/cli/Linux网络管理.md) - - [Linux 硬件管理](linux/cli/Linux硬件管理.md) - - [Linux 软件管理](linux/cli/Linux硬件管理.md) - - [**Linux 系统运维**](linux/ops/README.md) - - [linux 典型运维应用](linux/ops/linux典型运维应用.md) - - [samba 使用详解](linux/ops/samba.md) - - [Systemd 教程](linux/ops/systemd.md) - - [Vim 应用指南](linux/ops/vim.md) - - [Zsh 应用指南](linux/ops/zsh.md) - - [**软件运维**](linux/soft/README.md) - - 开发环境 - - [JDK 安装](linux/soft/jdk-install.md) - - [Maven 安装](linux/soft/maven-install.md) - - [Nodejs 安装](linux/soft/nodejs-install.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 运维](linux/soft/elastic) - - [Kafka 运维](linux/soft/kafka-install.md) - - [RocketMQ 运维](linux/soft/rocketmq-install.md) - - [Nacos 运维](linux/soft/nacos-install.md) - - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) - - 服务器 - - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) - - [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) - - [Docker 应用指南](docker/docker.md) - - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) -- **脚本** - - [**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) - - [**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) +# 【目录】 + +- [**Linux 命令**](linux/cli/README.md) + - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) + - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) + - [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) + - [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) + - [Linux 用户管理](linux/cli/Linux用户管理.md) + - [Linux 系统管理](linux/cli/Linux系统管理.md) + - [Linux 网络管理](linux/cli/Linux网络管理.md) + - [Linux 硬件管理](linux/cli/Linux硬件管理.md) + - [Linux 软件管理](linux/cli/Linux硬件管理.md) +- [**Linux 系统运维**](linux/ops/README.md) + - [linux 典型运维应用](linux/ops/linux典型运维应用.md) + - [samba 使用详解](linux/ops/samba.md) + - [Systemd 教程](linux/ops/systemd.md) + - [Vim 应用指南](linux/ops/vim.md) + - [Zsh 应用指南](linux/ops/zsh.md) +- [**软件运维**](linux/soft/README.md) + - 开发环境 + - [JDK 安装](linux/soft/jdk-install.md) + - [Maven 安装](linux/soft/maven-install.md) + - [Nodejs 安装](linux/soft/nodejs-install.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 运维](linux/soft/elastic/README.md) + - [Kafka 运维](linux/soft/kafka-install.md) + - [RocketMQ 运维](linux/soft/rocketmq-install.md) + - [Nacos 运维](linux/soft/nacos-install.md) + - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) + - 服务器 + - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) + - [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/README.md) + - [Docker 快速入门](docker/docker-quickstart.md) + - [Dockerfile 最佳实践](docker/docker-dockerfile.md) + - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) + - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) + - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) + - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) From cdc6b778e04e4507b2c9ff0bae55c0723493bfa9 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 2 Jan 2020 17:28:47 +0800 Subject: [PATCH 27/64] update docs --- README.md | 1 + docs/docker/docker-cheat-sheet.md | 2 +- docs/docker/docker-dockerfile.md | 87 ++++++---- docs/docker/kubernetes.md | 86 +++++----- docs/index.html | 275 +++++++----------------------- docs/sidebar.md | 2 +- 6 files changed, 165 insertions(+), 288 deletions(-) diff --git a/README.md b/README.md index 340f0197..9665411a 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ - [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) - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) diff --git a/docs/docker/docker-cheat-sheet.md b/docs/docker/docker-cheat-sheet.md index d5765fd9..49cdab37 100644 --- a/docs/docker/docker-cheat-sheet.md +++ b/docs/docker/docker-cheat-sheet.md @@ -114,7 +114,7 @@ sudo systemctl restart docker - [`docker rm`](https://docs.docker.com/engine/reference/commandline/rm) 删除容器。 - 如果要删除一个运行中的容器,可以添加 `-f` 参数。Docker 会发送 `SIGKILL` 信号给容器。 - [`docker update`](https://docs.docker.com/engine/reference/commandline/update/) 调整容器的资源限制。 -- `docker container prune` 清理掉所有处于终止状态的容器。 +- 清理掉所有处于终止状态的容器。 通常情况下,不使用任何命令行选项启动一个容器,该容器将会立即启动并停止。若需保持其运行,你可以使用 `docker run -td container_id` 命令。选项 `-t` 表示分配一个 pseudo-TTY 会话,`-d` 表示自动将容器与终端分离(也就是说在后台运行容器,并输出容器 ID)。 diff --git a/docs/docker/docker-dockerfile.md b/docs/docker/docker-dockerfile.md index d13d7d03..60afc544 100644 --- a/docs/docker/docker-dockerfile.md +++ b/docs/docker/docker-dockerfile.md @@ -2,31 +2,30 @@ -- [Dockerfile 指令](#dockerfile-指令) - - [FROM(指定基础镜像)](#from指定基础镜像) - - [RUN(执行命令)](#run执行命令) - - [COPY(复制文件)](#copy复制文件) - - [ADD(更高级的复制文件)](#add更高级的复制文件) - - [CMD(容器启动命令)](#cmd容器启动命令) - - [ENTRYPOINT(入口点)](#entrypoint入口点) - - [ENV(设置环境变量)](#env设置环境变量) - - [ARG(构建参数)](#arg构建参数) - - [VOLUME(定义匿名卷)](#volume定义匿名卷) - - [EXPOSE(暴露端口)](#expose暴露端口) - - [WORKDIR(指定工作目录)](#workdir指定工作目录) - - [USER(指定当前用户)](#user指定当前用户) - - [HEALTHCHECK(健康检查)](#healthcheck健康检查) - - [ONBUILD(为他人作嫁衣裳)](#onbuild为他人作嫁衣裳) -- [引用和引申](#引用和引申) +- [一、Dockerfile 指令](#一dockerfile-指令) + - [FROM(指定基础镜像)](#from指定基础镜像) + - [RUN(执行命令)](#run执行命令) + - [COPY(复制文件)](#copy复制文件) + - [ADD(更高级的复制文件)](#add更高级的复制文件) + - [CMD(容器启动命令)](#cmd容器启动命令) + - [ENTRYPOINT(入口点)](#entrypoint入口点) + - [ENV(设置环境变量)](#env设置环境变量) + - [ARG(构建参数)](#arg构建参数) + - [VOLUME(定义匿名卷)](#volume定义匿名卷) + - [EXPOSE(暴露端口)](#expose暴露端口) + - [WORKDIR(指定工作目录)](#workdir指定工作目录) + - [USER(指定当前用户)](#user指定当前用户) + - [HEALTHCHECK(健康检查)](#healthcheck健康检查) + - [ONBUILD(为他人作嫁衣裳)](#onbuild为他人作嫁衣裳) +- [参考资料](#参考资料) -## Dockerfile 指令 +## 一、Dockerfile 指令 ### FROM(指定基础镜像) > 作用:**`FROM` 指令用于指定基础镜像**。 -> 所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 `nginx` 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 `FROM` 就是指定**基础镜像**,因此一个 `Dockerfile` 中 `FROM` 是必备的指令,并且必须是第一条指令。 @@ -56,7 +55,6 @@ FROM scratch > ``` > > - _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。 -> 既然 `RUN` 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样: @@ -107,8 +105,6 @@ RUN buildDeps='gcc libc6-dev make' \ ### COPY(复制文件) -> 作用: -> > **`COPY` 指令将从构建上下文目录中 `<源路径>` 的文件/目录复制到新的一层的镜像内的 `<目标路径>` 位置。** 格式: @@ -144,8 +140,6 @@ COPY --chown=10:11 files* /mydir/ ### ADD(更高级的复制文件) -> 作用: -> > `ADD` 指令和 `COPY` 的格式和性质基本一致。但是在 `COPY` 基础上增加了一些功能。 > > 比如 `<源路径>` 可以是一个 `URL`,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 `<目标路径>`去。下载后的文件权限自动设置为 `600`,如果这并不是想要的权限,那么还需要增加额外的一层 `RUN` 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 `RUN` 指令进行解压缩。所以不如直接使用 `RUN` 指令,然后使用 `wget` 或者 `curl` 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。 @@ -179,8 +173,6 @@ ADD --chown=10:11 files* /mydir/ ### CMD(容器启动命令) -> 作用: -> > 之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。`CMD` 指令就是用于指定默认的容器主进程的启动命令的。 `CMD` 指令的格式和 `RUN` 相似,也是两种格式: @@ -356,7 +348,7 @@ uid=0(root) gid=0(root) groups=0(root) ### ENV(设置环境变量) -> 作用:`ENV` 指令用于设置环境变量。无论是后面的其它指令,如 `RUN`,还是运行时的应用,都可以直接使用这里定义的环境变量。 +> `ENV` 指令用于设置环境变量。无论是后面的其它指令,如 `RUN`,还是运行时的应用,都可以直接使用这里定义的环境变量。 格式: @@ -396,8 +388,6 @@ RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux- ### ARG(构建参数) -> 作用: -> > `Dockerfile` 中的 `ARG` 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 `docker build` 中用 `--build-arg <参数名>=<值>` 来覆盖。 > > 构建参数和 `ENV` 的效果一样,都是设置环境变量。所不同的是,`ARG` 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 `ARG` 保存密码之类的信息,因为 `docker history` 还是可以看到所有值的。 @@ -429,8 +419,6 @@ docker run -d -v mydata:/data xxxx ### EXPOSE(暴露端口) -> 作用: -> > `EXPOSE` 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 `docker run -P` 时,会自动随机映射 `EXPOSE` 的端口。 > > 要将 `EXPOSE` 和在运行时使用 `-p <宿主端口>:<容器端口>` 区分开来。`-p`,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 `EXPOSE` 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。 @@ -439,8 +427,6 @@ docker run -d -v mydata:/data xxxx ### WORKDIR(指定工作目录) -> 作用: -> > 使用 `WORKDIR` 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,`WORKDIR` 会帮你建立目录。 格式:`WORKDIR <工作目录路径>`。 @@ -460,10 +446,41 @@ RUN echo "hello" > world.txt 因此如果需要改变以后各层的工作目录的位置,那么应该使用 `WORKDIR` 指令。 +### LABEL + +`LABEL`用于为镜像添加元数据,元数以键值对的形式指定: + +``` +LABEL = = = ... +``` + +使用`LABEL`指定元数据时,一条`LABEL`指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条`LABEL`指令指定,以免生成过多的中间镜像。 + +如,通过`LABEL`指定一些元数据: + +``` +LABEL version="1.0" description="这是一个Web服务器" by="IT笔录" +``` + +指定后可以通过`docker inspect`查看: + +``` +$sudo docker inspect itbilu/test +"Labels": { + "version": "1.0", + "description": "这是一个Web服务器", + "by": "IT笔录" +}, +``` + +*注意;*`Dockerfile`中还有个`MAINTAINER`命令,该命令用于指定镜像作者。但`MAINTAINER`并不推荐使用,更推荐使用`LABEL`来指定镜像作者。如: + +``` +LABEL maintainer="itbilu.com" +``` + ### USER(指定当前用户) -> 作用: -> > `USER` 指令和 `WORKDIR` 相似,都是改变环境状态并影响以后的层。`WORKDIR` 是改变工作目录,`USER` 则是改变之后层的执行 `RUN`, `CMD` 以及 `ENTRYPOINT` 这类命令的身份。 > > 当然,和 `WORKDIR` 一样,`USER` 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。 @@ -597,7 +614,7 @@ CMD [ "npm", "start" ] 把这个 `Dockerfile` 放到 Node.js 项目的根目录,构建好镜像后,就可以直接拿来启动容器运行。但是如果我们还有第二个 Node.js 项目也差不多呢?好吧,那就再把这个 `Dockerfile` 复制到第二个项目里。那如果有第三个项目呢?再复制么?文件的副本越多,版本控制就越困难,让我们继续看这样的场景维护的问题。 -如果第一个 Node.js 项目在开发过程中,发现这个 `Dockerfile` 里存在问题,比如敲错字了、或者需要安装额外的包,然后开发人员修复了这个 `Dockerfile`,再次构建,问题解决。� 第一个项目没问题了,但是第二个项目呢?虽然最初 `Dockerfile` 是复制、粘贴自第一个项目的,但是并不会因为第一个项目修复了他们的 `Dockerfile`,而第二个项目的 `Dockerfile` 就会被自动修复。 +如果第一个 Node.js 项目在开发过程中,发现这个 `Dockerfile` 里存在问题,比如敲错字了、或者需要安装额外的包,然后开发人员修复了这个 `Dockerfile`,再次构建,问题解决。第一个项目没问题了,但是第二个项目呢?虽然最初 `Dockerfile` 是复制、粘贴自第一个项目的,但是并不会因为第一个项目修复了他们的 `Dockerfile`,而第二个项目的 `Dockerfile` 就会被自动修复。 那么我们可不可以做一个基础镜像,然后各个项目使用这个基础镜像呢?这样基础镜像更新,各个项目不用同步 `Dockerfile`的变化,重新构建后就继承了基础镜像的更新?好吧,可以,让我们看看这样的结果。那么上面的这个 `Dockerfile` 就会变为: diff --git a/docs/docker/kubernetes.md b/docs/docker/kubernetes.md index c2a780de..e3fe13eb 100644 --- a/docs/docker/kubernetes.md +++ b/docs/docker/kubernetes.md @@ -1,21 +1,27 @@ -# Kubernetes +# Kubernetes 应用指南 -> Kubernetes 是用于自动部署,扩展和管理 Docker 应用程序的开源系统。简称 K8S +> Kubernetes 是谷歌开源的容器集群管理系统 是用于自动部署,扩展和管理 Docker 应用程序的开源系统,简称 K8S。 > > 关键词: `docker` -- [功能](#功能) -- [简介](#简介) -- [基础](#基础) -- [进阶](#进阶) -- [命令](#命令) -- [引用和引申](#引用和引申) +- [一、K8S 简介](#一k8s-简介) +- [二、K8S 命令](#二k8s-命令) +- [参考资料](#参考资料) -## 功能 +## 一、K8S 简介 + +K8S 主控组件(Master) 包含三个进程,都运行在集群中的某个节上,通常这个节点被称为 master 节点。这些进程包括:`kube-apiserver`、`kube-controller-manager` 和 `kube-scheduler`。 + +集群中的每个非 master 节点都运行两个进程: + +- kubelet,和 master 节点进行通信。 +- kube-proxy,一种网络代理,将 Kubernetes 的网络服务代理到每个节点上。 + +### K8S 功能 - 基于容器的应用部署、维护和滚动升级 - 负载均衡和服务发现 @@ -25,40 +31,37 @@ - 广泛的 Volume 支持 - 插件机制保证扩展性 -## 简介 +### K8S 核心组件 -Kubernetes 主控组件(Master) 包含三个进程,都运行在集群中的某个节上,通常这个节点被称为 master 节点。这些进程包括:`kube-apiserver`、`kube-controller-manager` 和 `kube-scheduler`。 +Kubernetes 主要由以下几个核心组件组成: -集群中的每个非 master 节点都运行两个进程: +- etcd 保存了整个集群的状态; +- apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制; +- controller manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等; +- scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上; +- kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理; +- Container runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI); +- kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡 -- kubelet,和 master 节点进行通信。 -- kube-proxy,一种网络代理,将 Kubernetes 的网络服务代理到每个节点上。 - -### Kubernetes 对象 - -Kubernetes 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。 +![img](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LDAOok5ngY4pc1lEDes%2F-LpOIkR-zouVcB8QsFj_%2F-LpOIpZIYxaDoF-FJMZk%2Farchitecture.png?generation=1569161437087842&alt=media) +### K8S 核心概念 -- Pod - kubernetes 对象模型中最小的单元,它代表集群中一个正在运行的进程。 -- Service -- Volume -- Namespace +K8S 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。
![img](http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp)
-高级对象 - -- ReplicaSet -- Deployment -- StatefulSet -- DaemonSet -- Job - -## 基础 - -## 进阶 +- `Pod` - K8S 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 K8S 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。 +- `Node` - Node 是 Pod 真正运行的主机,可以是物理机,也可以是虚拟机。为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 docker 或者 rkt)、`kubelet` 和 `kube-proxy` 服务。 +- `Namespace` - Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 pods, services, replication controllers 和 deployments 等都是属于某一个 namespace 的(默认是 default),而 node, persistentVolumes 等则不属于任何 namespace。 +- `Service` - Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 endpoints 上。每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。 +- `Label` - Label 是识别 K8S 对象的标签,以 key/value 的方式附加到对象上(key 最长不能超过 63 字节,value 可以为空,也可以是不超过 253 字节的字符串)。Label 不提供唯一性,并且实际上经常是很多对象(如 Pods)都使用相同的 label 来标志具体的应用。Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 label 的对象(比如 ReplicaSet 和 Service 用 label 来选择一组 Pod)。Label Selector 支持以下几种方式: + - 等式,如 `app=nginx` 和 `env!=production` + - 集合,如 `env in (production, qa)` + - 多个 label(它们之间是 AND 关系),如 `app=nginx,env=test` +- `Annotations` - Annotations 是 key/value 形式附加于对象的注解。不同于 Labels 用于标志和选择对象,Annotations 则是用来记录一些附加信息,用来辅助应用部署、安全策略以及调度策略等。比如 deployment 使用 annotations 来记录 rolling update 的状态。 -## 命令 +## 二、K8S 命令 ### 客户端配置 @@ -163,14 +166,15 @@ kubectl logs pod_name kubectl logs -f pod_name -c my-container ``` -## 引用和引申 +## 参考资料 -- 官方 - - [Github](https://github.com/kubernetes/kubernetes) - - [官网](https://kubernetes.io/) -- 教程 +- **官方** + - [Kubernetes Github](https://github.com/kubernetes/kubernetes) + - [Kubernetes 官网](https://kubernetes.io/) +- **教程** - [Kubernetes 中文指南](https://jimmysong.io/kubernetes-handbook/) -- 文章 + - [kubernetes-handbook](https://github.com/rootsongjc/kubernetes-handbook) +- **文章** - https://github.com/LeCoupa/awesome-cheatsheets/blob/master/tools/kubernetes.sh -- 更多资源 +- **更多资源** - [awesome-kubernetes](https://github.com/ramitsurana/awesome-kubernetes) diff --git a/docs/index.html b/docs/index.html index 633d6c7d..94e2303a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -10,252 +10,83 @@ name="viewport" /> - + + + - - - -
正在加载...
+ + - - + + + + + + + + + + + + diff --git a/docs/sidebar.md b/docs/sidebar.md index 5e19d9cc..9d62b486 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -1,4 +1,4 @@ -# 【目录】 +# linux-tutorial - [**Linux 命令**](linux/cli/README.md) - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) From d13e0f38333624315937f7c7e60a550f8f481461 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 2 Jan 2020 22:25:10 +0800 Subject: [PATCH 28/64] add git scripts --- codes/shell/git/check-git.sh | 65 ++++++++++++++ codes/shell/git/update-git.sh | 156 ++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 codes/shell/git/check-git.sh create mode 100644 codes/shell/git/update-git.sh diff --git a/codes/shell/git/check-git.sh b/codes/shell/git/check-git.sh new file mode 100644 index 00000000..25f2256e --- /dev/null +++ b/codes/shell/git/check-git.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# ASCII 颜色变量 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="\033[0m" + +# 常量 +YES=0 +NO=1 +# ------------------------------------------------------------------------------ + +printf "$BLUE" +cat << EOF +# ------------------------------------------------------------------------------ +# 检查当前目录是否为 git 工程 +# ------------------------------------------------------------------------------ +EOF +printf "$RESET" + +# 检查指定的路径是不是一个 git 项目 +checkGit() { + source=$1 + if [[ -d "${source}" ]]; then + cd ${source} || return ${NO} + # (1)删除git状态零时文件 + if [[ -f "gitstatus.tmp" ]]; then + rm -rf gitstatus.tmp + fi + + # (2)判断是否存在 .git 目录 + if [[ -d "./.git" ]]; then + # (3)判断git是否可用 + git status &> gitstatus.tmp + gitStatus=false + grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true + rm -rf gitstatus.tmp + if [[ ${gitStatus} == true ]]; then + return ${YES} + else + return ${NO} + fi + fi + fi + return ${NO} +} + +##################################### MAIN ##################################### +ROOT=$(pwd) +printf "${BLUE}当前目录为:${ROOT} ${RESET}\n" + +checkGit ${ROOT} +if [[ "${YES}" == "$?" ]]; then + printf "${GREEN}${ROOT} 是 git 工程。${RESET}\n" + exit ${YES} +else + printf "${RED}${ROOT} 不是 git 工程。${RESET}\n" + exit ${NO} +fi diff --git a/codes/shell/git/update-git.sh b/codes/shell/git/update-git.sh new file mode 100644 index 00000000..0ea5188f --- /dev/null +++ b/codes/shell/git/update-git.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# console color +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" + +# 常量 +YES=0 +NO=1 +SUCCEED=0 +FAILED=1 +# ------------------------------------------------------------------------------ + +printf "$BLUE" +cat << EOF +################################################################################ +# 项目初始化脚本 +################################################################################ +EOF +printf "$RESET" + +# 检查指定的路径是不是一个 git 项目 +checkGit() { + source=$1 + if [[ -d "${source}" ]]; then + cd ${source} || return ${NO} + # (1)删除git状态零时文件 + if [[ -f "gitstatus.tmp" ]]; then + rm -rf gitstatus.tmp + fi + + # (2)判断是否存在 .git 目录 + if [[ -d "./.git" ]]; then + # (3)判断git是否可用 + git status &> gitstatus.tmp + gitStatus=false + grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true + rm -rf gitstatus.tmp + if [[ ${gitStatus} == true ]]; then + return ${YES} + else + return ${NO} + fi + fi + fi + return ${NO} +} + +cloneOrFetchGit() { + root=$1 + group=$2 + project=$3 + branch=$4 + + if [[ ! -d "${root}" ]]; then + printf "${YELLOW}>>>> ${root} is not directory.${RESET}\n" + return ${FAILED} + fi + + if [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]]; then + printf "${YELLOW}>>>> group, project, branch must not be empty.${RESET}\n" + return ${FAILED} + fi + + source=${root}/${group}/${project} + printf "${CYAN}>>>> project directory is ${source}.${RESET}\n" + printf "${CYAN}>>>> git url is ${REPOSITORY}:${group}/${project}.git.${RESET}\n" + mkdir -p ${root}/${group} + + checkGit ${source} + if [[ "${YES}" == "$?" ]]; then + # 如果 ${source} 是 git 项目,执行 pull 操作 + cd ${source} || return ${FAILED} + + git checkout -f ${branch} + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${RED}>>>> git checkout ${branch} failed.${RESET}\n" + return ${FAILED} + fi + printf "${GREEN}>>>> git checkout ${branch} succeed.${RESET}\n" + + git reset --hard + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${RED}>>>> git reset --hard failed.${RESET}\n" + return ${FAILED} + fi + printf "${GREEN}>>>> git reset --hard succeed.${RESET}\n" + + git pull + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${RED}>>>> git pull failed.${RESET}\n" + return ${FAILED} + fi + printf "${GREEN}>>>> git pull succeed.${RESET}\n" + else + # 如果 ${source} 不是 git 项目,执行 clone 操作 + + git clone "${REPOSITORY}:${group}/${project}.git" ${source} + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${RED}>>>> git clone ${project} failed.${RESET}\n" + return ${FAILED} + fi + printf "${GREEN}>>>> git clone ${project} succeed.${RESET}\n" + + cd ${source} || return ${FAILED} + + git checkout -f ${branch} + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${RED}>>>> git checkout ${branch} failed.${RESET}\n" + return ${FAILED} + fi + printf "${GREEN}>>>> git checkout ${branch} succeed.${RESET}\n" + fi + + return ${SUCCEED} +} + +doCloneOrFetchGit() { + cloneOrFetchGit $1 $2 $3 $4 + if [[ "$?" == "${SUCCEED}" ]]; then + printf "\n${GREEN}>>>> Update git project [$2/$3] succeed.${RESET}\n" + return ${SUCCEED} + else + printf "\n${RED}>>>> Update git project [$2/$3] failed.${RESET}\n" + return ${FAILED} + fi +} + +##################################### MAIN ##################################### +ROOT=$(pwd) + +# 这里需要根据实际情况替换 Git 仓库、项目组、项目、代码分支 +REPOSITORY="git@gitee.com" + +printf "${CYAN}Current path is ${ROOT}.${RESET}\n" + +doCloneOrFetchGit ${ROOT} turnon linux-tutorial master +r1=$? +doCloneOrFetchGit ${ROOT} turnon nginx-tutorial master +r2=$? + +if [[ "${r1}" == "${SUCCEED}" && "${r2}" == "${SUCCEED}" ]]; then + printf "\n${GREEN}Succeed.${RESET}\n" + exit ${SUCCEED} +else + printf "\n${RED}Failed.${RESET}\n" + exit ${FAILED} +fi + From bde246d98459923fee28d86768f26a2c884d6e5f Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Fri, 3 Jan 2020 22:41:37 +0800 Subject: [PATCH 29/64] update --- README.md | 2 + codes/linux/lib/env.sh | 53 ++++++ codes/linux/lib/file.sh | 82 +++++++++ codes/linux/lib/git.sh | 112 +++++++++++++ codes/linux/lib/nodejs.sh | 108 ++++++++++++ codes/linux/lib/string.sh | 50 ++++++ codes/linux/libtest/README.md | 11 ++ codes/linux/libtest/env-test.sh | 40 +++++ codes/linux/libtest/git-check.sh | 21 +++ codes/linux/libtest/git-update.sh | 37 +++++ codes/linux/libtest/nodejs-test.sh | 19 +++ codes/shell/git/check-git.sh | 65 -------- codes/shell/git/update-git.sh | 156 ------------------ codes/shell/lib/env.sh | 53 ++++++ .../log.txt" | 66 ++++++++ ...64\346\227\266\346\226\207\344\273\266.sh" | 14 +- ...64\346\227\266\347\233\256\345\275\225.sh" | 14 ++ ...72\346\234\254\346\223\215\344\275\234.sh" | 24 +-- .../\350\257\273\346\226\207\344\273\266.sh" | 12 ++ .../lib.sh" | 21 +++ ...50\345\272\223\345\207\275\346\225\260.sh" | 11 +- .../\350\204\232\346\234\254\345\272\223.sh" | 21 --- ...73\345\217\226\346\225\260\346\215\256.sh" | 12 -- ...64\346\227\266\347\233\256\345\275\225.sh" | 16 -- ...64\346\227\266\346\226\207\344\273\266.sh" | 12 -- docs/README.md | 2 + docs/coverpage.md | 4 +- docs/docker/docker-dockerfile.md | 4 + docs/index.html | 14 +- docs/sidebar.md | 1 + 30 files changed, 746 insertions(+), 311 deletions(-) create mode 100644 codes/linux/lib/env.sh create mode 100644 codes/linux/lib/file.sh create mode 100644 codes/linux/lib/git.sh create mode 100644 codes/linux/lib/nodejs.sh create mode 100644 codes/linux/lib/string.sh create mode 100644 codes/linux/libtest/README.md create mode 100644 codes/linux/libtest/env-test.sh create mode 100644 codes/linux/libtest/git-check.sh create mode 100644 codes/linux/libtest/git-update.sh create mode 100644 codes/linux/libtest/nodejs-test.sh delete mode 100644 codes/shell/git/check-git.sh delete mode 100644 codes/shell/git/update-git.sh create mode 100644 codes/shell/lib/env.sh create mode 100644 "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/log.txt" rename "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" => "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" (51%) create mode 100644 "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" rename "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" => "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\347\233\256\345\275\225\345\237\272\346\234\254\346\223\215\344\275\234.sh" (50%) create mode 100644 "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\350\257\273\346\226\207\344\273\266.sh" create mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/lib.sh" delete mode 100644 "codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" delete mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" delete mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" delete mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" diff --git a/README.md b/README.md index 9665411a..0d7124f3 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@

linux-tutorial

+> 📚 **linux-tutorial** 是一个 Linux 教程。 +> > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) diff --git a/codes/linux/lib/env.sh b/codes/linux/lib/env.sh new file mode 100644 index 00000000..4c41818e --- /dev/null +++ b/codes/linux/lib/env.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ 颜色状态 + +# Regular Color +C_BLACK="\033[0;30m" +C_RED="\033[0;31m" +C_GREEN="\033[0;32m" +C_YELLOW="\033[0;33m" +C_BLUE="\033[0;34m" +C_MAGENTA="\033[0;35m" +C_CYAN="\033[0;36m" +C_WHITE="\033[0;37m" + +# Bold Color +C_B_BLACK="\033[1;30m" +C_B_RED="\033[1;31m" +C_B_GREEN="\033[1;32m" +C_B_YELLOW="\033[1;33m" +C_B_BLUE="\033[1;34m" +C_B_MAGENTA="\033[1;35m" +C_B_CYAN="\033[1;36m" +C_B_WHITE="\033[1;37m" + +# Underline Color +C_U_BLACK="\033[4;30m" +C_U_RED="\033[4;31m" +C_U_GREEN="\033[4;32m" +C_U_YELLOW="\033[4;33m" +C_U_BLUE="\033[4;34m" +C_U_MAGENTA="\033[4;35m" +C_U_CYAN="\033[4;36m" +C_U_WHITE="\033[4;37m" + +# Background Color +C_BG_BLACK="\033[40m" +C_BG_RED="\033[41m" +C_BG_GREEN="\033[42m" +C_BG_YELLOW="\033[43m" +C_BG_BLUE="\033[44m" +C_BG_MAGENTA="\033[45m" +C_BG_CYAN="\033[46m" +C_BG_WHITE="\033[47m" + +# Reset Color +C_RESET="$(tput sgr0)" + +# ------------------------------------------------------------------------------ 常用状态值 + +YES=0 +NO=1 +SUCCEED=0 +FAILED=1 diff --git a/codes/linux/lib/file.sh b/codes/linux/lib/file.sh new file mode 100644 index 00000000..ecbcf6bd --- /dev/null +++ b/codes/linux/lib/file.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# 装载其它库 +ROOT=`dirname ${BASH_SOURCE[0]}` +source ${ROOT}/env.sh + +# ------------------------------------------------------------------------------ 文件操作函数 + +# 文件是否存在 +isFileExists() { + if [[ -e $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +isFile() { + if [[ -f $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +isDirectory() { + if [[ -d $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +isFileReadable() { + if [[ -r $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +isFileWritable() { + if [[ -w $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +isFileExecutable() { + if [[ -x $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +# 检查文件夹是否存在,不存在则创建 +createFolderIfNotExist() { + if [ ! -d "$1" ]; then + mkdir -p "$1" + fi +} + +# 重建目录,如果目录已存在,则删除后重建;如果不存在,直接新建 +recreateDir() { + if [[ ! $1 ]]; then + printf "${C_B_RED}<<<< Please input dir path.${C_RESET}\n" + return ${FAILED} + fi + + rm -rf $1 + mkdir -p $1 + + isDirectory $1 + if [[ "$?" != "${SUCCEED}" ]]; then + printf "${C_B_RED}<<<< create $1 failed.${C_RESET}\n" + return ${FAILED} + fi + + return ${SUCCEED} +} diff --git a/codes/linux/lib/git.sh b/codes/linux/lib/git.sh new file mode 100644 index 00000000..6e33ca7a --- /dev/null +++ b/codes/linux/lib/git.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash + +# 装载其它库 +ROOT=`dirname ${BASH_SOURCE[0]}` +source ${ROOT}/env.sh + +# ------------------------------------------------------------------------------ git 操作函数 + +# 检查指定的路径是不是一个 git 项目 +checkGit() { + local source=$1 + if [[ -d "${source}" ]]; then + cd ${source} || return ${NO} + # (1)删除git状态零时文件 + if [[ -f "gitstatus.tmp" ]]; then + rm -rf gitstatus.tmp + fi + + # (2)判断是否存在 .git 目录 + if [[ -d "./.git" ]]; then + # (3)判断git是否可用 + 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} + else + return ${NO} + fi + fi + + return ${NO} + fi + + printf "${C_B_B_YELLOW}${source} is invalid dir.${C_RESET}\n" + return ${NO} +} + +# clone 或 fetch 操作 +# 如果本地代码目录已经是 git 仓库,执行 pull;若不是,则执行 clone +# 依次传入 Git 仓库、Git 项目组、Git 项目名、分支、本地代码目录 +cloneOrPullGit() { + + local repository=$1 + local group=$2 + local project=$3 + local branch=$4 + local root=$5 + + if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then + printf "${C_B_YELLOW}>>>> Please input root, group, project, branch.${C_RESET}\n" + return ${FAILED} + fi + + if [[ ! -d "${root}" ]]; then + printf "${C_B_YELLOW}>>>> ${root} is not directory.${C_RESET}\n" + return ${FAILED} + fi + + local source=${root}/${group}/${project} + printf "${C_B_CYAN}>>>> project directory is ${source}.${C_RESET}\n" + printf "${C_B_CYAN}>>>> git url is ${repository}:${group}/${project}.git.${C_RESET}\n" + mkdir -p ${root}/${group} + + checkGit ${source} + if [[ "${YES}" == "$?" ]]; then + # 如果 ${source} 是 git 项目,执行 pull 操作 + cd ${source} || return ${FAILED} + + git checkout -f ${branch} + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" + return ${FAILED} + fi + printf "${C_B_GREEN}>>>> git checkout ${branch} succeed.${C_RESET}\n" + + git reset --hard + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${C_B_RED}<<<< git reset --hard failed.${C_RESET}\n" + return ${FAILED} + fi + printf "${C_B_GREEN}>>>> git reset --hard succeed.${C_RESET}\n" + + git pull + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${C_B_RED}<<<< git pull failed.${C_RESET}\n" + return ${FAILED} + fi + printf "${C_B_GREEN}>>>> git pull succeed.${C_RESET}\n" + else + # 如果 ${source} 不是 git 项目,执行 clone 操作 + + git clone "${repository}:${group}/${project}.git" ${source} + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${C_B_RED}<<<< git clone ${project} failed.${C_RESET}\n" + return ${FAILED} + fi + printf "${C_B_GREEN}>>>> git clone ${project} succeed.${C_RESET}\n" + + cd ${source} || return ${FAILED} + + git checkout -f ${branch} + if [[ "${SUCCEED}" != "$?" ]]; then + printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" + return ${FAILED} + fi + printf "${C_B_GREEN}>>>> git checkout ${branch} succeed.${C_RESET}\n" + fi + + return ${SUCCEED} +} diff --git a/codes/linux/lib/nodejs.sh b/codes/linux/lib/nodejs.sh new file mode 100644 index 00000000..38d3849e --- /dev/null +++ b/codes/linux/lib/nodejs.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash + +# 装载其它库 +ROOT=`dirname ${BASH_SOURCE[0]}` +source ${ROOT}/file.sh + +# ------------------------------------------------------------------------------ nodejs 操作函数 + +# install Node Version Manager(nvm) +installNvm() { + local nvmVersion=0.35.2 + if [[ $1 ]]; then + local nvmVersion=$1 + fi + + recreateDir "~/.nvm" + curl -o- https://raw.githubusercontent.com/creationix/nvm/v${nvmVersion}/install.sh | bash + source ~/.nvm/nvm.sh + if [[ "$?" != "${YES}" ]]; then + return ${FAILED} + fi + + # Check + nvm version + if [[ "$?" != "${YES}" ]]; then + return ${FAILED} + fi + return ${SUCCEED} +} + +# Check nodejs version +checkNodejsVersion() { + if [[ ! $1 ]]; then + printf "${C_B_RED}<<<< please specified expect nodejs version.${C_RESET}\n" + return ${FAILED} + fi + + local expectVersion=$1 + + source /root/.bashrc + local nodeVersion=$(nvm version) + if [[ "$?" != "${YES}" ]]; then + printf "${C_B_YELLOW}>>>> nvm not installed.${C_RESET}\n" + + local nvmVersion=v0.35.2 + installNvm "${nvmVersion}" + if [[ "$?" != "${SUCCEED}" ]]; then + return ${FAILED} + fi + nodeVersion=$(nvm version) + fi + + if [[ "${nodeVersion}" != "v${expectVersion}" ]]; then + printf "${C_B_YELLOW}>>>> current nodejs version is ${nodeVersion}, not ${expectVersion}.${C_RESET}\n" + nvm install ${expectVersion} + nvm use ${expectVersion} + fi + + return ${SUCCEED} +} + +# build nodejs project +buildNodejsProject() { + if [[ ! $1 ]]; then + printf "${C_B_RED}<<<< please input nodejs project path.${C_RESET}\n" + return ${FAILED} + fi + + if [[ ! $2 ]]; then + printf "${C_B_RED}<<<< please input nodejs version.${C_RESET}\n" + return ${FAILED} + fi + + isDirectory $1 + if [[ "$?" != "${YES}" ]]; then + printf "${C_B_RED}<<<< $1 is not valid path.${C_RESET}\n" + return ${FAILED} + fi + + local project=$1 + local nodeVersion=$2 + printf "${C_B_BLUE}>>>> build nodejs project $1 begin.${C_RESET}\n" + cd ${project} || (printf "${C_B_RED}<<<< ${project} is not exists.${C_RESET}\n" && exit 1) + + checkNodejsVersion ${nodeVersion} + + npm install + if [[ "$?" != "${YES}" ]]; then + printf "${C_B_RED}<<<< update dependencies failed.${C_RESET}\n" + return ${FAILED} + else + printf "${C_B_GREEN}>>>> update dependencies succeed.${C_RESET}\n" + fi + + npm run build + if [[ "$?" != "${YES}" ]]; then + printf "${C_B_RED}<<<< build failed.${C_RESET}\n" + return ${FAILED} + else + printf "${C_B_GREEN}<<<< build succeed.${C_RESET}\n" + fi + return ${SUCCEED} +} + +# package nodejs artifact dir (default is dist) +packageDist() { + zip -o -r -q ${branch}.zip * +} diff --git a/codes/linux/lib/string.sh b/codes/linux/lib/string.sh new file mode 100644 index 00000000..9a752796 --- /dev/null +++ b/codes/linux/lib/string.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +strIsEmpty() { + if [[ -z $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +strIsNotEmpty() { + if [[ -n $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +strIsBlank() { + if [[ ! $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +strIsNotBlank() { + if [[ $1 ]]; then + return ${YES} + else + return ${NO} + fi +} + +strEquals() { + if [[ "$1" = "$2" ]]; then + return ${YES} + else + return ${NO} + fi +} + +strStartWith() { + if [[ "$1" == "$2*" ]]; then + return ${YES} + else + return ${NO} + fi +} + diff --git a/codes/linux/libtest/README.md b/codes/linux/libtest/README.md new file mode 100644 index 00000000..2051e94d --- /dev/null +++ b/codes/linux/libtest/README.md @@ -0,0 +1,11 @@ +# Git 脚本工具 + +这里汇总一些常用的、简单的 git shell 脚本。 + +更多功能强大的工具,可以参考 [资源](#资源) + +## 资源 + +- [git-extras](https://github.com/tj/git-extras) - 丰富的 git 扩展支持 +- [gitflow](https://github.com/nvie/gitflow) - git-flow shell 版本 +- [gitflow-avh](https://github.com/petervanderdoes/gitflow-avh) - git-flow shell 版本 \ No newline at end of file diff --git a/codes/linux/libtest/env-test.sh b/codes/linux/libtest/env-test.sh new file mode 100644 index 00000000..e1a67f76 --- /dev/null +++ b/codes/linux/libtest/env-test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# 装载其它库 +source ../lib/env.sh + +# ------------------------------------------------------------------------------ 颜色变量测试 +printf "${C_B_YELLOW}测试彩色打印:${C_RESET}\n" + +printf "${C_BLACK}Hello.${C_RESET}\n" +printf "${C_RED}Hello.${C_RESET}\n" +printf "${C_GREEN}Hello.${C_RESET}\n" +printf "${C_YELLOW}Hello.${C_RESET}\n" +printf "${C_BLUE}Hello.${C_RESET}\n" +printf "${C_PURPLE}Hello.${C_RESET}\n" +printf "${C_CYAN}Hello.${C_RESET}\n" + +printf "${C_B_BLACK}Hello.${C_RESET}\n" +printf "${C_B_RED}Hello.${C_RESET}\n" +printf "${C_B_GREEN}Hello.${C_RESET}\n" +printf "${C_B_YELLOW}Hello.${C_RESET}\n" +printf "${C_B_BLUE}Hello.${C_RESET}\n" +printf "${C_B_PURPLE}Hello.${C_RESET}\n" +printf "${C_B_CYAN}Hello.${C_RESET}\n" + +printf "${C_U_BLACK}Hello.${C_RESET}\n" +printf "${C_U_RED}Hello.${C_RESET}\n" +printf "${C_U_GREEN}Hello.${C_RESET}\n" +printf "${C_U_YELLOW}Hello.${C_RESET}\n" +printf "${C_U_BLUE}Hello.${C_RESET}\n" +printf "${C_U_PURPLE}Hello.${C_RESET}\n" +printf "${C_U_CYAN}Hello.${C_RESET}\n" + +printf "${C_BG_BLACK}Hello.${C_RESET}\n" +printf "${C_BG_RED}Hello.${C_RESET}\n" +printf "${C_BG_GREEN}Hello.${C_RESET}\n" +printf "${C_BG_YELLOW}Hello.${C_RESET}\n" +printf "${C_BG_BLUE}Hello.${C_RESET}\n" +printf "${C_BG_PURPLE}Hello.${C_RESET}\n" +printf "${C_BG_CYAN}Hello.${C_RESET}\n" + diff --git a/codes/linux/libtest/git-check.sh b/codes/linux/libtest/git-check.sh new file mode 100644 index 00000000..9d3358f7 --- /dev/null +++ b/codes/linux/libtest/git-check.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +source ../lib/git.sh + +##################################### MAIN ##################################### +if [[ $1 ]]; then + DIR=$1 +else + DIR=$(pwd) +fi + +printf "${C_B_BLUE}Current path is: ${DIR} ${C_RESET}\n" + +checkGit ${DIR} +if [[ "${YES}" == "$?" ]]; then + printf "${C_B_GREEN}${DIR} is git project.${C_RESET}\n" + exit ${YES} +else + printf "${C_B_RED}${DIR} is not git project.${C_RESET}\n" + exit ${NO} +fi diff --git a/codes/linux/libtest/git-update.sh b/codes/linux/libtest/git-update.sh new file mode 100644 index 00000000..0c457cfe --- /dev/null +++ b/codes/linux/libtest/git-update.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +source ../lib/env.sh +source ../lib/git.sh + +doCloneOrPullGit() { + cloneOrPullGit $1 $2 $3 $4 $5 + if [[ "$?" == "${SUCCEED}" ]]; then + printf "\n${C_GREEN}>>>> Update git project [$2/$3] succeed.${C_RESET}\n" + return ${SUCCEED} + else + printf "\n${C_RED}>>>> Update git project [$2/$3] failed.${C_RESET}\n" + return ${FAILED} + fi +} + +##################################### MAIN ##################################### +ROOT=$(dirname ${BASH_SOURCE[0]}) + +# 这里需要根据实际情况替换 Git 仓库、项目组、项目、代码分支 +REPOSITORY="git@gitee.com" + +printf "${C_CYAN}Current path is ${ROOT}.${C_RESET}\n" + +doCloneOrPullGit ${REPOSITORY} turnon linux-tutorial master ${ROOT} +r1=$? +doCloneOrPullGit ${REPOSITORY} turnon nginx-tutorial master ${ROOT} +r2=$? + +if [[ "${r1}" == "${SUCCEED}" && "${r2}" == "${SUCCEED}" ]]; then + printf "\n${C_GREEN}Succeed.${C_RESET}\n" + exit ${SUCCEED} +else + printf "\n${C_RED}Failed.${C_RESET}\n" + exit ${FAILED} +fi + diff --git a/codes/linux/libtest/nodejs-test.sh b/codes/linux/libtest/nodejs-test.sh new file mode 100644 index 00000000..696371ff --- /dev/null +++ b/codes/linux/libtest/nodejs-test.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# 装载其它库 +source ../lib/nodejs.sh + +# ------------------------------------------------------------------------------ +ROOT=$(pwd) + +path=${ROOT} +if [[ $1 ]]; then + path=$1 +fi + +version=10.15.0 +if [[ $2 ]]; then + version=$2 +fi + +buildNodejsProject ${path} ${version} diff --git a/codes/shell/git/check-git.sh b/codes/shell/git/check-git.sh deleted file mode 100644 index 25f2256e..00000000 --- a/codes/shell/git/check-git.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash - -# ------------------------------------------------------------------------------ -# ASCII 颜色变量 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="\033[0m" - -# 常量 -YES=0 -NO=1 -# ------------------------------------------------------------------------------ - -printf "$BLUE" -cat << EOF -# ------------------------------------------------------------------------------ -# 检查当前目录是否为 git 工程 -# ------------------------------------------------------------------------------ -EOF -printf "$RESET" - -# 检查指定的路径是不是一个 git 项目 -checkGit() { - source=$1 - if [[ -d "${source}" ]]; then - cd ${source} || return ${NO} - # (1)删除git状态零时文件 - if [[ -f "gitstatus.tmp" ]]; then - rm -rf gitstatus.tmp - fi - - # (2)判断是否存在 .git 目录 - if [[ -d "./.git" ]]; then - # (3)判断git是否可用 - git status &> gitstatus.tmp - gitStatus=false - grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true - rm -rf gitstatus.tmp - if [[ ${gitStatus} == true ]]; then - return ${YES} - else - return ${NO} - fi - fi - fi - return ${NO} -} - -##################################### MAIN ##################################### -ROOT=$(pwd) -printf "${BLUE}当前目录为:${ROOT} ${RESET}\n" - -checkGit ${ROOT} -if [[ "${YES}" == "$?" ]]; then - printf "${GREEN}${ROOT} 是 git 工程。${RESET}\n" - exit ${YES} -else - printf "${RED}${ROOT} 不是 git 工程。${RESET}\n" - exit ${NO} -fi diff --git a/codes/shell/git/update-git.sh b/codes/shell/git/update-git.sh deleted file mode 100644 index 0ea5188f..00000000 --- a/codes/shell/git/update-git.sh +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env bash - -# ------------------------------------------------------------------------------ -# console color -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" - -# 常量 -YES=0 -NO=1 -SUCCEED=0 -FAILED=1 -# ------------------------------------------------------------------------------ - -printf "$BLUE" -cat << EOF -################################################################################ -# 项目初始化脚本 -################################################################################ -EOF -printf "$RESET" - -# 检查指定的路径是不是一个 git 项目 -checkGit() { - source=$1 - if [[ -d "${source}" ]]; then - cd ${source} || return ${NO} - # (1)删除git状态零时文件 - if [[ -f "gitstatus.tmp" ]]; then - rm -rf gitstatus.tmp - fi - - # (2)判断是否存在 .git 目录 - if [[ -d "./.git" ]]; then - # (3)判断git是否可用 - git status &> gitstatus.tmp - gitStatus=false - grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true - rm -rf gitstatus.tmp - if [[ ${gitStatus} == true ]]; then - return ${YES} - else - return ${NO} - fi - fi - fi - return ${NO} -} - -cloneOrFetchGit() { - root=$1 - group=$2 - project=$3 - branch=$4 - - if [[ ! -d "${root}" ]]; then - printf "${YELLOW}>>>> ${root} is not directory.${RESET}\n" - return ${FAILED} - fi - - if [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]]; then - printf "${YELLOW}>>>> group, project, branch must not be empty.${RESET}\n" - return ${FAILED} - fi - - source=${root}/${group}/${project} - printf "${CYAN}>>>> project directory is ${source}.${RESET}\n" - printf "${CYAN}>>>> git url is ${REPOSITORY}:${group}/${project}.git.${RESET}\n" - mkdir -p ${root}/${group} - - checkGit ${source} - if [[ "${YES}" == "$?" ]]; then - # 如果 ${source} 是 git 项目,执行 pull 操作 - cd ${source} || return ${FAILED} - - git checkout -f ${branch} - if [[ "${SUCCEED}" != "$?" ]]; then - printf "${RED}>>>> git checkout ${branch} failed.${RESET}\n" - return ${FAILED} - fi - printf "${GREEN}>>>> git checkout ${branch} succeed.${RESET}\n" - - git reset --hard - if [[ "${SUCCEED}" != "$?" ]]; then - printf "${RED}>>>> git reset --hard failed.${RESET}\n" - return ${FAILED} - fi - printf "${GREEN}>>>> git reset --hard succeed.${RESET}\n" - - git pull - if [[ "${SUCCEED}" != "$?" ]]; then - printf "${RED}>>>> git pull failed.${RESET}\n" - return ${FAILED} - fi - printf "${GREEN}>>>> git pull succeed.${RESET}\n" - else - # 如果 ${source} 不是 git 项目,执行 clone 操作 - - git clone "${REPOSITORY}:${group}/${project}.git" ${source} - if [[ "${SUCCEED}" != "$?" ]]; then - printf "${RED}>>>> git clone ${project} failed.${RESET}\n" - return ${FAILED} - fi - printf "${GREEN}>>>> git clone ${project} succeed.${RESET}\n" - - cd ${source} || return ${FAILED} - - git checkout -f ${branch} - if [[ "${SUCCEED}" != "$?" ]]; then - printf "${RED}>>>> git checkout ${branch} failed.${RESET}\n" - return ${FAILED} - fi - printf "${GREEN}>>>> git checkout ${branch} succeed.${RESET}\n" - fi - - return ${SUCCEED} -} - -doCloneOrFetchGit() { - cloneOrFetchGit $1 $2 $3 $4 - if [[ "$?" == "${SUCCEED}" ]]; then - printf "\n${GREEN}>>>> Update git project [$2/$3] succeed.${RESET}\n" - return ${SUCCEED} - else - printf "\n${RED}>>>> Update git project [$2/$3] failed.${RESET}\n" - return ${FAILED} - fi -} - -##################################### MAIN ##################################### -ROOT=$(pwd) - -# 这里需要根据实际情况替换 Git 仓库、项目组、项目、代码分支 -REPOSITORY="git@gitee.com" - -printf "${CYAN}Current path is ${ROOT}.${RESET}\n" - -doCloneOrFetchGit ${ROOT} turnon linux-tutorial master -r1=$? -doCloneOrFetchGit ${ROOT} turnon nginx-tutorial master -r2=$? - -if [[ "${r1}" == "${SUCCEED}" && "${r2}" == "${SUCCEED}" ]]; then - printf "\n${GREEN}Succeed.${RESET}\n" - exit ${SUCCEED} -else - printf "\n${RED}Failed.${RESET}\n" - exit ${FAILED} -fi - diff --git a/codes/shell/lib/env.sh b/codes/shell/lib/env.sh new file mode 100644 index 00000000..4c41818e --- /dev/null +++ b/codes/shell/lib/env.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ 颜色状态 + +# Regular Color +C_BLACK="\033[0;30m" +C_RED="\033[0;31m" +C_GREEN="\033[0;32m" +C_YELLOW="\033[0;33m" +C_BLUE="\033[0;34m" +C_MAGENTA="\033[0;35m" +C_CYAN="\033[0;36m" +C_WHITE="\033[0;37m" + +# Bold Color +C_B_BLACK="\033[1;30m" +C_B_RED="\033[1;31m" +C_B_GREEN="\033[1;32m" +C_B_YELLOW="\033[1;33m" +C_B_BLUE="\033[1;34m" +C_B_MAGENTA="\033[1;35m" +C_B_CYAN="\033[1;36m" +C_B_WHITE="\033[1;37m" + +# Underline Color +C_U_BLACK="\033[4;30m" +C_U_RED="\033[4;31m" +C_U_GREEN="\033[4;32m" +C_U_YELLOW="\033[4;33m" +C_U_BLUE="\033[4;34m" +C_U_MAGENTA="\033[4;35m" +C_U_CYAN="\033[4;36m" +C_U_WHITE="\033[4;37m" + +# Background Color +C_BG_BLACK="\033[40m" +C_BG_RED="\033[41m" +C_BG_GREEN="\033[42m" +C_BG_YELLOW="\033[43m" +C_BG_BLUE="\033[44m" +C_BG_MAGENTA="\033[45m" +C_BG_CYAN="\033[46m" +C_BG_WHITE="\033[47m" + +# Reset Color +C_RESET="$(tput sgr0)" + +# ------------------------------------------------------------------------------ 常用状态值 + +YES=0 +NO=1 +SUCCEED=0 +FAILED=1 diff --git "a/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/log.txt" "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/log.txt" new file mode 100644 index 00000000..353a7bc0 --- /dev/null +++ "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/log.txt" @@ -0,0 +1,66 @@ +Add git message title here + +# See links to relevant web pages, issue trackers, etc. +See: https://github.com/ +See: [Git Page](https://github.com/) + +# List all co-authors, so people can contact with them +Co-authored-by: Name + +# Why is this change happening, e.g. goals, use cases, stories, etc.? +Why: + +# How is this change happening, e.g. implementations, algorithms, etc.? +How: + +# Tags suitable for searching, such as hashtags, keywords, etc. +Tags: + +# ## Help ## +# +# Subject line imperative uppercase verbs: +# +# Add = Create a capability e.g. feature, test, dependency. +# Drop = Delete a capability e.g. feature, test, dependency. +# Fix = Fix an issue e.g. bug, typo, accident, misstatement. +# Bump = Increase the version of something e.g. a dependency. +# Make = Change the build process, or tools, or infrastructure. +# Start = Begin doing something; e.g. enable a toggle, feature flag, etc. +# Stop = End doing something; e.g. disable a toggle, feature flag, etc. +# Refactor = A change that MUST be just refactoring. +# Reformat = A change that MUST be just format, e.g. indent line, trim space, etc. +# Rephrase = A change that MUST be just textual, e.g. edit a comment, doc, etc. +# Optimize = A change that MUST be just about performance, e.g. speed up code. +# Document = A change that MUST be only in the documentation, e.g. help files. +# +# For the subject line: +# * Use 50 characters maximum. +# * Do not use a sentence-ending period. +# +# For the body text: +# * Use as many lines as you like. +# * Use 72 characters maximum per line for typical word wrap text. +# +# +# ## About ## +# +# This is our git commit messages. +# You can edit this template if you want +# +# ## Usage ## +# +# Put the template file here: +# +# ~/.git_commit_template.txt +# +# Configure git to use the template file by running: +# +# git config --global commit.template ~/.git_commit_template.txt +# +# Add the template file to the ~/.gitconfig file: +# +# [commit] +# template = ~/.git_commit_template.txt +# +# you could put it in other location if you prefer +# diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" similarity index 51% rename from "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" rename to "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" index 82533a9b..cf294d73 100644 --- "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\346\234\254\345\234\260\344\270\264\346\227\266\346\226\207\344\273\266.sh" +++ "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" @@ -1,13 +1,10 @@ #!/usr/bin/env bash -# creating and using a temp file +tempFile=`mktemp test.XXXXXX` -tempfile=`mktemp test.XXXXXX` - -exec 3> $tempfile - -echo "This script writes to temp file $tempfile" +exec 3> ${tempFile} +echo "This script writes to temp file ${tempFile}" echo "This is the first line" >&3 echo "This is the second line" >&3 echo "This is the last line" >&3 @@ -15,8 +12,7 @@ echo "This is the last line" >&3 exec 3>&- echo "Done creating temp file. The contents are:" +cat ${tempFile} -cat $tempfile - -rm -f $tempfile 2> /dev/null +rm -f ${tempFile} 2> /dev/null diff --git "a/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" new file mode 100644 index 00000000..de2454e8 --- /dev/null +++ "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +tempDir=`mktemp -d dir.XXXXXX` +cd ${tempDir} || exit 1 + +tempFile1=`mktemp temp.XXXXXX` +tempFile2=`mktemp temp.XXXXXX` +exec 7> ${tempFile1} +exec 8> ${tempFile2} + +echo "Sending data to directory $tempDir" +echo "This is a test line of data for $tempFile1" >&7 +echo "This is a test line of data for $tempFile2" >&8 + diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\347\233\256\345\275\225\345\237\272\346\234\254\346\223\215\344\275\234.sh" similarity index 50% rename from "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" rename to "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\347\233\256\345\275\225\345\237\272\346\234\254\346\223\215\344\275\234.sh" index 81689be6..cd020681 100644 --- "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\347\233\256\345\275\225\346\223\215\344\275\234.sh" +++ "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\347\233\256\345\275\225\345\237\272\346\234\254\346\223\215\344\275\234.sh" @@ -1,23 +1,27 @@ #!/usr/bin/env bash -################################################################################### +# ------------------------------------------------------------------------------ # 目录操作示例 # @author: Zhang Peng -################################################################################### +# ------------------------------------------------------------------------------ # 创建目录(整个文件路径中的目录如果不存在,都会一一创建,如果目录已存在,则什么也不做) mkdir -p /home/linux-tutorial/temp -# 进入目录 -cd /home/linux-tutorial/temp +# 进入目录,如果失败,则退出脚本 +cd /home/linux-tutorial/temp || exit 1 -# 查看目录路径 -current_dir=$(pwd) -echo "当前目录是:${current_dir}" +# 获取当前目录路径 +CURRENT_DIR=$(pwd) +echo "当前目录是:${CURRENT_DIR}" -# 查看目录上一级路径 -parent_dir=$(dirname $(pwd)) -echo "父目录是:${parent_dir}" +# 获取上一级目录路径 +PARENT_DIR=$(dirname $(pwd)) +echo "父目录是:${PARENT_DIR}" + +# 获取当前执行脚本名 +DIR_NAME=$(basename $0) +echo "当前执行脚本名是:${DIR_NAME}" # 复制目录(复制 temp 目录所有内容,并命名新文件夹叫 temp2) cp -rf /home/linux-tutorial/temp /home/linux-tutorial/temp2 diff --git "a/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\350\257\273\346\226\207\344\273\266.sh" "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\350\257\273\346\226\207\344\273\266.sh" new file mode 100644 index 00000000..d9f14cd8 --- /dev/null +++ "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\350\257\273\346\226\207\344\273\266.sh" @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +FILE=log.txt + +count=1 +cat ${FILE} | while read line +do + echo "$count: $line" + count=$[ $count + 1 ] +done +echo "Finished reading." + diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/lib.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/lib.sh" new file mode 100644 index 00000000..ea297bbb --- /dev/null +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/lib.sh" @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +add() { + echo $[ $1 + $2 ] +} + +sub() { + echo $[ $1 - $2 ] +} + +mul() { + echo $[ $1 * $2 ] +} + +div() { + if [[ $2 -ne 0 ]]; then + echo $[ $1 / $2 ] + else + echo -1 + fi +} diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" index d2b9e918..c479bd8b 100644 --- "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" +++ "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\344\275\277\347\224\250\345\272\223\345\207\275\346\225\260.sh" @@ -1,8 +1,9 @@ #!/usr/bin/env bash -#using a library file the wrong way +# 装载其它脚本 +source lib.sh -../脚本库.sh - -result=`addem 10 15` -echo "The result is $result" +echo "20 + 10 = $(add 20 10)" +echo "20 - 10 = $(sub 20 10)" +echo "20 * 10 = $(mul 20 10)" +echo "20 / 10 = $(div 20 10)" diff --git "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" "b/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" deleted file mode 100644 index 06cc93cf..00000000 --- "a/codes/shell/\350\204\232\346\234\254\345\207\275\346\225\260/\350\204\232\346\234\254\345\272\223.sh" +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -# myscript functions - -function addem { - echo $[ $1 + $2 ] -} - -function multem { - echo $[ $1 * $2 ] -} - -function divem - -{ -if [ $2 -ne 0]; then -echo $[ $1 / $2 ] -else -echo -1 -fi -} diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" deleted file mode 100644 index 2a63ea07..00000000 --- "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\344\273\216\346\226\207\344\273\266\344\270\255\350\257\273\345\217\226\346\225\260\346\215\256.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# reading data from a file - -count=1 -cat test | while read line -do - echo "Line $count: $line" - count=$[ $count + 1 ] -done -echo "Finished processing the file" - diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" deleted file mode 100644 index b841042e..00000000 --- "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\210\233\345\273\272\344\270\264\346\227\266\347\233\256\345\275\225.sh" +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -# using a temporary directory - -tempdir=`mktemp -d dir.XXXXXX` -cd $tempdir - -tempfile1=`mktemp temp.XXXXXX` -tempfile2=`mktemp temp.XXXXXX` -exec 7> $tempfile1 -exec 8> $tempfile2 - -echo "Sending data to directory $tempdir" -echo "This is a test line of data for $tempfile1" >&7 -echo "This is a test line of data for $tempfile2" >&8 - diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" deleted file mode 100644 index 66d02dd3..00000000 --- "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\234\250tmp\347\233\256\345\275\225\345\210\233\345\273\272\344\270\264\346\227\266\346\226\207\344\273\266.sh" +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# creating a temp file in /tmp - -tempfile=`mktemp -t tmp.XXXXXX` - -echo "This is a test file" > $tempfile -echo "This is the second line of the test" >> $tempfile - -echo ” The temp is locate at : $tempfile “ -cat $tempfile -rm -f $tempfile diff --git a/docs/README.md b/docs/README.md index a0da52bf..10b2c7b9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,7 @@ # linux-tutorial +> 📚 **linux-tutorial** 是一个 Linux 教程。 +> > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) diff --git a/docs/coverpage.md b/docs/coverpage.md index 3f90a16e..3ef7d970 100644 --- a/docs/coverpage.md +++ b/docs/coverpage.md @@ -1,7 +1,7 @@ -![logo](http://dunwu.test.upcdn.net/common/logo/zp.png) +
# linux-tutorial -> linux-tutorial 是一个 Linux 教程。 +> 📚 **linux-tutorial** 是一个 Linux 教程。 [开始阅读](README.md) diff --git a/docs/docker/docker-dockerfile.md b/docs/docker/docker-dockerfile.md index 60afc544..28bc6dca 100644 --- a/docs/docker/docker-dockerfile.md +++ b/docs/docker/docker-dockerfile.md @@ -658,6 +658,10 @@ FROM my-node 是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。 + + +有任何的问题或建议,欢迎给我留言 :laughing: + ## 参考资料 - [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/) diff --git a/docs/index.html b/docs/index.html index 94e2303a..ee804fcf 100644 --- a/docs/index.html +++ b/docs/index.html @@ -29,6 +29,7 @@ } /*超链接悬浮样式*/ + .cover-main a:hover, .content a:hover, .sidebar a:hover, .sidebar ul li a:hover, @@ -37,6 +38,10 @@ text-decoration: underline !important; } + .sidebar-nav ul { + padding-left: 15px; + } + /*侧边栏样式*/ .sidebar .sidebar-nav h1 { background-color: #f8f8f8; @@ -44,17 +49,19 @@ padding-left: 10px; text-align: center; font-size: 12px; - font-family: 'Microsoft YaHei', 'Trebuchet MS', Arial, 'Lucida Grande', Verdana, Lucida, Helvetica, sans-serif; text-transform: uppercase; } /*文章标题加动态刷新颜色效果*/ + section.cover h1 span, .markdown-section h1 span { + font-weight: 600; + font-family: zillaslab, Palatino, 'Palatino Linotype', 'Microsoft YaHei', serif; background-image: -webkit-linear-gradient(left, #9fa5d5, #c4cdd2 50%, #e8f5ca); -webkit-text-fill-color: transparent; -webkit-background-clip: text; -webkit-background-size: 200% 100%; - -webkit-animation: hue 3s infinite linear; + -webkit-animation: hue 5s infinite linear; text-shadow: 2px 2px 2px transparent; } @@ -89,7 +96,8 @@ autoHeader: false, maxLevel: 4, subMaxLevel: 2, - formatUpdated: '{MM}/{DD} {HH}:{mm}', + mergeNavbar: true, + formatUpdated: '{YYYY}/{MM}/{DD} {HH}:{mm}', search: { maxAge: 86400000, paths: ['/'], diff --git a/docs/sidebar.md b/docs/sidebar.md index 9d62b486..c8cb4dc6 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -1,5 +1,6 @@ # linux-tutorial +- [简介](README.md) - [**Linux 命令**](linux/cli/README.md) - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) From 645534bef91d65d49123e28d7e0853edd6834c35 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Sun, 2 Feb 2020 17:56:28 +0800 Subject: [PATCH 30/64] update --- README.md | 2 +- assets/docker.xmind | Bin 0 -> 207898 bytes assets/linux.xmind | Bin 0 -> 808111 bytes codes/docker/docker-compose-demo/Dockerfile | 5 + codes/docker/docker-compose-demo/app.py | 13 ++ .../docker-compose-demo/docker-compose.yml | 10 ++ codes/docker/docker-compose-demo/run.sh | 4 + codes/linux/lib/env.sh | 50 +++++++ codes/linux/lib/git.sh | 26 ++-- codes/linux/lib/java.sh | 135 ++++++++++++++++++ codes/linux/lib/maven.sh | 66 +++++++++ codes/linux/lib/net.sh | 31 ++++ codes/linux/libtest/git-update.sh | 5 +- codes/linux/soft/README.md | 4 +- codes/linux/soft/docker-install.sh | 21 ++- docs/README.md | 12 +- docs/book.json | 4 +- docs/docker/README.md | 3 +- docs/docker/docker-cheat-sheet.md | 106 ++++++++------ docs/docker/docker-compose.md | 110 ++++++++++++++ docs/docker/docker-dockerfile.md | 14 +- docs/docker/docker-quickstart.md | 3 +- 22 files changed, 543 insertions(+), 81 deletions(-) create mode 100644 assets/docker.xmind create mode 100644 assets/linux.xmind create mode 100644 codes/docker/docker-compose-demo/Dockerfile create mode 100644 codes/docker/docker-compose-demo/app.py create mode 100644 codes/docker/docker-compose-demo/docker-compose.yml create mode 100644 codes/docker/docker-compose-demo/run.sh create mode 100644 codes/linux/lib/java.sh create mode 100644 codes/linux/lib/maven.sh create mode 100644 codes/linux/lib/net.sh create mode 100644 docs/docker/docker-compose.md diff --git a/README.md b/README.md index 0d7124f3..e3e825ec 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- + logo

diff --git a/assets/docker.xmind b/assets/docker.xmind new file mode 100644 index 0000000000000000000000000000000000000000..293f1ff67baa351bcba0c1dc3fd8f43d18853d9c GIT binary patch literal 207898 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ÂJUi%ZIK0=tnod{kK8; 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%0DKK8iBF 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 IA42#40rGZ4f&c&j literal 0 HcmV?d00001 diff --git a/assets/linux.xmind b/assets/linux.xmind new file mode 100644 index 0000000000000000000000000000000000000000..291a618dde3ef17ad8fc60ae1b0b5e31c7dc2382 GIT binary patch literal 808111 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 (l1h7rR+L6{}CnV2j+J1?h{@HVrOX_3dpG|?b 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

jREm}RIq+0C}6z?Itp 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

FyLFL;%k^z7Y= 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

oC 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 literal 0 HcmV?d00001 diff --git a/codes/docker/docker-compose-demo/Dockerfile b/codes/docker/docker-compose-demo/Dockerfile new file mode 100644 index 00000000..7c4b8c7a --- /dev/null +++ b/codes/docker/docker-compose-demo/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.6-alpine +ADD . /code +WORKDIR /code +RUN pip install redis flask +CMD ["python", "app.py"] diff --git a/codes/docker/docker-compose-demo/app.py b/codes/docker/docker-compose-demo/app.py new file mode 100644 index 00000000..f0dfc208 --- /dev/null +++ b/codes/docker/docker-compose-demo/app.py @@ -0,0 +1,13 @@ +from flask import Flask +from redis import Redis + +app = Flask(__name__) +redis = Redis(host='redis', port=6379) + +@app.route('/') +def hello(): + count = redis.incr('hits') + return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/codes/docker/docker-compose-demo/docker-compose.yml b/codes/docker/docker-compose-demo/docker-compose.yml new file mode 100644 index 00000000..80d8ced4 --- /dev/null +++ b/codes/docker/docker-compose-demo/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + + web: + build: . + ports: + - "5000:5000" + + redis: + image: "redis:alpine" diff --git a/codes/docker/docker-compose-demo/run.sh b/codes/docker/docker-compose-demo/run.sh new file mode 100644 index 00000000..0913e24e --- /dev/null +++ b/codes/docker/docker-compose-demo/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +set -x +docker-compose up diff --git a/codes/linux/lib/env.sh b/codes/linux/lib/env.sh index 4c41818e..9176eec4 100644 --- a/codes/linux/lib/env.sh +++ b/codes/linux/lib/env.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +# ------------------------------------------------------------------------------ +# 常用变量库 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + # ------------------------------------------------------------------------------ 颜色状态 # Regular Color @@ -51,3 +56,48 @@ YES=0 NO=1 SUCCEED=0 FAILED=1 + +# 显示打印日志的时间 +DATE=`date "+%Y-%m-%d %H:%M:%S"` +# 那个用户在操作 +USER=$(whoami) + +# ------------------------------------------------------------------------------ log + +logInfo() { + #($0脚本本身,$@将参数作为整体传输调用) + echo "[${DATE}] [${USER}] [INFO] [$0] [$@] execute succeed." >> /var/log/shell.log +} + +logWarn() { + #($0脚本本身,$@将参数作为整体传输调用) + echo "[${DATE}] [${USER}] [WARN] [$0] [$@] execute succeed." >> /var/log/shell.log +} + +logError() { + #($0脚本本身,$@将参数作为整体传输调用) + echo "[${DATE}] [${USER}] [ERROR] [$0] [$@] execute failed." >> /var/log/shell.log +} + +printInfo() { + echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" +} + +printWarn() { + echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" +} + +printError() { + echo -e "${C_B_RED}[ERROR] $@${C_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${SUCCEED} ]]; then + logInfo "$@ succeed" + echo -e "${C_B_GREEN}[INFO] [$0] [$@] execute succeed.${C_RESET}" + else + logError "$@ failed" + echo -e "${C_B_RED}[ERROR] [$0] [$@] execute failed.${C_RESET}" + fi +} diff --git a/codes/linux/lib/git.sh b/codes/linux/lib/git.sh index 6e33ca7a..1a1b422f 100644 --- a/codes/linux/lib/git.sh +++ b/codes/linux/lib/git.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +# ------------------------------------------------------------------------------ +# Git 基本操作脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + # 装载其它库 ROOT=`dirname ${BASH_SOURCE[0]}` source ${ROOT}/env.sh @@ -33,7 +38,7 @@ checkGit() { return ${NO} fi - printf "${C_B_B_YELLOW}${source} is invalid dir.${C_RESET}\n" + printf "${C_B_YELLOW}${source} is invalid dir.${C_RESET}\n" return ${NO} } @@ -49,18 +54,18 @@ cloneOrPullGit() { local root=$5 if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then - printf "${C_B_YELLOW}>>>> Please input root, group, project, branch.${C_RESET}\n" + printf "${C_B_YELLOW}Please input root, group, project, branch.${C_RESET}\n" return ${FAILED} fi if [[ ! -d "${root}" ]]; then - printf "${C_B_YELLOW}>>>> ${root} is not directory.${C_RESET}\n" + printf "${C_B_YELLOW}${root} is not directory.${C_RESET}\n" return ${FAILED} fi local source=${root}/${group}/${project} - printf "${C_B_CYAN}>>>> project directory is ${source}.${C_RESET}\n" - printf "${C_B_CYAN}>>>> git url is ${repository}:${group}/${project}.git.${C_RESET}\n" + printf "${C_B_MAGENTA}project directory is ${source}.${C_RESET}\n" + printf "${C_B_MAGENTA}git url is ${repository}:${group}/${project}.git.${C_RESET}\n" mkdir -p ${root}/${group} checkGit ${source} @@ -73,21 +78,21 @@ cloneOrPullGit() { printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git checkout ${branch} succeed.${C_RESET}\n" + printf "${C_B_GREEN}git checkout ${branch} succeed.${C_RESET}\n" git reset --hard if [[ "${SUCCEED}" != "$?" ]]; then printf "${C_B_RED}<<<< git reset --hard failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git reset --hard succeed.${C_RESET}\n" + printf "${C_B_GREEN}git reset --hard succeed.${C_RESET}\n" git pull if [[ "${SUCCEED}" != "$?" ]]; then printf "${C_B_RED}<<<< git pull failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git pull succeed.${C_RESET}\n" + printf "${C_B_GREEN}git pull succeed.${C_RESET}\n" else # 如果 ${source} 不是 git 项目,执行 clone 操作 @@ -96,7 +101,7 @@ cloneOrPullGit() { printf "${C_B_RED}<<<< git clone ${project} failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git clone ${project} succeed.${C_RESET}\n" + printf "${C_B_GREEN}git clone ${project} succeed.${C_RESET}\n" cd ${source} || return ${FAILED} @@ -105,8 +110,9 @@ cloneOrPullGit() { printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git checkout ${branch} succeed.${C_RESET}\n" + printf "${C_B_GREEN}git checkout ${branch} succeed.${C_RESET}\n" fi + printf "${C_B_GREEN}Clone or pull git project [$2/$3:$4] succeed.${C_RESET}\n" return ${SUCCEED} } diff --git a/codes/linux/lib/java.sh b/codes/linux/lib/java.sh new file mode 100644 index 00000000..0d66f228 --- /dev/null +++ b/codes/linux/lib/java.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# Java 应用运维脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ env preparation +# load libs +CURRENT_PATH=`dirname ${BASH_SOURCE[0]}` +source ${CURRENT_PATH}/env.sh + +# ------------------------------------------------------------------------------ functions + +stopServer() { + if [[ ! $1 ]]; then + printError "please input java app name" + return ${FAILED} + fi + + local javaAppName=$1 + local pid=`jps | grep ${javaAppName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + kill -9 ${pid} + if [[ $? -eq ${SUCCEED} ]]; then + printInfo "stop ${javaAppName} succeed" + return ${SUCCEED} + else + printError "stop ${javaAppName} failed" + return ${FAILED} + fi + else + printWarn "${javaAppName} is not running" + return ${SUCCEED} + fi +} + +startServer() { + if [[ ! $1 ]]; then + printError "please input java app name" + return ${FAILED} + fi + + # >>>> 1. check java app is started or not + # >>>> 1.1. exit script if the app is started + local javaAppName=$1 + local pid=`jps | grep ${javaAppName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + printInfo "${javaAppName} is started, PID: ${pid}" + return ${SUCCEED} + fi + + # >>>> 2. package options + # GC OPTS + local javaOptions="-server -Xms1g -Xmx2g -Xss256k" + javaOptions="${javaOptions} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=4" + + # GC LOG OPTS + javaOptions="${javaOptions} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" + javaOptions="${javaOptions} -verbose:gc -Xloggc:${LOG_PATH}/${javaAppName}.gc.log" + javaOptions="${javaOptions} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" + + # Heap Dump OPTS + javaOptions="${javaOptions} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" + javaOptions="${javaOptions} -XX:HeapDumpPath=${LOG_PATH}/${javaAppName}.heapdump.hprof" + + # APP OPTS + javaOptions="${javaOptions} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8" + if [[ ${PROFILE} ]]; then + javaOptions="${javaOptions} -Dspring.profiles.active=${PROFILE}" + fi + + # DEBUG OPTS + if [[ "${DEBUG}" == "on" ]]; then + # JMX OPTS + local ip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/') + local jmxPort=$(expr 10000 + ${PORT}) + javaOptions="${javaOptions} -Dcom.sun.management.jmxremote=true" + javaOptions="${javaOptions} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" + javaOptions="${javaOptions} -Djava.rmi.server.hostname=${ip} -Dcom.sun.management.jmxremote.port=${jmxPort}" + + # Remote Debug + local debugPort=$(expr 20000 + ${PORT}) + javaOptions="${javaOptions} -Xdebug -Xnoagent -Djava.compiler=NONE" + javaOptions="${javaOptions} -Xrunjdwp:transport=dt_socket,address=${debugPort},server=y,suspend=n" + fi + + # CLASSPATH + local appOptions="-classpath ${ROOT_PATH}/lib/* -Dlogging.config=file:${ROOT_PATH}/config/logback.dev.xml" + appOptions="${appOptions} --spring.config.location=classpath:/,classpath:/config/,file:${ROOT_PATH},file:${ROOT_PATH}/config/" + if [[ ${PORT} ]]; then + appOptions="${appOptions} --server.port=${PORT}" + fi + + # >>>> 3. create log dir and console log file + mkdir -p ${LOG_PATH} + if [[ ! -f ${CONSOLE_LOG} ]]; then + touch ${CONSOLE_LOG} + fi + + # >>>> 4. start java app + printInfo "starting ${javaAppName}, execute cli: " + printInfo "nohup java ${javaOptions} -jar ${ROOT_PATH}/${javaAppName}.jar ${appOptions} >> ${CONSOLE_LOG} 2>&1 &" + nohup java ${javaOptions} -jar ${ROOT_PATH}/${javaAppName}.jar ${appOptions} >> ${CONSOLE_LOG} 2>&1 & + + # >>>> 5. check java app is started or not + local pid=`jps | grep ${javaAppName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + printInfo "start ${javaAppName} succeed, PID: ${pid}" + return ${SUCCEED} + else + printError "start ${javaAppName} failed" + return ${FAILED} + fi +} + +# ------------------------------------------------------------------------------ main +export LANG="zh_CN.UTF-8" +ROOT_PATH=$(cd ${CURRENT_PATH}/..; pwd) + +APP_NAME=java-app +LOG_PATH=/var/log/myapp +CONSOLE_LOG=${LOG_PATH}/${APP_NAME}.console.log + +PORT=8888 +PROFILE=dev +DEBUG=off + +startServer ${APP_NAME} +#stopServer ${APP_NAME} +if [[ $? -eq ${SUCCEED} ]]; then + exit ${SUCCEED} +else + exit ${FAILED} +fi diff --git a/codes/linux/lib/maven.sh b/codes/linux/lib/maven.sh new file mode 100644 index 00000000..64fc15fc --- /dev/null +++ b/codes/linux/lib/maven.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# maven 项目操作脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# 装载其它库 +ROOT=`dirname ${BASH_SOURCE[0]}` +source ${ROOT}/env.sh + +mavenBuild() { + local source=$1 + mavenCheck $1 + if [[ "${SUCCEED}" != "$?" ]]; then + return ${FAILED} + fi + + if [[ -d "${source}" ]]; then + cd ${source} + if [[ -f "${source}/settings.xml" ]]; then + callAndLog "mvn clean install -B -U -s ${source}/settings.xml -Dmaven.test.skip=true" + else + callAndLog "mvn clean install -DskipTests=true -B -U" + fi + cd - + return ${SUCCEED} + else + printf "${C_B_RED}please input valid maven project path.${C_RESET}\n" + return ${FAILED} + fi +} + +mavenCheck() { + local source=$1 + if [[ -d "${source}" ]]; then + cd ${source} + if [[ -f "${source}/pom.xml" ]]; then + return ${YES} + else + printf "${C_B_RED}pom.xml is not exists.${C_RESET}\n" + return ${NO} + fi + cd - + return ${YES} + else + printf "${C_B_RED}please input valid maven project path.${C_RESET}\n" + return ${NO} + fi +} + +##################################### MAIN ##################################### +printf "\n${C_B_GREEN}>>>> maven build begin.${C_RESET}\n\n" + +printf "${C_B_MAGENTA}Current path is ${ROOT}.${C_RESET}\n" + +mavenBuild ${ROOT}/.. +r1=$? + +if [[ "${r1}" == "${SUCCEED}" ]]; then + printf "\n${C_B_GREEN}<<<< maven build succeed.${C_RESET}\n\n" + exit ${SUCCEED} +else + printf "\n${C_B_RED}<<<< maven build failed.${C_RESET}\n\n" + exit ${FAILED} +fi diff --git a/codes/linux/lib/net.sh b/codes/linux/lib/net.sh new file mode 100644 index 00000000..ce8dbd3f --- /dev/null +++ b/codes/linux/lib/net.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# ---------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# ---------------------------------------------------------------------------------- + +printf "${PURPLE}" +cat << EOF +# ---------------------------------------------------------------------------------- +# XXX 脚本 +# @author: Zhang Peng +# ---------------------------------------------------------------------------------- +EOF +printf "${RESET}" + +printf "${BLUE}>>>>>>>> begin.\n${RESET}" + +printf "${GREEN}[OK]\n${RESET}" +printf "${RED}[ERROR]\n${RESET}" + +printf "${BLUE}<<<<<<<< end.\n${RESET}" + +IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/'` diff --git a/codes/linux/libtest/git-update.sh b/codes/linux/libtest/git-update.sh index 0c457cfe..2baea11d 100644 --- a/codes/linux/libtest/git-update.sh +++ b/codes/linux/libtest/git-update.sh @@ -28,10 +28,9 @@ doCloneOrPullGit ${REPOSITORY} turnon nginx-tutorial master ${ROOT} r2=$? if [[ "${r1}" == "${SUCCEED}" && "${r2}" == "${SUCCEED}" ]]; then - printf "\n${C_GREEN}Succeed.${C_RESET}\n" + printf "\n${C_B_GREEN}<<<< Init workspace Succeed.${C_RESET}\n\n" exit ${SUCCEED} else - printf "\n${C_RED}Failed.${C_RESET}\n" + printf "\n${C_B_RED}<<<< Init workspace Failed.${C_RESET}\n\n" exit ${FAILED} fi - diff --git a/codes/linux/soft/README.md b/codes/linux/soft/README.md index fcd4f709..c78aa35a 100644 --- a/codes/linux/soft/README.md +++ b/codes/linux/soft/README.md @@ -13,7 +13,7 @@ - [RocketMQ 安装](#rocketmq-安装) - [Nacos 安装](#nacos-安装) - [ZooKeeper 安装](#zookeeper-安装) -- [Nginx 安装](#nginx-安装) +- [Nginx 运维](#nginx-安装) - [Fastdfs 安装](#fastdfs-安装) - [Docker 安装](#docker-安装) @@ -190,7 +190,7 @@ curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zoo wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zookeeper-install.sh | bash ``` -## Nginx 安装 +## Nginx 运维 **安装说明** diff --git a/codes/linux/soft/docker-install.sh b/codes/linux/soft/docker-install.sh index 549406ae..fd142bc9 100644 --- a/codes/linux/soft/docker-install.sh +++ b/codes/linux/soft/docker-install.sh @@ -26,23 +26,22 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install docker begin.${RESET}\n" # uninstall old version docker -sudo yum remove docker \ - docker-client \ - docker-client-latest \ - docker-common \ - docker-latest \ - docker-latest-logrotate \ - docker-logrotate \ - docker-selinux \ - docker-engine-selinux \ - docker-engine +sudo yum remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-engine + # install required libs sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # add docker yum repo sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum makecache fast # install docker -sudo yum -y install docker-ce +sudo yum install docker-ce docker-ce-cli containerd.io sudo systemctl start docker docker version printf "${GREEN}<<<<<<<< install docker end.${RESET}\n" diff --git a/docs/README.md b/docs/README.md index 10b2c7b9..08cfab50 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,14 @@ -# linux-tutorial +

+ + logo + +

+ +

+ license +

+ +

linux-tutorial

> 📚 **linux-tutorial** 是一个 Linux 教程。 > diff --git a/docs/book.json b/docs/book.json index 17705e65..59e70a6c 100644 --- a/docs/book.json +++ b/docs/book.json @@ -58,9 +58,7 @@ }, "sharing": { "weibo": true, - "all": [ - "weibo" - ] + "all": ["weibo"] }, "tbfed-pagefooter": { "copyright": "Copyright © Zhang Peng 2017", diff --git a/docs/docker/README.md b/docs/docker/README.md index 7715d4eb..bc54df6b 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -8,8 +8,9 @@ - **官方** - [Docker 官网](http://www.docker.com) - - [Docker Github](https://github.com/moby/moby) - [Docker 官方文档](https://docs.docker.com/) + - [Docker Github](https://github.com/moby/moby) + - [Docker Compose Github](https://github.com/docker/compose) - [Docker Hub](https://hub.docker.com/) - [Docker 开源](https://www.docker.com/community/open-source) - **资源整理** diff --git a/docs/docker/docker-cheat-sheet.md b/docs/docker/docker-cheat-sheet.md index 49cdab37..f3ad99d1 100644 --- a/docs/docker/docker-cheat-sheet.md +++ b/docs/docker/docker-cheat-sheet.md @@ -308,6 +308,48 @@ $ docker run --rm -it --net iptastic --ip 203.0.113.2 nginx $ curl 203.0.113.2 ``` +## 暴露端口(Exposing ports) + +通过宿主容器暴露输入端口相当 [繁琐但有效的](https://docs.docker.com/engine/reference/run/#expose-incoming-ports)。 + +例如使用 `-p` 将容器端口映射到宿主端口上(只使用本地主机 (localhost) 接口): + +``` +docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage +``` + +你可以使用 [EXPOSE](https://docs.docker.com/engine/reference/builder/#expose) 告知 Docker,该容器在运行时监听指定的端口: + +``` +EXPOSE +``` + +但是注意 EXPOSE 并不会直接暴露端口,你需要用参数 `-p` 。比如说你要在 localhost 上暴露容器的端口: + +``` +iptables -t nat -A DOCKER -p tcp --dport -j DNAT --to-destination : +``` + +如果你是在 Virtualbox 中运行 Docker,那么你需要配置端口转发 (forward the port)。使用 [forwarded_port](https://docs.vagrantup.com/v2/networking/forwarded_ports.html) 在 Vagrantfile 上配置暴露的端口范围,这样你就可以动态地映射了: + +``` +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + ... + + (49000..49900).each do |port| + config.vm.network :forwarded_port, :host => port, :guest => port + end + + ... +end +``` + +如果你忘记了将什么端口映射到宿主机上的话,可使用 `docker port` 查看: + +``` +docker port CONTAINER $CONTAINERPORT +``` + ## 仓管中心和仓库(Registry & Repository) 仓库 (repository) 是 _被托管(hosted)_ 的已命名镜像 (tagged images) 的集合,这组镜像用于构建容器文件系统。 @@ -411,19 +453,27 @@ $ALIAS_PORT_1337_TCP_ADDR 通常,Docker 容器(亦可理解为「服务」)之间的链接,是「服务发现」的一个子集。如果你打算在生产中大规模使用 Docker,这将是一个很大的问题。请参阅[The Docker Ecosystem: Service Discovery and Distributed Configuration Stores](https://www.digitalocean.com/community/tutorials/the-docker-ecosystem-service-discovery-and-distributed-configuration-stores) 获取更多信息。 -## 卷标(Volumes) +## 卷标(Volumes)和挂载 + +### 卷标 Docker 的卷标 (volumes) 是 [独立的文件系统](https://docs.docker.com/engine/tutorials/dockervolumes/)。它们并非必须连接到特定的容器上。 -### 生命周期 +`数据卷` 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性: -- [`docker volume create`](https://docs.docker.com/engine/reference/commandline/volume_create/) -- [`docker volume rm`](https://docs.docker.com/engine/reference/commandline/volume_rm/) +- `数据卷` 可以在容器之间共享和重用 +- 对 `数据卷` 的修改会立马生效 +- 对 `数据卷` 的更新,不会影响镜像 +- `数据卷` 默认会一直存在,即使容器被删除 -### 信息 +卷标相关命令: + +- [`docker volume create`](https://docs.docker.com/engine/reference/commandline/volume_create/) - 创建卷标 +- [`docker volume rm`](https://docs.docker.com/engine/reference/commandline/volume_rm/) - 删除卷标 -- [`docker volume ls`](https://docs.docker.com/engine/reference/commandline/volume_ls/) -- [`docker volume inspect`](https://docs.docker.com/engine/reference/commandline/volume_inspect/) +- [`docker volume ls`](https://docs.docker.com/engine/reference/commandline/volume_ls/) - 查看卷标 +- [`docker volume inspect`](https://docs.docker.com/engine/reference/commandline/volume_inspect/) - 查看数据卷的具体信息 +- [`docker volume prune`](https://docs.docker.com/engine/reference/commandline/volume_prune/) - 清理无主的数据卷 卷标在不能使用链接(只有 TCP/IP)的情况下非常有用。例如,如果你有两个 Docker 实例需要通讯并在文件系统上留下记录。 @@ -445,47 +495,11 @@ docker run -v /Users/wsargent/myapp/src:/src 记得,[文件也可以被挂载为卷标](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#将文件挂载为卷标)。 -## 暴露端口(Exposing ports) - -通过宿主容器暴露输入端口相当 [繁琐但有效的](https://docs.docker.com/engine/reference/run/#expose-incoming-ports)。 - -例如使用 `-p` 将容器端口映射到宿主端口上(只使用本地主机 (localhost) 接口): - -``` -docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage -``` - -你可以使用 [EXPOSE](https://docs.docker.com/engine/reference/builder/#expose) 告知 Docker,该容器在运行时监听指定的端口: - -``` -EXPOSE -``` - -但是注意 EXPOSE 并不会直接暴露端口,你需要用参数 `-p` 。比如说你要在 localhost 上暴露容器的端口: - -``` -iptables -t nat -A DOCKER -p tcp --dport -j DNAT --to-destination : -``` +### 挂载 -如果你是在 Virtualbox 中运行 Docker,那么你需要配置端口转发 (forward the port)。使用 [forwarded_port](https://docs.vagrantup.com/v2/networking/forwarded_ports.html) 在 Vagrantfile 上配置暴露的端口范围,这样你就可以动态地映射了: +使用 `--mount` 标记可以指定挂载一个本地主机的目录到容器中去。 -``` -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - ... - - (49000..49900).each do |port| - config.vm.network :forwarded_port, :host => port, :guest => port - end - - ... -end -``` - -如果你忘记了将什么端口映射到宿主机上的话,可使用 `docker port` 查看: - -``` -docker port CONTAINER $CONTAINERPORT -``` +在用 `docker run` 命令的时候,使用 `--mount` 标记来将 `数据卷` 挂载到容器里。在一次 `docker run` 中可以挂载多个 `数据卷`。 ## 最佳实践 diff --git a/docs/docker/docker-compose.md b/docs/docker/docker-compose.md new file mode 100644 index 00000000..3fec8712 --- /dev/null +++ b/docs/docker/docker-compose.md @@ -0,0 +1,110 @@ +# Docker Compose + +> [compose](https://github.com/docker/compose) 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 `OpenStack` 中的 `Heat` 十分类似。 + +## 一、Compose 简介 + +**`Compose` 的定位是:定义和运行多个 Docker 容器的应用**。 使用一个 `Dockerfile` 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。 + +`Compose` 恰好满足了这样的需求。它允许用户通过一个单独的 `docker-compose.yml` 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 + +`Compose` 中有两个重要的概念: + +- **服务 (`service`)**:一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。 +- **项目 (`project`)**:由一组关联的应用容器组成的一个完整业务单元,在 `docker-compose.yml` 文件中定义。 + + `Compose` 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。 + +## 二、安装卸载 + +`Compose` 支持 Linux、macOS、Windows10 三大平台。 + +Linux 安装方式: + +```bash +sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose +``` + +> :bell: 详情请参考:[Install Docker Compose](https://docs.docker.com/compose/install/) + +## 三、快速入门 + +### web 应用 + +新建文件夹,在该目录中编写 `app.py` 文件 + +```python +from flask import Flask +from redis import Redis + +app = Flask(__name__) +redis = Redis(host='redis', port=6379) + +@app.route('/') +def hello(): + count = redis.incr('hits') + return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) +``` + +### Dockerfile + +编写 `Dockerfile` 文件,内容为 + +```docker +FROM python:3.6-alpine +ADD . /code +WORKDIR /code +RUN pip install redis flask +CMD ["python", "app.py"] +``` + +### docker-compose.yml + +编写 `docker-compose.yml` 文件,这个是 Compose 使用的主模板文件。 + +```yaml +version: '3' +services: + + web: + build: . + ports: + - "5000:5000" + + redis: + image: "redis:alpine" +``` + +### 运行 compose 项目 + +```bash +$ docker-compose up +``` + +此时访问本地 `5000` 端口,每次刷新页面,计数就会加 1。 + +## 四、命令 + +> :bell: 请参考: +> +> - [Compose 官方命令说明文档](https://docs.docker.com/compose/reference/) +> - [Compose 命令说明中文文档](https://yeasy.gitbooks.io/docker_practice/content/compose/commands.html) + +## 五、模板文件 + +> `docker-compose.yml` 文件是 Docker Compose 的模板文件,其作用类似于 Dockerfile 和 Docker。 + +[docker-compose.yml 支持的默认环境变量官方文档](https://docs.docker.com/compose/env-file/) + +## 参考资料 + +- **官方** + - [Docker Compose Github](https://github.com/docker/compose) + - [Docker Compose 官方文档](https://docs.docker.com/compose/) +- **教程** + - [Docker — 从入门到实践 - Docker Compose 项目]( https://yeasy.gitbooks.io/docker_practice/content/compose/ ) + diff --git a/docs/docker/docker-dockerfile.md b/docs/docker/docker-dockerfile.md index 28bc6dca..492e092b 100644 --- a/docs/docker/docker-dockerfile.md +++ b/docs/docker/docker-dockerfile.md @@ -21,7 +21,15 @@ -## 一、Dockerfile 指令 +## 一、Dockerfile 简介 + +Docker 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。 + +Dockerfile 是一个文本文件,其内包含了一条条的 **指令(Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。 + +### 使用 Dockerfile 构建镜像 + +## 二、Dockerfile 指令详解 ### FROM(指定基础镜像) @@ -658,6 +666,8 @@ FROM my-node 是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。 +## 二、最佳实践 + 有任何的问题或建议,欢迎给我留言 :laughing: @@ -665,6 +675,6 @@ FROM my-node ## 参考资料 - [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/) -- [Dockerfile 最佳实践文档](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) +- [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) - [Docker 官方镜像 Dockerfile](https://github.com/docker-library/docs) - [Dockerfile 指令详解](https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile/) diff --git a/docs/docker/docker-quickstart.md b/docs/docker/docker-quickstart.md index ff00c0eb..6fe68340 100644 --- a/docs/docker/docker-quickstart.md +++ b/docs/docker/docker-quickstart.md @@ -117,7 +117,7 @@ $ sudo service docker start $ sudo systemctl start docker ``` -## 三、hello world 实例 +## 三、Hello World 实例 下面,我们通过最简单的 image 文件"[hello world"](https://hub.docker.com/r/library/hello-world/),感受一下 Docker。 @@ -354,3 +354,4 @@ $ sudo systemctl start docker ## 参考资料 - [Docker 入门教程](https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) +- [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) \ No newline at end of file From 42aced52c34683ba78b7976cce0e37d817ecd8ca Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 10 Feb 2020 23:11:22 +0800 Subject: [PATCH 31/64] update docs --- .gitignore | 2 +- README.md | 49 +++--- codes/linux/README.md | 6 +- codes/linux/soft/README.md | 32 ++-- codes/linux/sys/README.md | 12 +- docs/.remarkrc | 9 + docs/.textlint.terms.json | 8 + docs/.textlintrc.js | 23 +++ docs/.vuepress/config.js | 45 +++++ docs/.vuepress/enhanceApp.js | 7 + docs/.vuepress/public/favicon.ico | Bin 0 -> 16958 bytes .../public/images/dunwu-logo-100.png | Bin 0 -> 7664 bytes .../public/images/dunwu-logo-200.png | Bin 0 -> 18730 bytes .../.vuepress/public/images/dunwu-logo-50.png | Bin 0 -> 2804 bytes docs/.vuepress/public/images/dunwu-logo.png | Bin 0 -> 15561 bytes docs/README.md | 22 +-- docs/book.json | 69 -------- docs/coverpage.md | 7 - docs/index.html | 154 ------------------ docs/linux/cli/README.md | 18 +- .../linux/cli/linux-cli-dir.md | 0 .../linux/cli/linux-cli-file-compress.md | 0 .../linux/cli/linux-cli-file.md | 2 +- .../linux/cli/linux-cli-hardware.md | 0 .../linux/cli/linux-cli-help.md | 0 .../linux/cli/linux-cli-net.md | 0 .../linux/cli/linux-cli-software.md | 0 .../linux/cli/linux-cli-system.md | 0 .../linux/cli/linux-cli-user.md | 0 docs/linux/ops/README.md | 9 +- .../linux/ops/linux-base-ops.md | 0 docs/linux/soft/elastic/README.md | 2 +- docs/linux/soft/nexus-ops.md | 6 +- docs/package.json | 58 ++++--- docs/sidebar.md | 50 ------ prettier.config.js | 7 - scripts/deploy.sh | 44 +++++ 37 files changed, 251 insertions(+), 390 deletions(-) create mode 100644 docs/.remarkrc create mode 100644 docs/.textlint.terms.json create mode 100644 docs/.textlintrc.js create mode 100644 docs/.vuepress/config.js create mode 100644 docs/.vuepress/enhanceApp.js create mode 100644 docs/.vuepress/public/favicon.ico create mode 100644 docs/.vuepress/public/images/dunwu-logo-100.png create mode 100644 docs/.vuepress/public/images/dunwu-logo-200.png create mode 100644 docs/.vuepress/public/images/dunwu-logo-50.png create mode 100644 docs/.vuepress/public/images/dunwu-logo.png delete mode 100644 docs/book.json delete mode 100644 docs/coverpage.md delete mode 100644 docs/index.html rename "docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" => docs/linux/cli/linux-cli-dir.md (100%) rename "docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" => docs/linux/cli/linux-cli-file-compress.md (100%) rename "docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" => docs/linux/cli/linux-cli-file.md (98%) rename "docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" => docs/linux/cli/linux-cli-hardware.md (100%) rename "docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" => docs/linux/cli/linux-cli-help.md (100%) rename "docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" => docs/linux/cli/linux-cli-net.md (100%) rename "docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" => docs/linux/cli/linux-cli-software.md (100%) rename "docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" => docs/linux/cli/linux-cli-system.md (100%) rename "docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" => docs/linux/cli/linux-cli-user.md (100%) rename "docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" => docs/linux/ops/linux-base-ops.md (100%) delete mode 100644 docs/sidebar.md delete mode 100644 prettier.config.js create mode 100644 scripts/deploy.sh diff --git a/.gitignore b/.gitignore index 4a99239d..83948575 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ package-lock.json node_modules # temp folders -build +.temp dist _book _jsdoc diff --git a/README.md b/README.md index e3e825ec..5c39611f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@

linux-tutorial

- > 📚 **linux-tutorial** 是一个 Linux 教程。 > > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) @@ -26,26 +25,26 @@ > 学习 Linux 的第一步:当然是从 [Linux 命令](docs/linux/cli/README.md) 入手了。 -- [查看 Linux 命令帮助信息](docs/linux/cli/查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` -- [Linux 文件目录管理](docs/linux/cli/Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` -- [Linux 文件内容查看命令](docs/linux/cli/Linux文件内容查看编辑.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` -- [Linux 文件压缩和解压](docs/linux/cli/Linux文件压缩和解压.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` -- [Linux 用户管理](docs/linux/cli/Linux用户管理.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` -- [Linux 系统管理](docs/linux/cli/Linux系统管理.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` -- [Linux 网络管理](docs/linux/cli/Linux网络管理.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硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` -- [Linux 软件管理](docs/linux/cli/Linux硬件管理.md) - 关键词:`rpm`, `yum`, `apt-get` +- [查看 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 运维 > Linux 系统的常见运维工作。 -- [linux 典型运维应用](docs/linux/ops/linux典型运维应用.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab -- [Samba 应用](docs/linux/ops/samba.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 典型运维应用](docs/linux/ops/linux-base-ops.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +- [Samba](docs/linux/ops/samba.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) ### 软件运维 @@ -67,7 +66,7 @@ - [Elastic 运维](docs/linux/soft/elastic) - [Kafka 运维](docs/linux/soft/kafka-install.md) - [RocketMQ 运维](docs/linux/soft/rocketmq-install.md) - - [Zookeeper 运维](https://github.com/dunwu/javaweb/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) - 服务器 - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) @@ -76,13 +75,15 @@ - [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 教程](docs/docker/README.md) - - [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) - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) diff --git a/codes/linux/README.md b/codes/linux/README.md index 1cac0bea..5d0cb039 100644 --- a/codes/linux/README.md +++ b/codes/linux/README.md @@ -6,20 +6,20 @@ (1)下载脚本 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/download.sh | bash ``` (2)执行脚本 -```sh +```shell cd /tmp/dunwu-ops ./dunwu-ops.sh ``` (3)清除脚本 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/clear.sh | bash ``` diff --git a/codes/linux/soft/README.md b/codes/linux/soft/README.md index c78aa35a..fd0010c5 100644 --- a/codes/linux/soft/README.md +++ b/codes/linux/soft/README.md @@ -29,7 +29,7 @@ 执行以下任意命令即可执行安装脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zsh-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zsh-install.sh | bash ``` @@ -44,7 +44,7 @@ JDK8 会被安装到 `/usr/lib/jvm/java` 路径。 执行以下任意命令即可执行安装脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/jdk8-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/jdk8-install.sh | bash ``` @@ -60,7 +60,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/jd 执行以下任意命令即可执行安装脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/maven-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/maven-install.sh | bash ``` @@ -75,7 +75,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/ma 执行以下任意命令即可执行安装脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nodejs-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nodejs-install.sh | bash ``` @@ -90,7 +90,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/no 执行以下任意命令即可执行安装脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/mongodb-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/mongodb-install.sh | bash ``` @@ -107,7 +107,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/mo - 默认安装 - 执行以下任意命令即可: -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash ``` @@ -115,7 +115,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/re - 自定义安装 - 下载脚本到本地,并按照以下格式执行: -```sh +```shell sh redis-install.sh [version] [port] [password] ``` @@ -133,7 +133,7 @@ sh redis-install.sh [version] [port] [password] 使用方法: -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/tomcat8-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/tomcat8-install.sh | bash ``` @@ -146,7 +146,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/to 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/kafka-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/kafka-install.sh | bash ``` @@ -159,7 +159,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/ka 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/rocketmq-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/rocketmq-install.sh | bash ``` @@ -172,7 +172,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/ro 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nacos-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nacos-install.sh | bash ``` @@ -185,7 +185,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/na 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zookeeper-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zookeeper-install.sh | bash ``` @@ -203,7 +203,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zo - 默认安装 - 执行以下任意命令即可: -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nginx-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nginx-install.sh | bash ``` @@ -224,7 +224,7 @@ sh nginx-install.sh [version] 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash ``` @@ -235,7 +235,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fa 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/docker-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/docker-install.sh | bash ``` @@ -246,7 +246,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/do 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash ``` diff --git a/codes/linux/sys/README.md b/codes/linux/sys/README.md index 29669281..e0bc15d0 100644 --- a/codes/linux/sys/README.md +++ b/codes/linux/sys/README.md @@ -19,7 +19,7 @@ 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/change-yum-repo.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/change-yum-repo.sh | bash ``` @@ -44,7 +44,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash ``` @@ -62,7 +62,7 @@ lib 清单(可以根据需要,在 install-libs.sh 中把不需要的工具 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash ``` @@ -71,7 +71,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/ins 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash ``` @@ -80,7 +80,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/sto 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash ``` @@ -89,7 +89,7 @@ wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set 使用方法:执行以下任意命令即可执行脚本。 -```sh +```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash ``` diff --git a/docs/.remarkrc b/docs/.remarkrc new file mode 100644 index 00000000..5accdbba --- /dev/null +++ b/docs/.remarkrc @@ -0,0 +1,9 @@ +{ + "plugins": [ + "preset-lint-recommended", + "preset-lint-consistent", + + ["lint-list-item-indent", "space"], + ["lint-heading-style", false] + ] +} diff --git a/docs/.textlint.terms.json b/docs/.textlint.terms.json new file mode 100644 index 00000000..7ea8ecd7 --- /dev/null +++ b/docs/.textlint.terms.json @@ -0,0 +1,8 @@ +[ + "Stylus", + "VuePress", + [ + "front[- ]matter", + "frontmatter" + ] +] diff --git a/docs/.textlintrc.js b/docs/.textlintrc.js new file mode 100644 index 00000000..068b522b --- /dev/null +++ b/docs/.textlintrc.js @@ -0,0 +1,23 @@ +module.exports = { + rules: { + '@textlint-rule/no-unmatched-pair': true, + apostrophe: true, + 'common-misspellings': true, + diacritics: true, + 'en-capitalization': { + allowHeading: false + }, + 'stop-words': { + severity: 'warning' + }, + terminology: { + terms: `${__dirname}/.textlint.terms.json` + }, + 'write-good': { + severity: 'warning' + } + }, + filters: { + comments: true + } +} diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 00000000..bdaa9343 --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,45 @@ +/** + * @see https://vuepress.vuejs.org/zh/ + */ +module.exports = { + port: "4000", + dest: "dist", + base: "/linux-tutorial/", + title: "LINUX-TUTORIAL", + description: "数据库教程", + head: [["link", {rel: "icon", href: `/favicon.ico`}]], + markdown: { + externalLinks: { + target: "_blank", rel: "noopener noreferrer" + } + }, + themeConfig: { + logo: "images/dunwu-logo-100.png", + repo: "dunwu/linux-tutorial", + repoLabel: "Github", + editLinks: true, + smoothScroll: true, + locales: { + "/": { + label: "简体中文", selectText: "Languages", editLinkText: "帮助我们改善此页面!", lastUpdated: "上次更新", nav: [{ + text: "Linux 命令", link: "/linux/cli/", + }, { + text: "Linux 运维", link: "/linux/ops/", + }, { + text: "Linux 软件运维", link: "/linux/soft/", + }, { + text: "Docker 教程", link: "/docker/", + }, { + text: "博客", link: "https://github.com/dunwu/blog", target: "_blank", rel: "" + }], sidebar: "auto", sidebarDepth: 2 + } + } + }, + plugins: [["@vuepress/back-to-top", true], ["@vuepress/pwa", { + serviceWorker: true, updatePopup: true + }], ["@vuepress/medium-zoom", true], ["container", { + type: "vue", before: '
', after: "
" + }], ["container", { + type: "upgrade", before: info => ``, after: "" + }], ["flowchart"]] +}; diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js new file mode 100644 index 00000000..7b3605fc --- /dev/null +++ b/docs/.vuepress/enhanceApp.js @@ -0,0 +1,7 @@ +export default ({ Vue, isServer }) => { + if (!isServer) { + import('vue-toasted' /* webpackChunkName: "notification" */).then(module => { + Vue.use(module.default) + }) + } +} diff --git a/docs/.vuepress/public/favicon.ico b/docs/.vuepress/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..51e9bfa0baf0e75bbf1c8ccc49bc0ec656e60a7c GIT binary patch literal 16958 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

~k$!O4qv?+8^ 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 literal 0 HcmV?d00001 diff --git a/docs/README.md b/docs/README.md index 08cfab50..84f3c820 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,21 +26,21 @@ > 学习 Linux 的第一步:当然是从 [Linux 命令](linux/cli/README.md) 入手了。 -- [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` -- [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` -- [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` -- [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` -- [Linux 用户管理](linux/cli/Linux用户管理.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` -- [Linux 系统管理](linux/cli/Linux系统管理.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` -- [Linux 网络管理](linux/cli/Linux网络管理.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` -- [Linux 硬件管理](linux/cli/Linux硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` -- [Linux 软件管理](linux/cli/Linux硬件管理.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-hardware.md) - 关键词:`rpm`, `yum`, `apt-get` ### Linux 系统运维 > Linux 系统的常见运维工作。 -- [linux 典型运维应用](linux/ops/linux典型运维应用.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +- [linux 典型运维应用](linux/ops/linux-base-ops.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab - [Samba 应用](linux/ops/samba.md) - [Systemd 应用](linux/ops/systemd.md) - [Vim 应用](linux/ops/vim.md) @@ -67,7 +67,7 @@ - [Elastic 运维](linux/soft/elastic) - [Kafka 运维](linux/soft/kafka-install.md) - [RocketMQ 运维](linux/soft/rocketmq-install.md) - - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) + - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) - [Nacos 运维](linux/soft/nacos-install.md) - 服务器 - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) diff --git a/docs/book.json b/docs/book.json deleted file mode 100644 index 59e70a6c..00000000 --- a/docs/book.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "gitbook": "3.2.2", - "title": "linux-tutorial", - "language": "zh-hans", - "root": "./", - "structure": { - "summary": "sidebar.md" - }, - "links": { - "sidebar": { - "linux-tutorial": "https://github.com/dunwu/linux-tutorial" - } - }, - "plugins": [ - "-lunr", - "-search", - "advanced-emoji@^0.2.2", - "anchor-navigation-ex@1.0.10", - "anchors@^0.7.1", - "edit-link@^2.0.2", - "expandable-chapters-small@^0.1.7", - "github@^2.0.0", - "search-plus@^0.0.11", - "simple-page-toc@^0.1.1", - "splitter@^0.0.8", - "tbfed-pagefooter@^0.0.1" - ], - "pluginsConfig": { - "anchor-navigation-ex": { - "showLevel": false, - "associatedWithSummary": true, - "multipleH1": true, - "mode": "float", - "isRewritePageTitle": false, - "float": { - "showLevelIcon": false, - "level1Icon": "fa fa-hand-o-right", - "level2Icon": "fa fa-hand-o-right", - "level3Icon": "fa fa-hand-o-right" - }, - "pageTop": { - "showLevelIcon": false, - "level1Icon": "fa fa-hand-o-right", - "level2Icon": "fa fa-hand-o-right", - "level3Icon": "fa fa-hand-o-right" - } - }, - "edit-link": { - "base": "https://github.com/dunwu/linux-tutorial/blob/master/docs", - "label": "编辑此页面" - }, - "github": { - "url": "https://github.com/dunwu" - }, - "simple-page-toc": { - "maxDepth": 4, - "skipFirstH1": true - }, - "sharing": { - "weibo": true, - "all": ["weibo"] - }, - "tbfed-pagefooter": { - "copyright": "Copyright © Zhang Peng 2017", - "modify_label": "该文件上次修订时间:", - "modify_format": "YYYY-MM-DD HH:mm:ss" - } - } -} diff --git a/docs/coverpage.md b/docs/coverpage.md deleted file mode 100644 index 3ef7d970..00000000 --- a/docs/coverpage.md +++ /dev/null @@ -1,7 +0,0 @@ -

- -# linux-tutorial - -> 📚 **linux-tutorial** 是一个 Linux 教程。 - -[开始阅读](README.md) diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index ee804fcf..00000000 --- a/docs/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - linux-tutorial - - - - - - - - - - -
正在加载...
- - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/linux/cli/README.md b/docs/linux/cli/README.md index 41442c4c..988fc3c3 100644 --- a/docs/linux/cli/README.md +++ b/docs/linux/cli/README.md @@ -2,15 +2,15 @@ ## 常用命令 -- [查看 Linux 命令帮助信息](查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` -- [Linux 文件目录管理](Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` -- [Linux 文件内容查看命令](Linux文件内容查看编辑.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` -- [Linux 文件压缩和解压](Linux文件压缩和解压.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` -- [Linux 用户管理](Linux用户管理.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` -- [Linux 系统管理](Linux系统管理.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` -- [Linux 网络管理](Linux网络管理.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` -- [Linux 硬件管理](Linux硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` -- [Linux 软件管理](Linux硬件管理.md) - 关键词:`rpm`, `yum`, `apt-get` +- [查看 Linux 命令帮助信息](linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` +- [Linux 文件目录管理](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-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` +- [Linux 文件压缩和解压](linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` +- [Linux 用户管理](linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` +- [Linux 系统管理](linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` +- [Linux 网络管理](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-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` +- [Linux 软件管理](linux-cli-hardware.md) - 关键词:`rpm`, `yum`, `apt-get` ## 资料 diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" b/docs/linux/cli/linux-cli-dir.md similarity index 100% rename from "docs/linux/cli/Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" rename to docs/linux/cli/linux-cli-dir.md diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" b/docs/linux/cli/linux-cli-file-compress.md similarity index 100% rename from "docs/linux/cli/Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" rename to docs/linux/cli/linux-cli-file-compress.md diff --git "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" b/docs/linux/cli/linux-cli-file.md similarity index 98% rename from "docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" rename to docs/linux/cli/linux-cli-file.md index a2714212..03e5b3e4 100644 --- "a/docs/linux/cli/Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" +++ b/docs/linux/cli/linux-cli-file.md @@ -127,7 +127,7 @@ sed '/^test/'d file > > 参考:http://man.linuxde.net/vi > -> 引申阅读:[Vim 快速指南](https://github.com/dunwu/OS/blob/master/docs/vim.md) +> 引申阅读:[Vim 入门指南](https://github.com/dunwu/OS/blob/master/docs/vim.md) ### 2.8. grep diff --git "a/docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" b/docs/linux/cli/linux-cli-hardware.md similarity index 100% rename from "docs/linux/cli/Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" rename to docs/linux/cli/linux-cli-hardware.md diff --git "a/docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" b/docs/linux/cli/linux-cli-help.md similarity index 100% rename from "docs/linux/cli/\346\237\245\347\234\213Linux\345\221\275\344\273\244\345\270\256\345\212\251\344\277\241\346\201\257.md" rename to docs/linux/cli/linux-cli-help.md diff --git "a/docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" b/docs/linux/cli/linux-cli-net.md similarity index 100% rename from "docs/linux/cli/Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" rename to docs/linux/cli/linux-cli-net.md diff --git "a/docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" b/docs/linux/cli/linux-cli-software.md similarity index 100% rename from "docs/linux/cli/Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" rename to docs/linux/cli/linux-cli-software.md diff --git "a/docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" b/docs/linux/cli/linux-cli-system.md similarity index 100% rename from "docs/linux/cli/Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" rename to docs/linux/cli/linux-cli-system.md diff --git "a/docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" b/docs/linux/cli/linux-cli-user.md similarity index 100% rename from "docs/linux/cli/Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" rename to docs/linux/cli/linux-cli-user.md diff --git a/docs/linux/ops/README.md b/docs/linux/ops/README.md index e28138a4..6aaf4516 100644 --- a/docs/linux/ops/README.md +++ b/docs/linux/ops/README.md @@ -1,3 +1,8 @@ +# -- [linux 典型运维应用](linux典型运维应用.md) -- [samba 使用详解](samba.md) +- [linux 典型运维应用](linux-base-ops.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +- [Samba](samba.md) +- [Systemd](systemd.md) +- [Vim](vim.md) +- [Iptables](iptables.md) +- [oh-my-zsh](zsh.md) diff --git "a/docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" b/docs/linux/ops/linux-base-ops.md similarity index 100% rename from "docs/linux/ops/linux\345\205\270\345\236\213\350\277\220\347\273\264\345\272\224\347\224\250.md" rename to docs/linux/ops/linux-base-ops.md diff --git a/docs/linux/soft/elastic/README.md b/docs/linux/soft/elastic/README.md index 6bebd4ed..d3a3e133 100644 --- a/docs/linux/soft/elastic/README.md +++ b/docs/linux/soft/elastic/README.md @@ -14,7 +14,7 @@ ## 目录 -[Elastic 技术栈之快速指南](elastic-quickstart.md) +[Elastic 技术栈之入门指南](elastic-quickstart.md) [Elastic 技术栈之 Logstash 基础](elastic-logstash.md) diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index d404d55f..7d6e3b86 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -39,7 +39,7 @@ 这里,如果想通过命令方式直接下载(比如用脚本安装),可以在[官方历史发布版本页面](https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3)中找到合适版本,然后执行以下命令: -```sh +```shell wget -O /opt/maven/nexus-unix.tar.gz http://download.sonatype.com/nexus/3/nexus-3.13.0-01-unix.tar.gz tar -zxf nexus-unix.tar.gz ``` @@ -55,7 +55,7 @@ tar -zxf nexus-unix.tar.gz 执行 `./nexus`,可以查看允许执行的参数,如下所示,含义可谓一目了然: -```sh +```shell $ ./nexus Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} ``` @@ -203,7 +203,7 @@ Nexus 中的仓库有以下类型: 示例: -```sh +```shell # 编译并打包 maven 项目 $ mvn clean package -Dmaven.skip.test=true -P zp diff --git a/docs/package.json b/docs/package.json index 55269c87..ade50f7a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,33 +1,39 @@ { "name": "linux-tutorial", - "author": "Zhang Peng", - "homepage": "http://dunwu.github.io/linux-tutorial", - "repository": { - "type": "git", - "url": "git@github.com:dunwu/linux-tutorial.git" - }, + "version": "1.0.0", + "private": true, "scripts": { - "start": "docsify serve ./ --port 4000", - "clean": "rimraf _book", - "install": "gitbook install", - "serve": "gitbook serve", - "build": "npm run clean & gitbook build", - "pdf": "gitbook pdf ." - }, - "dependencies": { - "gitbook-plugin-advanced-emoji": "^0.2.2", - "gitbook-plugin-anchor-navigation-ex": "^1.0.10", - "gitbook-plugin-anchors": "^0.7.1", - "gitbook-plugin-edit-link": "^2.0.2", - "gitbook-plugin-expandable-chapters-small": "^0.1.7", - "gitbook-plugin-github": "^2.0.0", - "gitbook-plugin-search-plus": "0.0.11", - "gitbook-plugin-simple-page-toc": "^0.1.2", - "gitbook-plugin-splitter": "0.0.8", - "gitbook-plugin-tbfed-pagefooter": "0.0.1" + "clean": "rimraf dist && rimraf .temp", + "build": "npm run clean && vuepress build ./ --temp .temp", + "dev": "vuepress dev ./ --temp .temp", + "lint-md": "npm run lint-md:style && npm run lint-md:wording", + "lint-md:style": "remark --quiet --frail .", + "lint-md:wording": "textlint ./**/*.md", + "show-help": "vuepress --help", + "view-info": "vuepress view-info ./ --temp .temp" }, "devDependencies": { - "gh-pages": "^2.1.1", - "rimraf": "^3.0.0" + "@textlint-rule/textlint-rule-no-unmatched-pair": "^1.0.7", + "@vuepress/plugin-back-to-top": "^1.3.0", + "@vuepress/plugin-medium-zoom": "^1.3.0", + "@vuepress/plugin-pwa": "^1.3.0", + "@vuepress/theme-vue": "^1.3.0", + "remark-cli": "^7.0.0", + "remark-lint": "^6.0.5", + "remark-preset-lint-consistent": "^2.0.3", + "remark-preset-lint-recommended": "^3.0.3", + "rimraf": "^3.0.1", + "textlint": "^11.3.1", + "textlint-filter-rule-comments": "^1.2.2", + "textlint-rule-apostrophe": "^1.0.0", + "textlint-rule-common-misspellings": "^1.0.1", + "textlint-rule-diacritics": "^1.0.0", + "textlint-rule-en-capitalization": "^2.0.2", + "textlint-rule-stop-words": "^1.0.17", + "textlint-rule-terminology": "^1.1.30", + "textlint-rule-write-good": "^1.6.2", + "vue-toasted": "^1.1.25", + "vuepress": "^1.3.0", + "vuepress-plugin-flowchart": "^1.4.2" } } diff --git a/docs/sidebar.md b/docs/sidebar.md deleted file mode 100644 index c8cb4dc6..00000000 --- a/docs/sidebar.md +++ /dev/null @@ -1,50 +0,0 @@ -# linux-tutorial - -- [简介](README.md) -- [**Linux 命令**](linux/cli/README.md) - - [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md) - - [Linux 文件目录管理](linux/cli/Linux文件目录管理.md) - - [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md) - - [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md) - - [Linux 用户管理](linux/cli/Linux用户管理.md) - - [Linux 系统管理](linux/cli/Linux系统管理.md) - - [Linux 网络管理](linux/cli/Linux网络管理.md) - - [Linux 硬件管理](linux/cli/Linux硬件管理.md) - - [Linux 软件管理](linux/cli/Linux硬件管理.md) -- [**Linux 系统运维**](linux/ops/README.md) - - [linux 典型运维应用](linux/ops/linux典型运维应用.md) - - [samba 使用详解](linux/ops/samba.md) - - [Systemd 教程](linux/ops/systemd.md) - - [Vim 应用指南](linux/ops/vim.md) - - [Zsh 应用指南](linux/ops/zsh.md) -- [**软件运维**](linux/soft/README.md) - - 开发环境 - - [JDK 安装](linux/soft/jdk-install.md) - - [Maven 安装](linux/soft/maven-install.md) - - [Nodejs 安装](linux/soft/nodejs-install.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 运维](linux/soft/elastic/README.md) - - [Kafka 运维](linux/soft/kafka-install.md) - - [RocketMQ 运维](linux/soft/rocketmq-install.md) - - [Nacos 运维](linux/soft/nacos-install.md) - - [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md) - - 服务器 - - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) - - [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/README.md) - - [Docker 快速入门](docker/docker-quickstart.md) - - [Dockerfile 最佳实践](docker/docker-dockerfile.md) - - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) diff --git a/prettier.config.js b/prettier.config.js deleted file mode 100644 index eb6bb1f5..00000000 --- a/prettier.config.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @see https://prettier.io/docs/en/options.html - * @see https://prettier.io/docs/en/configuration.html - */ -module.exports = { - tabWidth: 2, semi: false, singleQuote: true -} diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 00000000..3ebd471c --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env sh + +# ------------------------------------------------------------------------------ +# gh-pages 部署脚本 +# @author Zhang Peng +# @since 2020/2/10 +# ------------------------------------------------------------------------------ + +# 装载其它库 +ROOT_DIR=$(cd `dirname $0`/..; pwd) + +# 确保脚本抛出遇到的错误 +set -e + +cd ${ROOT_DIR}/docs + +# 生成静态文件 +npm install +npm run build + +# 进入生成的文件夹 +cd dist + +# 如果是发布到自定义域名 +# echo 'www.example.com' > CNAME + +git init +git checkout -b gh-pages && git add . +git commit -m 'deploy' + +# 如果发布到 https://.github.io/ +if [[ ${GITHUB_TOKEN} && ${GITEE_TOKEN} ]]; then + echo "使用 token 公钥部署 gh-pages" + # ${GITHUB_TOKEN} 是 Github 私人令牌;${GITEE_TOKEN} 是 Gitee 私人令牌 + # ${GITHUB_TOKEN} 和 ${GITEE_TOKEN} 都是环境变量;travis-ci 构建时会传入变量 + git push --force --quiet "https://dunwu:${GITHUB_TOKEN}@github.com/dunwu/linux-tutorial.git" gh-pages + git push --force --quiet "https://turnon:${GITEE_TOKEN}@gitee.com/turnon/linux-tutorial.git" gh-pages +else + echo "使用 ssh 公钥部署 gh-pages" + git push -f git@github.com:dunwu/linux-tutorial.git gh-pages + git push -f git@gitee.com:turnon/linux-tutorial.git gh-pages +fi + +cd ${ROOT_DIR} From e963f5e234935308772b7d2f39318bbf9932f2af Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Mon, 10 Feb 2020 23:13:58 +0800 Subject: [PATCH 32/64] update docs --- .travis.yml | 24 ++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7f7498fb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +# 持续集成 CI +# @see https://docs.travis-ci.com/user/tutorial/ + +language: node_js + +sudo: required + +node_js: stable + +branches: + only: + - master + +before_install: + - export TZ=Asia/Shanghai + +script: bash ./scripts/deploy.sh + +notifications: + email: + recipients: + - forbreak@163.com + on_success: change + on_failure: always diff --git a/README.md b/README.md index 5c39611f..6aa14469 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,11 @@

license + build

linux-tutorial

+ > 📚 **linux-tutorial** 是一个 Linux 教程。 > > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) From c1e96a71340a5bd26acf373cdf32b2c55c18b844 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 11 Feb 2020 13:11:30 +0800 Subject: [PATCH 33/64] update docs --- README.md | 25 ++- docs/README.md | 167 ++++++--------- docs/linux/cli/README.md | 7 +- docs/linux/ops/README.md | 13 +- docs/linux/ops/crontab.md | 189 +++++++++++++++++ docs/linux/ops/firewalld.md | 38 ++++ .../ops/{linux-base-ops.md => network-ops.md} | 191 ++---------------- docs/linux/ops/ntp.md | 186 +++++++++++++++++ docs/linux/soft/README.md | 6 + 9 files changed, 528 insertions(+), 294 deletions(-) create mode 100644 docs/linux/ops/crontab.md create mode 100644 docs/linux/ops/firewalld.md rename docs/linux/ops/{linux-base-ops.md => network-ops.md} (55%) create mode 100644 docs/linux/ops/ntp.md diff --git a/README.md b/README.md index 6aa14469..fc223c0c 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,7 @@ > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) -| 📖 | 🐚 | 📚 | -| :-----------: | :-----------: | :-----------: | -| [文章](#文章) | [脚本](#脚本) | [资源](#资源) | - -## 文章 +## 📖 内容 ### Linux 命令 @@ -41,8 +37,11 @@ > Linux 系统的常见运维工作。 -- [linux 典型运维应用](docs/linux/ops/linux-base-ops.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +- [网络运维](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) @@ -52,7 +51,7 @@ > 部署在 Linux 系统上的软件运维。 > -> 配套安装脚本:🐚 [软件运维配置脚本集合](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) @@ -71,9 +70,9 @@ - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) - [Nacos 运维](docs/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) -- [数据库 📚](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) - [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md) @@ -90,21 +89,21 @@ - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) -## 脚本 +## ⌨ 脚本 ### Shell 脚本大全 **Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 -源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) +源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell) ### CentOS 运维脚本集合 本人作为一名 Java 后端,苦于经常在 CentOS 环境上开荒虚拟机。为提高效率,写了一套 Shell 脚本,提供如下功能:安装常用 lib 库、命令工具、设置 DNS、NTP、配置国内 yum 源、一键安装常用软件等。 -源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) +源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux) -## 资源 +## 📚 资料 - **Linux 命令** - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) diff --git a/docs/README.md b/docs/README.md index 84f3c820..b25c52b7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,14 +1,13 @@ -
+--- +home: true +heroImage: /images/dunwu-logo-200.png +heroText: LINUX-TUTORIAL +tagline: 📚 linux-tutorial 是一个 Linux 教程。 +actionLink: / +footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu +--- -

- license -

- -

linux-tutorial

+# Linux 教程 > 📚 **linux-tutorial** 是一个 Linux 教程。 > @@ -16,136 +15,92 @@ > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) -| 📖 | 🐚 | 📚 | -| :-----------: | :-----------: | :-----------: | -| [文章](#文章) | [脚本](#脚本) | [资源](#资源) | - -## 文章 +## 📖 内容 ### Linux 命令 -> 学习 Linux 的第一步:当然是从 [Linux 命令](linux/cli/README.md) 入手了。 +> 学习 Linux 的第一步:当然是从 [Linux 命令](docs/linux/cli/README.md) 入手了。 -- [查看 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-hardware.md) - 关键词:`rpm`, `yum`, `apt-get` +- [查看 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 运维 > Linux 系统的常见运维工作。 -- [linux 典型运维应用](linux/ops/linux-base-ops.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab -- [Samba 应用](linux/ops/samba.md) -- [Systemd 应用](linux/ops/systemd.md) -- [Vim 应用](linux/ops/vim.md) -- [Iptables 应用](linux/ops/iptables.md) -- [oh-my-zsh 应用](linux/ops/zsh.md) +- [网络运维](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 系统上的软件运维。 > -> 配套安装脚本:🐚 [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) +> 配套安装脚本:⌨ [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - 开发环境 - - [JDK 安装](linux/soft/jdk-install.md) - - [Maven 安装](linux/soft/maven-install.md) - - [Nodejs 安装](linux/soft/nodejs-install.md) + - [JDK 安装](docs/linux/soft/jdk-install.md) + - [Maven 安装](docs/linux/soft/maven-install.md) + - [Nodejs 安装](docs/linux/soft/nodejs-install.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) + - [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) - 中间件服务 - - [Elastic 运维](linux/soft/elastic) - - [Kafka 运维](linux/soft/kafka-install.md) - - [RocketMQ 运维](linux/soft/rocketmq-install.md) + - [Elastic 运维](docs/linux/soft/elastic) + - [Kafka 运维](docs/linux/soft/kafka-install.md) + - [RocketMQ 运维](docs/linux/soft/rocketmq-install.md) - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) - - [Nacos 运维](linux/soft/nacos-install.md) + - [Nacos 运维](docs/linux/soft/nacos-install.md) - 服务器 - - [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial) - - [Tomcat 运维](linux/soft/tomcat-install.md) -- [数据库 📚](https://github.com/dunwu/db-tutorial) + - [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚 + - [Tomcat 运维](docs/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/README.md) - - [Docker 快速入门](docker/docker-quickstart.md) - - [Dockerfile 最佳实践](docker/docker-dockerfile.md) - - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) - [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) - [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) -## 脚本 +## ⌨ 脚本 ### Shell 脚本大全 **Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 -源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) +源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell) ### CentOS 运维脚本集合 本人作为一名 Java 后端,苦于经常在 CentOS 环境上开荒虚拟机。为提高效率,写了一套 Shell 脚本,提供如下功能:安装常用 lib 库、命令工具、设置 DNS、NTP、配置国内 yum 源、一键安装常用软件等。 -源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys) - -## 资源 - -- **Linux 命令** - - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - - [Linux 命令大全](https://man.linuxde.net/) - - [linux-command](https://github.com/jaywcjlove/linux-command) -- **社区网站** - - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 - - [实验楼](https://www.shiyanlou.com/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 - - [鸟哥的 linux 私房菜](http://linux.vbird.org/) - 非常适合 Linux 入门初学者看的教程。 - - [Linux 公社](http://www.linuxidc.com/) - Linux 相关的新闻、教程、主题、壁纸都有。 - - [Linux Today](http://www.linuxde.net) - Linux 新闻资讯发布,Linux 职业技术学习!。 -- **知识相关** - - [Linux 思维导图整理](http://www.jianshu.com/p/59f759207862) - - [Linux 初学者进阶学习资源整理](http://www.jianshu.com/p/fe2a790b41eb) - - [Linux 基础入门(新版)](https://www.shiyanlou.com/courses/1) - - [【译】Linux 概念架构的理解](http://www.jianshu.com/p/c5ae8f061cfe) [En](http://oss.org.cn/ossdocs/linux/kernel/a1/index.html) - - [Linux 守护进程的启动方法](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html) - - [Linux 编程之内存映射](https://www.shiyanlou.com/questions/2992) - - [Linux 知识点小结](https://blog.huachao.me/2016/1/Linux%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B0%8F%E7%BB%93/) - - [10 大白帽黑客专用的 Linux 操作系统](https://linux.cn/article-6971-1.html) -- **软件工具** - - [超赞的 Linux 软件](https://www.gitbook.com/book/alim0x/awesome-linux-software-zh_cn/details) Github 仓库[Zh](https://github.com/alim0x/Awesome-Linux-Software-zh_CN) [En](https://github.com/VoLuong/Awesome-Linux-Software) - - [程序员喜欢的 9 款最佳的 Linux 文件比较工具](http://os.51cto.com/art/201607/513796.htm) - - [提高 Linux 开发效率的 5 个工具](http://www.codeceo.com/article/5-linux-productivity-tools.html) - - [你要了解的 11 款面向 Linux 系统的一流备份实用工具](http://os.51cto.com/art/201603/508027.htm) - - [16 个很有用的在线工具](http://www.simlinux.com/archives/264.html) - - Adobe 软件的最佳替代品 [原文在这里](https://linux.cn/article-8928-1.html) - - [Evince (Adobe Acrobat Reader)](https://wiki.gnome.org/Apps/Evince) 一个“支持多种文档格式的文档查看器”,可以查看 PDF,还支持各种漫画书格式 - - [Pixlr (Adobe Photoshop)](https://pixlr.com/) 一个强大的图像编辑工具 - - [Inkscape (Adobe Illustrator)](https://inkscape.org/zh/) 一个专业的矢量图形编辑器 - - [Pinegrow Web Editor (Adobe Dreamweaver)](https://pinegrow.com/) 一个可视化编辑制作 HTML 网站 - - [Scribus (Adobe InDesign)](https://www.scribus.net/) 一个开源电子杂志制作软件 - - [Webflow (Adobe Muse)](https://webflow.com/) 一款可以帮助用户不用编码就可以快速创建网站的谷歌浏览器插件。 - - [Tupi (Adobe Animate)](http://www.maefloresta.com/portal/) 一款可以创建 HTML5 动画的工具。 - - [Black Magic Fusion (Adobe After Effects)](https://www.blackmagicdesign.com) 一款先进的合成软件,广泛应用于视觉特效、广电影视设计以及 3D 动画设计等领域。 -- **中国开源镜像** - - [阿里云开源镜像站](http://mirrors.aliyun.com/) - - [网易开源镜像站](http://mirrors.163.com/) - - [搜狐开源镜像站](http://mirrors.sohu.com/) - - [北京交通大学](http://mirror.bjtu.edu.cn/) - - [兰州大学](http://mirror.lzu.edu.cn/) - - [厦门大学](http://mirrors.xmu.edu.cn/) - - [上海交通大学](http://ftp.sjtu.edu.cn/) - - [清华大学](http://mirrors.tuna.tsinghua.edu.cn/) - - [中国科学技术大学](http://mirrors.ustc.edu.cn/) - - [东北大学](http://mirror.neu.edu.cn/) - - [浙江大学](http://mirrors.zju.edu.cn/) - - [东软信息学院](http://mirrors.neusoft.edu.cn/) +源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux) + +## 🚪 传送门 + +◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ diff --git a/docs/linux/cli/README.md b/docs/linux/cli/README.md index 988fc3c3..0ca5c652 100644 --- a/docs/linux/cli/README.md +++ b/docs/linux/cli/README.md @@ -1,6 +1,6 @@ # Linux 命令行 -## 常用命令 +## 📖 内容 - [查看 Linux 命令帮助信息](linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - [Linux 文件目录管理](linux-cli-dir.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` @@ -12,9 +12,12 @@ - [Linux 硬件管理](linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` - [Linux 软件管理](linux-cli-hardware.md) - 关键词:`rpm`, `yum`, `apt-get` -## 资料 +## 📚 资料 - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - [Linux命令大全](https://man.linuxde.net/) - [linux-command](https://github.com/jaywcjlove/linux-command) +## 🚪 传送门 + +◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ diff --git a/docs/linux/ops/README.md b/docs/linux/ops/README.md index 6aaf4516..f9b0f0f9 100644 --- a/docs/linux/ops/README.md +++ b/docs/linux/ops/README.md @@ -1,8 +1,17 @@ -# +# Linux 系统运维 -- [linux 典型运维应用](linux-base-ops.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab +## 📖 内容 + +- [网络运维](network-ops.md) - [Samba](samba.md) +- [NTP](ntp.md) +- [Firewalld](firewalld.md) +- [Crontab](crontab.md) - [Systemd](systemd.md) - [Vim](vim.md) - [Iptables](iptables.md) - [oh-my-zsh](zsh.md) + +## 🚪 传送门 + +◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ diff --git a/docs/linux/ops/crontab.md b/docs/linux/ops/crontab.md new file mode 100644 index 00000000..29139e61 --- /dev/null +++ b/docs/linux/ops/crontab.md @@ -0,0 +1,189 @@ +# 定时任务 - crontab + +> 环境:CentOS + +通过 `crontab` 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script 脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。这个命令非常适合周期性的日志分析或数据备份等工作。 + +## crond 服务 + +Linux 通过 crond 服务来支持 crontab。 + +### 检查 `crond` 服务 + +使用 `systemctl list-unit-files` 命令确认 `crond` 服务是否已安装。 + +```shell +$ systemctl list-unit-files | grep crond +crond.service enabled +``` + +如果为 enabled,表示服务正运行。 + +### crond 服务命令 + +开机自动启动 crond 服务:`chkconfig crond on` + +或者,按以下命令手动启动: + +```shell +systemctl enable crond.service # 开启服务(开机自动启动服务) +systemctl disable crond.service # 关闭服务(开机不会自动启动服务) +systemctl start crond.service # 启动服务 +systemctl stop crond.service # 停止服务 +systemctl restart crond.service # 重启服务 +systemctl reload crond.service # 重新载入配置 +systemctl status crond.service # 查看服务状态 +``` + +## crontab + +### crontab 命令 + +crontab 命令格式如下: + +```shell +crontab [-u user] file crontab [-u user] [ -e | -l | -r ] +``` + +说明: + +- `-u user`:用来设定某个用户的 crontab 服务; +- `file`:file 是命令文件的名字,表示将 file 做为 crontab 的任务列表文件并载入 crontab。如果在命令行中没有指定这个文件,crontab 命令将接受标准输入(键盘)上键入的命令,并将它们载入 crontab。 +- `-e`:编辑某个用户的 crontab 文件内容。如果不指定用户,则表示编辑当前用户的 crontab 文件。 +- `-l`:显示某个用户的 crontab 文件内容,如果不指定用户,则表示显示当前用户的 crontab 文件内容。 +- `-r`:从/var/spool/cron 目录中删除某个用户的 crontab 文件,如果不指定用户,则默认删除当前用户的 crontab 文件。 +- `-i`:在删除用户的 crontab 文件时给确认提示。 + +有两种方法写入定时任务: + +- 在命令行输入:`crontab -e` 然后添加相应的任务,存盘退出。 +- 直接编辑 `/etc/crontab` 文件,即 `vi /etc/crontab`,添加相应的任务。 + +### crontab 文件 + +crontab 要执行的定时任务都被保存在 `/etc/crontab` 文件中。 + +crontab 的文件格式如下: + +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200211113339.png) + +#### 标准字段 + +**逗号**用于分隔列表。例如,在第 5 个字段(星期几)中使用 `MON,WED,FRI` 表示周一、周三和周五。 + +**连字符**定义范围。例如,`2000-2010` 表示 2000 年至 2010 年期间的每年,包括 2000 年和 2010 年。 + +除非用反斜杠(\)转义,否则命令中的**百分号(%)**会被替换成换行符,第一个百分号后面的所有数据都会作为标准输入发送给命令。 + +| 字段 | 是否必填 | 允许值 | 允许特殊字符 | +| :----------- | :------- | :-------------- | :----------- | +| Minutes | 是 | 0–59 | `*`,`-` | +| Hours | 是 | 0–23 | `*`,`-` | +| Day of month | 是 | 1–31 | `*`,`-` | +| Month | 是 | 1–12 or JAN–DEC | `*`,`-` | +| Day of week | 是 | 0–6 or SUN–SAT | `*`,`-` | + +`/etc/crontab` 文件示例: + +```shell +SHELL=/bin/bash +PATH=/sbin:/bin:/usr/sbin:/usr/bin +MAILTO=root + +# For details see man 4 crontabs + +# Example of job definition: +# .---------------- minute (0 - 59) +# | .------------- hour (0 - 23) +# | | .---------- day of month (1 - 31) +# | | | .------- month (1 - 12) OR jan,feb,mar,apr ... +# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat +# | | | | | +# * * * * * user-name command to be executed + +# 每两个小时以root身份执行 /home/hello.sh 脚本 +0 */2 * * * root /home/hello.sh +``` + +### crontab 实例 + +#### 实例 1:每 1 分钟执行一次 myCommand + +```shell +* * * * * myCommand +``` + +#### 实例 2:每小时的第 3 和第 15 分钟执行 + +```shell +3,15 * * * * myCommand +``` + +#### 实例 3:在上午 8 点到 11 点的第 3 和第 15 分钟执行 + +```shell +3,15 8-11 * * * myCommand +``` + +#### 实例 4:每隔两天的上午 8 点到 11 点的第 3 和第 15 分钟执行 + +```shell +3,15 8-11 */2 * * myCommand +``` + +#### 实例 5:每周一上午 8 点到 11 点的第 3 和第 15 分钟执行 + +```shell +3,15 8-11 * * 1 myCommand +``` + +#### 实例 6:每晚的 21:30 重启 smb + +```shell +30 21 * * * /etc/init.d/smb restart +``` + +#### 实例 7:每月 1、10、22 日的 4 : 45 重启 smb + +```shell +45 4 1,10,22 * * /etc/init.d/smb restart +``` + +#### 实例 8:每周六、周日的 1 : 10 重启 smb + +```shell +10 1 * * 6,0 /etc/init.d/smb restart +``` + +#### 实例 9:每天 18 : 00 至 23 : 00 之间每隔 30 分钟重启 smb + +```shell +0,30 18-23 * * * /etc/init.d/smb restart +``` + +#### 实例 10:每星期六的晚上 11 : 00 pm 重启 smb + +```shell +0 23 * * 6 /etc/init.d/smb restart +``` + +#### 实例 11:每一小时重启 smb + +```shell +* */1 * * * /etc/init.d/smb restart +``` + +#### 实例 12:晚上 11 点到早上 7 点之间,每隔一小时重启 smb + +```shell +0 23-7 * * * /etc/init.d/smb restart +``` + +## 参考资料 + +- **文章** + - [crontab 定时任务](https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/crontab.html) + - [linux 定时执行脚本](https://blog.csdn.net/z_yong_cool/article/details/79288397) +- **在线工具** + - [https://tool.lu/crontab/](https://tool.lu/crontab/) + - [https://cron.qqe2.com/](https://cron.qqe2.com/) diff --git a/docs/linux/ops/firewalld.md b/docs/linux/ops/firewalld.md new file mode 100644 index 00000000..b639986f --- /dev/null +++ b/docs/linux/ops/firewalld.md @@ -0,0 +1,38 @@ +# 防火墙 - Firewalld + +## 一、firewalld 服务命令 + +```shell +systemctl enable firewalld.service # 开启服务(开机自动启动服务) +systemctl disable firewalld.service # 关闭服务(开机不会自动启动服务) +systemctl start firewalld.service # 启动服务 +systemctl stop firewalld.service # 停止服务 +systemctl restart firewalld.service # 重启服务 +systemctl reload firewalld.service # 重新载入配置 +systemctl status firewalld.service # 查看服务状态 +``` + +## 二、firewall-cmd 命令 + +`firewall-cmd` 命令用于配置防火墙。 + +```shell +firewall-cmd --version # 查看版本 +firewall-cmd --help # 查看帮助 +firewall-cmd --state # 显示状态 +firewall-cmd --reload # 更新防火墙规则 +firewall-cmd --get-active-zones # 查看区域信息 +firewall-cmd --get-zone-of-interface=eth0 # 查看指定接口所属区域 +firewall-cmd --panic-on # 拒绝所有包 +firewall-cmd --panic-off # 取消拒绝状态 +firewall-cmd --query-panic # 查看是否拒绝 + +firewall-cmd --zone=public --list-ports # 查看所有打开的端口 +firewall-cmd --zone=public --query-port=80/tcp # 查看是否有开放的 80 TCP 端口 +firewall-cmd --zone=public --add-port=80/tcp --permanent # 添加开发端口(--permanent永久生效,没有此参数重启后失效) +firewall-cmd --zone=public --remove-port=80/tcp --permanent # 永久删除开放的 80 TCP 端口 +``` + +## 参考资料 + +- [CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) \ No newline at end of file diff --git a/docs/linux/ops/linux-base-ops.md b/docs/linux/ops/network-ops.md similarity index 55% rename from docs/linux/ops/linux-base-ops.md rename to docs/linux/ops/network-ops.md index d4596d90..60792170 100644 --- a/docs/linux/ops/linux-base-ops.md +++ b/docs/linux/ops/network-ops.md @@ -2,13 +2,13 @@ > 💡 如果没有特殊说明,本文的案例都是针对 Centos 发行版本。 -## 1. 网络操作 +## 网络操作 -### 1.1. 无法访问外网域名 +### 无法访问外网域名 (1)在 hosts 中添加本机实际 IP 和本机实际域名的映射 -```bash +```shell echo "192.168.0.1 hostname" >> /etc/hosts ``` @@ -18,7 +18,7 @@ echo "192.168.0.1 hostname" >> /etc/hosts 执行 `vi /etc/resolv.conf` ,添加以下内容: -```bash +```shell nameserver 114.114.114.114 nameserver 8.8.8.8 ``` @@ -31,59 +31,7 @@ nameserver 8.8.8.8 (3)测试一下能否 ping 通 www.baidu.com -### 1.2. 开启、关闭防火墙 - -firewalld 的基本使用 - -```bash -启动:systemctl start firewalld -关闭:systemctl stop firewalld -查看状态:systemctl status firewalld -开机禁用:systemctl disable firewalld -开机启用:systemctl enable firewalld -``` - -systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 service 和 chkconfig 的功能于一体。 - -``` -启动一个服务:systemctl start firewalld.service -关闭一个服务:systemctl stop firewalld.service -重启一个服务:systemctl restart firewalld.service -显示一个服务的状态:systemctl status firewalld.service -在开机时启用一个服务:systemctl enable firewalld.service -在开机时禁用一个服务:systemctl disable firewalld.service -查看服务是否开机启动:systemctl is-enabled firewalld.service -查看已启动的服务列表:systemctl list-unit-files|grep enabled -查看启动失败的服务列表:systemctl --failed -``` - -配置 firewalld-cmd - -``` -查看版本:firewall-cmd --version -查看帮助:firewall-cmd --help -显示状态:firewall-cmd --state -查看所有打开的端口:firewall-cmd --zone=public --list-ports -更新防火墙规则:firewall-cmd --reload -查看区域信息: firewall-cmd --get-active-zones -查看指定接口所属区域:firewall-cmd --get-zone-of-interface=eth0 -拒绝所有包:firewall-cmd --panic-on -取消拒绝状态:firewall-cmd --panic-off -查看是否拒绝:firewall-cmd --query-panic -``` - -开启防火墙端口 - -``` -添加:firewall-cmd --zone=public --add-port=80/tcp --permanent (--permanent永久生效,没有此参数重启后失效) -重新载入:firewall-cmd --reload -查看:firewall-cmd --zone= public --query-port=80/tcp -删除:firewall-cmd --zone= public --remove-port=80/tcp --permanent -``` - -> :point_right: 参考:[CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) - -### 1.3. 配置网卡 +### 配置网卡 使用 root 权限编辑 `/etc/sysconfig/network-scripts/ifcfg-eno16777736X` 文件 @@ -114,54 +62,11 @@ DNS1=8.8.8.8                         # DNS域名解析 修改完后,执行 `systemctl restart network.service` 重启网卡服务。 -## 2. 系统维护 - -### 2.1. 使用 NTP 进行时间同步 - -(1)先安装时钟同步工具 ntp - -``` -yum -y install ntp -``` - -ntp 的配置文件路径为: `/etc/ntp.conf` - -(2)启动 NTP 服务 - -```bash -systemctl start ntpd.service -``` - -(3)放开防火墙 123 端口 - -NTP 服务的端口是 123,使用的是 udp 协议,所以 NTP 服务器的防火墙必须对外开放 udp 123 这个端口。 - -``` -/sbin/iptables -A INPUT -p UDP -i eth0 -s 192.168.0.0/24 --dport 123 -j ACCEPT -``` - -(4)执行时间同步 - -``` -/usr/sbin/ntpdate ntp.sjtu.edu.cn -``` - -ntp.sjtu.edu.cn 是上海交通大学 ntp 服务器。 - -(5)自动定时同步时间 - -执行如下命令,就可以在每天凌晨 3 点同步系统时间: - -``` -echo "* 3 * * * /usr/sbin/ntpdate ntp.sjtu.edu.cn" >> /etc/crontab -systemctl restart crond.service -``` +## 系统维护 -> :point_right: 参考:https://www.cnblogs.com/quchunhui/p/7658853.html +## 自动化脚本 -## 3. 自动化脚本 - -### 3.1. Linux 开机自启动脚本 +### Linux 开机自启动脚本 (1)在 `/etc/rc.local` 文件中添加命令 @@ -174,7 +79,7 @@ systemctl restart crond.service 执行 `vim /etc/rc.local` 命令,输入以下内容: -```bash +```shell #!/bin/sh # # This script will be executed *after* all the other init scripts. @@ -195,7 +100,7 @@ Linux 开机的时候,会加载运行 `/etc/rc.d/init.d` 目录下的程序, 简单的说,运行级就是操作系统当前正在运行的功能级别。 -``` +```shell 不同的运行级定义如下: # 0 - 停机(千万不能把initdefault 设置为0 ) # 1 - 单用户模式   进入方法#init s = init 1 @@ -216,14 +121,14 @@ Linux 开机的时候,会加载运行 `/etc/rc.d/init.d` 目录下的程序, (2)查看当前系统的启动级别 -```bash +```shell $ runlevel N 3 ``` (3)设定启动级别 -``` +```shell # 98 为启动序号 # 2 是系统的运行级别,可自己调整,注意不要忘了结尾的句点 $ update-rc.d mysql start 98 2 . @@ -244,69 +149,14 @@ $ update-rc.d mysql start 98 2 . > :point_right: 参考: > -> - https://blog.csdn.net/linuxshine/article/details/50717272 -> - https://www.cnblogs.com/ssooking/p/6094740.html - -### 3.2. 定时执行脚本 +> - [linux 添加开机自启动脚本示例详解](https://blog.csdn.net/linuxshine/article/details/50717272) +> - [linux 设置开机自启动](https://www.cnblogs.com/ssooking/p/6094740.html) -(1)安装 crontab +### 定时执行脚本 -(2)开启 crontab 服务 +## 配置 -开机自动启动 crond 服务:`chkconfig crond on` - -或者,按以下命令手动启动: - -```bash -# 启动服务 -systemctl start crond.service -# 停止服务 -systemctl stop crond.service -# 重启服务 -systemctl restart crond.service -# 重新载入配置 -systemctl reload crond.service -# 查看状态 -systemctl status crond.service -``` - -(3)设置需要执行的脚本 - -有两种方法: - -- 在命令行输入:`crontab -e` 然后添加相应的任务,存盘退出。 -- 直接编辑 `/etc/crontab` 文件,即 `vi /etc/crontab`,添加相应的任务。 - -示例: - -```bash -SHELL=/bin/bash -PATH=/sbin:/bin:/usr/sbin:/usr/bin -MAILTO=root - -# For details see man 4 crontabs - -# Example of job definition: -# .---------------- minute (0 - 59) -# | .------------- hour (0 - 23) -# | | .---------- day of month (1 - 31) -# | | | .------- month (1 - 12) OR jan,feb,mar,apr ... -# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat -# | | | | | -# * * * * * user-name command to be executed - -# 每天早上3点时钟同步 -* 3 * * * /usr/sbin/ntpdate ntp.sjtu.edu.cn - -# 每两个小时以root身份执行 /home/hello.sh 脚本 -0 */2 * * * root /home/hello.sh -``` - -> :point_right: 参考:[linux 定时执行脚本](https://blog.csdn.net/z_yong_cool/article/details/79288397) - -## 4. 配置 - -### 4.1. 设置 Linux 启动模式 +### 设置 Linux 启动模式 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) 2. 单用户模式,就像 Win9X 下的安全模式 @@ -318,12 +168,11 @@ MAILTO=root 设置方法: -```bash -$ sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab +```shell +sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab ``` -## 5. 参考资料 +## 参考资料 - [CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) - - [linux 定时执行脚本](https://blog.csdn.net/z_yong_cool/article/details/79288397) diff --git a/docs/linux/ops/ntp.md b/docs/linux/ops/ntp.md new file mode 100644 index 00000000..f8d58dfb --- /dev/null +++ b/docs/linux/ops/ntp.md @@ -0,0 +1,186 @@ +# 时间服务器 - NTP + +## 一、NTP 简介 + +网络时间协议(英语:Network Time Protocol,缩写:NTP)是在数据网络潜伏时间可变的计算机系统之间通过分组交换进行时钟同步的一个网络协议,位于 OSI 模型的应用层。自 1985 年以来,NTP 是目前仍在使用的最古老的互联网协议之一。NTP 由特拉华大学的 David L. Mills(英语:David L. Mills)设计。 + +**NTP 意图将所有参与计算机的协调世界时(UTC)时间同步到几毫秒的误差内**。 + +NTP 要点: + +- 地球共有 24 个时区,而以格林威治时间 (GMT) 为标准时间; +- 中国本地时间为 GMT +8 小时; +- 最准确的时间为使用原子钟 (Atomic clock) 所计算的,例如 UTC (Coordinated Universal Time) 就是一例; +- Linux 系统本来就有两种时间,一种是 Linux 以 `1970/01/01` 开始计数的系统时间,一种则是 BIOS 记载的硬件时间; +- Linux 可以透过网络校时,最常见的网络校时为使用 NTP 服务器,这个服务启动在 `udp port 123`; +- 时区档案主要放置于 `/usr/share/zoneinfo/` 目录下,而本地时区则参考 `/etc/localtime`; +- NTP 服务器为一种阶层式的服务,所以 NTP 服务器本来就会与上层时间服务器作时间的同步化, 因此 `nptd` 与 `ntpdate` 两个指令不可同时使用; +- NTP 服务器的联机状态可以使用 `ntpstat` 及 `ntpq -p` 来查询; +- NTP 提供的客户端软件为 `ntpdate` 这个指令; +- 在 Linux 下想要手动处理时间时,需以 `date` 设定时间后,以 `hwclock -w` 来写入 BIOS 所记录的时间。 +- NTP 服务器之间的时间误差不可超过 1000 秒,否则 NTP 服务会自动关闭。 + +> 更多 NTP 详情可以参考:[鸟哥的 Linux 私房菜-- NTP 时间服务器](http://cn.linux.vbird.org/linux_server/0440ntp.php) + +## 二、ntpd 服务 + +> 环境:CentOS + +### yum 安装 + +CentOS 安装 NTP 很简单,执行以下命令即可: + +```shell +yum -y install ntp +``` + +### ntpd 配置 + +ntp 的配置文件路径为: `/etc/ntp.conf` ,参考配置: + +```shell +# 1. 先处理权限方面的问题,包括放行上层服务器以及开放区网用户来源: +# restrict default kod nomodify notrap nopeer noquery # 拒绝 IPv4 的用户 +# restrict -6 default kod nomodify notrap nopeer noquery # 拒绝 IPv6 的用户 +restrict default nomodify notrap nopeer noquery +#restrict 192.168.100.0 mask 255.255.255.0 nomodify # 放行同局域网来源(根据网关和子网掩码决定) +restrict 127.0.0.1 # 默认值,放行本机 IPv4 来源 +restrict ::1 # 默认值,放行本机 IPv6 来源 + +# 2. 设定 NTP 主机来源 +# 注释掉默认 NTP 来源 +# server 0.centos.pool.ntp.org iburst +# server 1.centos.pool.ntp.org iburst +# server 2.centos.pool.ntp.org iburst +# server 3.centos.pool.ntp.org iburst +# 设置国内 NTP 来源 +server cn.pool.ntp.org prefer # 以这个主机为优先 +server ntp1.aliyun.com +server ntp.sjtu.edu.cn + +# 3. 预设时间差异分析档案与暂不用到的 keys 等,不需要更改它: +driftfile /var/lib/ntp/drift +keys /etc/ntp/keys +includefile /etc/ntp/crypto/pw +``` + +> 注意:如果更改配置,必须重启 NTP 服务(`systemctl restart ntpd`)才能生效。 + +### 放开防火墙限制 + +NTP 服务的端口是 `123`,使用的是 udp 协议,所以 NTP 服务器的防火墙必须对外开放 udp 123 这个端口。 + +如果防火墙使用 **`iptables`**,执行以下命令: + +```shell +iptables -A INPUT -p UDP -i eth0 -s 192.168.0.0/24 --dport 123 -j ACCEPT +``` + +如果防火墙使用 **`firewalld`**,执行以下命令: + +```shell +firewall-cmd --zone=public --add-port=123/udp --permanent +``` + +### ntpd 服务命令 + +```shell +systemctl enable ntpd.service # 开启服务(开机自动启动服务) +systemctl disable ntpd.service # 关闭服务(开机不会自动启动服务) +systemctl start ntpd.service # 启动服务 +systemctl stop ntpd.service # 停止服务 +systemctl restart ntpd.service # 重启服务 +systemctl reload ntpd.service # 重新载入配置 +systemctl status ntpd.service # 查看服务状态 +``` + +### 查看 ntp 服务状态 + +#### 验证 NTP 服务正常工作 + +执行 `ntpstat` 可以查看 ntp 服务器有无和上层 ntp 连通,,如果成功,可以看到类似以下的内容: + +```shell +$ ntpstat +synchronised to NTP server (5.79.108.34) at stratum 3 + time correct to within 1129 ms + polling server every 64 s +``` + +#### 查看 ntp 服务器与上层 ntp 的状态 + +```shell +ntpq -p + remote refid st t when poll reach delay offset jitter +============================================================================== +*ntp1.ams1.nl.le 130.133.1.10 2 u 36 64 367 230.801 5.271 2.791 + 120.25.115.20 10.137.53.7 2 u 33 64 377 25.930 15.908 3.168 + time.cloudflare 10.21.8.251 3 u 31 64 367 251.109 16.976 3.264 +``` + +## 三、ntpdate 命令 + +> 注意:NTP 服务器为一种阶层式的服务,所以 NTP 服务器本来就会与上层时间服务器作时间的同步化, 因此 `nptd` 与 `ntpdate` 两个指令不可同时使用。 + +### 手动执行时间同步 + +`ntpdate` 命令是 NTP 的客户端软件,它可以用于请求时间同步。 + +语法: + +```shell +/usr/sbin/ntpdate +``` + +`ntp_server` 可以从 [国内 NTP 服务器](#国内 NTP 服务器) 中选择。 + +示例: + +```shell +$ ntpdate cn.pool.ntp.org +11 Feb 10:47:12 ntpdate[30423]: step time server 84.16.73.33 offset -49.894774 sec +``` + +### 自动定时同步时间 + +如果需要自动定时同步时间,可以利用 [Crontab](#crontab) 工具。本质就是用 crontab 定时执行一次手动时间同步命令 ntp。 + +示例:执行如下命令,就可以在每天凌晨 3 点同步系统时间: + +```shell +echo "* 3 * * * /usr/sbin/ntpdate cn.pool.ntp.org" >> /etc/crontab # 修改 crond 服务配置 +systemctl restart crond # 重启 crond 服务以生效 +``` + +## 四、国内 NTP 服务器 + +以下 NTP 服务器搜集自网络: + +```shell +cn.pool.ntp.org # 最常用的国内NTP服务器,参考:https://www.ntppool.org/zh/use.html +cn.ntp.org.cn # 中国 +edu.ntp.org.cn # 中国教育网 +ntp1.aliyun.com # 阿里云 +ntp2.aliyun.com # 阿里云 +ntp.sjtu.edu.cn # 上海交通大学 +s1a.time.edu.cn # 北京邮电大学 +s1b.time.edu.cn # 清华大学 +s1c.time.edu.cn # 北京大学 +s1d.time.edu.cn # 东南大学 +s1e.time.edu.cn # 清华大学 +s2a.time.edu.cn # 清华大学 +s2b.time.edu.cn # 清华大学 +s2c.time.edu.cn # 北京邮电大学 +s2d.time.edu.cn # 西南地区网络中心 +s2e.time.edu.cn # 西北地区网络中心 +s2f.time.edu.cn # 东北地区网络中心 +s2g.time.edu.cn # 华东南地区网络中心 +s2h.time.edu.cn # 四川大学网络管理中心 +s2j.time.edu.cn # 大连理工大学网络中心 +s2k.time.edu.cn # CERNET桂林主节点 +``` + +## 参考资料 + +- [鸟哥的 Linux 私房菜-- NTP 时间服务器](http://cn.linux.vbird.org/linux_server/0440ntp.php) +- [Linux 配置 ntp 时间服务器](https://www.cnblogs.com/quchunhui/p/7658853.html) diff --git a/docs/linux/soft/README.md b/docs/linux/soft/README.md index 2792f088..fd7ccfbd 100644 --- a/docs/linux/soft/README.md +++ b/docs/linux/soft/README.md @@ -1,5 +1,7 @@ # 软件安装配置 +## 📖 内容 + - [JDK 安装](jdk-install.md) - [Elastic 安装](elastic) - [Gitlab 安装](kafka-install.md) @@ -13,3 +15,7 @@ - [Tomcat 安装](tomcat-install.md) - [Zookeeper 安装](zookeeper-ops.md) - [Nacos 安装](nacos-install.md) + +## 🚪 传送门 + +◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ From e1773d60e6e399c6da731ee5527e7500ce4bd175 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 12 Feb 2020 22:46:17 +0800 Subject: [PATCH 34/64] update scripts --- codes/linux/dunwu-ops.sh | 51 +++++++------ codes/linux/dunwu-soft.sh | 54 ++++++++------ codes/linux/dunwu-sys.sh | 62 +++++++++------- codes/linux/lib/docker.sh | 87 ++++++++++++++++++++++ codes/linux/lib/env.sh | 103 -------------------------- codes/linux/lib/file.sh | 2 +- codes/linux/lib/git.sh | 79 ++++++++++++-------- codes/linux/lib/java.sh | 2 +- codes/linux/lib/maven.sh | 75 ++++++++++--------- codes/linux/lib/utils.sh | 118 ++++++++++++++++++++++++++++++ codes/linux/libtest/env-test.sh | 10 +-- codes/linux/libtest/git-check.sh | 16 ++-- codes/linux/libtest/git-update.sh | 2 +- 13 files changed, 409 insertions(+), 252 deletions(-) create mode 100644 codes/linux/lib/docker.sh delete mode 100644 codes/linux/lib/env.sh create mode 100644 codes/linux/lib/utils.sh diff --git a/codes/linux/dunwu-ops.sh b/codes/linux/dunwu-ops.sh index 14a1b2b6..0196988a 100644 --- a/codes/linux/dunwu-ops.sh +++ b/codes/linux/dunwu-ops.sh @@ -1,39 +1,47 @@ #!/usr/bin/env bash -# --------------------------------------------------------------------------------- -# 控制台颜色 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" -# --------------------------------------------------------------------------------- +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# CentOS 常用软件一键安装脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ libs +# 装载其它库 +LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) + +if [[ ! -x ${LINUX_SCRIPTS_DIR}/lib/utils.sh ]]; then + logError "必要脚本库 ${LINUX_SCRIPTS_DIR}/lib/utils.sh 不存在!" + exit 1 +fi + +source ${LINUX_SCRIPTS_DIR}/lib/utils.sh + +# ------------------------------------------------------------------------------ functions # 打印头部信息 printHeadInfo() { - printf "${BLUE}\n" - cat << EOF +printf "${C_B_BLUE}\n" +cat << EOF ################################################################################### # 欢迎使用 Dunwu Shell 运维脚本 # 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### EOF - printf "${RESET}\n" +printf "${C_RESET}\n" } # 打印尾部信息 printFootInfo() { - printf "${BLUE}\n" - cat << EOF +printf "${C_B_BLUE}\n" +cat << EOF ################################################################################### # 脚本执行结束,感谢使用! ################################################################################### EOF - printf "${RESET}\n" +printf "${C_RESET}\n" } # 检查操作系统环境 @@ -41,13 +49,13 @@ checkOsVersion() { if (($1 == 1)); then platform=`uname -i` if [[ ${platform} != "x86_64" ]]; then - printf "\n${RED}脚本仅支持 64 位操作系统!${RESET}\n" + logError "脚本仅支持 64 位操作系统!" exit 1 fi version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` if [[ ${version} != 7 ]]; then - printf "\n${RED}脚本仅支持 CentOS 7!${RESET}\n" + logError "脚本仅支持 CentOS 7!" exit 1 fi fi @@ -70,7 +78,7 @@ selectAndExecTask() { printFootInfo exit 0 ;; *) - printf "\n${RED}输入项不支持!${RESET}\n" + logWarn "输入项不支持!" selectAndExecTask ;; esac break @@ -78,6 +86,7 @@ selectAndExecTask() { } -######################################## MAIN ######################################## +# ------------------------------------------------------------------------------ main + checkOsVersion 1 selectAndExecTask diff --git a/codes/linux/dunwu-soft.sh b/codes/linux/dunwu-soft.sh index 77573110..023c518a 100644 --- a/codes/linux/dunwu-soft.sh +++ b/codes/linux/dunwu-soft.sh @@ -1,30 +1,38 @@ #!/usr/bin/env bash -# --------------------------------------------------------------------------------- -# 控制台颜色 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" -# --------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------ +# CentOS 常用软件一键安装脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ -printf "${BLUE}\n" +# ------------------------------------------------------------------------------ libs +# 装载其它库 +LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) + +if [[ ! -x ${LINUX_SCRIPTS_DIR}/lib/utils.sh ]]; then + logError "必要脚本库 ${LINUX_SCRIPTS_DIR}/lib/utils.sh 不存在!" + exit 1 +fi + +source ${LINUX_SCRIPTS_DIR}/lib/utils.sh + +# ------------------------------------------------------------------------------ functions +# 打印头部信息 +printHeadInfo() { +printf "${C_B_BLUE}\n" cat << EOF ################################################################################### -# 欢迎使用 Dunwu Shell 软件安装脚本 +# 欢迎使用 CentOS 常用软件一键安装脚本 # 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### EOF -printf "${RESET}\n" +printf "${C_RESET}\n" +} # print menu printMenu() { - printf "${PURPLE}" + printf "${C_B_MAGENTA}" menus=( docker fastdfs gitlab jdk8 jenkins kafka maven mongodb mysql nacos nexus nginx nodejs redis rocketmq tomcat8 zookeeper zsh exit ) for i in "${!menus[@]}"; do index=`expr ${i} + 1` @@ -35,7 +43,7 @@ printMenu() { fi done - printf "\n\n${BLUE}请选择需要安装的软件:${RESET}" + printf "\n\n${C_B_BLUE}请选择需要安装的软件:${C_RESET}" } # exec shell to install soft @@ -46,22 +54,24 @@ main() { no=`expr ${index} - 1` len=${#menus[*]} if [[ ${index} -gt ${len} ]]; then - printf "${RED}输入项不支持!\n${RESET}" - exit -1 + logWarn "输入项不支持!" + exit 1 fi key=${menus[$no]} if [[ ${key} == 'exit' ]]; then - printf "${GREEN}退出 Dunwu 软件安装脚本。\n${RESET}" + logInfo "退出软件安装脚本。" exit 0 fi sh soft/${key}-install.sh printf "\n" main else - printf "${RED}输入项不支持!\n${RESET}" - exit -1 + logWarn "输入项不支持!" + exit 1 fi } -######################################## MAIN ######################################## +# ------------------------------------------------------------------------------ main + +printHeadInfo main diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index bf2d3fbb..de3ad755 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -1,18 +1,26 @@ #!/usr/bin/env bash -# --------------------------------------------------------------------------------- -# 控制台颜色 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" -# --------------------------------------------------------------------------------- - -printf "${BLUE}\n" +# ------------------------------------------------------------------------------ +# CentOS 环境初始化脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ libs +# 装载其它库 +LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) + +if [[ ! -x ${LINUX_SCRIPTS_DIR}/lib/utils.sh ]]; then + logError "必要脚本库 ${LINUX_SCRIPTS_DIR}/lib/utils.sh 不存在!" + exit 1 +fi + +source ${LINUX_SCRIPTS_DIR}/lib/utils.sh + +# ------------------------------------------------------------------------------ functions + +# 打印头部信息 +printHeadInfo() { +printf "${C_B_BLUE}\n" cat << EOF ################################################################################### # 欢迎使用 Dunwu Shell 环境初始化脚本(设置环境配置、安装基本的命令工具) @@ -20,7 +28,8 @@ cat << EOF # @author: Zhang Peng ################################################################################### EOF -printf "${RESET}\n" +printf "${C_RESET}\n" +} menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { @@ -29,33 +38,34 @@ main() { do case ${item} in "替换yum镜像") - sh ${path}/sys/change-yum-repo.sh + sh ${LINUX_SCRIPTS_DIR}/sys/change-yum-repo.sh main ;; "安装基本的命令工具") - sh ${path}/sys/install-tools.sh + sh ${LINUX_SCRIPTS_DIR}/sys/install-tools.sh main ;; "安装常用libs") - sh ${path}/sys/install-libs.sh + sh ${LINUX_SCRIPTS_DIR}/sys/install-libs.sh main ;; "系统配置") - sh ${path}/sys/sys-settings.sh ${path}/sys + sh ${LINUX_SCRIPTS_DIR}/sys/sys-settings.sh ${LINUX_SCRIPTS_DIR}/sys main ;; "全部执行") - sh ${path}/sys/change-yum-repo.sh - sh ${path}/sys/install-tools.sh - sh ${path}/sys/install-libs.sh - sh ${path}/sys/sys-settings.sh ${path}/sys - printf "${GREEN}执行完毕,退出。${RESET}\n" ;; + sh ${LINUX_SCRIPTS_DIR}/sys/change-yum-repo.sh + sh ${LINUX_SCRIPTS_DIR}/sys/install-tools.sh + sh ${LINUX_SCRIPTS_DIR}/sys/install-libs.sh + sh ${LINUX_SCRIPTS_DIR}/sys/sys-settings.sh ${LINUX_SCRIPTS_DIR}/sys + logInfo "执行完毕,退出" ;; "退出") exit 0 ;; *) - printf "${RED}输入项不支持!${RESET}\n" + logWarn "输入项不支持!" main ;; esac break done } -######################################## MAIN ######################################## -path=$(pwd) +# ------------------------------------------------------------------------------ main + +printHeadInfo main diff --git a/codes/linux/lib/docker.sh b/codes/linux/lib/docker.sh new file mode 100644 index 00000000..3fca3e6a --- /dev/null +++ b/codes/linux/lib/docker.sh @@ -0,0 +1,87 @@ +#!/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 " (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} + fi + + local source=$1 + local repository=$2 + local tag=$3 + + dockerCheck ${source} + if [[ "${SUCCEED}" != "$?" ]]; then + return ${FAILED} + fi + + cd ${source} + callAndLog docker build -t ${repository}:${tag} . + if [[ "${SUCCEED}" != "$?" ]]; then + logError "docker build -t ${repository}:${tag} failed" + return ${FAILED} + fi + + cd - +} + +dockerPush() { + if [[ ! $1 ]] || [[ ! $2 ]]; then + logError "you must input following params in order:" + echo -e "${C_B_RED}" + echo " (1) repository" + echo " (2) tag" + echo -e "\nEg. dockerBuild dunwu/dockerApp 0.0.1" + echo -e "${C_RESET}" + return ${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}" + callAndLog docker rmi ${dockerHashId} + fi + + logInfo "try to push new image: ${repository}:${tag}" + callAndLog docker push ${repository}:${tag} +} + +# 判断指定路径下是否为 docker 工程 +# @param $1: 第一个参数为 docker 项目路径 +dockerCheck() { + local source=$1 + if [[ -d "${source}" ]]; then + cd ${source} + if [[ -f "${source}/Dockerfile" ]]; then + return ${YES} + else + logError "Dockerfile is not exists" + return ${NO} + fi + cd - + return ${YES} + else + logError "${source} is not valid docker project" + return ${NO} + fi +} diff --git a/codes/linux/lib/env.sh b/codes/linux/lib/env.sh deleted file mode 100644 index 9176eec4..00000000 --- a/codes/linux/lib/env.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash - -# ------------------------------------------------------------------------------ -# 常用变量库 -# @author Zhang Peng -# ------------------------------------------------------------------------------ - -# ------------------------------------------------------------------------------ 颜色状态 - -# Regular Color -C_BLACK="\033[0;30m" -C_RED="\033[0;31m" -C_GREEN="\033[0;32m" -C_YELLOW="\033[0;33m" -C_BLUE="\033[0;34m" -C_MAGENTA="\033[0;35m" -C_CYAN="\033[0;36m" -C_WHITE="\033[0;37m" - -# Bold Color -C_B_BLACK="\033[1;30m" -C_B_RED="\033[1;31m" -C_B_GREEN="\033[1;32m" -C_B_YELLOW="\033[1;33m" -C_B_BLUE="\033[1;34m" -C_B_MAGENTA="\033[1;35m" -C_B_CYAN="\033[1;36m" -C_B_WHITE="\033[1;37m" - -# Underline Color -C_U_BLACK="\033[4;30m" -C_U_RED="\033[4;31m" -C_U_GREEN="\033[4;32m" -C_U_YELLOW="\033[4;33m" -C_U_BLUE="\033[4;34m" -C_U_MAGENTA="\033[4;35m" -C_U_CYAN="\033[4;36m" -C_U_WHITE="\033[4;37m" - -# Background Color -C_BG_BLACK="\033[40m" -C_BG_RED="\033[41m" -C_BG_GREEN="\033[42m" -C_BG_YELLOW="\033[43m" -C_BG_BLUE="\033[44m" -C_BG_MAGENTA="\033[45m" -C_BG_CYAN="\033[46m" -C_BG_WHITE="\033[47m" - -# Reset Color -C_RESET="$(tput sgr0)" - -# ------------------------------------------------------------------------------ 常用状态值 - -YES=0 -NO=1 -SUCCEED=0 -FAILED=1 - -# 显示打印日志的时间 -DATE=`date "+%Y-%m-%d %H:%M:%S"` -# 那个用户在操作 -USER=$(whoami) - -# ------------------------------------------------------------------------------ log - -logInfo() { - #($0脚本本身,$@将参数作为整体传输调用) - echo "[${DATE}] [${USER}] [INFO] [$0] [$@] execute succeed." >> /var/log/shell.log -} - -logWarn() { - #($0脚本本身,$@将参数作为整体传输调用) - echo "[${DATE}] [${USER}] [WARN] [$0] [$@] execute succeed." >> /var/log/shell.log -} - -logError() { - #($0脚本本身,$@将参数作为整体传输调用) - echo "[${DATE}] [${USER}] [ERROR] [$0] [$@] execute failed." >> /var/log/shell.log -} - -printInfo() { - echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" -} - -printWarn() { - echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" -} - -printError() { - echo -e "${C_B_RED}[ERROR] $@${C_RESET}" -} - -callAndLog () { - $* - if [[ $? -eq ${SUCCEED} ]]; then - logInfo "$@ succeed" - echo -e "${C_B_GREEN}[INFO] [$0] [$@] execute succeed.${C_RESET}" - else - logError "$@ failed" - echo -e "${C_B_RED}[ERROR] [$0] [$@] execute failed.${C_RESET}" - fi -} diff --git a/codes/linux/lib/file.sh b/codes/linux/lib/file.sh index ecbcf6bd..465f1aed 100644 --- a/codes/linux/lib/file.sh +++ b/codes/linux/lib/file.sh @@ -2,7 +2,7 @@ # 装载其它库 ROOT=`dirname ${BASH_SOURCE[0]}` -source ${ROOT}/env.sh +source ${ROOT}/utils.sh # ------------------------------------------------------------------------------ 文件操作函数 diff --git a/codes/linux/lib/git.sh b/codes/linux/lib/git.sh index 1a1b422f..3948da04 100644 --- a/codes/linux/lib/git.sh +++ b/codes/linux/lib/git.sh @@ -6,24 +6,38 @@ # ------------------------------------------------------------------------------ # 装载其它库 -ROOT=`dirname ${BASH_SOURCE[0]}` -source ${ROOT}/env.sh +LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` + +if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then + logError "必要脚本库 ${LINUX_SCRIPTS_LIB_DIR}/utils.sh 不存在!" + exit 1 +fi + +source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh # ------------------------------------------------------------------------------ git 操作函数 +GIT_LOCAL_BRANCH= +getGitLocalBranch() { + GIT_LOCAL_BRANCH=$(git symbolic-ref -q --short HEAD) +} + +GIT_ORIGIN_BRANCH= +getGitOriginBranch() { + GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}") +} + # 检查指定的路径是不是一个 git 项目 checkGit() { - local source=$1 - if [[ -d "${source}" ]]; then - cd ${source} || return ${NO} - # (1)删除git状态零时文件 - if [[ -f "gitstatus.tmp" ]]; then + local source=$1 + if [[ -d "${source}" ]]; then + cd ${source} || return ${NO} + # (1)删除git状态零时文件 + if [[ -f "gitstatus.tmp" ]]; then rm -rf gitstatus.tmp - fi + fi - # (2)判断是否存在 .git 目录 - if [[ -d "./.git" ]]; then - # (3)判断git是否可用 + # (2)判断git是否可用 git status &> gitstatus.tmp local gitStatus=false grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true @@ -33,13 +47,12 @@ checkGit() { else return ${NO} fi - fi - return ${NO} - fi + return ${NO} + fi - printf "${C_B_YELLOW}${source} is invalid dir.${C_RESET}\n" - return ${NO} + logError "${source} is invalid dir." + return ${NO} } # clone 或 fetch 操作 @@ -54,18 +67,18 @@ cloneOrPullGit() { local root=$5 if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then - printf "${C_B_YELLOW}Please input root, group, project, branch.${C_RESET}\n" + logError "Please input root, group, project, branch." return ${FAILED} fi if [[ ! -d "${root}" ]]; then - printf "${C_B_YELLOW}${root} is not directory.${C_RESET}\n" + logError "${root} is not directory." return ${FAILED} fi local source=${root}/${group}/${project} - printf "${C_B_MAGENTA}project directory is ${source}.${C_RESET}\n" - printf "${C_B_MAGENTA}git url is ${repository}:${group}/${project}.git.${C_RESET}\n" + logInfo "project directory is ${source}." + logInfo "git url is ${repository}:${group}/${project}.git." mkdir -p ${root}/${group} checkGit ${source} @@ -75,44 +88,46 @@ cloneOrPullGit() { git checkout -f ${branch} if [[ "${SUCCEED}" != "$?" ]]; then - printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" + logError "<<<< git checkout ${branch} failed." return ${FAILED} fi - printf "${C_B_GREEN}git checkout ${branch} succeed.${C_RESET}\n" + logInfo "git checkout ${branch} succeed." - git reset --hard + getGitOriginBranch + git fetch --all + git reset --hard ${GIT_ORIGIN_BRANCH} if [[ "${SUCCEED}" != "$?" ]]; then - printf "${C_B_RED}<<<< git reset --hard failed.${C_RESET}\n" + logError "<<<< git reset --hard ${GIT_ORIGIN_BRANCH} failed." return ${FAILED} fi - printf "${C_B_GREEN}git reset --hard succeed.${C_RESET}\n" + logInfo "git reset --hard ${GIT_ORIGIN_BRANCH} succeed." git pull if [[ "${SUCCEED}" != "$?" ]]; then - printf "${C_B_RED}<<<< git pull failed.${C_RESET}\n" + logError "<<<< git pull failed." return ${FAILED} fi - printf "${C_B_GREEN}git pull succeed.${C_RESET}\n" + logInfo "git pull succeed." else # 如果 ${source} 不是 git 项目,执行 clone 操作 git clone "${repository}:${group}/${project}.git" ${source} if [[ "${SUCCEED}" != "$?" ]]; then - printf "${C_B_RED}<<<< git clone ${project} failed.${C_RESET}\n" + logError "<<<< git clone ${project} failed." return ${FAILED} fi - printf "${C_B_GREEN}git clone ${project} succeed.${C_RESET}\n" + logInfo "git clone ${project} succeed." cd ${source} || return ${FAILED} git checkout -f ${branch} if [[ "${SUCCEED}" != "$?" ]]; then - printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" + logError "<<<< git checkout ${branch} failed." return ${FAILED} fi - printf "${C_B_GREEN}git checkout ${branch} succeed.${C_RESET}\n" + logInfo "git checkout ${branch} succeed." fi - printf "${C_B_GREEN}Clone or pull git project [$2/$3:$4] succeed.${C_RESET}\n" + logInfo "Clone or pull git project [$2/$3:$4] succeed." return ${SUCCEED} } diff --git a/codes/linux/lib/java.sh b/codes/linux/lib/java.sh index 0d66f228..976326e8 100644 --- a/codes/linux/lib/java.sh +++ b/codes/linux/lib/java.sh @@ -8,7 +8,7 @@ # ------------------------------------------------------------------------------ env preparation # load libs CURRENT_PATH=`dirname ${BASH_SOURCE[0]}` -source ${CURRENT_PATH}/env.sh +source ${CURRENT_PATH}/utils.sh # ------------------------------------------------------------------------------ functions diff --git a/codes/linux/lib/maven.sh b/codes/linux/lib/maven.sh index 64fc15fc..01cbcb49 100644 --- a/codes/linux/lib/maven.sh +++ b/codes/linux/lib/maven.sh @@ -6,31 +6,52 @@ # ------------------------------------------------------------------------------ # 装载其它库 -ROOT=`dirname ${BASH_SOURCE[0]}` -source ${ROOT}/env.sh +LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` -mavenBuild() { +if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then + echo "必要脚本库 ${LINUX_SCRIPTS_LIB_DIR}/utils.sh 不存在!" + exit 1 +fi + +source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh + +# 执行 maven 操作 +# @param $1: 第一个参数为 maven 项目路径 +# @param $2: 第二个参数为 maven 操作,如 package、install、deploy +# @param $3: 第三个参数为 maven profile 【非必填】 +mavenOperation() { local source=$1 - mavenCheck $1 + local lifecycle=$2 + local profile=$3 + + mavenCheck ${source} if [[ "${SUCCEED}" != "$?" ]]; then return ${FAILED} fi - if [[ -d "${source}" ]]; then - cd ${source} - if [[ -f "${source}/settings.xml" ]]; then - callAndLog "mvn clean install -B -U -s ${source}/settings.xml -Dmaven.test.skip=true" - else - callAndLog "mvn clean install -DskipTests=true -B -U" - fi - cd - - return ${SUCCEED} - else - printf "${C_B_RED}please input valid maven project path.${C_RESET}\n" - return ${FAILED} - fi + if [[ ! "${lifecycle}" ]]; then + logError "please input maven lifecycle" + return ${FAILED} + fi + + local mvnCli="mvn clean ${lifecycle} -DskipTests=true -B -U" + + if [[ ${profile} ]]; then + mvnCli="${mvnCli} -P${profile}" + fi + + cd ${source} + if [[ -f "${source}/settings.xml" ]]; then + mvnCli="${mvnCli} -s ${source}/settings.xml" + fi + + callAndLog "${mvnCli}" + cd - + return ${SUCCEED} } +# 判断指定路径下是否为 maven 工程 +# @param $1: 第一个参数为 maven 项目路径 mavenCheck() { local source=$1 if [[ -d "${source}" ]]; then @@ -38,29 +59,13 @@ mavenCheck() { if [[ -f "${source}/pom.xml" ]]; then return ${YES} else - printf "${C_B_RED}pom.xml is not exists.${C_RESET}\n" + logError "pom.xml is not exists" return ${NO} fi cd - return ${YES} else - printf "${C_B_RED}please input valid maven project path.${C_RESET}\n" + logError "please input valid maven project path" return ${NO} fi } - -##################################### MAIN ##################################### -printf "\n${C_B_GREEN}>>>> maven build begin.${C_RESET}\n\n" - -printf "${C_B_MAGENTA}Current path is ${ROOT}.${C_RESET}\n" - -mavenBuild ${ROOT}/.. -r1=$? - -if [[ "${r1}" == "${SUCCEED}" ]]; then - printf "\n${C_B_GREEN}<<<< maven build succeed.${C_RESET}\n\n" - exit ${SUCCEED} -else - printf "\n${C_B_RED}<<<< maven build failed.${C_RESET}\n\n" - exit ${FAILED} -fi diff --git a/codes/linux/lib/utils.sh b/codes/linux/lib/utils.sh new file mode 100644 index 00000000..2900760a --- /dev/null +++ b/codes/linux/lib/utils.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# Shell Utils +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ 颜色状态 + +# 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" + +# 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" + +# 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" + +# 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" + +# Reset Color +export C_RESET="$(tput sgr0)" + +# ------------------------------------------------------------------------------ 常用状态值 + +export YES=0 +export NO=1 +export SUCCEED=0 +export FAILED=1 + +# ------------------------------------------------------------------------------ 常用状态值 + +# 显示打印日志的时间 +DATE=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) +# 日志路径 +LOG_DIR=/var/log/dunwu +LOG_PATH=${LOG_DIR}/shell.log + +createLogFileIfNotExists() { + if [[ ! -f "${LOG_PATH}" ]]; then + sudo mkdir -p "${LOG_DIR}" + touch "${LOG_PATH}" + fi +} + +logInfo() { + echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" + createLogFileIfNotExists + echo "[${DATE}] [${USER}] [INFO] [$0] [$@] execute succeed." >> "${LOG_PATH}" +} + +logWarn() { + echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" + createLogFileIfNotExists + echo "[${DATE}] [${USER}] [WARN] [$0] [$@] execute succeed." >> "${LOG_PATH}" +} + +logError() { + echo -e "${C_B_RED}[ERROR] $@${C_RESET}" + createLogFileIfNotExists + echo "[${DATE}] [${USER}] [ERROR] [$0] [$@] execute failed." >> "${LOG_PATH}" +} + +printInfo() { + echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" +} + +printWarn() { + echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" +} + +printError() { + echo -e "${C_B_RED}[ERROR] $@${C_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${SUCCEED} ]]; then + logInfo "$@ succeed" + echo -e "${C_B_GREEN}[INFO] [$0] [$@] execute succeed.${C_RESET}" + return ${SUCCEED} + else + logError "$@ failed" + echo -e "${C_B_RED}[ERROR] [$0] [$@] execute failed.${C_RESET}" + return ${FAILED} + fi +} diff --git a/codes/linux/libtest/env-test.sh b/codes/linux/libtest/env-test.sh index e1a67f76..9d3ad706 100644 --- a/codes/linux/libtest/env-test.sh +++ b/codes/linux/libtest/env-test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # 装载其它库 -source ../lib/env.sh +source ../lib/utils.sh # ------------------------------------------------------------------------------ 颜色变量测试 printf "${C_B_YELLOW}测试彩色打印:${C_RESET}\n" @@ -11,7 +11,7 @@ printf "${C_RED}Hello.${C_RESET}\n" printf "${C_GREEN}Hello.${C_RESET}\n" printf "${C_YELLOW}Hello.${C_RESET}\n" printf "${C_BLUE}Hello.${C_RESET}\n" -printf "${C_PURPLE}Hello.${C_RESET}\n" +printf "${C_MAGENTA}Hello.${C_RESET}\n" printf "${C_CYAN}Hello.${C_RESET}\n" printf "${C_B_BLACK}Hello.${C_RESET}\n" @@ -19,7 +19,7 @@ printf "${C_B_RED}Hello.${C_RESET}\n" printf "${C_B_GREEN}Hello.${C_RESET}\n" printf "${C_B_YELLOW}Hello.${C_RESET}\n" printf "${C_B_BLUE}Hello.${C_RESET}\n" -printf "${C_B_PURPLE}Hello.${C_RESET}\n" +printf "${C_B_MAGENTA}Hello.${C_RESET}\n" printf "${C_B_CYAN}Hello.${C_RESET}\n" printf "${C_U_BLACK}Hello.${C_RESET}\n" @@ -27,7 +27,7 @@ printf "${C_U_RED}Hello.${C_RESET}\n" printf "${C_U_GREEN}Hello.${C_RESET}\n" printf "${C_U_YELLOW}Hello.${C_RESET}\n" printf "${C_U_BLUE}Hello.${C_RESET}\n" -printf "${C_U_PURPLE}Hello.${C_RESET}\n" +printf "${C_U_MAGENTA}Hello.${C_RESET}\n" printf "${C_U_CYAN}Hello.${C_RESET}\n" printf "${C_BG_BLACK}Hello.${C_RESET}\n" @@ -35,6 +35,6 @@ printf "${C_BG_RED}Hello.${C_RESET}\n" printf "${C_BG_GREEN}Hello.${C_RESET}\n" printf "${C_BG_YELLOW}Hello.${C_RESET}\n" printf "${C_BG_BLUE}Hello.${C_RESET}\n" -printf "${C_BG_PURPLE}Hello.${C_RESET}\n" +printf "${C_BG_MAGENTA}Hello.${C_RESET}\n" printf "${C_BG_CYAN}Hello.${C_RESET}\n" diff --git a/codes/linux/libtest/git-check.sh b/codes/linux/libtest/git-check.sh index 9d3358f7..1761300a 100644 --- a/codes/linux/libtest/git-check.sh +++ b/codes/linux/libtest/git-check.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash +source ../lib/utils.sh source ../lib/git.sh ##################################### MAIN ##################################### @@ -9,13 +10,18 @@ else DIR=$(pwd) fi -printf "${C_B_BLUE}Current path is: ${DIR} ${C_RESET}\n" +printInfo "Current path is: ${DIR}." +# 判断是否为 git 项目 checkGit ${DIR} if [[ "${YES}" == "$?" ]]; then - printf "${C_B_GREEN}${DIR} is git project.${C_RESET}\n" - exit ${YES} + printInfo "${DIR} is git project." else - printf "${C_B_RED}${DIR} is not git project.${C_RESET}\n" - exit ${NO} + printError "${DIR} is not git project." fi + +# 获取 git 分支 +getGitLocalBranch +printInfo "git local branch: ${GIT_LOCAL_BRANCH}" +getGitOriginBranch +printInfo "git origin branch: ${GIT_ORIGIN_BRANCH}" diff --git a/codes/linux/libtest/git-update.sh b/codes/linux/libtest/git-update.sh index 2baea11d..eef7edae 100644 --- a/codes/linux/libtest/git-update.sh +++ b/codes/linux/libtest/git-update.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -source ../lib/env.sh +source ../lib/utils.sh source ../lib/git.sh doCloneOrPullGit() { From 18d2a58725022b22e0b8e5d5e514a5f38787b37b Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 10 Mar 2020 22:54:15 +0800 Subject: [PATCH 35/64] update --- ...34\347\250\213\347\231\273\345\275\225.sh" | 29 ++ codes/linux/lib/docker.sh | 44 +-- codes/linux/lib/git.sh | 74 ++-- codes/linux/lib/java.sh | 2 +- codes/linux/lib/maven.sh | 42 +- codes/linux/lib/mysql.sh | 154 ++++++++ codes/linux/lib/utils.sh | 129 +++---- ...57\345\276\204\346\223\215\344\275\234.sh" | 12 + docs/.vuepress/config.js | 2 +- docs/README.md | 76 ++-- docs/linux/cli/free.md | 155 ++++++++ docs/linux/cli/grep.md | 245 ++++++++++++ docs/linux/cli/iostat.md | 70 ++++ docs/linux/cli/iotop.md | 79 ++++ docs/linux/cli/scp.md | 83 ++++ docs/linux/cli/top.md | 88 +++++ docs/linux/cli/vmstat.md | 95 +++++ docs/linux/expect.md | 180 +++++++++ docs/linux/ops/zsh.md | 2 +- docs/linux/soft/jenkins-ops.md | 362 ++++++++++++++++++ docs/linux/soft/jenkins.md | 165 -------- 21 files changed, 1732 insertions(+), 356 deletions(-) create mode 100644 "codes/expect/\350\277\234\347\250\213\347\231\273\345\275\225.sh" create mode 100644 codes/linux/lib/mysql.sh create mode 100644 "codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\346\226\207\344\273\266\350\267\257\345\276\204\346\223\215\344\275\234.sh" create mode 100644 docs/linux/cli/free.md create mode 100644 docs/linux/cli/grep.md create mode 100644 docs/linux/cli/iostat.md create mode 100644 docs/linux/cli/iotop.md create mode 100644 docs/linux/cli/scp.md create mode 100644 docs/linux/cli/top.md create mode 100644 docs/linux/cli/vmstat.md create mode 100644 docs/linux/expect.md create mode 100644 docs/linux/soft/jenkins-ops.md delete mode 100644 docs/linux/soft/jenkins.md diff --git "a/codes/expect/\350\277\234\347\250\213\347\231\273\345\275\225.sh" "b/codes/expect/\350\277\234\347\250\213\347\231\273\345\275\225.sh" new file mode 100644 index 00000000..045a0765 --- /dev/null +++ "b/codes/expect/\350\277\234\347\250\213\347\231\273\345\275\225.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 3fca3e6a..7ef09f3c 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 3948da04..1846174c 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 976326e8..84276bcc 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 01cbcb49..b55907fa 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 00000000..43493444 --- /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 2900760a..e96a8e5c 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/\346\226\207\344\273\266\346\223\215\344\275\234/\346\226\207\344\273\266\350\267\257\345\276\204\346\223\215\344\275\234.sh" "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\346\226\207\344\273\266\350\267\257\345\276\204\346\223\215\344\275\234.sh" new file mode 100644 index 00000000..a246d253 --- /dev/null +++ "b/codes/shell/\346\226\207\344\273\266\346\223\215\344\275\234/\346\226\207\344\273\266\350\267\257\345\276\204\346\223\215\344\275\234.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 bdaa9343..c5f617ba 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 b25c52b7..bef89e10 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 命令帮助信息](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 命令](linux/cli/README.md) 入手了。 + +- [查看 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 00000000..385ea0e4 --- /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 00000000..37508fdc --- /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 00000000..c4fce1ea --- /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 00000000..42dd7354 --- /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 00000000..1af18c81 --- /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 00000000..6b5f5756 --- /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 00000000..c8d52c03 --- /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 00000000..24c7d39e --- /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 de9106da..00000000 --- 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 From 83bf9ef8b2fb76b4ea3f416cd318c9881557c134 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 1 Apr 2020 09:53:27 +0800 Subject: [PATCH 36/64] update --- .../lifecycle.sh" | 162 ++++++++++++++++++ .../start.sh" | 42 +++++ .../stop.sh" | 22 +++ .../utils.sh" | 115 +++++++++++++ docs/mac/soft/ruby-install.md | 65 +++++++ 5 files changed, 406 insertions(+) create mode 100644 "codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/lifecycle.sh" create mode 100644 "codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/start.sh" create mode 100644 "codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/stop.sh" create mode 100644 "codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/utils.sh" create mode 100644 docs/mac/soft/ruby-install.md diff --git "a/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/lifecycle.sh" "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/lifecycle.sh" new file mode 100644 index 00000000..f3d627d5 --- /dev/null +++ "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/lifecycle.sh" @@ -0,0 +1,162 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# 应用终止脚本 +# @author Zhang Peng +# ----------------------------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------ libs + +SCRIPTS_DIR=$(cd `dirname $0`; pwd) + +if [[ ! -x ${SCRIPTS_DIR}/utils.sh ]]; then + logError "${SCRIPTS_DIR}/utils.sh not exists!" + exit 1 +fi +source ${SCRIPTS_DIR}/utils.sh + + +# ------------------------------------------------------------------------------ functions + +stopServer() { + if [[ ! $1 ]]; then + logError "please input java app name" + return ${ENV_FAILED} + fi + + local appName=$1 + local pid=`jps | grep ${appName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + kill -9 ${pid} + if [[ $? -eq ${ENV_SUCCEED} ]]; then + printInfo "stop ${appName} succeed" + return ${ENV_SUCCEED} + else + logError "stop ${appName} failed" + return ${ENV_FAILED} + fi + else + printWarn "${appName} is not running" + return ${ENV_SUCCEED} + fi +} + +startServer() { + + # >>>> validate params + if [[ ! $1 ]] || [[ ! $2 ]] || [[ ! $3 ]] || [[ ! $4 ]]; then + logError "you must input following params in order:" + echo -e "${ENV_COLOR_B_RED}" + echo " (1)jarPath" + echo " (2)libPath" + echo " (3)confPath" + echo " (4)logPath" + echo " (5)appName [optional]" + echo " (6)port [optional]" + echo " (7)profile [optional]" + echo " (8)debug [optional]" + echo -e "\nEg. startServer /usr/lib/dunwu/app.jar /usr/lib/dunwu/lib /usr/lib/dunwu/conf /var/log/dunwu dunwu 8888 prod off" + echo -e "${ENV_COLOR_RESET}" + return ${ENV_FAILED} + fi + + local jarPath=$1 + local libPath=$2 + local confPath=$3 + local logPath=$4 + local appName=${5:-myapp} + local port=${6:-8888} + local profile=${7:-prod} + local debug=${8:-off} + + # >>>> 1. check java app is started or not + # >>>> 1.1. exit script if the app is started + local pid=`jps | grep ${appName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + printInfo "${appName} is started, PID: ${pid}" + return ${ENV_SUCCEED} + fi + + # >>>> 2. package options + # GC OPTS + local javaOptions="-server -Xms1g -Xmx2g -Xss256k" + javaOptions="${javaOptions} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=4" + + # GC LOG OPTS + javaOptions="${javaOptions} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" + javaOptions="${javaOptions} -verbose:gc -Xloggc:${logPath}/${appName}.gc.log" + javaOptions="${javaOptions} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" + + # Heap Dump OPTS + javaOptions="${javaOptions} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" + javaOptions="${javaOptions} -XX:HeapDumpPath=${logPath}/${appName}.heapdump.hprof" + + # APP OPTS + javaOptions="${javaOptions} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8" + if [[ ${profile} ]]; then + javaOptions="${javaOptions} -Dspring.profiles.active=${profile}" + fi + + # DEBUG OPTS + if [[ "${debug}" == "on" ]]; then + # JMX OPTS + local ip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/') + local jmxPort=$(expr 10000 + ${port}) + javaOptions="${javaOptions} -Dcom.sun.management.jmxremote=true" + javaOptions="${javaOptions} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" + javaOptions="${javaOptions} -Djava.rmi.server.hostname=${ip} -Dcom.sun.management.jmxremote.port=${jmxPort}" + + # Remote Debug + local debugPort=$(expr 20000 + ${port}) + javaOptions="${javaOptions} -Xdebug -Xnoagent -Djava.compiler=NONE" + javaOptions="${javaOptions} -Xrunjdwp:transport=dt_socket,address=${debugPort},server=y,suspend=n" + fi + + # CLASSPATH + local appOptions="-classpath ${libPath}/* -Dlogging.config=file:${confPath}/logback.${profile}.xml" + local springConfigFiles="classpath:/,classpath:/config/" + local springConfigFiles="${springConfigFiles},file:${confPath}/,file:${confPath}/application.properties" + appOptions="${appOptions} --spring.config.location=${springConfigFiles}" + appOptions="${appOptions} --spring.cache.ehcache.config=file:${confPath}/config/ehcache.xml" + if [[ ${port} ]]; then + appOptions="${appOptions} --server.port=${port}" + fi + + # >>>> 3. create log dir and console log file + local consoleLogPath=${logPath}/${appName}.console.log + mkdir -p ${logPath} + if [[ ! -x ${consoleLogPath} ]]; then + touch ${consoleLogPath} + fi + + # >>>> 4. start java app + # print bootstrap info + printInfo "starting ${appName}" + echo -e "${ENV_COLOR_B_GREEN}" + echo -e "${ENV_COLOR_B_CYAN}\nBOOT PARAMS:${ENV_COLOR_B_GREEN}\n\n" + echo "appName=${appName}" + echo "jarPath=${jarPath}" + echo "libPath=${libPath}" + echo "confPath=${confPath}" + echo "logPath=${logPath}" + echo "port=${port}" + echo "profile=${profile}" + echo "debug=${debug}" + echo -e "${ENV_COLOR_B_CYAN}\nEXEC CLI:${ENV_COLOR_B_GREEN}\n\n" + echo "nohup java ${javaOptions} -jar ${jarPath} ${appOptions} >> ${consoleLogPath} 2>&1 &" + echo -e "${ENV_COLOR_RESET}" + + # exec boot cli + nohup java ${javaOptions} -jar ${jarPath} ${appOptions} >> ${consoleLogPath} 2>&1 & + + # >>>> 5. check java app is started or not + local pid=`jps | grep ${appName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + printInfo "start ${appName} succeed, PID: ${pid}" + return ${ENV_SUCCEED} + else + logError "start ${appName} failed" + return ${ENV_FAILED} + fi +} diff --git "a/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/start.sh" "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/start.sh" new file mode 100644 index 00000000..6aa0382e --- /dev/null +++ "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/start.sh" @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# myapp 启动脚本,用于【虚拟机环境】 +# @author Zhang Peng +# ----------------------------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------ libs + +SCRIPTS_DIR=$(dirname ${BASH_SOURCE[0]}) +if [[ ! -x ${SCRIPTS_DIR}/lifecycle.sh ]]; then + logError "${SCRIPTS_DIR}/lifecycle.sh not exists!" + exit 1 +fi +source ${SCRIPTS_DIR}/lifecycle.sh + + +# ------------------------------------------------------------------------------ main + +APP_DIR=$(cd `dirname $0`/..; pwd) + +export LANG="zh_CN.UTF-8" +APP=myapp +JAR_PATH=${APP_DIR}/myapp.jar +LIB_PATH=${APP_DIR}/lib +CONF_PATH=${APP_DIR}/config +LOG_DIR=/var/log/dunwu +PORT=8888 +PROFILE=prod +DEBUG=off + +declare -a serial +serial=(on off) +echo -n "是否启动 debug 模式(可选值:on|off):" +read DEBUG +if ! echo ${serial[@]} | grep -q ${DEBUG}; then + echo "是否启动 debug 模式(可选值:on|off)" + exit 1 +fi + +startServer ${JAR_PATH} ${LIB_PATH} ${CONF_PATH} ${LOG_DIR} ${APP} ${PORT} ${PROFILE} ${DEBUG} diff --git "a/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/stop.sh" "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/stop.sh" new file mode 100644 index 00000000..a73cc5df --- /dev/null +++ "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/stop.sh" @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# 应用启动脚本 +# @author Zhang Peng +# ----------------------------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------ libs + +SCRIPTS_DIR=$(dirname ${BASH_SOURCE[0]}) +if [[ ! -x ${SCRIPTS_DIR}/lifecycle.sh ]]; then + logError "${SCRIPTS_DIR}/lifecycle.sh not exists!" + exit 1 +fi +source ${SCRIPTS_DIR}/lifecycle.sh + + +# ------------------------------------------------------------------------------ main + +export LANG="zh_CN.UTF-8" +stopServer myapp diff --git "a/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/utils.sh" "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/utils.sh" new file mode 100644 index 00000000..259ded00 --- /dev/null +++ "b/codes/linux/build/Java\345\272\224\347\224\250\350\277\220\350\241\214\350\204\232\346\234\254\346\250\241\346\235\277/utils.sh" @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# Shell Utils +# 使用此脚本,应该先 export ENV_LOG_PATH,指定日志路径;否则将使用默认日志路径 /var/log/shell.log +# @author Zhang Peng +# ----------------------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------ env params + +# 颜色状态 +# Regular Color +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 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 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 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 ENV_COLOR_RESET="$(tput sgr0)" + +# 常用状态值 +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) +# 日志路径 +LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} +# 日志目录 +LOG_DIR=${LOG_PATH%/*} + +createLogFileIfNotExists() { + if [[ ! -x "${LOG_PATH}" ]]; then + mkdir -p "${LOG_DIR}" + touch "${LOG_PATH}" + fi +} + +logInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" +} + +logWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" +} + +logError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} + +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} + +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + logInfo "$@" + return ${ENV_SUCCEED} + else + logError "$@ EXECUTE ENV_FAILED" + return ${ENV_FAILED} + fi +} diff --git a/docs/mac/soft/ruby-install.md b/docs/mac/soft/ruby-install.md new file mode 100644 index 00000000..4138881a --- /dev/null +++ b/docs/mac/soft/ruby-install.md @@ -0,0 +1,65 @@ +# 安装 Ruby + +## 安装 rvm + +### 下载安装 rvm + +- 先安装好 RVM +- RVM 是一个便捷的多版本 Ruby 环境的管理和切换工具 + 官网:[https://rvm.io/](https://links.jianshu.com/go?to=https%3A%2F%2Frvm.io%2F) +- 在终端控制台命令: + $ curl -sSL [https://get.rvm.io](https://links.jianshu.com/go?to=https%3A%2F%2Fget.rvm.io) | bash -s stable 之后按回车键 +- 截止到目前 最新的版本是 1.29.9 +- 如下所示: + +```shell +:~ admin$ curl -sSL https://get.rvm.io | bash -s stable +Downloading https://github.com/rvm/rvm/archive/1.29.1.tar.gz +Downloading https://github.com/rvm/rvm/releases/download/1.29.1/1.29.1.tar.gz.asc +Found PGP signature at: 'https://github.com/rvm/rvm/releases/download/1.29.1/1.29.1.tar.gz.asc', +but no GPG software exists to validate it, skipping. + +Installing RVM to /Users/admin/.rvm/ + Adding rvm PATH line to /Users/admin/.profile /Users/admin/.mkshrc /Users/admin/.bashrc /Users/admin/.zshrc. + Adding rvm loading line to /Users/admin/.profile /Users/admin/.bash_profile /Users/admin/.zlogin. +Installation of RVM in /Users/admin/.rvm/ is almost complete: + + * To start using RVM you need to run `source /Users/admin/.rvm/scripts/rvm` + in all your open shell windows, in rare cases you need to reopen all shell windows. + +# admin, +# +# Thank you for using RVM! +# We sincerely hope that RVM helps to make your life easier and more enjoyable!!! +# +# ~Wayne, Michal & team. + +In case of problems: https://rvm.io/help and https://twitter.com/rvm_io +``` + +等待一两分钟,成功安装好 RVM。 + +### 设置环境变量 + + + +```shell +# 1.2 然后,载入 RVM 环境: +$ source /etc/profile.d/rvm.sh +$ sudo chmod -R 777 /usr/local/rvm/archives + +# 1.3 修改 RVM 下载 Ruby 的源,到 Ruby China 的镜像 +$ echo "ruby_url=https://cache.ruby-china.com/pub/ruby" > /usr/local/rvm/user/db +$ rvm install 2.7.0 --disable-binary + +// 如下所示: +AdmindeiMac-4:~ admin$ source ~/.rvm/scripts/rvm +AdmindeiMac-4:~ admin$ echo "ruby_url=https://cache.ruby-china.org/pub/ruby" > ~/.rvm/user/db +AdmindeiMac-4:~ admin$ rvm -v +rvm 1.29.9 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io/] +如果能显示版本号,则安装成功。 +``` + +## 参考资料 + +- [MAC_Ruby 安装](https://www.jianshu.com/p/c073e6fc01f5) \ No newline at end of file From 0da2791a7329016d0a0ce31c80d4930af27510f2 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 2 Apr 2020 10:07:17 +0800 Subject: [PATCH 37/64] update zsh & oh-my-zsh install scripts --- codes/linux/soft/zsh-install.sh | 10 +- codes/linux/soft/zsh/oh-my-zsh-install.sh | 294 ++++++++++++++++++++++ 2 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 codes/linux/soft/zsh/oh-my-zsh-install.sh diff --git a/codes/linux/soft/zsh-install.sh b/codes/linux/soft/zsh-install.sh index 1a01c633..df2300dc 100644 --- a/codes/linux/soft/zsh-install.sh +++ b/codes/linux/soft/zsh-install.sh @@ -40,7 +40,10 @@ command -v git > /dev/null 2>&1 || { yum install -y zsh chsh -s /bin/zsh # install oh-my-zsh -curl -o- https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh | bash +# 由于国内经常无法使用 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" +# 所以,索性将安装脚本下载下来直接使用 +#curl -o- https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh | bash +zsh/oh-my-zsh-install.sh # choose oh-my-zsh theme sed -i "s/^ZSH_THEME=.*/ZSH_THEME=\"ys\"/g" ~/.zshrc # install oh-my-zsh plugins @@ -48,6 +51,9 @@ git clone https://github.com/zsh-users/zsh-autosuggestions.git ~/.oh-my-zsh/plug git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/plugins/zsh-syntax-highlighting sed -i "s/^plugins=.*/plugins=(git z wd extract zsh-autosuggestions zsh-syntax-highlighting)/g" ~/.zshrc # reload zsh - +# 注册到 /etc/shells +echo "/usr/bin/zsh" >> /etc/shells +# 切换 shell +chsh -s $(which zsh) printf "${GREEN}<<<<<<<< install zsh finished${RESET}\n" printf "${GREEN}Please reboot to take effect.${RESET}\n" diff --git a/codes/linux/soft/zsh/oh-my-zsh-install.sh b/codes/linux/soft/zsh/oh-my-zsh-install.sh new file mode 100644 index 00000000..ebf27522 --- /dev/null +++ b/codes/linux/soft/zsh/oh-my-zsh-install.sh @@ -0,0 +1,294 @@ +# ------------------------------------------------------------------------------------------------------------------- +# 由于国内经常无法使用 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" +# 所以,索性将安装脚本下载下来直接使用 +# ------------------------------------------------------------------------------------------------------------------- + +#!/bin/sh +# +# This script should be run via curl: +# sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" +# or wget: +# sh -c "$(wget -qO- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" +# +# As an alternative, you can first download the install script and run it afterwards: +# wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh +# sh install.sh +# +# You can tweak the install behavior by setting variables when running the script. For +# example, to change the path to the Oh My Zsh repository: +# ZSH=~/.zsh sh install.sh +# +# Respects the following environment variables: +# ZSH - path to the Oh My Zsh repository folder (default: $HOME/.oh-my-zsh) +# REPO - name of the GitHub repo to install from (default: ohmyzsh/ohmyzsh) +# REMOTE - full remote URL of the git repo to install (default: GitHub via HTTPS) +# BRANCH - branch to check out immediately after install (default: master) +# +# Other options: +# CHSH - 'no' means the installer will not change the default shell (default: yes) +# RUNZSH - 'no' means the installer will not run zsh after the install (default: yes) +# KEEP_ZSHRC - 'yes' means the installer will not replace an existing .zshrc (default: no) +# +# You can also pass some arguments to the install script to set some these options: +# --skip-chsh: has the same behavior as setting CHSH to 'no' +# --unattended: sets both CHSH and RUNZSH to 'no' +# --keep-zshrc: sets KEEP_ZSHRC to 'yes' +# For example: +# sh install.sh --unattended +# +set -e + +# Default settings +ZSH=${ZSH:-~/.oh-my-zsh} +REPO=${REPO:-ohmyzsh/ohmyzsh} +REMOTE=${REMOTE:-https://github.com/${REPO}.git} +BRANCH=${BRANCH:-master} + +# Other options +CHSH=${CHSH:-yes} +RUNZSH=${RUNZSH:-yes} +KEEP_ZSHRC=${KEEP_ZSHRC:-no} + + +command_exists() { + command -v "$@" >/dev/null 2>&1 +} + +error() { + echo ${RED}"Error: $@"${RESET} >&2 +} + +setup_color() { + # Only use colors if connected to a terminal + if [ -t 1 ]; then + RED=$(printf '\033[31m') + GREEN=$(printf '\033[32m') + YELLOW=$(printf '\033[33m') + BLUE=$(printf '\033[34m') + BOLD=$(printf '\033[1m') + RESET=$(printf '\033[m') + else + RED="" + GREEN="" + YELLOW="" + BLUE="" + BOLD="" + RESET="" + fi +} + +setup_ohmyzsh() { + # Prevent the cloned repository from having insecure permissions. Failing to do + # so causes compinit() calls to fail with "command not found: compdef" errors + # for users with insecure umasks (e.g., "002", allowing group writability). Note + # that this will be ignored under Cygwin by default, as Windows ACLs take + # precedence over umasks except for filesystems mounted with option "noacl". + umask g-w,o-w + + echo "${BLUE}Cloning Oh My Zsh...${RESET}" + + command_exists git || { + error "git is not installed" + exit 1 + } + + if [ "$OSTYPE" = cygwin ] && git --version | grep -q msysgit; then + error "Windows/MSYS Git is not supported on Cygwin" + error "Make sure the Cygwin git package is installed and is first on the \$PATH" + exit 1 + fi + + git clone -c core.eol=lf -c core.autocrlf=false \ + -c fsck.zeroPaddedFilemode=ignore \ + -c fetch.fsck.zeroPaddedFilemode=ignore \ + -c receive.fsck.zeroPaddedFilemode=ignore \ + --depth=1 --branch "$BRANCH" "$REMOTE" "$ZSH" || { + error "git clone of oh-my-zsh repo failed" + exit 1 + } + + echo +} + +setup_zshrc() { + # Keep most recent old .zshrc at .zshrc.pre-oh-my-zsh, and older ones + # with datestamp of installation that moved them aside, so we never actually + # destroy a user's original zshrc + echo "${BLUE}Looking for an existing zsh config...${RESET}" + + # Must use this exact name so uninstall.sh can find it + OLD_ZSHRC=~/.zshrc.pre-oh-my-zsh + if [ -f ~/.zshrc ] || [ -h ~/.zshrc ]; then + # Skip this if the user doesn't want to replace an existing .zshrc + if [ $KEEP_ZSHRC = yes ]; then + echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Keeping...${RESET}" + return + fi + if [ -e "$OLD_ZSHRC" ]; then + OLD_OLD_ZSHRC="${OLD_ZSHRC}-$(date +%Y-%m-%d_%H-%M-%S)" + if [ -e "$OLD_OLD_ZSHRC" ]; then + error "$OLD_OLD_ZSHRC exists. Can't back up ${OLD_ZSHRC}" + error "re-run the installer again in a couple of seconds" + exit 1 + fi + mv "$OLD_ZSHRC" "${OLD_OLD_ZSHRC}" + + echo "${YELLOW}Found old ~/.zshrc.pre-oh-my-zsh." \ + "${GREEN}Backing up to ${OLD_OLD_ZSHRC}${RESET}" + fi + echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Backing up to ${OLD_ZSHRC}${RESET}" + mv ~/.zshrc "$OLD_ZSHRC" + fi + + echo "${GREEN}Using the Oh My Zsh template file and adding it to ~/.zshrc.${RESET}" + + sed "/^export ZSH=/ c\\ +export ZSH=\"$ZSH\" +" "$ZSH/templates/zshrc.zsh-template" > ~/.zshrc-omztemp + mv -f ~/.zshrc-omztemp ~/.zshrc + + echo +} + +setup_shell() { + # Skip setup if the user wants or stdin is closed (not running interactively). + if [ $CHSH = no ]; then + return + fi + + # If this user's login shell is already "zsh", do not attempt to switch. + if [ "$(basename "$SHELL")" = "zsh" ]; then + return + fi + + # If this platform doesn't provide a "chsh" command, bail out. + if ! command_exists chsh; then + cat <<-EOF + I can't change your shell automatically because this system does not have chsh. + ${BLUE}Please manually change your default shell to zsh${RESET} + EOF + return + fi + + echo "${BLUE}Time to change your default shell to zsh:${RESET}" + + # Prompt for user choice on changing the default login shell + printf "${YELLOW}Do you want to change your default shell to zsh? [Y/n]${RESET} " + read opt + case $opt in + y*|Y*|"") echo "Changing the shell..." ;; + n*|N*) echo "Shell change skipped."; return ;; + *) echo "Invalid choice. Shell change skipped."; return ;; + esac + + # Check if we're running on Termux + case "$PREFIX" in + *com.termux*) termux=true; zsh=zsh ;; + *) termux=false ;; + esac + + if [ "$termux" != true ]; then + # Test for the right location of the "shells" file + if [ -f /etc/shells ]; then + shells_file=/etc/shells + elif [ -f /usr/share/defaults/etc/shells ]; then # Solus OS + shells_file=/usr/share/defaults/etc/shells + else + error "could not find /etc/shells file. Change your default shell manually." + return + fi + + # Get the path to the right zsh binary + # 1. Use the most preceding one based on $PATH, then check that it's in the shells file + # 2. If that fails, get a zsh path from the shells file, then check it actually exists + if ! zsh=$(which zsh) || ! grep -qx "$zsh" "$shells_file"; then + if ! zsh=$(grep '^/.*/zsh$' "$shells_file" | tail -1) || [ ! -f "$zsh" ]; then + error "no zsh binary found or not present in '$shells_file'" + error "change your default shell manually." + return + fi + fi + fi + + # We're going to change the default shell, so back up the current one + if [ -n "$SHELL" ]; then + echo $SHELL > ~/.shell.pre-oh-my-zsh + else + grep "^$USER:" /etc/passwd | awk -F: '{print $7}' > ~/.shell.pre-oh-my-zsh + fi + + # Actually change the default shell to zsh + if ! chsh -s "$zsh"; then + error "chsh command unsuccessful. Change your default shell manually." + else + export SHELL="$zsh" + echo "${GREEN}Shell successfully changed to '$zsh'.${RESET}" + fi + + echo +} + +main() { + # Run as unattended if stdin is closed + if [ ! -t 0 ]; then + RUNZSH=no + CHSH=no + fi + + # Parse arguments + while [ $# -gt 0 ]; do + case $1 in + --unattended) RUNZSH=no; CHSH=no ;; + --skip-chsh) CHSH=no ;; + --keep-zshrc) KEEP_ZSHRC=yes ;; + esac + shift + done + + setup_color + + if ! command_exists zsh; then + echo "${YELLOW}Zsh is not installed.${RESET} Please install zsh first." + exit 1 + fi + + if [ -d "$ZSH" ]; then + cat <<-EOF + ${YELLOW}You already have Oh My Zsh installed.${RESET} + You'll need to remove '$ZSH' if you want to reinstall. + EOF + exit 1 + fi + + setup_ohmyzsh + setup_zshrc + setup_shell + + printf "$GREEN" + cat <<-'EOF' + __ __ + ____ / /_ ____ ___ __ __ ____ _____/ /_ + / __ \/ __ \ / __ `__ \/ / / / /_ / / ___/ __ \ + / /_/ / / / / / / / / / / /_/ / / /_(__ ) / / / + \____/_/ /_/ /_/ /_/ /_/\__, / /___/____/_/ /_/ + /____/ ....is now installed! + + + Please look over the ~/.zshrc file to select plugins, themes, and options. + + p.s. Follow us on https://twitter.com/ohmyzsh + + p.p.s. Get stickers, shirts, and coffee mugs at https://shop.planetargon.com/collections/oh-my-zsh + + EOF + printf "$RESET" + + if [ $RUNZSH = no ]; then + echo "${YELLOW}Run zsh to try it out.${RESET}" + exit + fi + + exec zsh -l +} + +main "$@" From db7bccdc6867c3963a4a71c9357cb3bdc94996c5 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 2 Apr 2020 10:38:28 +0800 Subject: [PATCH 38/64] update docker install script --- codes/linux/soft/docker-install.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/codes/linux/soft/docker-install.sh b/codes/linux/soft/docker-install.sh index fd142bc9..8907e749 100644 --- a/codes/linux/soft/docker-install.sh +++ b/codes/linux/soft/docker-install.sh @@ -45,3 +45,18 @@ sudo yum install docker-ce docker-ce-cli containerd.io sudo systemctl start docker docker version printf "${GREEN}<<<<<<<< install docker end.${RESET}\n" + +printf "${GREEN}>>>>>>>> replace chinese docker mirror registry${RESET}\n" +if [[ -f "/etc/docker/daemon.json" ]]; then + mv /etc/docker/daemon.json /etc/docker/daemon.json.bak +else + mkdir -p /etc/docker +fi +touch /etc/docker/daemon.json +cat >> /etc/docker/daemon.json << EOF +{ + "registry-mirrors": ["https://hub-mirror.c.163.com"] +} +EOF +systemctl daemon-reload +systemctl restart docker From 097cfb45effb02c63a6e3c373de644213eed192d Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Fri, 3 Apr 2020 15:15:02 +0800 Subject: [PATCH 39/64] update docs and scripts --- README.md | 4 +- codes/linux/lib/mysql.sh | 154 ------------ codes/linux/soft/config/mysql/my.cnf | 4 +- codes/linux/soft/gitlab-install.sh | 119 +++++++-- codes/linux/{ => soft}/lib/docker.sh | 0 codes/linux/{ => soft}/lib/file.sh | 0 codes/linux/{ => soft}/lib/git.sh | 0 codes/linux/{ => soft}/lib/java.sh | 0 codes/linux/{ => soft}/lib/maven.sh | 0 codes/linux/soft/lib/mysql.sh | 233 ++++++++++++++++++ codes/linux/{ => soft}/lib/net.sh | 0 codes/linux/{ => soft}/lib/nodejs.sh | 0 codes/linux/{ => soft}/lib/string.sh | 0 codes/linux/{ => soft}/lib/utils.sh | 26 +- codes/linux/soft/mysql-backup.sh | 99 +++----- codes/linux/soft/mysql-install.sh | 148 ++++++++--- codes/linux/soft/mysql-recovery.sh | 36 +++ docs/docker/kubernetes.md | 2 +- docs/linux/cli/linux-cli-dir.md | 4 +- ...14\347\232\204\350\211\272\346\234\257.md" | 6 +- docs/linux/ops/samba.md | 2 +- docs/linux/ops/vim.md | 30 +-- docs/linux/soft/elastic/elastic-beats.md | 4 +- docs/linux/soft/elastic/elastic-kibana.md | 4 +- docs/linux/soft/elastic/elastic-logstash.md | 2 +- docs/linux/soft/elastic/elastic-quickstart.md | 4 +- .../soft/{gitlab-install.md => gitlab-ops.md} | 82 +++--- docs/linux/soft/jdk-install.md | 10 +- docs/linux/soft/jenkins-ops.md | 20 +- docs/linux/soft/nexus-ops.md | 27 +- docs/linux/soft/svn-ops.md | 2 +- 31 files changed, 634 insertions(+), 388 deletions(-) delete mode 100644 codes/linux/lib/mysql.sh rename codes/linux/{ => soft}/lib/docker.sh (100%) rename codes/linux/{ => soft}/lib/file.sh (100%) rename codes/linux/{ => soft}/lib/git.sh (100%) rename codes/linux/{ => soft}/lib/java.sh (100%) rename codes/linux/{ => soft}/lib/maven.sh (100%) create mode 100644 codes/linux/soft/lib/mysql.sh rename codes/linux/{ => soft}/lib/net.sh (100%) rename codes/linux/{ => soft}/lib/nodejs.sh (100%) rename codes/linux/{ => soft}/lib/string.sh (100%) rename codes/linux/{ => soft}/lib/utils.sh (86%) create mode 100644 codes/linux/soft/mysql-recovery.sh rename docs/linux/soft/{gitlab-install.md => gitlab-ops.md} (73%) diff --git a/README.md b/README.md index fc223c0c..3689463e 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,8 @@ - [Nodejs 安装](docs/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) + - [Gitlab 运维](docs/linux/soft/gitlab-ops.md) + - [Jenkins 运维](docs/linux/soft/jenkins-ops.md) - [Svn 运维](docs/linux/soft/svn-ops.md) - [YApi 运维](docs/linux/soft/yapi-ops.md) - 中间件服务 diff --git a/codes/linux/lib/mysql.sh b/codes/linux/lib/mysql.sh deleted file mode 100644 index 43493444..00000000 --- a/codes/linux/lib/mysql.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/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/soft/config/mysql/my.cnf b/codes/linux/soft/config/mysql/my.cnf index 8b1b765c..aa72e8cd 100644 --- a/codes/linux/soft/config/mysql/my.cnf +++ b/codes/linux/soft/config/mysql/my.cnf @@ -18,9 +18,9 @@ collation_server = utf8mb4_0900_ai_ci # LOG # ------------------------------------------------------------------------------- -log_error = /var/log/mysql/mysql-error.log +log_error = /var/log/mysql/mysql.log slow_query_log = 1 -slow_query_log_file = /var/log/mysql/mysql-slow.log +slow_query_log_file = /var/log/mysql/mysql_slow_query_log.log long_query_time = 3 min_examined_row_limit = 100 expire_logs_days = 7 diff --git a/codes/linux/soft/gitlab-install.sh b/codes/linux/soft/gitlab-install.sh index f06b5e65..b3ce9f78 100644 --- a/codes/linux/soft/gitlab-install.sh +++ b/codes/linux/soft/gitlab-install.sh @@ -1,25 +1,112 @@ #!/usr/bin/env bash -################################################################################### +# ----------------------------------------------------------------------------------------------------- # 安装 Gitlab 脚本 # 仅适用于 CentOS7 发行版本 # @author: Zhang Peng -################################################################################### +# ----------------------------------------------------------------------------------------------------- -echo -e "\n>>>>>>>>> install gitlab" +# ------------------------------------------------------------------------------ env -echo -e "\n>>>>>>>>> 安装和配置必要依赖" -sudo yum install -y curl policycoreutils-python openssh-server -sudo systemctl enable sshd -sudo systemctl start sshd -sudo firewall-cmd --permanent --add-service=http -sudo systemctl reload firewalld +# Regular Color +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 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" +# Reset Color +export ENV_COLOR_RESET="$(tput sgr0)" -echo -e "\n>>>>>>>>> 安装和配置邮件服务" -sudo yum install postfix -sudo systemctl enable postfix -sudo systemctl start postfix +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 -echo -e "\n>>>>>>>>> 通过 yum 安装 gitlab" -curl -o- https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash -sudo EXTERNAL_URL="http://gitlab.example.com" yum install -y gitlab-ce +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} + +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} + +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} + +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} + +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} + +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} + +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} + +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} + +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + printInfo "$@" + return ${ENV_SUCCEED} + else + printError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} + +# ------------------------------------------------------------------------------ main + +printInfo ">>>> 安装 gitlab" +printInfo ">>>> 安装和配置必要依赖" +yum install -y curl policycoreutils-python openssh-server +systemctl enable sshd +systemctl start sshd +printInfo ">>>> 关闭防火墙" +firewall-cmd --permanent --add-service=http +systemctl reload firewalld +printInfo ">>>> 安装和配置邮件服务" +yum install postfix +systemctl enable postfix +systemctl start postfix +printInfo ">>>> 通过 yum 安装 gitlab" +curl -o- https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash +EXTERNAL_URL="http://gitlab.transwarp.io" yum install -y gitlab-ce diff --git a/codes/linux/lib/docker.sh b/codes/linux/soft/lib/docker.sh similarity index 100% rename from codes/linux/lib/docker.sh rename to codes/linux/soft/lib/docker.sh diff --git a/codes/linux/lib/file.sh b/codes/linux/soft/lib/file.sh similarity index 100% rename from codes/linux/lib/file.sh rename to codes/linux/soft/lib/file.sh diff --git a/codes/linux/lib/git.sh b/codes/linux/soft/lib/git.sh similarity index 100% rename from codes/linux/lib/git.sh rename to codes/linux/soft/lib/git.sh diff --git a/codes/linux/lib/java.sh b/codes/linux/soft/lib/java.sh similarity index 100% rename from codes/linux/lib/java.sh rename to codes/linux/soft/lib/java.sh diff --git a/codes/linux/lib/maven.sh b/codes/linux/soft/lib/maven.sh similarity index 100% rename from codes/linux/lib/maven.sh rename to codes/linux/soft/lib/maven.sh diff --git a/codes/linux/soft/lib/mysql.sh b/codes/linux/soft/lib/mysql.sh new file mode 100644 index 00000000..33d3faff --- /dev/null +++ b/codes/linux/soft/lib/mysql.sh @@ -0,0 +1,233 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# 数据库操作脚本 +# 支持操作: +# 备份 Mysql +# 恢复 Mysql +# @author: Zhang Peng +# ----------------------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------ env + +# Regular Color +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 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" +# Reset Color +export ENV_COLOR_RESET="$(tput sgr0)" + +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + printInfo "$@" + return ${ENV_SUCCEED} + else + printError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} + +# ------------------------------------------------------------------------------ env + +# Mysql 操作的环境变量,使用方法: +# 可以在执行本脚本之前,export 以下环境变量,否则将按照默认配置执行 + +# Mysql HOST(默认为 127.0.0.1) +ENV_MYSQL_HOST="${ENV_MYSQL_HOST:-127.0.0.1}" +# Mysql 端口(默认为 3306) +ENV_MYSQL_PORT=${ENV_MYSQL_PORT:-3306} +# Mysql 用户名(默认为 root) +ENV_MYSQL_USERNAME=${ENV_MYSQL_USERNAME:-root} +# Mysql 密码(默认为 root) +ENV_MYSQL_PASSWORD=${ENV_MYSQL_PASSWORD:-root} +# Mysql 备份文件最大数量(默认为 7 天) +ENV_BACKUP_MAX_NUM=${ENV_BACKUP_MAX_NUM:-7} + +# 备份模式:备份所有数据库(--all-databases)|备份指定数据库列表 +ENV_MYSQL_DATABASES="${ENV_MYSQL_DATABASES:---all-databases}" +# 备份路径 +ENV_MYSQL_BACKUP_DIR="${ENV_MYSQL_BACKUP_DIR:-/var/lib/mysql/backup}" +# 备份日志路径 +export ENV_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" + +magentaOutput "------------------------------------------------------------------------------" +magentaOutput "Mysql 脚本操作环境变量:" +magentaOutput "ENV_MYSQL_HOST:${ENV_MYSQL_HOST}" +magentaOutput "ENV_MYSQL_PORT:${ENV_MYSQL_PORT}" +magentaOutput "ENV_MYSQL_USERNAME:${ENV_MYSQL_USERNAME}" +magentaOutput "ENV_MYSQL_PASSWORD:${ENV_MYSQL_PASSWORD}" +magentaOutput "ENV_BACKUP_MAX_NUM:${ENV_BACKUP_MAX_NUM}" +magentaOutput "ENV_MYSQL_DATABASES:${ENV_MYSQL_DATABASES}" +magentaOutput "ENV_MYSQL_BACKUP_DIR:${ENV_MYSQL_BACKUP_DIR}" +magentaOutput "ENV_LOG_PATH:${ENV_LOG_PATH}" +magentaOutput "------------------------------------------------------------------------------" + + +# ------------------------------------------------------------------------------ functions + +# 备份所有 database(schema) +backupAllDatabase() { + + #时间戳 + local timestamp=$(date +"%Y%m%d") + + #备份所有数据库 + printInfo ">>>> 备份所有数据库开始" + mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} --all-databases > "${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql" 2>> ${ENV_LOG_PATH}; + + #检查备份结果是否成功 + if [[ "$?" != ${ENV_SUCCEED} ]]; then + printError "<<<< 备份所有数据库失败" + return ${ENV_FAILED} + fi + + # 压缩备份sql文件,删除旧的备份文件 + cd "${ENV_MYSQL_BACKUP_DIR}" + if [[ ! -f "${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql" ]]; then + printError "备份文件 ${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql 不存在" + return ${ENV_FAILED} + fi + #为节约硬盘空间,将数据库压缩 + tar zcf "all-${timestamp}.tar.gz" "all-${timestamp}.sql" > /dev/null + #删除原始文件,只留压缩后文件 + rm -f "all-${timestamp}.sql" + #只保存期限内的备份文件,其余删除 + find "${ENV_MYSQL_BACKUP_DIR} -name all-*.tar.gz -type f -mtime +${ENV_BACKUP_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1 + + printInfo "<<<< 备份所有数据库成功\n" + return ${ENV_SUCCEED} +} + +# 备份指定的 database(schema) +backupSelectedDatabase() { + + #时间戳 + local timestamp=$(date +"%Y%m%d") + + #数据库,如有多个库用空格分开 + databaseList="${ENV_MYSQL_DATABASES}" + + #备份指定数据库列表 + printInfo ">>>> 备份指定数据库开始" + for database in ${databaseList}; do + + printInfo "正在备份数据库:${database}" + mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} "${database}" > "${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" 2>> ${ENV_LOG_PATH}; + if [[ "$?" != 0 ]]; then + printError "<<<< 备份 ${database} 失败" + return ${ENV_FAILED} + fi + + # 压缩备份sql文件,删除旧的备份文件 + cd "${ENV_MYSQL_BACKUP_DIR}" + if [[ ! -f "${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" ]]; then + printError "备份文件 ${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql 不存在" + return ${ENV_FAILED} + fi + #为节约硬盘空间,将数据库压缩 + tar zcf "${database}-${timestamp}.tar.gz" "${database}-${timestamp}.sql" > /dev/null + #删除原始文件,只留压缩后文件 + rm -f "${database}-${timestamp}.sql" + #只保存期限内的备份文件,其余删除 + find "${ENV_MYSQL_BACKUP_DIR} -name ${database}-*.tar.gz -type f -mtime +${ENV_BACKUP_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1 + done + + printInfo "<<<< 备份数据库 ${ENV_MYSQL_DATABASES} 成功\n" + return ${ENV_SUCCEED} +} + +# 备份 Mysql +backupMysql() { + #创建备份目录及日志文件 + mkdir -p ${ENV_MYSQL_BACKUP_DIR} + if [[ ! -f ${ENV_LOG_PATH} ]]; then + touch ${ENV_LOG_PATH} + fi + + #正式备份数据库 + if [[ ${ENV_MYSQL_DATABASES} == "--all-databases" ]]; then + backupAllDatabase + else + backupSelectedDatabase + fi +} + +# 恢复 Mysql +recoveryMysql() { + #创建备份目录及日志文件 + mkdir -p ${ENV_MYSQL_BACKUP_DIR} + if [[ ! -f ${ENV_LOG_PATH} ]]; then + touch ${ENV_LOG_PATH} + fi + + printInfo ">>>> 恢复数据库开始" + + mysql -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} < ${ENV_LOG_PATH} + if [[ "$?" != 0 ]]; then + printError "<<<< 恢复数据库失败" + return ${ENV_FAILED} + fi + + printInfo "<<<< 恢复数据库成功\n" + return ${ENV_SUCCEED} +} diff --git a/codes/linux/lib/net.sh b/codes/linux/soft/lib/net.sh similarity index 100% rename from codes/linux/lib/net.sh rename to codes/linux/soft/lib/net.sh diff --git a/codes/linux/lib/nodejs.sh b/codes/linux/soft/lib/nodejs.sh similarity index 100% rename from codes/linux/lib/nodejs.sh rename to codes/linux/soft/lib/nodejs.sh diff --git a/codes/linux/lib/string.sh b/codes/linux/soft/lib/string.sh similarity index 100% rename from codes/linux/lib/string.sh rename to codes/linux/soft/lib/string.sh diff --git a/codes/linux/lib/utils.sh b/codes/linux/soft/lib/utils.sh similarity index 86% rename from codes/linux/lib/utils.sh rename to codes/linux/soft/lib/utils.sh index e96a8e5c..7370f364 100644 --- a/codes/linux/lib/utils.sh +++ b/codes/linux/soft/lib/utils.sh @@ -71,18 +71,38 @@ createLogFileIfNotExists() { fi } +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } - logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } - logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists @@ -92,11 +112,9 @@ logError() { printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } - printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } - printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } diff --git a/codes/linux/soft/mysql-backup.sh b/codes/linux/soft/mysql-backup.sh index 79aba5ab..2e6e9cfa 100644 --- a/codes/linux/soft/mysql-backup.sh +++ b/codes/linux/soft/mysql-backup.sh @@ -1,70 +1,39 @@ #!/usr/bin/env bash -#数据库IP -dbServer="127.0.0.1" -#数据库用户名 -dbUser="root" -#数据密码 -dbPassword="Tw#123456" -# 备份模式:备份所有数据库(ALL)|备份指定数据库列表(CUSTOM) -backupMode="ALL" -#backupMode="CUSTOM" -#数据库,如有多个库用空格分开 -databaseList="mysql sys" -#备份日期 -backupDate=`date +"%Y%m%d"` -#备份路径 -backupPath="/var/lib/mysql/backup" -#备份日志路径 -logPath="${backupPath}/mysql-backup.log" +# ----------------------------------------------------------------------------------------------------- +# Mysql 备份脚本 +# 可以通过 crond 服务,设置称为定时执行脚本: +# (1)执行 crontab -e 编辑定时执行任务,如:59 23 * * * /home/scripts/mysql-backup.sh +# (2)vi /etc/crontab,编辑 crontab 文件后保存,可以通过 crontab -l 查看 +# @author Zhang Peng +# ----------------------------------------------------------------------------------------------------- -#日志记录头部 -mkdir -p ${backupPath} -touch ${logPath} -echo "------------------------------------------------------------------" >> ${logPath} -beginTime=`date +"%Y-%m-%d %H:%M:%S"` -echo "备份数据库开始,时间:${beginTime}" >> ${logPath} +# ------------------------------------------------------------------------------ env +# Mysql Host +export ENV_MYSQL_HOST="127.0.0.1" +# Mysql 端口 +export ENV_MYSQL_PORT=3306 +# Mysql 用户名 +export ENV_MYSQL_USERNAME=root +# Mysql 密码 +export ENV_MYSQL_PASSWORD=root +# Mysql 备份文件最大数量 +export ENV_BACKUP_MAX_NUM=7 +# 备份模式:备份所有数据库(--all-databases)|备份指定数据库列表 +export ENV_MYSQL_DATABASES=--all-databases +# 备份路径 +export ENV_MYSQL_BACKUP_DIR=/var/lib/mysql/backup +# 备份日志路径 +export ENV_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" -#正式备份数据库 -if [[ ${backupMode} == "ALL" ]];then - filename="all-${backupDate}" - #备份所有数据库 - source=`mysqldump -h ${dbServer} -u ${dbUser} -p${dbPassword} --all-databases > ${backupPath}/${filename}.sql` - 2>> ${logPath}; - #备份成功以下操作 - if [[ "$?" == 0 ]];then - cd ${backupPath} - #为节约硬盘空间,将数据库压缩 - tar zcf ${filename}.tar.gz ${filename}.sql > /dev/null - #删除原始文件,只留压缩后文件 - rm -f ${backupPath}/${filename}.sql - #删除七天前备份,也就是只保存7天内的备份 - find ${backupPath} -name "*.tar.gz" -type f -mtime +7 -exec rm -rf {} \; > /dev/null 2>&1 - echo ">>>> 备份所有数据库成功!" >> ${logPath} - else - #备份失败则进行以下操作 - echo ">>>> 备份所有数据库失败!" >> ${logPath} - fi -else - #备份指定数据库列表 - for database in ${databaseList}; do - filename="${database}-${backupDate}" - source=`mysqldump -h ${dbServer} -u ${dbUser} -p${dbPassword} ${database} > ${backupPath}/${filename}.sql` 2>> ${logPath}; - #备份成功以下操作 - if [[ "$?" == 0 ]];then - cd ${backupPath} - #为节约硬盘空间,将数据库压缩 - tar zcf ${filename}.tar.gz ${filename}.sql > /dev/null - #删除原始文件,只留压缩后文件 - rm -f ${backupPath}/${filename}.sql - #删除七天前备份,也就是只保存7天内的备份 - find ${backupPath} -name "*.tar.gz" -type f -mtime +7 -exec rm -rf {} \; > /dev/null 2>&1 - echo ">>>> 备份数据库 ${database} 成功!" >> ${logPath} - else - #备份失败则进行以下操作 - echo ">>>> 备份数据库 ${database} 失败!" >> ${logPath} - fi - done +# ------------------------------------------------------------------------------ libs +LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` +if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh ]]; then + echo "${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh not exists!" + exit 1 fi -endTime=`date +"%Y-%m-%d %H:%M:%S"` -echo "备份数据库结束,时间:${endTime}" >> ${logPath} +source ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh + +# ------------------------------------------------------------------------------ main +# 执行备份方法 +backupMysql diff --git a/codes/linux/soft/mysql-install.sh b/codes/linux/soft/mysql-install.sh index 24e39a0b..84b09b0a 100644 --- a/codes/linux/soft/mysql-install.sh +++ b/codes/linux/soft/mysql-install.sh @@ -1,71 +1,143 @@ #!/usr/bin/env bash -################################################################################### -# 控制台颜色 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" -################################################################################### - -printf "${BLUE}" -cat << EOF - -################################################################################### -# 安装 mysql 脚本 -# @system: 适用于 Centos7 发行版本。 +# ----------------------------------------------------------------------------------------------------- +# 安装 Mysql 脚本 +# 仅适用于 CentOS7 发行版本 # @author: Zhang Peng -################################################################################### +# ----------------------------------------------------------------------------------------------------- -EOF -printf "${RESET}" +# ------------------------------------------------------------------------------ env + +# Regular Color +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 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" +# Reset Color +export ENV_COLOR_RESET="$(tput sgr0)" + +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) -printf "${GREEN}>>>>>>>> install mysql begin.${RESET}\n" +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} + +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} + +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} + +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} + +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} + +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} + +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} + +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} + +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + printInfo "$@" + return ${ENV_SUCCEED} + else + printError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} + +# ------------------------------------------------------------------------------ main + +printInfo ">>>> install mysql begin" command -v wget > /dev/null 2>&1 || { - printf "${RED}Require wget but it's not installed.${RESET}\n"; + printError "Require wget but it's not installed" exit 1; } command -v rpm > /dev/null 2>&1 || { - printf "${RED}Require rpm but it's not installed.${RESET}\n"; + printError "Require rpm but it's not installed" exit 1; } command -v yum > /dev/null 2>&1 || { - printf "${RED}Require yum but it's not installed.${RESET}\n"; + printError "Require yum but it's not installed" exit 1; } -# 使用 rpm 安装 mysql -printf "${CYAN}>>>> yum install mysql${RESET}\n" +printInfo ">>>> install mysql by rpm" wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm sudo rpm -Uvh mysql80-community-release-el7-3.noarch.rpm sudo yum install mysql-community-server -printf "${CYAN}>>>> replace settings${RESET}\n" +printInfo ">>>> modify my.cnf" cp /etc/my.cnf /etc/my.cnf.bak wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/mysql/my.cnf -O /etc/my.cnf -# 创建空的慢查询日志文件 + +printInfo ">>>> create mysql log file" mkdir -p /var/log/mysql touch /var/log/mysql/mysql-slow.log chmod 777 /var/log/mysql/mysql-slow.log chown -R mysql:mysql /var/log/mysql -# 设置开机启动 -printf "${CYAN}>>>> start mysqld${RESET}\n" -systemctl enable mysqld -systemctl start mysqld -systemctl daemon-reload - +printInfo ">>>> modify limits.conf" cat >> /etc/security/limits.conf << EOF mysql soft nofile 65536 mysql hard nofile 65536 EOF -password=$(grep "password" /var/log/mysqld.log | awk '{print $NF}') -printf "临时密码为:${PURPLE}${password}${RESET},请登录 mysql 后重置新密码\n" +printInfo ">>>> start mysqld" +systemctl enable mysqld +systemctl start mysqld +systemctl daemon-reload + +printInfo ">>>> 管理员密码如下,请登录 mysql 后重置新密码:" +password=$(grep "password" /var/log/mysql/mysql.log | awk '{print $NF}') +blueOutput "${password}" -printf "${GREEN}<<<<<<<< install mysql end.${RESET}\n" +printInfo "<<<< install mysql success" diff --git a/codes/linux/soft/mysql-recovery.sh b/codes/linux/soft/mysql-recovery.sh new file mode 100644 index 00000000..eb7bf412 --- /dev/null +++ b/codes/linux/soft/mysql-recovery.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# MYSQL 恢复脚本 +# @author Zhang Peng +# ----------------------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------ env +# Mysql Host +export ENV_MYSQL_HOST="127.0.0.1" +# Mysql 端口 +export ENV_MYSQL_PORT=3306 +# Mysql 用户名 +export ENV_MYSQL_USERNAME=root +# Mysql 密码 +export ENV_MYSQL_PASSWORD=root +# Mysql 备份文件最大数量 +export ENV_BACKUP_MAX_NUM=7 +# 备份模式:备份所有数据库(--all-databases)|备份指定数据库列表 +export ENV_MYSQL_DATABASES=--all-databases +# 备份路径 +export ENV_MYSQL_BACKUP_DIR=/var/lib/mysql/backup +# 备份日志路径 +export ENV_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" + +# ------------------------------------------------------------------------------ libs +LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` +if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh ]]; then + echo "${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh not exists!" + exit 1 +fi +source ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh + +# ------------------------------------------------------------------------------ main +# 执行备份方法 +recoveryMysql diff --git a/docs/docker/kubernetes.md b/docs/docker/kubernetes.md index e3fe13eb..d16b6c57 100644 --- a/docs/docker/kubernetes.md +++ b/docs/docker/kubernetes.md @@ -49,7 +49,7 @@ Kubernetes 主要由以下几个核心组件组成: K8S 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。 -
![img](http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp) - `Pod` - K8S 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 K8S 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。 - `Node` - Node 是 Pod 真正运行的主机,可以是物理机,也可以是虚拟机。为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 docker 或者 rkt)、`kubelet` 和 `kube-proxy` 服务。 diff --git a/docs/linux/cli/linux-cli-dir.md b/docs/linux/cli/linux-cli-dir.md index 6969934e..c5c9ad9c 100644 --- a/docs/linux/cli/linux-cli-dir.md +++ b/docs/linux/cli/linux-cli-dir.md @@ -8,7 +8,7 @@ linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -
![img](http://dunwu.test.upcdn.net/cs/os/linux/linux-folders.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/linux-folders.png!zp) ### 1.2. Linux 文件属性 @@ -35,7 +35,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot 每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 -
![img](http://dunwu.test.upcdn.net/snap/20180920180927171909.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20180920180927171909.png!zp) 从左至右用 0-9 这些数字来表示。 diff --git "a/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" "b/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" index 1210a346..5921f595 100644 --- "a/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" +++ "b/docs/linux/cli/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" @@ -4,7 +4,7 @@ _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](RE # 命令行的艺术 -
![img](https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+![img](https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - [前言](#前言) - [基础](#基础) @@ -18,7 +18,7 @@ _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](RE - [更多资源](#更多资源) - [免责声明](#免责声明) -
![img](https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/cowsay.png)
+![img](https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/cowsay.png) 熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际上,它会提高你作为工程师的灵活性以及生产力。本文是一份我在 Linux 上工作时,发现的一些命令行使用技巧的摘要。有些技巧非常基础,而另一些则相当复杂,甚至晦涩难懂。这篇文章并不长,但当你能够熟练掌握这里列出的所有技巧时,你就学会了很多关于命令行的东西了。 @@ -616,6 +616,6 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir ## 授权条款 -
![img](http://creativecommons.org/licenses/by-sa/4.0/)
+![img](http://creativecommons.org/licenses/by-sa/4.0/) 本文使用授权协议 [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)。 diff --git a/docs/linux/ops/samba.md b/docs/linux/ops/samba.md index e3187e7c..dc0e4c50 100644 --- a/docs/linux/ops/samba.md +++ b/docs/linux/ops/samba.md @@ -149,7 +149,7 @@ Windows: 访问:`\\<你的ip>\<你的共享路径>` : -
![img](http://dunwu.test.upcdn.net/snap/20180920180928161334.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20180920180928161334.png!zp) Mac: diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 95e78782..5fe92f4b 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -147,7 +147,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > > \> 如果你认为单词是由 blank 字符分隔符,那么你需要使用大写的 E 和 W。(注:程序语句) > - >
![img](http://upload-images.jianshu.io/upload_images/3101171-46f752c581d79057.jpg)
+ > ![img](http://upload-images.jianshu.io/upload_images/3101171-46f752c581d79057.jpg) 下面,让我来说说最强的光标移动: @@ -196,7 +196,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。 > - `3fa` → 在当前行查找第三个出现的 a。 > - `F` 和 `T` → 和 `f` 和 `t` 一样,只不过是相反方向。 ->
![img](http://upload-images.jianshu.io/upload_images/3101171-00835b8316330c58.jpg)
+> ![img](http://upload-images.jianshu.io/upload_images/3101171-00835b8316330c58.jpg) 还有一个很有用的命令是 `dt"` → 删除所有的内容,直到遇到双引号—— `"。` @@ -218,7 +218,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `v2i)` → 会选择 `map (+) ("foo")` > - `v2a)` → 会选择 `(map (+) ("foo"))` -
![img](http://upload-images.jianshu.io/upload_images/3101171-0b109d66a6111c83.png)
+![img](http://upload-images.jianshu.io/upload_images/3101171-0b109d66a6111c83.png) #### 2.4.3. 块操作: `` @@ -229,7 +229,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 - `` → 向下移动 (你也可以使用 hjkl 来移动光标,或是使用%,或是别的) - `I-- [ESC]` → I 是插入,插入“`--`”,按 ESC 键来为每一行生效。 -
![img](http://upload-images.jianshu.io/upload_images/3101171-8b093a0f65707949.gif?imageMogr2/auto-orient/strip)
+![img](http://upload-images.jianshu.io/upload_images/3101171-8b093a0f65707949.gif?imageMogr2/auto-orient/strip) 在 Windows 下的 vim,你需要使用 `` 而不是 `` ,`` 是拷贝剪贴板。 @@ -237,7 +237,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 在 Insert 模式下,你可以输入一个词的开头,然后按 `或是,自动补齐功能就出现了……` -
![img](http://upload-images.jianshu.io/upload_images/3101171-e2ae877e67880ff7.gif?imageMogr2/auto-orient/strip)
+![img](http://upload-images.jianshu.io/upload_images/3101171-e2ae877e67880ff7.gif?imageMogr2/auto-orient/strip) #### 2.4.5. 宏录制: `qa` 操作序列 `q`, `@a`, `@@` @@ -266,7 +266,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > > - 现在做 `100@@` 会创建新的 100 行,并把数据增加到 103. -
![img](http://upload-images.jianshu.io/upload_images/3101171-f1889f8bca723964.gif?imageMogr2/auto-orient/strip)
+![img](http://upload-images.jianshu.io/upload_images/3101171-f1889f8bca723964.gif?imageMogr2/auto-orient/strip) #### 2.4.6. 可视化选择: `v`,`V`,`` @@ -276,7 +276,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 - `<` 或 `>` → 左右缩进 - `=` → 自动给缩进 (注:这个功能相当强大,我太喜欢了) -
![img](http://upload-images.jianshu.io/upload_images/3101171-fe1e19983fca213f.gif?imageMogr2/auto-orient/strip)
+![img](http://upload-images.jianshu.io/upload_images/3101171-fe1e19983fca213f.gif?imageMogr2/auto-orient/strip) 在所有被选择的行后加上点东西: @@ -285,7 +285,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 - `$` 到行最后 - `A`, 输入字符串,按 `ESC。` -
![img](http://upload-images.jianshu.io/upload_images/3101171-b192601247334c4e.gif?imageMogr2/auto-orient/strip)
+![img](http://upload-images.jianshu.io/upload_images/3101171-b192601247334c4e.gif?imageMogr2/auto-orient/strip) #### 2.4.7. 分屏: `:split` 和 `vsplit`. @@ -296,7 +296,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 > - `_` (或 `|`) : 最大化尺寸 (| 垂直分屏) > - `+` (或 `-`) : 增加尺寸 -
![img](http://upload-images.jianshu.io/upload_images/3101171-f329d01e299cb366.gif?imageMogr2/auto-orient/strip)
+![img](http://upload-images.jianshu.io/upload_images/3101171-f329d01e299cb366.gif?imageMogr2/auto-orient/strip) ## 3. Vim Cheat Sheet @@ -308,33 +308,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet.png!zp) ### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/basic-vim-cheat-sheet.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/basic-vim-cheat-sheet.png!zp) ### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png!zp) ### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-02.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-02.png!zp) ### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-01.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-01.png!zp) -
![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-02.png!zp)
+![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-02.png!zp) ## 4. 资料 diff --git a/docs/linux/soft/elastic/elastic-beats.md b/docs/linux/soft/elastic/elastic-beats.md index b6bce288..3b33d540 100644 --- a/docs/linux/soft/elastic/elastic-beats.md +++ b/docs/linux/soft/elastic/elastic-beats.md @@ -18,7 +18,7 @@ Beats 是安装在服务器上的数据中转代理。 Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。 -
![img](https://www.elastic.co/guide/en/beats/libbeat/current/images/beats-platform.png)
+![img](https://www.elastic.co/guide/en/beats/libbeat/current/images/beats-platform.png) Beats 有多种类型,可以根据实际应用需要选择合适的类型。 @@ -47,7 +47,7 @@ Filebeat带有内部模块(auditd,Apache,Nginx,System和MySQL),可 FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。 -
![img](https://www.elastic.co/guide/en/beats/filebeat/current/images/filebeat.png)
+![img](https://www.elastic.co/guide/en/beats/filebeat/current/images/filebeat.png) ## 安装 diff --git a/docs/linux/soft/elastic/elastic-kibana.md b/docs/linux/soft/elastic/elastic-kibana.md index 41a43a44..07bcb3ce 100644 --- a/docs/linux/soft/elastic/elastic-kibana.md +++ b/docs/linux/soft/elastic/elastic-kibana.md @@ -4,7 +4,7 @@ 单击侧面导航栏中的 `Discover` ,可以显示 `Kibana` 的数据查询功能功能。 -
![img](https://www.elastic.co/guide/en/kibana/current/images/tutorial-discover.png)
+![img](https://www.elastic.co/guide/en/kibana/current/images/tutorial-discover.png) 在搜索栏中,您可以输入Elasticsearch查询条件来搜索您的数据。您可以在 `Discover` 页面中浏览结果并在 `Visualize` 页面中创建已保存搜索条件的可视化。 @@ -14,7 +14,7 @@ 默认情况下,每个匹配文档都显示所有字段。要选择要显示的文档字段,请将鼠标悬停在“可用字段”列表上,然后单击要包含的每个字段旁边的添加按钮。例如,如果只添加account_number,则显示将更改为包含五个帐号的简单列表: -
![img](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-discover-3.png)
+![img](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-discover-3.png) ### 查询语义 diff --git a/docs/linux/soft/elastic/elastic-logstash.md b/docs/linux/soft/elastic/elastic-logstash.md index 5c4abb2c..6acf5067 100644 --- a/docs/linux/soft/elastic/elastic-logstash.md +++ b/docs/linux/soft/elastic/elastic-logstash.md @@ -32,7 +32,7 @@ Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`f 这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。 -
![img](https://www.elastic.co/guide/en/logstash/current/static/images/basic_logstash_pipeline.png)
+![img](https://www.elastic.co/guide/en/logstash/current/static/images/basic_logstash_pipeline.png) - input 负责从数据源采集数据。 - filter 将数据修改为你指定的格式或内容。 diff --git a/docs/linux/soft/elastic/elastic-quickstart.md b/docs/linux/soft/elastic/elastic-quickstart.md index d3d12814..25ab374f 100644 --- a/docs/linux/soft/elastic/elastic-quickstart.md +++ b/docs/linux/soft/elastic/elastic-quickstart.md @@ -32,7 +32,7 @@ ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/ ### Elastic 架构 -
![img](https://www.elastic.co/guide/en/logstash/current/static/images/deploy3.png)
+![img](https://www.elastic.co/guide/en/logstash/current/static/images/deploy3.png) > **说明** > @@ -274,7 +274,7 @@ output { 大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。 -
![img](http://upload-images.jianshu.io/upload_images/3101171-cd876d79a14955b0.png)
+![img](http://upload-images.jianshu.io/upload_images/3101171-cd876d79a14955b0.png) ## 资料 diff --git a/docs/linux/soft/gitlab-install.md b/docs/linux/soft/gitlab-ops.md similarity index 73% rename from docs/linux/soft/gitlab-install.md rename to docs/linux/soft/gitlab-ops.md index 3ba93bc5..8827ab34 100644 --- a/docs/linux/soft/gitlab-install.md +++ b/docs/linux/soft/gitlab-ops.md @@ -1,31 +1,16 @@ -# Gitlab 安装 +# Gitlab 运维 -> 环境: -> -> OS: CentOS7 +## gitlab 安装 - +### Gitlab 的普通安装 -- [安装 gitlab](#安装-gitlab) - - [常规安装 gitlab](#常规安装-gitlab) - - [Docker 安装 gitlab](#docker-安装-gitlab) -- [安装 gitlab-ci-multi-runner](#安装-gitlab-ci-multi-runner) - - [常规安装 gitlab-ci-multi-runner](#常规安装-gitlab-ci-multi-runner) - - [Docker 安装 gitlab-ci-multi-runner](#docker-安装-gitlab-ci-multi-runner) -- [自签名证书](#自签名证书) - - [创建证书](#创建证书) -- [gitlab 配置](#gitlab-配置) -- [更多内容](#更多内容) - - +#### 下载 -## 安装 gitlab -### 常规安装 gitlab 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 -
![img](http://dunwu.test.upcdn.net/snap/20190129155838.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20190129155838.png!zp) 以 CentOS7 为例: @@ -65,7 +50,7 @@ sudo EXTERNAL_URL="http://gitlab.example.com" yum install -y gitlab-ce 安装完成后,即可通过默认的 root 账户进行登录。更多细节可以参考:[documentation for detailed instructions on installing and configuration](https://docs.gitlab.com/omnibus/README.html#installation-and-configuration-using-omnibus-package) -### Docker 安装 gitlab +### Gitlab 的 Docker 安装 拉取镜像 @@ -87,9 +72,9 @@ docker run -d \ gitlab/gitlab-ce ``` -
![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp) -## 安装 gitlab-ci-multi-runner +## gitlab-ci-multi-runner 安装 > 参考:https://docs.gitlab.com/runner/install/ @@ -138,7 +123,7 @@ sudo gitlab-runner register URL 和令牌信息在 Gitlab 的 Runner 管理页面获取: -
![img](http://dunwu.test.upcdn.net/snap/20190129163100.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20190129163100.png!zp) ``` Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) @@ -302,31 +287,31 @@ sudo gitlab-ctl restart 3. 打开 **Profile settings**. -
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings.png)
+![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings.png) 4. 跳转到 **SSH keys** tab 页 -
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys.png)
+![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys.png) 5. 黏贴你的 SSH 公钥内容到 Key 文本框 -
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png)
+![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png) 6. 为了便于识别,你可以为其命名 -
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_title.png)
+![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_title.png) 7. 点击 **Add key** 将 SSH 公钥添加到 GitLab -
![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_single_key.png)
+![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_single_key.png) ### 创建项目 -
![img](http://dunwu.test.upcdn.net/snap/20190131150658.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20190131150658.png!zp) 输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。 -
![img](http://dunwu.test.upcdn.net/snap/20190131150759.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20190131150759.png!zp) ### 克隆项目到本地 @@ -338,25 +323,44 @@ sudo gitlab-ctl restart 依次点击 **Project’s Dashboard** > **Issues** > **New Issue** 可以新建 Issue -
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_tracker_list.png)
+![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_tracker_list.png) 在项目中直接添加 issue -
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue.png)
+![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue.png) 在未关闭 issue 中,点击 **New Issue** 添加 issue -
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_open_issue.png)
+![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_open_issue.png) 通过项目面板添加 issue -
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_projects_dashboard.png)
+![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_projects_dashboard.png) 通过 issue 面板添加 issue -
![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png)
+![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png) + +## gitlab 权限配置 + +### 用户组的权限 + +- 用户组有这几种权限的概念:`Guest、Reporter、Developer、Master、Owner` +- 这个概念在设置用户组的时候会遇到,叫做:`Add user(s) to the group`,比如链接:`https:///` + +| 行为 | Guest | Reporter | Developer | Master | Owner | +| ---------- | ----- | -------- | --------- | ------ | ----- | +| 浏览组 | ✓ | ✓ | ✓ | ✓ | ✓ | +| 编辑组 | | | | | ✓ | +| 创建项目 | | | | ✓ | ✓ | +| 管理组成员 | | | | | ✓ | +| 移除组 | | | | | | + +## 资料 -## 更多内容 +- 官网:https://about.gitlab.com/ +- 中文网:https://www.gitlab.com.cn/ +- 官网下载:https://about.gitlab.com/downloads/ +- 官网安装说明:https://about.gitlab.com/installation/#centos-7 -- **引申** - - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) +- [操作系统、运维部署总结系列](https://github.com/dunwu/OS) \ No newline at end of file diff --git a/docs/linux/soft/jdk-install.md b/docs/linux/soft/jdk-install.md index 6dcb3f40..e4477193 100644 --- a/docs/linux/soft/jdk-install.md +++ b/docs/linux/soft/jdk-install.md @@ -22,13 +22,13 @@ a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/java b. 选择需要的版本: -
![img](http://dunwu.test.upcdn.net/snap/20180920181010164121.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20180920181010164121.png!zp) c. 选择对应操作系统的安装包: Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。 -
![img](http://dunwu.test.upcdn.net/snap/20180920181010164308.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20180920181010164308.png!zp) (2)运行安装包,按提示逐步安装 @@ -51,11 +51,11 @@ Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系 a. 安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置"; -
![img](https://www.runoob.com/wp-content/uploads/2013/12/win-java1.png)
+![img](https://www.runoob.com/wp-content/uploads/2013/12/win-java1.png) b. 选择"高级"选项卡,点击"环境变量"; -
![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win2.png)
+![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win2.png) 然后就会出现如下图所示的画面: @@ -78,7 +78,7 @@ a. "开始"->"运行",键入"cmd"; b. 键入命令: **java -version**、**java**、**javac** 几个命令,出现以下信息,说明环境变量配置成功; -
![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win9.png)
+![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win9.png) ## Linux 系统安装方法 diff --git a/docs/linux/soft/jenkins-ops.md b/docs/linux/soft/jenkins-ops.md index 0a4f0a32..2495d792 100644 --- a/docs/linux/soft/jenkins-ops.md +++ b/docs/linux/soft/jenkins-ops.md @@ -98,7 +98,7 @@ Jenkins 是一个强大的 CI 工具,虽然本身使用 Java 开发,但也 General 是构建任务的一些基本配置。名称,描述之类的。 -![](http://dunwu.test.upcdn.net/snap/20200310221814.png) +![img](http://dunwu.test.upcdn.net/snap/20200310221814.png) 重要配置项: @@ -111,7 +111,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Source Code Management**,即源码管理,就是配置你代码的存放位置。 -![](http://dunwu.test.upcdn.net/snap/20200310222110.png) +![img](http://dunwu.test.upcdn.net/snap/20200310222110.png) - **Git:** 支持主流的 Github 和 Gitlab 代码仓库。因我们的研发团队使用的是 gitlab,所以下面我只会对该项进行介绍。 - **Repository URL**:仓库地址。 @@ -124,7 +124,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Build Triggers**,即构建触发器,用于构建任务的触发器。 -![](http://dunwu.test.upcdn.net/snap/20200310222608.png) +![img](http://dunwu.test.upcdn.net/snap/20200310222608.png) 配置说明: @@ -138,7 +138,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Build Environment**,即构建环境,配置构建前的一些准备工作,如指定构建工具。 -![](http://dunwu.test.upcdn.net/snap/20200310223004.png) +![img](http://dunwu.test.upcdn.net/snap/20200310223004.png) ### Build @@ -146,7 +146,7 @@ Build,即构建。 点击下图中的 Add build step 按钮,会弹出一个构建任务菜单,可以根据实际需要来选择。 -![](http://dunwu.test.upcdn.net/snap/20200310223241.png) +![img](http://dunwu.test.upcdn.net/snap/20200310223241.png) 【说明】 @@ -162,11 +162,11 @@ Build,即构建。 **Post-build Actions**,即构建后操作,用于构建完本项目的一些后续操作,比如生成相应的代码测试报告。 -![](http://dunwu.test.upcdn.net/snap/20200310224106.png) +![img](http://dunwu.test.upcdn.net/snap/20200310224106.png) -![](http://dunwu.test.upcdn.net/snap/20200310224254.png) +![img](http://dunwu.test.upcdn.net/snap/20200310224254.png) -![](http://dunwu.test.upcdn.net/snap/20200310224331.png) +![img](http://dunwu.test.upcdn.net/snap/20200310224331.png) 个人较常用的配置: @@ -182,13 +182,13 @@ Build,即构建。 ### 开始构建 -![](http://dunwu.test.upcdn.net/snap/20200310224927.png) +![img](http://dunwu.test.upcdn.net/snap/20200310224927.png) 如上图所示,一切配置好后,即可点击 **Build Now** 开始构建。 ### 构建结果 -![](http://dunwu.test.upcdn.net/snap/20200310225234.png) +![img](http://dunwu.test.upcdn.net/snap/20200310225234.png) - **构建状态** - **Successful 蓝色**:构建完成,并且被认为是稳定的。 diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 7d6e3b86..374234b4 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -4,38 +4,19 @@ > > 关键词:maven, nexus > -> 部署环境 +> 部署环境: > > - Nexus 3.13.0 > - JDK 1.8 > - Maven 3.5.4 - - -- [1. 安装 Nexus](#1-安装-nexus) -- [2. 启动/停止 Nexus](#2-启动停止-nexus) -- [3. 搭建 Maven 私服](#3-搭建-maven-私服) - - [3.1. 配置仓库](#31-配置仓库) - - [3.2. 配置 settings.xml](#32-配置-settingsxml) - - [3.3. 配置 pom.xml](#33-配置-pomxml) - - [3.4. 执行 maven 构建](#34-执行-maven-构建) -- [4. 开机自启动](#4-开机自启动) -- [5. Nexus 备份和迁移](#5-nexus-备份和迁移) - - [5.1. 备份](#51-备份) - - [5.2. 迁移](#52-迁移) -- [6. FAQ](#6-faq) - - [6.1. 配置 INSTALL4J_JAVA_HOME](#61-配置-install4j_java_home) -- [7. 参考资料](#7-参考资料) - - - ## 1. 安装 Nexus 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 ![img](http://dunwu.test.upcdn.net/snap/20181127203029.png!zp) -本人希望将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 +本人将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 这里,如果想通过命令方式直接下载(比如用脚本安装),可以在[官方历史发布版本页面](https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3)中找到合适版本,然后执行以下命令: @@ -46,8 +27,8 @@ tar -zxf nexus-unix.tar.gz 解压后,有两个目录: -- nexus-3.13.0-01 - 包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 -- sonatype-work - 包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 +- `nexus-3.13.0-01` - 包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 +- `sonatype-work` - 包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 ## 2. 启动/停止 Nexus diff --git a/docs/linux/soft/svn-ops.md b/docs/linux/soft/svn-ops.md index 66d863fb..17469291 100644 --- a/docs/linux/soft/svn-ops.md +++ b/docs/linux/soft/svn-ops.md @@ -145,7 +145,7 @@ $ vi /etc/sysconfig/svnserve 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: -
![img](http://dunwu.test.upcdn.net/snap/20190129175443.png!zp)
+![img](http://dunwu.test.upcdn.net/snap/20190129175443.png!zp) ## 2. 参考资料 From 446670523501341f71b47bbc4eae763ed9b19cb8 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Sun, 26 Apr 2020 10:34:55 +0800 Subject: [PATCH 40/64] update docs and scripts --- codes/linux/soft/gitlab-install.sh | 24 +- codes/linux/soft/gitlab/gitlab-backup.sh | 46 ++++ codes/linux/soft/gitlab/gitlab.sh | 191 +++++++++++++++ codes/linux/soft/lib/mysql.sh | 18 +- codes/linux/soft/mysql-install.sh | 6 +- codes/linux/soft/nexus-install.sh | 158 ++++++++++-- ...06\347\240\201\344\274\240\350\276\223.sh" | 19 ++ docs/linux/ops/firewalld.md | 2 +- docs/linux/soft/gitlab-ops.md | 83 ++++++- docs/linux/soft/nexus-ops.md | 229 ++++++++++++------ docs/linux/soft/svn-ops.md | 58 +++-- 11 files changed, 672 insertions(+), 162 deletions(-) create mode 100644 codes/linux/soft/gitlab/gitlab-backup.sh create mode 100644 codes/linux/soft/gitlab/gitlab.sh create mode 100644 "codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\205\215\345\257\206\347\240\201\344\274\240\350\276\223.sh" diff --git a/codes/linux/soft/gitlab-install.sh b/codes/linux/soft/gitlab-install.sh index b3ce9f78..971a3a48 100644 --- a/codes/linux/soft/gitlab-install.sh +++ b/codes/linux/soft/gitlab-install.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- -# 安装 Gitlab 脚本 +# Gitlab 安装脚本 # 仅适用于 CentOS7 发行版本 +# 官方安裝參考:https://about.gitlab.com/install/#centos-7 # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- @@ -37,11 +38,6 @@ export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions -# 显示打印日志的时间 -SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") -# 那个用户在操作 -USER=$(whoami) - redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } @@ -95,18 +91,20 @@ callAndLog () { # ------------------------------------------------------------------------------ main -printInfo ">>>> 安装 gitlab" -printInfo ">>>> 安装和配置必要依赖" +# 官方安裝參考:https://about.gitlab.com/install/#centos-7 +printInfo ">>>> install gitlab on Centos7" +printInfo ">>>> Install and configure the necessary dependencies" yum install -y curl policycoreutils-python openssh-server systemctl enable sshd systemctl start sshd -printInfo ">>>> 关闭防火墙" -firewall-cmd --permanent --add-service=http -systemctl reload firewalld -printInfo ">>>> 安装和配置邮件服务" +printInfo ">>>> open http, https and ssh access in the system firewall" +sudo firewall-cmd --permanent --add-service=http +sudo firewall-cmd --permanent --add-service=https +sudo systemctl reload firewalld +printInfo ">>>> install postfix" yum install postfix systemctl enable postfix systemctl start postfix -printInfo ">>>> 通过 yum 安装 gitlab" +printInfo ">>>> Add the GitLab package repository and install the package" curl -o- https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash EXTERNAL_URL="http://gitlab.transwarp.io" yum install -y gitlab-ce diff --git a/codes/linux/soft/gitlab/gitlab-backup.sh b/codes/linux/soft/gitlab/gitlab-backup.sh new file mode 100644 index 00000000..0a341510 --- /dev/null +++ b/codes/linux/soft/gitlab/gitlab-backup.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# Gitlab 操作脚本 +# 支持操作: +# 备份 Gitlab +# 恢复 Gitlab +# @author: Zhang Peng +# ----------------------------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------ env + +# Gilab 操作的环境变量,使用方法: +# 可以在执行本脚本之前,export 以下环境变量,否则将按照默认配置执行 + +# Gitlab 备份文件最大数量(默认为 7 天) +export ENV_BACKUP_MAX_NUM=7 +# 备份路径 +export ENV_GITLAB_BACKUP_DIR="/var/opt/gitlab/backups" +# 备份日志路径 +export ENV_LOG_PATH="${ENV_GITLAB_BACKUP_DIR}/gitlab-backup.log" + +ENV_REMOTE_USER=root +ENV_REMOTE_HOST=172.22.6.42 + +# ------------------------------------------------------------------------------ load libs + +GIT_SCRIPTS_DIR=$(cd `dirname $0`; pwd) +if [[ ! -x ${GIT_SCRIPTS_DIR}/gitlab.sh ]]; then + echo "${GIT_SCRIPTS_DIR}/gitlab.sh not exists!" + exit 1 +fi +source ${GIT_SCRIPTS_DIR}/gitlab.sh + +# ------------------------------------------------------------------------------ functions + +backupGitlab +if [[ "$?" != ${ENV_SUCCEED} ]]; then + printError "退出" + exit ${ENV_FAILED} +fi + +#将备份文件传输到gitlab备份服务器 +BACKUP_FILE=$(find "${ENV_GITLAB_BACKUP_DIR}" -type f -name "*gitlab_backup.tar" | xargs ls -t | head -1) +scp ${BACKUP_FILE} ${ENV_REMOTE_USER}@${ENV_REMOTE_HOST}:${ENV_GITLAB_BACKUP_DIR} diff --git a/codes/linux/soft/gitlab/gitlab.sh b/codes/linux/soft/gitlab/gitlab.sh new file mode 100644 index 00000000..a2ba3c66 --- /dev/null +++ b/codes/linux/soft/gitlab/gitlab.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------------------------------- +# Gitlab 操作脚本 +# 支持操作: +# 备份 Gitlab +# 恢复 Gitlab +# @author: Zhang Peng +# ----------------------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------ env + +# Regular Color +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 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" +# Reset Color +export ENV_COLOR_RESET="$(tput sgr0)" + +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + printInfo "$@" + return ${ENV_SUCCEED} + else + printError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} + +# ------------------------------------------------------------------------------ env + +# Gilab 操作的环境变量,使用方法: +# 可以在执行本脚本之前,export 以下环境变量,否则将按照默认配置执行 + +# Gitlab 备份文件最大数量(默认为 7 天) +ENV_BACKUP_MAX_NUM=${ENV_BACKUP_MAX_NUM:-2} +# 备份路径 +ENV_GITLAB_BACKUP_DIR="${ENV_GITLAB_BACKUP_DIR:-/var/opt/gitlab/backups}" +# 备份日志路径 +ENV_LOG_PATH="${ENV_GITLAB_BACKUP_DIR}/gitlab-backup.log" + +magentaOutput "------------------------------------------------------------------------------" +magentaOutput "Gitlab 脚本操作环境变量:" +magentaOutput "ENV_BACKUP_MAX_NUM:${ENV_BACKUP_MAX_NUM}" +magentaOutput "ENV_GITLAB_BACKUP_DIR:${ENV_GITLAB_BACKUP_DIR}" +magentaOutput "ENV_LOG_PATH:${ENV_LOG_PATH}" +magentaOutput "------------------------------------------------------------------------------" + + +# ------------------------------------------------------------------------------ functions + +# 安装 Gitlab +installGitlab() { + # 官方安裝參考:https://about.gitlab.com/install/#centos-7 + printInfo ">>>> install gitlab on Centos7" + printInfo ">>>> Install and configure the necessary dependencies" + yum install -y curl policycoreutils-python openssh-server + systemctl enable sshd + systemctl start sshd + printInfo ">>>> open http, https and ssh access in the system firewall" + sudo firewall-cmd --permanent --add-service=http + sudo firewall-cmd --permanent --add-service=https + sudo systemctl reload firewalld + printInfo ">>>> install postfix" + yum install postfix + systemctl enable postfix + systemctl start postfix + printInfo ">>>> Add the GitLab package repository and install the package" + curl -o- https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash + EXTERNAL_URL="http://gitlab.transwarp.io" yum install -y gitlab-ce +} + +# 备份 Gitlab +backupGitlab() { + + #时间戳 + local beginTime=$(date +'%Y-%m-%d %H:%M:%S') + + #备份所有数据库 + printInfo ">>>> 备份 Gitlab 开始" + gitlab-rake gitlab:backup:create >> ${ENV_LOG_PATH}; + + #检查备份结果是否成功 + if [[ "$?" != ${ENV_SUCCEED} ]]; then + printError "<<<< 备份 Gitlab 失败" + return ${ENV_FAILED} + fi + + # 压缩备份sql文件,删除旧的备份文件 + cd "${ENV_GITLAB_BACKUP_DIR}" + + #只保存期限内的备份文件,其余删除 + find "${ENV_GITLAB_BACKUP_DIR} -name *_gitlab_backup.tar -type f -mtime +${ENV_BACKUP_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1 + + local endTime=$(date +'%Y-%m-%d %H:%M:%S') + local beginSeconds=$(date --date="${beginTime}" +%s) + local endSeconds=$(date --date="${endTime}" +%s) + printInfo "本次备份执行时间:$((endSeconds-beginSeconds)) s" + printInfo "<<<< 备份 Gitlab 成功\n" + return ${ENV_SUCCEED} +} + +# 恢复 Mysql +recoveryGitlab() { + + local version=$1 + if [[ !version ]]; then + printError "<<<< 未指定恢复版本" + return ${ENV_FAILED} + fi + + #创建备份目录及日志文件 + mkdir -p ${ENV_GITLAB_BACKUP_DIR} + if [[ ! -f ${ENV_LOG_PATH} ]]; then + touch ${ENV_LOG_PATH} + fi + + printInfo ">>>> 恢复 Gitlab 开始" + + gitlab-ctl stop unicorn + gitlab-ctl stop sidekiq + + gitlab-rake gitlab:backup:restore BACKUP=${version} + if [[ "$?" != 0 ]]; then + printError "<<<< 恢复 Gitlab 失败" + return ${ENV_FAILED} + fi + + printInfo "<<<< 恢复 Gitlab 成功\n" + return ${ENV_SUCCEED} +} diff --git a/codes/linux/soft/lib/mysql.sh b/codes/linux/soft/lib/mysql.sh index 33d3faff..27e5cbdd 100644 --- a/codes/linux/soft/lib/mysql.sh +++ b/codes/linux/soft/lib/mysql.sh @@ -108,7 +108,7 @@ ENV_MYSQL_DATABASES="${ENV_MYSQL_DATABASES:---all-databases}" # 备份路径 ENV_MYSQL_BACKUP_DIR="${ENV_MYSQL_BACKUP_DIR:-/var/lib/mysql/backup}" # 备份日志路径 -export ENV_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" +ENV_MYSQL_BACKUP_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" magentaOutput "------------------------------------------------------------------------------" magentaOutput "Mysql 脚本操作环境变量:" @@ -119,7 +119,7 @@ magentaOutput "ENV_MYSQL_PASSWORD:${ENV_MYSQL_PASSWORD}" magentaOutput "ENV_BACKUP_MAX_NUM:${ENV_BACKUP_MAX_NUM}" magentaOutput "ENV_MYSQL_DATABASES:${ENV_MYSQL_DATABASES}" magentaOutput "ENV_MYSQL_BACKUP_DIR:${ENV_MYSQL_BACKUP_DIR}" -magentaOutput "ENV_LOG_PATH:${ENV_LOG_PATH}" +magentaOutput "ENV_MYSQL_BACKUP_LOG_PATH:${ENV_MYSQL_BACKUP_LOG_PATH}" magentaOutput "------------------------------------------------------------------------------" @@ -133,7 +133,7 @@ backupAllDatabase() { #备份所有数据库 printInfo ">>>> 备份所有数据库开始" - mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} --all-databases > "${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql" 2>> ${ENV_LOG_PATH}; + mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} --all-databases > "${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql" 2>> ${ENV_MYSQL_BACKUP_LOG_PATH}; #检查备份结果是否成功 if [[ "$?" != ${ENV_SUCCEED} ]]; then @@ -172,7 +172,7 @@ backupSelectedDatabase() { for database in ${databaseList}; do printInfo "正在备份数据库:${database}" - mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} "${database}" > "${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" 2>> ${ENV_LOG_PATH}; + mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} "${database}" > "${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" 2>> ${ENV_MYSQL_BACKUP_LOG_PATH}; if [[ "$?" != 0 ]]; then printError "<<<< 备份 ${database} 失败" return ${ENV_FAILED} @@ -200,8 +200,8 @@ backupSelectedDatabase() { backupMysql() { #创建备份目录及日志文件 mkdir -p ${ENV_MYSQL_BACKUP_DIR} - if [[ ! -f ${ENV_LOG_PATH} ]]; then - touch ${ENV_LOG_PATH} + if [[ ! -f ${ENV_MYSQL_BACKUP_LOG_PATH} ]]; then + touch ${ENV_MYSQL_BACKUP_LOG_PATH} fi #正式备份数据库 @@ -216,13 +216,13 @@ backupMysql() { recoveryMysql() { #创建备份目录及日志文件 mkdir -p ${ENV_MYSQL_BACKUP_DIR} - if [[ ! -f ${ENV_LOG_PATH} ]]; then - touch ${ENV_LOG_PATH} + if [[ ! -f ${ENV_MYSQL_BACKUP_LOG_PATH} ]]; then + touch ${ENV_MYSQL_BACKUP_LOG_PATH} fi printInfo ">>>> 恢复数据库开始" - mysql -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} < ${ENV_LOG_PATH} + mysql -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} < ${ENV_MYSQL_BACKUP_LOG_PATH} if [[ "$?" != 0 ]]; then printError "<<<< 恢复数据库失败" return ${ENV_FAILED} diff --git a/codes/linux/soft/mysql-install.sh b/codes/linux/soft/mysql-install.sh index 84b09b0a..77fbb2cb 100644 --- a/codes/linux/soft/mysql-install.sh +++ b/codes/linux/soft/mysql-install.sh @@ -121,8 +121,10 @@ wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/conf printInfo ">>>> create mysql log file" mkdir -p /var/log/mysql -touch /var/log/mysql/mysql-slow.log -chmod 777 /var/log/mysql/mysql-slow.log +touch /var/log/mysql/mysql.log +touch /var/log/mysql/mysql_slow_query_log.log +chmod 777 /var/log/mysql/mysql.log +chmod 777 /var/log/mysql/mysql_slow_query_log.log chown -R mysql:mysql /var/log/mysql printInfo ">>>> modify limits.conf" diff --git a/codes/linux/soft/nexus-install.sh b/codes/linux/soft/nexus-install.sh index 39d06874..75dbb876 100644 --- a/codes/linux/soft/nexus-install.sh +++ b/codes/linux/soft/nexus-install.sh @@ -1,38 +1,146 @@ #!/usr/bin/env bash -################################################################################### -# 控制台颜色 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" -################################################################################### - -printf "${BLUE}" -cat << EOF - -################################################################################### +# ----------------------------------------------------------------------------------------------------- # 安装 sonatype nexus(用于搭建 maven 私服) 脚本 # @system: 适用于所有 linux 发行版本。 # sonatype nexus 会被安装到 /opt/maven 路径。 # 注意:sonatype nexus 要求必须先安装 JDK # @author: Zhang Peng -################################################################################### +# ----------------------------------------------------------------------------------------------------- -EOF -printf "${RESET}" +# ------------------------------------------------------------------------------ env + +# Regular Color +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 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" +# Reset Color +export ENV_COLOR_RESET="$(tput sgr0)" + +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} + +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} + +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} + +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} + +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} + +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} -printf "${GREEN}>>>>>>>> install nexus begin.${RESET}\n" +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} -mkdir -p /opt/maven -cd /opt/maven +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} -version=3.13.0-01 -curl -o /opt/maven/nexus-unix.tar.gz http://download.sonatype.com/nexus/3/nexus-${version}-unix.tar.gz +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} + +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + printInfo "$@" + return ${ENV_SUCCEED} + else + printError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} + +# ------------------------------------------------------------------------------ main +ENV_NEXUS_VERSION=${ENV_NEXUS_VERSION:-3.13.0-01} +ENV_NEXUS_DIR=${ENV_NEXUS_DIR:-/opt/maven} + +printInfo ">>>> install nexus begin." + +mkdir -p ${ENV_NEXUS_DIR} +printInfo "download nexus" +#由于国内网络问题,有可能下载失败 +curl -o ${ENV_NEXUS_DIR}/nexus-unix.tar.gz https://sonatype-download.global.ssl.fastly.net/repository/repositoryManager/3/nexus-${ENV_NEXUS_VERSION}-unix.tar.gz +if [[ "$?" != ${ENV_SUCCEED} ]]; then + printError "<<<< download nexus-${ENV_NEXUS_VERSION}-unix.tar.gz failed" + return ${ENV_FAILED} +fi tar -zxf nexus-unix.tar.gz -printf "${GREEN}<<<<<<<< install nexus end.${RESET}\n" +printInfo ">>>> setting systemd." +#通过设置 systemd,是的 nexus 注册为服务,开机自启动 +touch /lib/systemd/system/nexus.service +cat >> /lib/systemd/system/nexus.service << EOF +[Unit] +Description=nexus +After=network.target + +[Service] +Type=forking +LimitNOFILE=65536 #警告处理 +Environment=RUN_AS_USER=root +ExecStart=${ENV_NEXUS_DIR}/nexus-${ENV_NEXUS_VERSION}/bin/nexus start +ExecReload=${ENV_NEXUS_DIR}/nexus-${ENV_NEXUS_VERSION}/bin/nexus restart +ExecStop=${ENV_NEXUS_DIR}/nexus-${ENV_NEXUS_VERSION}/bin/nexus stop +Restart=on-failure +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable nexus +systemctl start nexus + +printInfo ">>>> setting firewalld." +firewall-cmd --zone=public --add-port=8081/tcp --permanent +firewall-cmd --reload +# 如果防火墻使用的是 iptables,使用如下配置: +#iptables -I INPUT -p tcp -m tcp --dport 8081 -j ACCEPT +#/etc/rc.d/init.d/iptables save +#service iptables restart + +printInfo "<<<<<<<< install nexus success." diff --git "a/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\205\215\345\257\206\347\240\201\344\274\240\350\276\223.sh" "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\205\215\345\257\206\347\240\201\344\274\240\350\276\223.sh" new file mode 100644 index 00000000..30264bda --- /dev/null +++ "b/codes/shell/\350\276\223\345\205\245\345\222\214\350\276\223\345\207\272/\345\205\215\345\257\206\347\240\201\344\274\240\350\276\223.sh" @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# 免密码传输 +# @author Zhang Peng +# @since 2020/4/8 +# ------------------------------------------------------------------------------ + +REMOTE_HOST=192.168.0.2 + +# 如果本机没有公私钥对,可以执行以下命令生成 ssh 公私钥对 +#ssh-keygen -t rsa + +# 服务器 A 上执行以下命令 +scp ~/.ssh/id_rsa.pub root@${REMOTE_HOST}:~/.ssh/id_rsa.pub.tmp + +# 服务器 B 上执行以下命令 +cat ~/.ssh/id_rsa.pub.tmp >> ~/.ssh/authorized_keys +rm ~/.ssh/id_rsa.pub.tmp diff --git a/docs/linux/ops/firewalld.md b/docs/linux/ops/firewalld.md index b639986f..90fb7784 100644 --- a/docs/linux/ops/firewalld.md +++ b/docs/linux/ops/firewalld.md @@ -29,7 +29,7 @@ firewall-cmd --query-panic # 查看是否拒绝 firewall-cmd --zone=public --list-ports # 查看所有打开的端口 firewall-cmd --zone=public --query-port=80/tcp # 查看是否有开放的 80 TCP 端口 -firewall-cmd --zone=public --add-port=80/tcp --permanent # 添加开发端口(--permanent永久生效,没有此参数重启后失效) +firewall-cmd --zone=public --add-port=8080/tcp --permanent # 添加开放端口(--permanent永久生效,没有此参数重启后失效) firewall-cmd --zone=public --remove-port=80/tcp --permanent # 永久删除开放的 80 TCP 端口 ``` diff --git a/docs/linux/soft/gitlab-ops.md b/docs/linux/soft/gitlab-ops.md index 8827ab34..1aff5f77 100644 --- a/docs/linux/soft/gitlab-ops.md +++ b/docs/linux/soft/gitlab-ops.md @@ -1,13 +1,11 @@ # Gitlab 运维 -## gitlab 安装 +## 一、gitlab 安装 ### Gitlab 的普通安装 #### 下载 - - 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 ![img](http://dunwu.test.upcdn.net/snap/20190129155838.png!zp) @@ -74,7 +72,7 @@ docker run -d \ ![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp) -## gitlab-ci-multi-runner 安装 +## 二、gitlab-ci-multi-runner 安装 > 参考:https://docs.gitlab.com/runner/install/ @@ -246,7 +244,9 @@ sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.csr sudo chmod 600 /etc/gitlab/ssl/gitlab.domain.com.* ``` -## gitlab 配置 +## 三、gitlab 配置 + +### 基本配置 ``` sudo vim /etc/gitlab/gitlab.rb @@ -278,7 +278,6 @@ sudo gitlab-ctl reconfigure sudo gitlab-ctl restart ``` - ### 创建你的 SSH key 1. 使用 Gitlab 的第一步是生成你自己的 SSH 密钥对(Github 也类似)。 @@ -341,7 +340,7 @@ sudo gitlab-ctl restart ![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png) -## gitlab 权限配置 +## 四、gitlab 权限配置 ### 用户组的权限 @@ -356,6 +355,74 @@ sudo gitlab-ctl restart | 管理组成员 | | | | | ✓ | | 移除组 | | | | | | +## 五、备份/迁移/升级 + +### 备份 + +#### 手动备份 + +执行 `gitlab-rake gitlab:backup:create` 开始备份全量数据,成功后,会在 `/var/opt/gitlab/backups` 下生产一个名称类似 `1585910556_2020_04_03_11.3.0_gitlab_backup.tar` 的压缩包。 + +### 定时自动备份 + +可以利用 crontab 来定时执行备份命令。 + +执行 `vim /etc/crontab` 或 `crontab -e` 手动编辑定时任务。 + +### 迁移 + +> 迁移前,需要确保新老机器的 Gitlab 版本号一致。 + +将备份的压缩包拷贝到新机器的备份路径下(默认为 `/var/opt/gitlab/backups`)。 + +(1)将备份文件权限修改为777,不然可能恢复的时候会出现权限不够,不能解压的问题 + +```shell +chmod 777 1585910556_2020_04_03_11.3.0_gitlab_backup.tar +``` + +(2)停止相关数据连接服务 + +```shell +gitlab-ctl stop unicorn +gitlab-ctl stop sidekiq +``` + +(3)从备份文件中恢复 Gitlab + +```shell +# gitlab-rake gitlab:backup:restore BACKUP=备份文件编号 +gitlab-rake gitlab:backup:restore BACKUP=1585910556_2020_04_03_11.3.0 +``` + +### 升级 + +升级前,一定要做好备份,记录当前 gitlab 的版本号。 + +第一步还是使用官方命令进行升级。 + +```shell +sudo yum install -y gitlab-ce +``` + +如果下载速度理想,就无需手动升级安装。不理想就需要`停止自动更新`,并手动下载安装包 + +访问官方地址,下载对应`版本`,对应`系统`的安装包。 + +注:可以根据`自动升级时下载的版本`,选择对应文件。 + +```http +https://packages.gitlab.com/gitlab/gitlab-ce +``` + +安装包手动上传至服务器,并`替换`下载未完成的安装包。下面是升级缓存地址: + +``` +/var/cache/yum/x86_64/7/gitlab_gitlab-ce/packages/ +``` + +再次执行官方升级命令即可完成自动安装。 + ## 资料 - 官网:https://about.gitlab.com/ @@ -363,4 +430,4 @@ sudo gitlab-ctl restart - 官网下载:https://about.gitlab.com/downloads/ - 官网安装说明:https://about.gitlab.com/installation/#centos-7 -- [操作系统、运维部署总结系列](https://github.com/dunwu/OS) \ No newline at end of file +- [操作系统、运维部署总结系列](https://github.com/dunwu/OS) diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 374234b4..5346413d 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -10,29 +10,51 @@ > - JDK 1.8 > - Maven 3.5.4 -## 1. 安装 Nexus +## 一、Nexus 安装 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 -![img](http://dunwu.test.upcdn.net/snap/20181127203029.png!zp) +![img](http://dunwu.test.upcdn.net/snap/20181127203029.png) 本人将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 这里,如果想通过命令方式直接下载(比如用脚本安装),可以在[官方历史发布版本页面](https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3)中找到合适版本,然后执行以下命令: ```shell +# 个人习惯将 nexus 安装在 /opt/maven 目录下 wget -O /opt/maven/nexus-unix.tar.gz http://download.sonatype.com/nexus/3/nexus-3.13.0-01-unix.tar.gz -tar -zxf nexus-unix.tar.gz + ``` -解压后,有两个目录: +- 【解压】执行 `tar -zxf nexus-unix.tar.gz` 命令,会解压出两个目录: + - `nexus-` - 程序目录。包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 + - `nexus-/etc` - 配置目录。 + - `nexus-/etc/nexus.properties` - nexus 核心配置文件(默认 etc 目录下有 `nexus-default.properties`,可以基于此修改)。 + - `sonatype-work` - 仓库目录。包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 +- [修改环境变量】执行 `vim /etc/profile`,在文件尾部添加以下内容: + +``` +NEXUS_HOME=/usr/program/nexus2.11.4 +export NEXUS_HOME +``` -- `nexus-3.13.0-01` - 包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 -- `sonatype-work` - 包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 +刷新环境变量:`source /etc/profile` -## 2. 启动/停止 Nexus +- 【检查安装是否成功】执行 `nexus -version` 查看是否安装成功。 +- 【防火墙】 + - iptabes + - 添加规则:`iptables -I INPUT -p tcp -m tcp --dport 8081 -j ACCEPT` + - 载入规则:`/etc/rc.d/init.d/iptables save` + - 重启 iptables:`service iptables restart` + - firewalld + - 添加规则:`firewall-cmd --zone=public --add-port=8081/tcp --permanent` + - 载入规则:`firewall-cmd --reload` -进入 nexus-3.13.0-01/bin 目录,有一个可执行脚本 nexus。 +## 二、Nexus 使用 + +### 启动/停止 Nexus + +进入 `nexus-3.13.0-01/bin` 目录,有一个可执行脚本 nexus。 执行 `./nexus`,可以查看允许执行的参数,如下所示,含义可谓一目了然: @@ -45,15 +67,13 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} - 停止 nexus - `./nexus stop` - 重启 nexus - `./nexus restart` -启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: - -![img](http://dunwu.test.upcdn.net/snap/20181127203131.png!zp) +Nexus 的默认启动端口为 `8081`,启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: -点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。 +![img](http://dunwu.test.upcdn.net/snap/20181127203131.png) -## 3. 搭建 Maven 私服 +点击右上角 Sign in 登录,默认用户名/密码为:`admin/admin123`。 -### 3.1. 配置仓库 +### 配置 maven 仓库 Nexus 中的仓库有以下类型: @@ -62,26 +82,116 @@ Nexus 中的仓库有以下类型: - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -![img](http://dunwu.test.upcdn.net/cs/java/javalib/maven/nexus.png!zp) +![img](http://dunwu.test.upcdn.net/cs/java/javalib/maven/nexus.png) -> **最佳实践** -> -> 建议配置如下: +建议配置如下: + +- **hosted 仓库** + - **maven-releases** - 存储私有仓库的发行版 jar 包 + - **maven-snapshots** - 存储私有仓库的快照版(调试版本) jar 包 +- **proxy 仓库** + - **maven-central-maven** - 中央库(如果没有配置 mirror,默认就从这里下载 jar 包),从 https://repo1.maven.org/maven2/ 获取资源 + - **maven-aliyun** - 国内 maven 仓库,提高访问速度。 +- **group 仓库** + - **maven-public** - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 + +![img](http://dunwu.test.upcdn.net/snap/20181127203156.png) + +> 其中: > -> - hosted 仓库 -> - maven-releases - 存储私有仓库的发行版 jar 包 -> - maven-snapshots - 存储私有仓库的快照版(调试版本) jar 包 -> - proxy 仓库 -> - maven-central - maven 中央库(如果没有配置 mirror,默认就从这里下载 jar 包),从 https://repo1.maven.org/maven2/ 获取资源 -> - maven-aliyun - 国内 maven 仓库,提高访问速度。 -> - group 仓库 -> - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 +> **maven-central**、**maven-public**、**maven-release**、**maven-snapshot** 仓库是默认配置好的 maven 仓库。maven-central 配置的是 `https://repo1.maven.org/maven2/` 的代理仓库,即 maven 中央仓库地址。 + +参考配置如下: + +![](http://dunwu.test.upcdn.net/snap/20200403165258.png) + +推荐配置的代理仓库: + +- OSS SNAPSHOT 仓库:`http://oss.jfrog.org/artifactory/oss-snapshot-local/` +- aliyun 仓库(受限于国内网络,可以通过它来加速):`http://maven.aliyun.com/nexus/content/groups/public/` + +### 配置 yum 仓库 + +![](http://dunwu.test.upcdn.net/snap/20200403201609.png) + +推荐配置的 yum 代理仓库: + +- aliyun yum 仓库:`http://mirrors.aliyun.com/centos` + +配置本地 yum: + +(1)新增 nexus.repo 文件,内容如下: + +```ini +[base] +name=Nexus +baseurl= http://:/repository/yum-aliyun/$releasever/os/$basearch/ +enabled=1 +gpgcheck=0 +priority=1 +``` + +(2)更新 yum 缓存,执行以下命令: + +```shell +yum clean all +yum makecache +``` + +### 定时任务 + +随着 jar 包越来越多,尤其是 SNAPSHOT 包由于不限制重复上传,尤其容易导致磁盘空间膨胀。所以,需要定期进行清理或修复。 + +Nexus 内置了多个定时任务,可以执行清理。 + +【示例】定期清理 SNAPSHOST + +![](http://dunwu.test.upcdn.net/snap/20200403173030.png) + +## 三、开机自启动 + +作为常用服务,有必要将 Nexus 设为 `systemd` 服务,以便在断电恢复后自动重启。 + +配置方法如下: + +在 `/lib/systemd/system` 目录下创建 `nexus.service` 文件,内容如下: + +```ini +[Unit] +Description=nexus +After=network.target + +[Service] +Type=forking +LimitNOFILE=65536 #警告处理 +Environment=RUN_AS_USER=root +ExecStart=/opt/maven/nexus-3.13.0-01/bin/nexus start +ExecReload=/opt/maven/nexus-3.13.0-01/bin/nexus restart +ExecStop=/opt/maven/nexus-3.13.0-01/bin/nexus stop +Restart=on-failure +PrivateTmp=true -![img](http://dunwu.test.upcdn.net/snap/20181127203156.png!zp) +[Install] +WantedBy=multi-user.target +``` -### 3.2. 配置 settings.xml +保存后,可以使用以下命令应用 nexus 服务: -如果要使用 Nexus,还必须在 settings.xml 和 pom.xml 中配置认证信息。 +- `systemctl enable nexus` - 启动 nexus 开机启动 +- `systemctl disable nexus` - 关闭 nexus 开机启动 +- `systemctl start nexus` - 启动 nexus 服务 +- `systemctl stop nexus` - 停止 nexus 服务 +- `systemctl restart nexus` - 重启 nexus 服务 + +> 执行 `systemctl enable nexus` 后,再执行 reboot 重启,重连后,可以检测是否成功开机自动重启。 + +## 四、Nexus 和 Maven + +Nexus 是 maven 私服。现在,Nexus 服务器已经部署好了,如何配合 maven 使用呢? + +### 配置 settings.xml + +如果要使用 Nexus,还必须在 `settings.xml` 和 `pom.xml` 中配置认证信息。 一份完整的 `settings.xml`: @@ -94,7 +204,10 @@ Nexus 中的仓库有以下类型: org.sonatype.plugins - + + D:\Tools\maven\.m2 + + releases @@ -108,11 +221,12 @@ Nexus 中的仓库有以下类型: - + public * + http://10.255.255.224:8081/repository/maven-public/ @@ -154,9 +268,9 @@ Nexus 中的仓库有以下类型: ``` -### 3.3. 配置 pom.xml +### 配置 pom.xml -在 pom.xml 中添加如下配置: +在 `pom.xml` 中添加如下配置,这样就可以执行 `mvn deploy`,将本地构建的 jar、war 等包发布到私服上。 ```xml @@ -178,9 +292,9 @@ Nexus 中的仓库有以下类型: > - `` 和 `` 的 id 必须和 `settings.xml` 配置文件中的 `` 标签中的 id 匹配。 > - `` 标签的地址需要和 maven 私服的地址匹配。 -### 3.4. 执行 maven 构建 +### 执行 maven 构建 -如果要使用 settings.xml 中的私服配置,必须通过指定 `-P zp` 来激活 profile。 +如果要使用 `settings.xml` 中的私服配置,必须通过指定 `-P zp` 来激活 profile。 示例: @@ -192,40 +306,9 @@ $ mvn clean package -Dmaven.skip.test=true -P zp $ mvn clean deploy -Dmaven.skip.test=true -P zp ``` -## 4. 开机自启动 - -将 Nexus 设置为 systemd 服务,以便开机自启动。 - -在 `/lib/systemd/system` 目录下创建 `nexus.service` 文件,内容如下: - -```ini -[Unit] -Description=nexus -After=network.target - -[Service] -Type=forking -LimitNOFILE=65536 #警告处理 -Environment=RUN_AS_USER=root -ExecStart=/opt/maven/nexus-3.13.0-01/bin/nexus start -ExecReload=/opt/maven/nexus-3.13.0-01/bin/nexus restart -ExecStop=/opt/maven/nexus-3.13.0-01/bin/nexus stop -Restart=on-failure -PrivateTmp=true - -[Install] -WantedBy=multi-user.target -``` - -保存后,可以使用以下命令应用 nexus 服务: - -- `systemctl enable nexus` - 启动 nexus 开机启动 -- `systemctl disable nexus` - 关闭 nexus 开机启动 -- `systemctl start nexus` - 启动 nexus 服务 -- `systemctl stop nexus` - 停止 nexus 服务 -- `systemctl restart nexus` - 重启 nexus 服务 +> 至此,已经可以正常向 Nexus 上传、下载 jar 包。 -## 5. Nexus 备份和迁移 +## 五、备份和迁移 Nexus 三个重要目录: @@ -235,17 +318,17 @@ Nexus 三个重要目录: | sonatype-work 目录 | sonatype-work | nexus/conf/nexus.xml 里面有 storage 的地址 | | storage 目录 | storage | 里面主要是各种程序的 jar 包等 | -### 5.1. 备份 +### 备份 Nexus 的数据都存储在 sonatype-work 目录,备份 Nexus 数据只需要将其打包即可。 -### 5.2. 迁移 +### 迁移 将原 Nexus 服务器中的 sonatype-work 目录迁移到新 Nexus 服务器的 sonatype-work 目录下。 -## 6. FAQ +## 六、FAQ -### 6.1. 配置 INSTALL4J_JAVA_HOME +### 配置 INSTALL4J_JAVA_HOME 我在工作中遇到 nexus systemctl 服务无法自启动的问题,通过查看状态,发现以下报错: @@ -255,7 +338,7 @@ Please define INSTALL4J_JAVA_HOME to point to a suitable JVM 通过排查,找到原因:即使环境上已安装 JDK,且配置了 JAVA_HOME,但 nexus 仍然无法正确找到 JDK,需要在 `/bin/nexus` 中指定 `INSTALL4J_JAVA_HOME_OVERRIDE=` -## 7. 参考资料 +## 参考资料 - [maven 私库 nexus3 安装及使用](https://blog.csdn.net/clj198606061111/article/details/52200928) - [Nexus 安装 使用说明](https://www.cnblogs.com/jtlgb/p/7473837.html) diff --git a/docs/linux/soft/svn-ops.md b/docs/linux/soft/svn-ops.md index 17469291..31ebbc15 100644 --- a/docs/linux/soft/svn-ops.md +++ b/docs/linux/soft/svn-ops.md @@ -4,30 +4,15 @@ > > 本文目的在于记录 svn 的安装、配置、使用。 - +## 安装 -- [1. 安装配置](#1-安装配置) - - [1.1. 安装 svn](#11-安装-svn) - - [1.2. 创建 svn 仓库](#12-创建-svn-仓库) - - [1.3. 配置 svnserve.conf](#13-配置-svnserveconf) - - [1.4. 配置 passwd](#14-配置-passwd) - - [1.5. 配置 authz](#15-配置-authz) - - [1.6. 启动关闭 svn](#16-启动关闭-svn) - - [1.7. 开机自启动 svn 方法](#17-开机自启动-svn-方法) - - [1.8. svn 客户端访问](#18-svn-客户端访问) -- [2. 参考资料](#2-参考资料) - - - -## 1. 安装配置 - -### 1.1. 安装 svn +### 安装 svn ```bash $ yum install -y subversion ``` -### 1.2. 创建 svn 仓库 +### 创建 svn 仓库 ```bash $ mkdir -p /share/svn @@ -38,11 +23,13 @@ conf db format hooks locks README.txt 在 conf 目录下有三个重要的配置文件 -- authz - 是权限控制文件 -- passwd - 是帐号密码文件 -- svnserve.conf - 是 SVN 服务配置文件 +- `authz` - 是权限控制文件 +- `passwd` - 是帐号密码文件 +- `svnserve.conf` - 是 SVN 服务配置文件 -### 1.3. 配置 svnserve.conf +## 配置 + +### 配置 svnserve.conf ```bash $ vim /share/svn/conf/svnserve.conf @@ -58,13 +45,13 @@ authz-db = authz #使用哪个文件作为权限文件 realm = /share/svn # 认证空间名,版本库所在目录 ``` -### 1.4. 配置 passwd +### 配置 passwd ```bash $ vim /share/svn/conf/passwd ``` -添加内容如下: +添加新用户的用户名/密码如下: ```ini [users] @@ -73,13 +60,13 @@ user2 = 123456 user3 = 123456 ``` -### 1.5. 配置 authz +### 配置 authz ```bash $ vim /share/svn/conf/authz ``` -添加内容如下: +指定用户的访问权限(`r` 为读权限;`w` 为写权限): ```ini [/] @@ -89,14 +76,16 @@ user3 = rw *= ``` -### 1.6. 启动关闭 svn +## 服务器管理 + +### 启动关闭 svn ```bash $ svnserve -d -r /share/svn # 启动 svn $ killall svnserve # 关闭 svn ``` -### 1.7. 开机自启动 svn 方法 +### 开机自启动 svn 方法 安装好 svn 服务后,默认是没有随系统启动自动启动的,而一般我们有要求 svn 服务稳定持续的提供服务。所以,有必要配置开机自启动 svn 服务。 @@ -137,7 +126,14 @@ $ vi /etc/sysconfig/svnserve 重启服务器后,执行 `ps -ef | grep svn` 应该可以看到 svn 服务的进程已经启动。 -### 1.8. svn 客户端访问 +- 启动一个服务 - systemctl start svnserve.service +- 关闭一个服务 - systemctl stop svnserve.service +- 重启一个服务 - systemctl restart svnserve.service +- 显示一个服务的状态 - systemctl status svnserve.service +- 在开机时启用一个服务 - systemctl enable svnserve.service +- 在开机时禁用一个服务 - systemctl disable svnserve.service + +## 客户端使用 进入 [svn 官方下载地址](https://tortoisesvn.net/downloads.html),选择合适的版本,下载并安装。 @@ -145,9 +141,9 @@ $ vi /etc/sysconfig/svnserve 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: -![img](http://dunwu.test.upcdn.net/snap/20190129175443.png!zp) +![img](http://dunwu.test.upcdn.net/snap/20190129175443.png) -## 2. 参考资料 +## 参考资料 - https://www.cnblogs.com/liuxianan/p/linux_install_svn_server.html - https://blog.csdn.net/testcs_dn/article/details/45395645 From 06f51aaec2392c9c8084ef021af9bc93f46043e3 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 28 Apr 2020 12:07:00 +0800 Subject: [PATCH 41/64] update codes --- codes/linux/dunwu-soft.sh | 130 ++++++++++++++++++++++-- codes/linux/soft/lib/git.sh | 184 +++++++++++++++++++++++++--------- docs/linux/soft/gitlab-ops.md | 127 +++++++++++------------ 3 files changed, 314 insertions(+), 127 deletions(-) diff --git a/codes/linux/dunwu-soft.sh b/codes/linux/dunwu-soft.sh index 023c518a..c542198c 100644 --- a/codes/linux/dunwu-soft.sh +++ b/codes/linux/dunwu-soft.sh @@ -5,16 +5,130 @@ # @author Zhang Peng # ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ libs -# 装载其它库 -LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) +# ------------------------------------------------------------------------------ env -if [[ ! -x ${LINUX_SCRIPTS_DIR}/lib/utils.sh ]]; then - logError "必要脚本库 ${LINUX_SCRIPTS_DIR}/lib/utils.sh 不存在!" - exit 1 -fi +# Regular Color +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 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 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 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 ENV_COLOR_RESET="$(tput sgr0)" -source ${LINUX_SCRIPTS_DIR}/lib/utils.sh +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) +# 日志路径 +LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} +# 日志目录 +LOG_DIR=${LOG_PATH%/*} + +createLogFileIfNotExists() { + if [[ ! -x "${LOG_PATH}" ]]; then + mkdir -p "${LOG_DIR}" + touch "${LOG_PATH}" + fi +} + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +logInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" +} +logWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" +} +logError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + logInfo "$@" + return ${ENV_SUCCEED} + else + logError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} # ------------------------------------------------------------------------------ functions # 打印头部信息 diff --git a/codes/linux/soft/lib/git.sh b/codes/linux/soft/lib/git.sh index 1846174c..c729cc7e 100644 --- a/codes/linux/soft/lib/git.sh +++ b/codes/linux/soft/lib/git.sh @@ -1,42 +1,154 @@ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- -# git operation utils +# Shell Utils # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- -# ------------------------------------------------------------------------------ load libs +# ------------------------------------------------------------------------------ env + +# Regular Color +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 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 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 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 ENV_COLOR_RESET="$(tput sgr0)" + +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + + +# ------------------------------------------------------------------------------ util functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) +# 日志路径 +LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} +# 日志目录 +LOG_DIR=${LOG_PATH%/*} + +createLogFileIfNotExists() { + if [[ ! -x "${LOG_PATH}" ]]; then + mkdir -p "${LOG_DIR}" + touch "${LOG_PATH}" + fi +} -LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} -if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then - logError "${LINUX_SCRIPTS_LIB_DIR}/utils.sh not exists!" - exit 1 -fi +logInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" +} +logWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" +} +logError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" +} -source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + logInfo "$@" + return ${ENV_SUCCEED} + else + logError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} -# ------------------------------------------------------------------------------ functions +# ------------------------------------------------------------------------------ git functions -GIT_LOCAL_BRANCH= getGitLocalBranch() { - GIT_LOCAL_BRANCH=$(git symbolic-ref -q --short HEAD) + export GIT_LOCAL_BRANCH=$(git symbolic-ref -q --short HEAD) } -GIT_ORIGIN_BRANCH= getGitOriginBranch() { - GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}") + export GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}") } # check specified path is git project or not +IS_GIT=false checkGit() { local source=$1 if [[ -d "${source}" ]]; then - cd ${source} || return ${ENV_NO} + cd ${source} # (1) delete gitstatus.tmp if [[ -f "gitstatus.tmp" ]]; then - rm -rf gitstatus.tmp + rm -rf gitstatus.tmp fi # (2) check git status @@ -45,16 +157,13 @@ checkGit() { grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true rm -rf gitstatus.tmp if [[ ${gitStatus} == true ]]; then - return ${ENV_YES} - else - return ${ENV_NO} + export IS_GIT=true + return fi - - return ${ENV_NO} fi logWarn "${source} is not exists." - return ${ENV_NO} + export IS_GIT=false } # execute git clone or fetch @@ -67,7 +176,7 @@ cloneOrPullGit() { local branch=$4 local root=$5 - if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then + if [[ ! ${repository} || ! ${group} || ! ${project} || ! ${branch} || ! ${root} ]]; then logError "Please input root, group, project, branch." return ${ENV_FAILED} fi @@ -80,52 +189,31 @@ cloneOrPullGit() { local source=${root}/${group}/${project} logInfo "project directory is ${source}." logInfo "git url is ${repository}:${group}/${project}.git." - mkdir -p ${root}/${group} checkGit ${source} - if [[ "${ENV_YES}" == "$?" ]]; then + if [[ "${IS_GIT}" == "true" ]]; then cd ${source} || return ${ENV_FAILED} - git fetch --all - git checkout -f ${branch} - if [[ "${ENV_SUCCEED}" != "$?" ]]; then - logError "<<<< git checkout ${branch} failed." - return ${ENV_FAILED} - fi + git checkout ${branch} logInfo "git checkout ${branch} succeed." - getGitOriginBranch + git fetch --all git reset --hard ${GIT_ORIGIN_BRANCH} - if [[ "${ENV_SUCCEED}" != "$?" ]]; then - logError "<<<< git reset --hard ${GIT_ORIGIN_BRANCH} failed." - return ${ENV_FAILED} - fi logInfo "git reset --hard ${GIT_ORIGIN_BRANCH} succeed." git pull - if [[ "${ENV_SUCCEED}" != "$?" ]]; then - logError "<<<< git pull failed." - return ${ENV_FAILED} - fi logInfo "git pull succeed." else git clone "${repository}:${group}/${project}.git" ${source} - if [[ "${ENV_SUCCEED}" != "$?" ]]; then - logError "<<<< git clone ${project} failed." - return ${ENV_FAILED} - fi logInfo "git clone ${project} succeed." cd ${source} || return ${ENV_FAILED} - git checkout -f ${branch} - if [[ "${ENV_SUCCEED}" != "$?" ]]; then - logError "<<<< git checkout ${branch} failed." - return ${ENV_FAILED} - fi + git checkout ${branch} logInfo "git checkout ${branch} succeed." fi logInfo "Clone or pull git project [$2/$3:$4] succeed." + cd ${SOURCE_DIR} return ${ENV_SUCCEED} } diff --git a/docs/linux/soft/gitlab-ops.md b/docs/linux/soft/gitlab-ops.md index 1aff5f77..b90dbd35 100644 --- a/docs/linux/soft/gitlab-ops.md +++ b/docs/linux/soft/gitlab-ops.md @@ -72,6 +72,60 @@ docker run -d \ ![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp) +### 自签名证书 + +首先,创建认证目录 + +``` +sudo mkdir -p /etc/gitlab/ssl +sudo chmod 700 /etc/gitlab/ssl +``` + +(1)创建 Private Key + +``` +sudo openssl genrsa -des3 -out /etc/gitlab/ssl/gitlab.domain.com.key 2048 +``` + +会提示输入密码,请记住 + +(2)生成 Certificate Request + +``` +sudo openssl req -new -key /etc/gitlab/ssl/gitlab.domain.com.key -out /etc/gitlab/ssl/gitlab.domain.com.csr +``` + +根据提示,输入信息 + +``` +Country Name (2 letter code) [XX]:CN +State or Province Name (full name) []:JS +Locality Name (eg, city) [Default City]:NJ +Organization Name (eg, company) [Default Company Ltd]:xxxxx +Organizational Unit Name (eg, section) []: +Common Name (eg, your name or your server's hostname) []:gitlab.xxxx.io +Email Address []: + +Please enter the following 'extra' attributes +to be sent with your certificate request +A challenge password []: +An optional company name []: +``` + +(3)移除 Private Key 中的密码短语 + +``` +sudo cp -v /etc/gitlab/ssl/gitlab.domain.com.{key,original} +sudo openssl rsa -in /etc/gitlab/ssl/gitlab.domain.com.original -out /etc/gitlab/ssl/gitlab.domain.com.key +sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.original +``` + +(4)设置文件权限 + +``` +sudo chmod 600 /etc/gitlab/ssl/gitlab.domain.com.* +``` + ## 二、gitlab-ci-multi-runner 安装 > 参考:https://docs.gitlab.com/runner/install/ @@ -176,74 +230,6 @@ docker run -d --name gitlab-runner --restart always \ gitlab/gitlab-runner:latest ``` -## 自签名证书 - -首先,创建认证目录 - -``` -sudo mkdir -p /etc/gitlab/ssl -sudo chmod 700 /etc/gitlab/ssl -``` - -### 创建证书 - -#### 创建 Private Key - -``` -sudo openssl genrsa -des3 -out /etc/gitlab/ssl/gitlab.domain.com.key 2048 -``` - -会提示输入密码,请记住 - -#### 生成 Certificate Request - -``` -sudo openssl req -new -key /etc/gitlab/ssl/gitlab.domain.com.key -out /etc/gitlab/ssl/gitlab.domain.com.csr -``` - -根据提示,输入信息 - -``` -Country Name (2 letter code) [XX]:CN -State or Province Name (full name) []:JS -Locality Name (eg, city) [Default City]:NJ -Organization Name (eg, company) [Default Company Ltd]:xxxxx -Organizational Unit Name (eg, section) []: -Common Name (eg, your name or your server's hostname) []:gitlab.xxxx.io -Email Address []: - -Please enter the following 'extra' attributes -to be sent with your certificate request -A challenge password []: -An optional company name []: -``` - -#### 移除 Private Key 中的密码短语 - -``` -sudo cp -v /etc/gitlab/ssl/gitlab.domain.com.{key,original} -sudo openssl rsa -in /etc/gitlab/ssl/gitlab.domain.com.original -out /etc/gitlab/ssl/gitlab.domain.com.key -sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.original -``` - -#### 创建证书 - -``` -sudo openssl x509 -req -days 1460 -in /etc/gitlab/ssl/gitlab.domain.com.csr -signkey /etc/gitlab/ssl/gitlab.domain.com.key -out /etc/gitlab/ssl/gitlab.domain.com.crt -``` - -#### 移除证书请求文件 - -``` -sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.csr -``` - -#### 设置文件权限 - -``` -sudo chmod 600 /etc/gitlab/ssl/gitlab.domain.com.* -``` - ## 三、gitlab 配置 ### 基本配置 @@ -375,7 +361,7 @@ sudo gitlab-ctl restart 将备份的压缩包拷贝到新机器的备份路径下(默认为 `/var/opt/gitlab/backups`)。 -(1)将备份文件权限修改为777,不然可能恢复的时候会出现权限不够,不能解压的问题 +(1)将备份文件权限修改为 777,不然可能恢复的时候会出现权限不够,不能解压的问题 ```shell chmod 777 1585910556_2020_04_03_11.3.0_gitlab_backup.tar @@ -423,11 +409,10 @@ https://packages.gitlab.com/gitlab/gitlab-ce 再次执行官方升级命令即可完成自动安装。 -## 资料 +## 参考资料 - 官网:https://about.gitlab.com/ - 中文网:https://www.gitlab.com.cn/ - 官网下载:https://about.gitlab.com/downloads/ - 官网安装说明:https://about.gitlab.com/installation/#centos-7 - - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) From 952a95c7469b156fda510dca492d19cd8e57044e Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 28 Apr 2020 12:09:03 +0800 Subject: [PATCH 42/64] update codes --- codes/linux/dunwu-ops.sh | 132 ++++++++++++++++++++++++++++++++++++--- codes/linux/dunwu-sys.sh | 130 +++++++++++++++++++++++++++++++++++--- 2 files changed, 244 insertions(+), 18 deletions(-) diff --git a/codes/linux/dunwu-ops.sh b/codes/linux/dunwu-ops.sh index 0196988a..64a3c53f 100644 --- a/codes/linux/dunwu-ops.sh +++ b/codes/linux/dunwu-ops.sh @@ -1,22 +1,134 @@ #!/usr/bin/env bash -#!/usr/bin/env bash - # ------------------------------------------------------------------------------ # CentOS 常用软件一键安装脚本 # @author Zhang Peng # ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ libs -# 装载其它库 -LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) +# ------------------------------------------------------------------------------ env + +# Regular Color +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 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 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 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 ENV_COLOR_RESET="$(tput sgr0)" + +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) +# 日志路径 +LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} +# 日志目录 +LOG_DIR=${LOG_PATH%/*} + +createLogFileIfNotExists() { + if [[ ! -x "${LOG_PATH}" ]]; then + mkdir -p "${LOG_DIR}" + touch "${LOG_PATH}" + fi +} + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +logInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" +} +logWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" +} +logError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" +} -if [[ ! -x ${LINUX_SCRIPTS_DIR}/lib/utils.sh ]]; then - logError "必要脚本库 ${LINUX_SCRIPTS_DIR}/lib/utils.sh 不存在!" - exit 1 -fi +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} -source ${LINUX_SCRIPTS_DIR}/lib/utils.sh +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + logInfo "$@" + return ${ENV_SUCCEED} + else + logError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} # ------------------------------------------------------------------------------ functions diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index de3ad755..7b00b71f 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -5,16 +5,130 @@ # @author Zhang Peng # ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ libs -# 装载其它库 -LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) +# ------------------------------------------------------------------------------ env -if [[ ! -x ${LINUX_SCRIPTS_DIR}/lib/utils.sh ]]; then - logError "必要脚本库 ${LINUX_SCRIPTS_DIR}/lib/utils.sh 不存在!" - exit 1 -fi +# Regular Color +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 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 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 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 ENV_COLOR_RESET="$(tput sgr0)" -source ${LINUX_SCRIPTS_DIR}/lib/utils.sh +# status +export ENV_YES=0 +export ENV_NO=1 +export ENV_SUCCEED=0 +export ENV_FAILED=1 + + +# ------------------------------------------------------------------------------ functions + +# 显示打印日志的时间 +SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") +# 那个用户在操作 +USER=$(whoami) +# 日志路径 +LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} +# 日志目录 +LOG_DIR=${LOG_PATH%/*} + +createLogFileIfNotExists() { + if [[ ! -x "${LOG_PATH}" ]]; then + mkdir -p "${LOG_DIR}" + touch "${LOG_PATH}" + fi +} + +redOutput() { + echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" +} +greenOutput() { + echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" +} +yellowOutput() { + echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" +} +blueOutput() { + echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" +} +magentaOutput() { + echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" +} +cyanOutput() { + echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" +} +whiteOutput() { + echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" +} + +logInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" +} +logWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" +} +logError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" + createLogFileIfNotExists + echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" +} + +printInfo() { + echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" +} +printWarn() { + echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" +} +printError() { + echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${ENV_SUCCEED} ]]; then + logInfo "$@" + return ${ENV_SUCCEED} + else + logError "$@ EXECUTE FAILED" + return ${ENV_FAILED} + fi +} # ------------------------------------------------------------------------------ functions From c68481a4714322256aace85325dcffddcbc47dd9 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 28 Apr 2020 12:11:54 +0800 Subject: [PATCH 43/64] update codes --- codes/linux/dunwu-sys.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/codes/linux/dunwu-sys.sh b/codes/linux/dunwu-sys.sh index 7b00b71f..9299cee8 100644 --- a/codes/linux/dunwu-sys.sh +++ b/codes/linux/dunwu-sys.sh @@ -145,6 +145,7 @@ EOF printf "${C_RESET}\n" } +LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { PS3="请输入命令编号:" From 8eee8c968d54c5757b813084a9bcaecc2d6e1675 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 3 Jun 2020 16:34:29 +0800 Subject: [PATCH 44/64] update docs --- README.md | 4 +++ docs/README.md | 54 +++++++++++++++++++++++++++++++++++++-- docs/docker/README.md | 15 ++++++++--- docs/linux/cli/README.md | 2 ++ docs/linux/soft/README.md | 36 ++++++++++++++++---------- 5 files changed, 92 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 3689463e..db9c6a26 100644 --- a/README.md +++ b/README.md @@ -152,3 +152,7 @@ - [东北大学](http://mirror.neu.edu.cn/) - [浙江大学](http://mirrors.zju.edu.cn/) - [东软信息学院](http://mirrors.neusoft.edu.cn/) + +## 🚪 传送门 + +◾ 🏠 [LINUX-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ diff --git a/docs/README.md b/docs/README.md index bef89e10..bb3d6492 100644 --- a/docs/README.md +++ b/docs/README.md @@ -57,8 +57,8 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu - [Nodejs 安装](linux/soft/nodejs-install.md) - 开发工具 - [Nexus 运维](linux/soft/nexus-ops.md) - - [Gitlab 运维](linux/soft/kafka-install.md) - - [Jenkins 运维](linux/soft/jenkins.md) + - [Gitlab 运维](linux/soft/gitlab-ops.md) + - [Jenkins 运维](linux/soft/jenkins-ops.md) - [Svn 运维](linux/soft/svn-ops.md) - [YApi 运维](linux/soft/yapi-ops.md) - 中间件服务 @@ -101,6 +101,56 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu 源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux) +## 📚 资料 + +- **Linux 命令** + - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) + - [Linux 命令大全](https://man.linuxde.net/) + - [linux-command](https://github.com/jaywcjlove/linux-command) +- **社区网站** + - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 + - [实验楼](https://www.shiyanlou.com/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 + - [鸟哥的 linux 私房菜](http://linux.vbird.org/) - 非常适合 Linux 入门初学者看的教程。 + - [Linux 公社](http://www.linuxidc.com/) - Linux 相关的新闻、教程、主题、壁纸都有。 + - [Linux Today](http://www.linuxde.net) - Linux 新闻资讯发布,Linux 职业技术学习!。 +- **知识相关** + - [Linux 思维导图整理](http://www.jianshu.com/p/59f759207862) + - [Linux 初学者进阶学习资源整理](http://www.jianshu.com/p/fe2a790b41eb) + - [Linux 基础入门(新版)](https://www.shiyanlou.com/courses/1) + - [【译】Linux 概念架构的理解](http://www.jianshu.com/p/c5ae8f061cfe) [En](http://oss.org.cn/ossdocs/linux/kernel/a1/index.html) + - [Linux 守护进程的启动方法](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html) + - [Linux 编程之内存映射](https://www.shiyanlou.com/questions/2992) + - [Linux 知识点小结](https://blog.huachao.me/2016/1/Linux%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B0%8F%E7%BB%93/) + - [10 大白帽黑客专用的 Linux 操作系统](https://linux.cn/article-6971-1.html) +- **软件工具** + - [超赞的 Linux 软件](https://www.gitbook.com/book/alim0x/awesome-linux-software-zh_cn/details) Github 仓库[Zh](https://github.com/alim0x/Awesome-Linux-Software-zh_CN) [En](https://github.com/VoLuong/Awesome-Linux-Software) + - [程序员喜欢的 9 款最佳的 Linux 文件比较工具](http://os.51cto.com/art/201607/513796.htm) + - [提高 Linux 开发效率的 5 个工具](http://www.codeceo.com/article/5-linux-productivity-tools.html) + - [你要了解的 11 款面向 Linux 系统的一流备份实用工具](http://os.51cto.com/art/201603/508027.htm) + - [16 个很有用的在线工具](http://www.simlinux.com/archives/264.html) + - Adobe 软件的最佳替代品 [原文在这里](https://linux.cn/article-8928-1.html) + - [Evince (Adobe Acrobat Reader)](https://wiki.gnome.org/Apps/Evince) 一个“支持多种文档格式的文档查看器”,可以查看 PDF,还支持各种漫画书格式 + - [Pixlr (Adobe Photoshop)](https://pixlr.com/) 一个强大的图像编辑工具 + - [Inkscape (Adobe Illustrator)](https://inkscape.org/zh/) 一个专业的矢量图形编辑器 + - [Pinegrow Web Editor (Adobe Dreamweaver)](https://pinegrow.com/) 一个可视化编辑制作 HTML 网站 + - [Scribus (Adobe InDesign)](https://www.scribus.net/) 一个开源电子杂志制作软件 + - [Webflow (Adobe Muse)](https://webflow.com/) 一款可以帮助用户不用编码就可以快速创建网站的谷歌浏览器插件。 + - [Tupi (Adobe Animate)](http://www.maefloresta.com/portal/) 一款可以创建 HTML5 动画的工具。 + - [Black Magic Fusion (Adobe After Effects)](https://www.blackmagicdesign.com) 一款先进的合成软件,广泛应用于视觉特效、广电影视设计以及 3D 动画设计等领域。 +- **中国开源镜像** + - [阿里云开源镜像站](http://mirrors.aliyun.com/) + - [网易开源镜像站](http://mirrors.163.com/) + - [搜狐开源镜像站](http://mirrors.sohu.com/) + - [北京交通大学](http://mirror.bjtu.edu.cn/) + - [兰州大学](http://mirror.lzu.edu.cn/) + - [厦门大学](http://mirrors.xmu.edu.cn/) + - [上海交通大学](http://ftp.sjtu.edu.cn/) + - [清华大学](http://mirrors.tuna.tsinghua.edu.cn/) + - [中国科学技术大学](http://mirrors.ustc.edu.cn/) + - [东北大学](http://mirror.neu.edu.cn/) + - [浙江大学](http://mirrors.zju.edu.cn/) + - [东软信息学院](http://mirrors.neusoft.edu.cn/) + ## 🚪 传送门 ◾ 🏠 [LINUX-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ diff --git a/docs/docker/README.md b/docs/docker/README.md index bc54df6b..c77d748a 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -1,10 +1,13 @@ # Docker 教程 -- [Docker 快速入门](docker-quickstart.md) -- [Dockerfile 最佳实践](docker-dockerfile.md) -- [Docker Cheat Sheet](docker-cheat-sheet.md) +## 📖 内容 -## 资源 +- [Docker 快速入门](docker/docker-quickstart.md) +- [Dockerfile 最佳实践](docker/docker-dockerfile.md) +- [Docker Cheat Sheet](docker/docker-cheat-sheet.md) +- [Kubernetes 应用指南](docker/kubernetes.md) + +## 📚 资料 - **官方** - [Docker 官网](http://www.docker.com) @@ -27,3 +30,7 @@ - **文章** - [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) - [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn) + +## 🚪 传送门 + +◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ diff --git a/docs/linux/cli/README.md b/docs/linux/cli/README.md index 0ca5c652..4c455df3 100644 --- a/docs/linux/cli/README.md +++ b/docs/linux/cli/README.md @@ -1,5 +1,7 @@ # Linux 命令行 +> 学习 Linux 的第一步:当然是从 Linux 命令入手了。 + ## 📖 内容 - [查看 Linux 命令帮助信息](linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` diff --git a/docs/linux/soft/README.md b/docs/linux/soft/README.md index fd7ccfbd..7355bd46 100644 --- a/docs/linux/soft/README.md +++ b/docs/linux/soft/README.md @@ -2,19 +2,29 @@ ## 📖 内容 -- [JDK 安装](jdk-install.md) -- [Elastic 安装](elastic) -- [Gitlab 安装](kafka-install.md) -- [Jenkins 安装](jenkins-install.md) -- [Kafka 安装](kafka-install.md) -- [Maven 安装](maven-install.md) -- [Nexus 安装](nexus-ops.md) -- [Nodejs 安装](nodejs-install.md) -- [RocketMQ 安装](rocketmq-install.md) -- [Svn 安装](svn-ops.md) -- [Tomcat 安装](tomcat-install.md) -- [Zookeeper 安装](zookeeper-ops.md) -- [Nacos 安装](nacos-install.md) +- 开发环境 + - [JDK 安装](jdk-install.md) + - [Maven 安装](maven-install.md) + - [Nodejs 安装](nodejs-install.md) +- 开发工具 + - [Nexus 运维](nexus-ops.md) + - [Gitlab 运维](gitlab-ops.md) + - [Jenkins 运维](jenkins-ops.md) + - [Svn 运维](svn-ops.md) + - [YApi 运维](yapi-ops.md) +- 中间件服务 + - [Elastic 运维](elastic) + - [RocketMQ 运维](rocketmq-install.md) + - [Nacos 运维](nacos-install.md) +- 服务器 + - [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚 + - [Tomcat 运维](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) +- 大数据服务 + - [Kafka 运维](kafka-install.md) + - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) ## 🚪 传送门 From 9d9c2e72b62f1cc61413e2ee0141c4d28aff688e Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 16 Jun 2020 07:18:01 +0800 Subject: [PATCH 45/64] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/linux/ops/crontab.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/linux/ops/crontab.md b/docs/linux/ops/crontab.md index 29139e61..1fd9eca3 100644 --- a/docs/linux/ops/crontab.md +++ b/docs/linux/ops/crontab.md @@ -65,7 +65,7 @@ crontab 要执行的定时任务都被保存在 `/etc/crontab` 文件中。 crontab 的文件格式如下: -![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200211113339.png) +![img](http://dunwu.test.upcdn.net/snap/20200211113339.png) #### 标准字段 From 42f2b4ea8a55267484602f3d71f557afcac0c5ea Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 24 Jun 2020 10:56:56 +0800 Subject: [PATCH 46/64] =?UTF-8?q?Redis=20=E4=B8=BB=E4=BB=8E=E9=9B=86?= =?UTF-8?q?=E7=BE=A4+=E5=93=A8=E5=85=B5=E6=A8=A1=E5=BC=8F=20=E7=9A=84?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E8=84=9A=E6=9C=AC=E3=80=81=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/redis/cluster/26381/sentinel.conf | 8 ++ .../config/redis/cluster/26382/sentinel.conf | 8 ++ .../config/redis/cluster/26383/sentinel.conf | 8 ++ .../soft/config/redis/cluster/6381/redis.conf | 8 +- .../soft/config/redis/cluster/6382/redis.conf | 8 +- .../soft/config/redis/cluster/6383/redis.conf | 8 +- .../soft/config/redis/cluster/6384/redis.conf | 8 +- .../soft/config/redis/cluster/6385/redis.conf | 8 +- .../soft/config/redis/cluster/6386/redis.conf | 8 +- .../soft/config/redis/cluster/6387/redis.conf | 12 ++ .../soft/config/redis/cluster/6388/redis.conf | 12 ++ .../soft/config/redis/cluster/6389/redis.conf | 12 ++ .../linux/soft/config/redis/cluster/README.md | 53 +++++++++ .../soft/config/redis/cluster/create-cluster | 102 +++++++++++++++++ .../config/redis/cluster/redis-cluster.sh | 104 ------------------ .../config/redis/cluster/start-cluster.sh | 19 ++++ .../config/redis/cluster/start-sentinel.sh | 3 + 17 files changed, 261 insertions(+), 128 deletions(-) create mode 100644 codes/linux/soft/config/redis/cluster/26381/sentinel.conf create mode 100644 codes/linux/soft/config/redis/cluster/26382/sentinel.conf create mode 100644 codes/linux/soft/config/redis/cluster/26383/sentinel.conf create mode 100644 codes/linux/soft/config/redis/cluster/6387/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6388/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/6389/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/README.md create mode 100644 codes/linux/soft/config/redis/cluster/create-cluster delete mode 100644 codes/linux/soft/config/redis/cluster/redis-cluster.sh create mode 100644 codes/linux/soft/config/redis/cluster/start-cluster.sh create mode 100644 codes/linux/soft/config/redis/cluster/start-sentinel.sh diff --git a/codes/linux/soft/config/redis/cluster/26381/sentinel.conf b/codes/linux/soft/config/redis/cluster/26381/sentinel.conf new file mode 100644 index 00000000..a4b3f94f --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/26381/sentinel.conf @@ -0,0 +1,8 @@ +port 26381 +daemonize yes +sentinel monitor redis-master 172.22.6.3 6381 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/26381/26381.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/26382/sentinel.conf b/codes/linux/soft/config/redis/cluster/26382/sentinel.conf new file mode 100644 index 00000000..db20a61d --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/26382/sentinel.conf @@ -0,0 +1,8 @@ +port 26382 +daemonize yes +sentinel monitor redis-master 172.22.6.3 6382 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/26382/26382.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/26383/sentinel.conf b/codes/linux/soft/config/redis/cluster/26383/sentinel.conf new file mode 100644 index 00000000..fc5af052 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/26383/sentinel.conf @@ -0,0 +1,8 @@ +port 26383 +daemonize yes +sentinel monitor redis-master 172.22.6.3 6383 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/26383/26383.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/6381/redis.conf b/codes/linux/soft/config/redis/cluster/6381/redis.conf index 943eff09..054c1f04 100644 --- a/codes/linux/soft/config/redis/cluster/6381/redis.conf +++ b/codes/linux/soft/config/redis/cluster/6381/redis.conf @@ -3,10 +3,10 @@ bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/cluster/6381/6381.conf +cluster-config-file /usr/local/redis/conf/6381/6381.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/cluster/6381 -pidfile /var/run/redis/redis-6381.pid -logfile /usr/local/redis/cluster/6381/6381.log +dir /usr/local/redis/conf/6381 +pidfile /usr/local/redis/conf/6381/6381.pid +logfile /usr/local/redis/conf/6381/6381.log diff --git a/codes/linux/soft/config/redis/cluster/6382/redis.conf b/codes/linux/soft/config/redis/cluster/6382/redis.conf index 7f1406c3..d5d62e1a 100644 --- a/codes/linux/soft/config/redis/cluster/6382/redis.conf +++ b/codes/linux/soft/config/redis/cluster/6382/redis.conf @@ -3,10 +3,10 @@ bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/cluster/6382/6382.conf +cluster-config-file /usr/local/redis/conf/6382/6382.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/cluster/6382 -pidfile /var/run/redis/redis-6382.pid -logfile /usr/local/redis/cluster/6382/6382.log +dir /usr/local/redis/conf/6382 +pidfile /usr/local/redis/conf/6382/6382.pid +logfile /usr/local/redis/conf/6382/6382.log diff --git a/codes/linux/soft/config/redis/cluster/6383/redis.conf b/codes/linux/soft/config/redis/cluster/6383/redis.conf index 22d2aa87..41e10ee3 100644 --- a/codes/linux/soft/config/redis/cluster/6383/redis.conf +++ b/codes/linux/soft/config/redis/cluster/6383/redis.conf @@ -3,10 +3,10 @@ bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/cluster/6383/6383.conf +cluster-config-file /usr/local/redis/conf/6383/6383.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/cluster/6383 -pidfile /var/run/redis/redis-6383.pid -logfile /usr/local/redis/cluster/6383/6383.log +dir /usr/local/redis/conf/6383 +pidfile /usr/local/redis/conf/6383/6383.pid +logfile /usr/local/redis/conf/6383/6383.log diff --git a/codes/linux/soft/config/redis/cluster/6384/redis.conf b/codes/linux/soft/config/redis/cluster/6384/redis.conf index b2756e64..b57fef5e 100644 --- a/codes/linux/soft/config/redis/cluster/6384/redis.conf +++ b/codes/linux/soft/config/redis/cluster/6384/redis.conf @@ -3,10 +3,10 @@ bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/cluster/6384/6384.conf +cluster-config-file /usr/local/redis/conf/6384/6384.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/cluster/6384 -pidfile /var/run/redis/redis-6384.pid -logfile /usr/local/redis/cluster/6384/6384.log +dir /usr/local/redis/conf/6384 +pidfile /usr/local/redis/conf/6384/6384.pid +logfile /usr/local/redis/conf/6384/6384.log diff --git a/codes/linux/soft/config/redis/cluster/6385/redis.conf b/codes/linux/soft/config/redis/cluster/6385/redis.conf index 28d36cf8..7535e79d 100644 --- a/codes/linux/soft/config/redis/cluster/6385/redis.conf +++ b/codes/linux/soft/config/redis/cluster/6385/redis.conf @@ -3,10 +3,10 @@ bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/cluster/6385/6385.conf +cluster-config-file /usr/local/redis/conf/6385/6385.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/cluster/6385 -pidfile /var/run/redis/redis-6385.pid -logfile /usr/local/redis/cluster/6385/6385.log +dir /usr/local/redis/conf/6385 +pidfile /usr/local/redis/conf/6385/6385.pid +logfile /usr/local/redis/conf/6385/6385.log diff --git a/codes/linux/soft/config/redis/cluster/6386/redis.conf b/codes/linux/soft/config/redis/cluster/6386/redis.conf index 5ab8bf7c..03f1127d 100644 --- a/codes/linux/soft/config/redis/cluster/6386/redis.conf +++ b/codes/linux/soft/config/redis/cluster/6386/redis.conf @@ -3,10 +3,10 @@ bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/cluster/6386/6386.conf +cluster-config-file /usr/local/redis/conf/6386/6386.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/cluster/6386 -pidfile /var/run/redis/redis-6386.pid -logfile /usr/local/redis/cluster/6386/6386.log +dir /usr/local/redis/conf/6386 +pidfile /usr/local/redis/conf/6386/6386.pid +logfile /usr/local/redis/conf/6386/6386.log diff --git a/codes/linux/soft/config/redis/cluster/6387/redis.conf b/codes/linux/soft/config/redis/cluster/6387/redis.conf new file mode 100644 index 00000000..31ccfe90 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6387/redis.conf @@ -0,0 +1,12 @@ +port 6387 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/6387/6387.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/6387 +pidfile /usr/local/redis/conf/6387/6387.pid +logfile /usr/local/redis/conf/6387/6387.log diff --git a/codes/linux/soft/config/redis/cluster/6388/redis.conf b/codes/linux/soft/config/redis/cluster/6388/redis.conf new file mode 100644 index 00000000..6f159f9a --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6388/redis.conf @@ -0,0 +1,12 @@ +port 6388 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/6388/6388.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/6388 +pidfile /usr/local/redis/conf/6388/6388.pid +logfile /usr/local/redis/conf/6388/6388.log diff --git a/codes/linux/soft/config/redis/cluster/6389/redis.conf b/codes/linux/soft/config/redis/cluster/6389/redis.conf new file mode 100644 index 00000000..e9a17b3e --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/6389/redis.conf @@ -0,0 +1,12 @@ +port 6389 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/6389/6389.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/6389 +pidfile /usr/local/redis/conf/6389/6389.pid +logfile /usr/local/redis/conf/6389/6389.log diff --git a/codes/linux/soft/config/redis/cluster/README.md b/codes/linux/soft/config/redis/cluster/README.md new file mode 100644 index 00000000..3d379085 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/README.md @@ -0,0 +1,53 @@ +# Redis 集群配置 + +## 使用方式 + +集群拓扑: + +- 三主六从,每个主节点有两个从节点。 +- 三哨兵,分别监听其中一个主节点。 + +启动方式: + +- 先执行 start-cluster.sh,会自动根据 6381 ~ 6389 目录启动服务器,并将其配置为集群。 +- 再执行 start-sentinel.sh,会根据 26381 ~ 26383 目录启动哨兵,监听集群中的三个主节点。 + +## 配置 + +(1)集群服务器配置 redis.conf + +``` +port 6381 +bind 0.0.0.0 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/6381/6381.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/6381 +pidfile /usr/local/redis/conf/6381/6381.pid +logfile /usr/local/redis/conf/6381/6381.log +``` + +端口号、配置目录(`/usr/local/redis/conf`)根据实际情况修改。 + +(2)哨兵服务器配置 sentinel.conf + +``` +port 26383 +daemonize yes +sentinel monitor redis-master 172.22.6.3 6383 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/26383/26383.log +``` + +端口号、配置目录(`/usr/local/redis/conf`)根据实际情况修改。 + +最重要的配置在于:sentinel monitor redis-master 172.22.6.3 6383 2 + +表示监听的服务器集群名叫 redis-master,当前哨兵监听的服务器节点是:172.22.6.3:6383,这个节点如果是主节点,一旦宕机,选举新的主节点,需要至少 2 个哨兵同意。 \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/create-cluster b/codes/linux/soft/config/redis/cluster/create-cluster new file mode 100644 index 00000000..ac133a59 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/create-cluster @@ -0,0 +1,102 @@ +#!/bin/bash + +# Settings +PORT=6380 +TIMEOUT=2000 +NODES=6 +REPLICAS=1 + +# You may want to put the above config parameters into config.sh in order to +# override the defaults without modifying this script. + +if [ -a config.sh ] +then + source "config.sh" +fi + +# Computed vars +ENDPORT=$((PORT+NODES)) + +if [ "$1" == "start" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + echo "Starting $PORT" + /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf + done + exit 0 +fi + +if [ "$1" == "create" ] +then + HOSTS="" + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + HOSTS="$HOSTS 127.0.0.1:$PORT" + done + /opt/redis/src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS + exit 0 +fi + +if [ "$1" == "stop" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + echo "Stopping $PORT" + /opt/redis/src/redis-cli -p $PORT shutdown nosave + done + exit 0 +fi + +if [ "$1" == "watch" ] +then + PORT=$((PORT+1)) + while [ 1 ]; do + clear + date + /opt/redis/src/redis-cli -p $PORT cluster nodes | head -30 + sleep 1 + done + exit 0 +fi + +if [ "$1" == "tail" ] +then + INSTANCE=$2 + PORT=$((PORT+INSTANCE)) + tail -f ${PORT}.log + exit 0 +fi + +if [ "$1" == "call" ] +then + while [ $((PORT < ENDPORT)) != "0" ]; do + PORT=$((PORT+1)) + /opt/redis/src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 + done + exit 0 +fi + +if [ "$1" == "clean" ] +then + rm -rf *.log + rm -rf appendonly*.aof + rm -rf dump*.rdb + rm -rf nodes*.conf + exit 0 +fi + +if [ "$1" == "clean-logs" ] +then + rm -rf *.log + exit 0 +fi + +echo "Usage: $0 [start|create|stop|watch|tail|clean]" +echo "start -- Launch Redis Cluster instances." +echo "create -- Create a cluster using redis-cli --cluster create." +echo "stop -- Stop Redis Cluster instances." +echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." +echo "tail -- Run tail -f of instance at base port + ID." +echo "clean -- Remove all instances data, logs, configs." +echo "clean-logs -- Remove just instances logs." diff --git a/codes/linux/soft/config/redis/cluster/redis-cluster.sh b/codes/linux/soft/config/redis/cluster/redis-cluster.sh deleted file mode 100644 index 23a0051b..00000000 --- a/codes/linux/soft/config/redis/cluster/redis-cluster.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env bash - -# --------------------------------------------------------------------------------- -# 控制台颜色 -BLACK="\033[1;30m" -RED="\033[1;31m" -GREEN="\033[1;32m" -YELLOW="\033[1;33m" -BLUE="\033[1;34m" -PURPLE="\033[1;35m" -CYAN="\033[1;36m" -RESET="$(tput sgr0)" -# --------------------------------------------------------------------------------- - -printf "${BLUE}\n" -cat << EOF -################################################################################### -# Redis 集群控制脚本 -# @system: 适用于 CentOS7+ -# @author: Zhang Peng -################################################################################### -EOF -printf "${RESET}\n" - -# Settings -PORT=6380 -NODES=6 -ENDPORT=$((PORT + NODES)) -TIMEOUT=2000 -REPLICAS=0 -PATH="/usr/local/redis" - -######################################## MAIN ######################################## -printf "${PURPLE}\n" -printf "Usage: $0 [start|create|stop|watch|tail|clean]\n" -printf "start -- Launch Redis Cluster instances.\n" -printf "create -- Create a cluster using redis-cli --cluster create.\n" -printf "stop -- Stop Redis Cluster instances.\n" -printf "watch -- Show CLUSTER NODES output (first 30 lines) of first node.\n" -printf "tail -- Run tail -f of instance at base port + ID.\n" -printf "clean -- Remove all instances data, logs, configs.\n" -printf "clean-logs -- Remove just instances logs.\n" -printf "${RESET}\n" - -case $1 in - "start") - while [[ $((PORT < ENDPORT)) != "0" ]]; do - PORT=$((PORT + 1)) - echo "Starting $PORT" - if [[ -e "${PATH}/cluster/${PORT}/redis.conf" ]]; then - ${PATH}/src/redis-server "${PATH}/cluster/${PORT}/redis.conf" - fi - done - ;; - "create") - HOSTS="" - while [[ $((PORT < ENDPORT)) != "0" ]]; do - PORT=$((PORT + 1)) - HOSTS="$HOSTS 127.0.0.1:$PORT" - done - ${PATH}/src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS - ;; - "stop") - while [[ $((PORT < ENDPORT)) != "0" ]]; do - PORT=$((PORT + 1)) - echo "Stopping $PORT" - ${PATH}/src/redis-cli -p $PORT shutdown nosave - done - ;; - "watch") - PORT=$((PORT + 1)) - while [[ 1 ]]; do - clear - date - ${PATH}/src/redis-cli -p $PORT cluster nodes | head -30 - sleep 1 - done - ;; - "tail") - INSTANCE=$2 - PORT=$((PORT + INSTANCE)) - tail -f ${PORT}.log - ;; - "call") - while [[ $((PORT < ENDPORT)) != "0" ]]; do - PORT=$((PORT + 1)) - ${PATH}/src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 - done - ;; - "clean") - rm -rf **/*.log - rm -rf **/appendonly*.aof - rm -rf **/dump*.rdb - rm -rf **/nodes*.conf - ;; - "clean-logs") - rm -rf **/*.log - ;; - "exit") - printf "${RED}Invalid option!${RESET}\n" - main - exit 0 - ;; -esac diff --git a/codes/linux/soft/config/redis/cluster/start-cluster.sh b/codes/linux/soft/config/redis/cluster/start-cluster.sh new file mode 100644 index 00000000..71eb01cc --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/start-cluster.sh @@ -0,0 +1,19 @@ +/opt/redis/src/redis-server /usr/local/redis/conf/6381/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6382/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6383/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6384/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6385/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6386/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6387/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6388/redis.conf + +/opt/redis/src/redis-server /usr/local/redis/conf/6389/redis.conf + +/opt/redis/src/redis-cli --cluster create 172.22.6.3:6381 172.22.6.3:6382 172.22.6.3:6383 172.22.6.3:6384 172.22.6.3:6385 172.22.6.3:6386 172.22.6.3:6387 172.22.6.3:6388 172.22.6.3:6389 --cluster-replicas 2 diff --git a/codes/linux/soft/config/redis/cluster/start-sentinel.sh b/codes/linux/soft/config/redis/cluster/start-sentinel.sh new file mode 100644 index 00000000..9b146735 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/start-sentinel.sh @@ -0,0 +1,3 @@ +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26381/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26382/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26383/sentinel.conf From b6d9c81ffbc2a402c592797719836c5ac5c8ff26 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Thu, 16 Jul 2020 11:21:46 +0800 Subject: [PATCH 47/64] =?UTF-8?q?Redis=20Cluster=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cluster/{26381 => 27001}/sentinel.conf | 6 +- .../cluster/{26382 => 27002}/sentinel.conf | 6 +- .../cluster/{26383 => 27003}/sentinel.conf | 6 +- .../config/redis/cluster/27004/sentinel.conf | 8 ++ .../config/redis/cluster/27005/sentinel.conf | 8 ++ .../config/redis/cluster/27006/sentinel.conf | 8 ++ .../soft/config/redis/cluster/6381/redis.conf | 12 --- .../soft/config/redis/cluster/6382/redis.conf | 12 --- .../soft/config/redis/cluster/6383/redis.conf | 12 --- .../soft/config/redis/cluster/6384/redis.conf | 12 --- .../soft/config/redis/cluster/6385/redis.conf | 12 --- .../soft/config/redis/cluster/6386/redis.conf | 12 --- .../soft/config/redis/cluster/6387/redis.conf | 12 --- .../soft/config/redis/cluster/6388/redis.conf | 12 --- .../soft/config/redis/cluster/6389/redis.conf | 12 --- .../soft/config/redis/cluster/7001/redis.conf | 12 +++ .../soft/config/redis/cluster/7002/redis.conf | 12 +++ .../soft/config/redis/cluster/7003/redis.conf | 12 +++ .../soft/config/redis/cluster/7004/redis.conf | 12 +++ .../soft/config/redis/cluster/7005/redis.conf | 12 +++ .../soft/config/redis/cluster/7006/redis.conf | 12 +++ .../soft/config/redis/cluster/7007/redis.conf | 12 +++ .../soft/config/redis/cluster/7008/redis.conf | 12 +++ .../soft/config/redis/cluster/7009/redis.conf | 12 +++ .../soft/config/redis/cluster/7010/redis.conf | 12 +++ .../soft/config/redis/cluster/7011/redis.conf | 12 +++ .../soft/config/redis/cluster/7012/redis.conf | 12 +++ .../linux/soft/config/redis/cluster/README.md | 28 ++--- .../{create-cluster => redis-cluster.sh} | 51 +++++---- .../config/redis/cluster/redis-cluster2.sh | 101 ++++++++++++++++++ .../config/redis/cluster/start-cluster.sh | 40 +++---- .../config/redis/cluster/start-cluster2.sh | 18 ++++ .../config/redis/cluster/start-sentinel.sh | 9 +- codes/linux/soft/config/redis/redis.conf | 2 +- 34 files changed, 363 insertions(+), 180 deletions(-) rename codes/linux/soft/config/redis/cluster/{26381 => 27001}/sentinel.conf (64%) rename codes/linux/soft/config/redis/cluster/{26382 => 27002}/sentinel.conf (64%) rename codes/linux/soft/config/redis/cluster/{26383 => 27003}/sentinel.conf (64%) create mode 100644 codes/linux/soft/config/redis/cluster/27004/sentinel.conf create mode 100644 codes/linux/soft/config/redis/cluster/27005/sentinel.conf create mode 100644 codes/linux/soft/config/redis/cluster/27006/sentinel.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6381/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6382/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6383/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6384/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6385/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6386/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6387/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6388/redis.conf delete mode 100644 codes/linux/soft/config/redis/cluster/6389/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7001/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7002/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7003/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7004/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7005/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7006/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7007/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7008/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7009/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7010/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7011/redis.conf create mode 100644 codes/linux/soft/config/redis/cluster/7012/redis.conf rename codes/linux/soft/config/redis/cluster/{create-cluster => redis-cluster.sh} (63%) create mode 100644 codes/linux/soft/config/redis/cluster/redis-cluster2.sh create mode 100644 codes/linux/soft/config/redis/cluster/start-cluster2.sh diff --git a/codes/linux/soft/config/redis/cluster/26381/sentinel.conf b/codes/linux/soft/config/redis/cluster/27001/sentinel.conf similarity index 64% rename from codes/linux/soft/config/redis/cluster/26381/sentinel.conf rename to codes/linux/soft/config/redis/cluster/27001/sentinel.conf index a4b3f94f..0b7a6f02 100644 --- a/codes/linux/soft/config/redis/cluster/26381/sentinel.conf +++ b/codes/linux/soft/config/redis/cluster/27001/sentinel.conf @@ -1,8 +1,8 @@ -port 26381 +port 27001 daemonize yes -sentinel monitor redis-master 172.22.6.3 6381 2 +sentinel monitor redis-master 172.22.6.3 7001 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 -logfile /usr/local/redis/conf/26381/26381.log \ No newline at end of file +logfile /usr/local/redis/conf/27001/27001.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/26382/sentinel.conf b/codes/linux/soft/config/redis/cluster/27002/sentinel.conf similarity index 64% rename from codes/linux/soft/config/redis/cluster/26382/sentinel.conf rename to codes/linux/soft/config/redis/cluster/27002/sentinel.conf index db20a61d..d208d9e4 100644 --- a/codes/linux/soft/config/redis/cluster/26382/sentinel.conf +++ b/codes/linux/soft/config/redis/cluster/27002/sentinel.conf @@ -1,8 +1,8 @@ -port 26382 +port 27002 daemonize yes -sentinel monitor redis-master 172.22.6.3 6382 2 +sentinel monitor redis-master 172.22.6.3 7002 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 -logfile /usr/local/redis/conf/26382/26382.log \ No newline at end of file +logfile /usr/local/redis/conf/27002/27002.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/26383/sentinel.conf b/codes/linux/soft/config/redis/cluster/27003/sentinel.conf similarity index 64% rename from codes/linux/soft/config/redis/cluster/26383/sentinel.conf rename to codes/linux/soft/config/redis/cluster/27003/sentinel.conf index fc5af052..c6d8588a 100644 --- a/codes/linux/soft/config/redis/cluster/26383/sentinel.conf +++ b/codes/linux/soft/config/redis/cluster/27003/sentinel.conf @@ -1,8 +1,8 @@ -port 26383 +port 27003 daemonize yes -sentinel monitor redis-master 172.22.6.3 6383 2 +sentinel monitor redis-master 172.22.6.3 7003 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 -logfile /usr/local/redis/conf/26383/26383.log \ No newline at end of file +logfile /usr/local/redis/conf/27003/27003.log \ No newline at end of file diff --git a/codes/linux/soft/config/redis/cluster/27004/sentinel.conf b/codes/linux/soft/config/redis/cluster/27004/sentinel.conf new file mode 100644 index 00000000..39f10881 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/27004/sentinel.conf @@ -0,0 +1,8 @@ +port 27004 +daemonize yes +sentinel monitor redis-master 172.22.6.3 7007 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/27004/27004.log diff --git a/codes/linux/soft/config/redis/cluster/27005/sentinel.conf b/codes/linux/soft/config/redis/cluster/27005/sentinel.conf new file mode 100644 index 00000000..a33aa836 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/27005/sentinel.conf @@ -0,0 +1,8 @@ +port 27005 +daemonize yes +sentinel monitor redis-master 172.22.6.3 7008 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/27005/27005.log diff --git a/codes/linux/soft/config/redis/cluster/27006/sentinel.conf b/codes/linux/soft/config/redis/cluster/27006/sentinel.conf new file mode 100644 index 00000000..a006f15e --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/27006/sentinel.conf @@ -0,0 +1,8 @@ +port 27006 +daemonize yes +sentinel monitor redis-master 172.22.6.3 7009 2 +sentinel down-after-milliseconds redis-master 5000 +sentinel failover-timeout redis-master 900000 +sentinel parallel-syncs redis-master 1 +#sentinel auth-pass redis-master 123456 +logfile /usr/local/redis/conf/27006/27006.log diff --git a/codes/linux/soft/config/redis/cluster/6381/redis.conf b/codes/linux/soft/config/redis/cluster/6381/redis.conf deleted file mode 100644 index 054c1f04..00000000 --- a/codes/linux/soft/config/redis/cluster/6381/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6381 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6381/6381.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6381 -pidfile /usr/local/redis/conf/6381/6381.pid -logfile /usr/local/redis/conf/6381/6381.log diff --git a/codes/linux/soft/config/redis/cluster/6382/redis.conf b/codes/linux/soft/config/redis/cluster/6382/redis.conf deleted file mode 100644 index d5d62e1a..00000000 --- a/codes/linux/soft/config/redis/cluster/6382/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6382 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6382/6382.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6382 -pidfile /usr/local/redis/conf/6382/6382.pid -logfile /usr/local/redis/conf/6382/6382.log diff --git a/codes/linux/soft/config/redis/cluster/6383/redis.conf b/codes/linux/soft/config/redis/cluster/6383/redis.conf deleted file mode 100644 index 41e10ee3..00000000 --- a/codes/linux/soft/config/redis/cluster/6383/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6383 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6383/6383.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6383 -pidfile /usr/local/redis/conf/6383/6383.pid -logfile /usr/local/redis/conf/6383/6383.log diff --git a/codes/linux/soft/config/redis/cluster/6384/redis.conf b/codes/linux/soft/config/redis/cluster/6384/redis.conf deleted file mode 100644 index b57fef5e..00000000 --- a/codes/linux/soft/config/redis/cluster/6384/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6384 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6384/6384.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6384 -pidfile /usr/local/redis/conf/6384/6384.pid -logfile /usr/local/redis/conf/6384/6384.log diff --git a/codes/linux/soft/config/redis/cluster/6385/redis.conf b/codes/linux/soft/config/redis/cluster/6385/redis.conf deleted file mode 100644 index 7535e79d..00000000 --- a/codes/linux/soft/config/redis/cluster/6385/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6385 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6385/6385.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6385 -pidfile /usr/local/redis/conf/6385/6385.pid -logfile /usr/local/redis/conf/6385/6385.log diff --git a/codes/linux/soft/config/redis/cluster/6386/redis.conf b/codes/linux/soft/config/redis/cluster/6386/redis.conf deleted file mode 100644 index 03f1127d..00000000 --- a/codes/linux/soft/config/redis/cluster/6386/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6386 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6386/6386.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6386 -pidfile /usr/local/redis/conf/6386/6386.pid -logfile /usr/local/redis/conf/6386/6386.log diff --git a/codes/linux/soft/config/redis/cluster/6387/redis.conf b/codes/linux/soft/config/redis/cluster/6387/redis.conf deleted file mode 100644 index 31ccfe90..00000000 --- a/codes/linux/soft/config/redis/cluster/6387/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6387 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6387/6387.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6387 -pidfile /usr/local/redis/conf/6387/6387.pid -logfile /usr/local/redis/conf/6387/6387.log diff --git a/codes/linux/soft/config/redis/cluster/6388/redis.conf b/codes/linux/soft/config/redis/cluster/6388/redis.conf deleted file mode 100644 index 6f159f9a..00000000 --- a/codes/linux/soft/config/redis/cluster/6388/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6388 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6388/6388.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6388 -pidfile /usr/local/redis/conf/6388/6388.pid -logfile /usr/local/redis/conf/6388/6388.log diff --git a/codes/linux/soft/config/redis/cluster/6389/redis.conf b/codes/linux/soft/config/redis/cluster/6389/redis.conf deleted file mode 100644 index e9a17b3e..00000000 --- a/codes/linux/soft/config/redis/cluster/6389/redis.conf +++ /dev/null @@ -1,12 +0,0 @@ -port 6389 -bind 0.0.0.0 -daemonize yes - -cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6389/6389.conf -cluster-node-timeout 10000 - -appendonly yes -dir /usr/local/redis/conf/6389 -pidfile /usr/local/redis/conf/6389/6389.pid -logfile /usr/local/redis/conf/6389/6389.log diff --git a/codes/linux/soft/config/redis/cluster/7001/redis.conf b/codes/linux/soft/config/redis/cluster/7001/redis.conf new file mode 100644 index 00000000..34bd0216 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7001/redis.conf @@ -0,0 +1,12 @@ +port 7001 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7001/7001.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7001 +pidfile /usr/local/redis/conf/7001/7001.pid +logfile /usr/local/redis/conf/7001/7001.log diff --git a/codes/linux/soft/config/redis/cluster/7002/redis.conf b/codes/linux/soft/config/redis/cluster/7002/redis.conf new file mode 100644 index 00000000..5e7fbc0d --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7002/redis.conf @@ -0,0 +1,12 @@ +port 7002 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7002/7002.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7002 +pidfile /usr/local/redis/conf/7002/7002.pid +logfile /usr/local/redis/conf/7002/7002.log diff --git a/codes/linux/soft/config/redis/cluster/7003/redis.conf b/codes/linux/soft/config/redis/cluster/7003/redis.conf new file mode 100644 index 00000000..e87573dd --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7003/redis.conf @@ -0,0 +1,12 @@ +port 7003 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7003/7003.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7003 +pidfile /usr/local/redis/conf/7003/7003.pid +logfile /usr/local/redis/conf/7003/7003.log diff --git a/codes/linux/soft/config/redis/cluster/7004/redis.conf b/codes/linux/soft/config/redis/cluster/7004/redis.conf new file mode 100644 index 00000000..1b4d6089 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7004/redis.conf @@ -0,0 +1,12 @@ +port 7004 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7004/7004.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7004 +pidfile /usr/local/redis/conf/7004/7004.pid +logfile /usr/local/redis/conf/7004/7004.log diff --git a/codes/linux/soft/config/redis/cluster/7005/redis.conf b/codes/linux/soft/config/redis/cluster/7005/redis.conf new file mode 100644 index 00000000..d959a8d6 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7005/redis.conf @@ -0,0 +1,12 @@ +port 7005 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7005/7005.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7005 +pidfile /usr/local/redis/conf/7005/7005.pid +logfile /usr/local/redis/conf/7005/7005.log diff --git a/codes/linux/soft/config/redis/cluster/7006/redis.conf b/codes/linux/soft/config/redis/cluster/7006/redis.conf new file mode 100644 index 00000000..6fd2635d --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7006/redis.conf @@ -0,0 +1,12 @@ +port 7006 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7006/7006.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7006 +pidfile /usr/local/redis/conf/7006/7006.pid +logfile /usr/local/redis/conf/7006/7006.log diff --git a/codes/linux/soft/config/redis/cluster/7007/redis.conf b/codes/linux/soft/config/redis/cluster/7007/redis.conf new file mode 100644 index 00000000..5cd28edc --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7007/redis.conf @@ -0,0 +1,12 @@ +port 7007 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7007/7007.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7007 +pidfile /usr/local/redis/conf/7007/7007.pid +logfile /usr/local/redis/conf/7007/7007.log diff --git a/codes/linux/soft/config/redis/cluster/7008/redis.conf b/codes/linux/soft/config/redis/cluster/7008/redis.conf new file mode 100644 index 00000000..54efbd84 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7008/redis.conf @@ -0,0 +1,12 @@ +port 7008 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7008/7008.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7008 +pidfile /usr/local/redis/conf/7008/7008.pid +logfile /usr/local/redis/conf/7008/7008.log diff --git a/codes/linux/soft/config/redis/cluster/7009/redis.conf b/codes/linux/soft/config/redis/cluster/7009/redis.conf new file mode 100644 index 00000000..41bdc4cc --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7009/redis.conf @@ -0,0 +1,12 @@ +port 7009 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7009/7009.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7009 +pidfile /usr/local/redis/conf/7009/7009.pid +logfile /usr/local/redis/conf/7009/7009.log diff --git a/codes/linux/soft/config/redis/cluster/7010/redis.conf b/codes/linux/soft/config/redis/cluster/7010/redis.conf new file mode 100644 index 00000000..b3b73f3d --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7010/redis.conf @@ -0,0 +1,12 @@ +port 7010 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7010/7010.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7010 +pidfile /usr/local/redis/conf/7010/7010.pid +logfile /usr/local/redis/conf/7010/7010.log diff --git a/codes/linux/soft/config/redis/cluster/7011/redis.conf b/codes/linux/soft/config/redis/cluster/7011/redis.conf new file mode 100644 index 00000000..ad94ad02 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7011/redis.conf @@ -0,0 +1,12 @@ +port 7011 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7011/7011.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7011 +pidfile /usr/local/redis/conf/7011/7011.pid +logfile /usr/local/redis/conf/7011/7011.log diff --git a/codes/linux/soft/config/redis/cluster/7012/redis.conf b/codes/linux/soft/config/redis/cluster/7012/redis.conf new file mode 100644 index 00000000..8b97944c --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/7012/redis.conf @@ -0,0 +1,12 @@ +port 7012 +bind 172.22.6.3 +daemonize yes + +cluster-enabled yes +cluster-config-file /usr/local/redis/conf/7012/7012.conf +cluster-node-timeout 10000 + +appendonly yes +dir /usr/local/redis/conf/7012 +pidfile /usr/local/redis/conf/7012/7012.pid +logfile /usr/local/redis/conf/7012/7012.log diff --git a/codes/linux/soft/config/redis/cluster/README.md b/codes/linux/soft/config/redis/cluster/README.md index 3d379085..f6322417 100644 --- a/codes/linux/soft/config/redis/cluster/README.md +++ b/codes/linux/soft/config/redis/cluster/README.md @@ -4,31 +4,31 @@ 集群拓扑: -- 三主六从,每个主节点有两个从节点。 -- 三哨兵,分别监听其中一个主节点。 +- 三主三从 +- 三哨兵 启动方式: -- 先执行 start-cluster.sh,会自动根据 6381 ~ 6389 目录启动服务器,并将其配置为集群。 -- 再执行 start-sentinel.sh,会根据 26381 ~ 26383 目录启动哨兵,监听集群中的三个主节点。 +- 先执行 redis-cluster.sh,会自动根据 7001 ~ 7006 目录启动服务器,并将其配置为集群。 +- 再执行 start-sentinel.sh,会根据 27001 ~ 27003 目录启动哨兵,监听集群中的三个主节点。 ## 配置 (1)集群服务器配置 redis.conf ``` -port 6381 +port 7001 bind 0.0.0.0 daemonize yes cluster-enabled yes -cluster-config-file /usr/local/redis/conf/6381/6381.conf +cluster-config-file /usr/local/redis/conf/7001/7001.conf cluster-node-timeout 10000 appendonly yes -dir /usr/local/redis/conf/6381 -pidfile /usr/local/redis/conf/6381/6381.pid -logfile /usr/local/redis/conf/6381/6381.log +dir /usr/local/redis/conf/7001 +pidfile /usr/local/redis/conf/7001/7001.pid +logfile /usr/local/redis/conf/7001/7001.log ``` 端口号、配置目录(`/usr/local/redis/conf`)根据实际情况修改。 @@ -36,18 +36,18 @@ logfile /usr/local/redis/conf/6381/6381.log (2)哨兵服务器配置 sentinel.conf ``` -port 26383 +port 27003 daemonize yes -sentinel monitor redis-master 172.22.6.3 6383 2 +sentinel monitor redis-master 172.22.6.3 7003 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 -logfile /usr/local/redis/conf/26383/26383.log +logfile /usr/local/redis/conf/27003/27003.log ``` 端口号、配置目录(`/usr/local/redis/conf`)根据实际情况修改。 -最重要的配置在于:sentinel monitor redis-master 172.22.6.3 6383 2 +最重要的配置在于:sentinel monitor redis-master 172.22.6.3 7003 2 -表示监听的服务器集群名叫 redis-master,当前哨兵监听的服务器节点是:172.22.6.3:6383,这个节点如果是主节点,一旦宕机,选举新的主节点,需要至少 2 个哨兵同意。 \ No newline at end of file +表示监听的服务器集群名叫 redis-master,当前哨兵监听的服务器节点是:172.22.6.3:7003,这个节点如果是主节点,一旦宕机,选举新的主节点,需要至少 2 个哨兵同意。 diff --git a/codes/linux/soft/config/redis/cluster/create-cluster b/codes/linux/soft/config/redis/cluster/redis-cluster.sh similarity index 63% rename from codes/linux/soft/config/redis/cluster/create-cluster rename to codes/linux/soft/config/redis/cluster/redis-cluster.sh index ac133a59..07c44d74 100644 --- a/codes/linux/soft/config/redis/cluster/create-cluster +++ b/codes/linux/soft/config/redis/cluster/redis-cluster.sh @@ -1,66 +1,65 @@ -#!/bin/bash +#!/usr/bin/env bash # Settings -PORT=6380 +HOST="172.22.6.3" +PORT=7000 TIMEOUT=2000 NODES=6 REPLICAS=1 +ENDPORT=$((PORT+NODES)) # You may want to put the above config parameters into config.sh in order to # override the defaults without modifying this script. -if [ -a config.sh ] +if [[ -a config.sh ]] then source "config.sh" fi -# Computed vars -ENDPORT=$((PORT+NODES)) - -if [ "$1" == "start" ] +if [[ "$1" == "create" ]] then - while [ $((PORT < ENDPORT)) != "0" ]; do + HOSTLIST="" + while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) - echo "Starting $PORT" - /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf + HOSTLIST="$HOSTLIST $HOST:$PORT" done + /opt/redis/src/redis-cli --cluster create ${HOSTLIST} --cluster-replicas ${REPLICAS} exit 0 fi -if [ "$1" == "create" ] +if [[ "$1" == "start" ]] then - HOSTS="" - while [ $((PORT < ENDPORT)) != "0" ]; do + while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) - HOSTS="$HOSTS 127.0.0.1:$PORT" + echo "Starting $PORT" + /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf done - /opt/redis/src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS exit 0 fi -if [ "$1" == "stop" ] +if [[ "$1" == "stop" ]] then - while [ $((PORT < ENDPORT)) != "0" ]; do + while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) echo "Stopping $PORT" - /opt/redis/src/redis-cli -p $PORT shutdown nosave + /opt/redis/src/redis-cli -p ${PORT} shutdown nosave done exit 0 fi -if [ "$1" == "watch" ] +if [[ "$1" == "watch" ]] then PORT=$((PORT+1)) while [ 1 ]; do clear date - /opt/redis/src/redis-cli -p $PORT cluster nodes | head -30 + /opt/redis/src/redis-cli -p ${PORT} cluster nodes | head -30 sleep 1 done exit 0 fi -if [ "$1" == "tail" ] +if [[ "$1" == "tail" ]] then INSTANCE=$2 PORT=$((PORT+INSTANCE)) @@ -68,16 +67,16 @@ then exit 0 fi -if [ "$1" == "call" ] +if [[ "$1" == "call" ]] then - while [ $((PORT < ENDPORT)) != "0" ]; do + while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) - /opt/redis/src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 + /opt/redis/src/redis-cli -p ${PORT} $2 $3 $4 $5 $6 $7 $8 $9 done exit 0 fi -if [ "$1" == "clean" ] +if [[ "$1" == "clean" ]] then rm -rf *.log rm -rf appendonly*.aof @@ -86,7 +85,7 @@ then exit 0 fi -if [ "$1" == "clean-logs" ] +if [[ "$1" == "clean-logs" ]] then rm -rf *.log exit 0 diff --git a/codes/linux/soft/config/redis/cluster/redis-cluster2.sh b/codes/linux/soft/config/redis/cluster/redis-cluster2.sh new file mode 100644 index 00000000..3e3b37e3 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/redis-cluster2.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +# Settings +HOST="172.22.6.3" +PORT=7000 +TIMEOUT=2000 +NODES=12 +REPLICAS=1 +ENDPORT=$((PORT+NODES)) + +# You may want to put the above config parameters into config.sh in order to +# override the defaults without modifying this script. + +if [[ -a config.sh ]] +then + source "config.sh" +fi + +if [[ "$1" == "create" ]] +then + HOSTLIST="" + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT+1)) + HOSTLIST="$HOSTLIST $HOST:$PORT" + done + /opt/redis/src/redis-cli --cluster create ${HOSTLIST} --cluster-replicas ${REPLICAS} + exit 0 +fi + +if [[ "$1" == "start" ]] +then + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT+1)) + echo "Starting $PORT" + /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf + done + exit 0 +fi + +if [[ "$1" == "stop" ]] +then + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT+1)) + echo "Stopping $PORT" + /opt/redis/src/redis-cli -p ${PORT} shutdown nosave + done + exit 0 +fi + +if [[ "$1" == "watch" ]] +then + PORT=$((PORT+1)) + while [ 1 ]; do + clear + date + /opt/redis/src/redis-cli -p ${PORT} cluster nodes | head -30 + sleep 1 + done + exit 0 +fi + +if [[ "$1" == "tail" ]] +then + INSTANCE=$2 + PORT=$((PORT+INSTANCE)) + tail -f ${PORT}.log + exit 0 +fi + +if [[ "$1" == "call" ]] +then + while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT+1)) + /opt/redis/src/redis-cli -p ${PORT} $2 $3 $4 $5 $6 $7 $8 $9 + done + exit 0 +fi + +if [[ "$1" == "clean" ]] +then + rm -rf *.log + rm -rf appendonly*.aof + rm -rf dump*.rdb + rm -rf nodes*.conf + exit 0 +fi + +if [[ "$1" == "clean-logs" ]] +then + rm -rf *.log + exit 0 +fi + +echo "Usage: $0 [start|create|stop|watch|tail|clean]" +echo "start -- Launch Redis Cluster instances." +echo "create -- Create a cluster using redis-cli --cluster create." +echo "stop -- Stop Redis Cluster instances." +echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." +echo "tail -- Run tail -f of instance at base port + ID." +echo "clean -- Remove all instances data, logs, configs." +echo "clean-logs -- Remove just instances logs." diff --git a/codes/linux/soft/config/redis/cluster/start-cluster.sh b/codes/linux/soft/config/redis/cluster/start-cluster.sh index 71eb01cc..c9ec4bea 100644 --- a/codes/linux/soft/config/redis/cluster/start-cluster.sh +++ b/codes/linux/soft/config/redis/cluster/start-cluster.sh @@ -1,19 +1,21 @@ -/opt/redis/src/redis-server /usr/local/redis/conf/6381/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6382/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6383/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6384/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6385/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6386/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6387/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6388/redis.conf - -/opt/redis/src/redis-server /usr/local/redis/conf/6389/redis.conf - -/opt/redis/src/redis-cli --cluster create 172.22.6.3:6381 172.22.6.3:6382 172.22.6.3:6383 172.22.6.3:6384 172.22.6.3:6385 172.22.6.3:6386 172.22.6.3:6387 172.22.6.3:6388 172.22.6.3:6389 --cluster-replicas 2 +#!/usr/bin/env bash + +PORT=6380 +NODES=6 +ENDPORT=$((PORT+NODES)) + +# 启动 4 个 redis server +while [[ $((PORT < ENDPORT)) != "0" ]]; do + PORT=$((PORT+1)) + echo "Starting $PORT" + /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf +done + +# 创建集群模式,设置副本为 1 +# redis cluster 会自动将 4 个节点设置为 一主一从 模式,并且为两个主节点做数据分片 +/opt/redis/src/redis-cli --cluster create 172.22.6.3:6381 172.22.6.3:6382 172.22.6.3:6383 172.22.6.3:6384 172.22.6.3:6385 172.22.6.3:6386 --cluster-replicas 1 + +# 启动哨兵 +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26381/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26382/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26383/sentinel.conf diff --git a/codes/linux/soft/config/redis/cluster/start-cluster2.sh b/codes/linux/soft/config/redis/cluster/start-cluster2.sh new file mode 100644 index 00000000..54b59c96 --- /dev/null +++ b/codes/linux/soft/config/redis/cluster/start-cluster2.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# 启动 4 个 redis server +/opt/redis/src/redis-server /usr/local/redis/conf/6381/redis.conf +/opt/redis/src/redis-server /usr/local/redis/conf/6382/redis.conf +/opt/redis/src/redis-server /usr/local/redis/conf/6383/redis.conf +/opt/redis/src/redis-server /usr/local/redis/conf/6384/redis.conf +/opt/redis/src/redis-server /usr/local/redis/conf/6385/redis.conf +/opt/redis/src/redis-server /usr/local/redis/conf/6386/redis.conf + +# 创建集群模式,设置副本为 1 +# redis cluster 会自动将 4 个节点设置为 一主一从 模式,并且为两个主节点做数据分片 +/opt/redis/src/redis-cli --cluster create 172.22.6.3:6381 172.22.6.3:6382 172.22.6.3:6383 172.22.6.3:6384 172.22.6.3:6385 172.22.6.3:6386 --cluster-replicas 1 + +# 启动哨兵 +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26381/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26382/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/26383/sentinel.conf diff --git a/codes/linux/soft/config/redis/cluster/start-sentinel.sh b/codes/linux/soft/config/redis/cluster/start-sentinel.sh index 9b146735..a21c7d63 100644 --- a/codes/linux/soft/config/redis/cluster/start-sentinel.sh +++ b/codes/linux/soft/config/redis/cluster/start-sentinel.sh @@ -1,3 +1,6 @@ -/opt/redis/src/redis-sentinel /usr/local/redis/conf/26381/sentinel.conf -/opt/redis/src/redis-sentinel /usr/local/redis/conf/26382/sentinel.conf -/opt/redis/src/redis-sentinel /usr/local/redis/conf/26383/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/27001/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/27002/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/27003/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/27004/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/27005/sentinel.conf +/opt/redis/src/redis-sentinel /usr/local/redis/conf/27006/sentinel.conf diff --git a/codes/linux/soft/config/redis/redis.conf b/codes/linux/soft/config/redis/redis.conf index 798b8799..90628501 100644 --- a/codes/linux/soft/config/redis/redis.conf +++ b/codes/linux/soft/config/redis/redis.conf @@ -66,7 +66,7 @@ # IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES # JUST COMMENT THE FOLLOWING LINE. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -bind 0.0.0.0 +bind 172.22.6.3 # Protected mode is a layer of security protection, in order to avoid that # Redis instances left open on the internet are accessed and exploited. From 40b0db918dab69f803cce813e6dbd319d01f570a Mon Sep 17 00:00:00 2001 From: lovvvve Date: Mon, 27 Jul 2020 17:32:35 +0800 Subject: [PATCH 48/64] Update ntp.md --- docs/linux/ops/ntp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/linux/ops/ntp.md b/docs/linux/ops/ntp.md index f8d58dfb..9a72bdb7 100644 --- a/docs/linux/ops/ntp.md +++ b/docs/linux/ops/ntp.md @@ -148,7 +148,7 @@ $ ntpdate cn.pool.ntp.org 示例:执行如下命令,就可以在每天凌晨 3 点同步系统时间: ```shell -echo "* 3 * * * /usr/sbin/ntpdate cn.pool.ntp.org" >> /etc/crontab # 修改 crond 服务配置 +echo "0 3 * * * /usr/sbin/ntpdate cn.pool.ntp.org" >> /etc/crontab # 修改 crond 服务配置 systemctl restart crond # 重启 crond 服务以生效 ``` From 1458840ac89ae01f9d6b826796d8b93a97165550 Mon Sep 17 00:00:00 2001 From: dunwu Date: Thu, 1 Oct 2020 09:06:56 +0800 Subject: [PATCH 49/64] update docs --- docs/linux/soft/nexus-ops.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 5346413d..73d3dffc 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -103,7 +103,7 @@ Nexus 中的仓库有以下类型: 参考配置如下: -![](http://dunwu.test.upcdn.net/snap/20200403165258.png) +![img](http://dunwu.test.upcdn.net/snap/20200403165258.png) 推荐配置的代理仓库: @@ -112,7 +112,7 @@ Nexus 中的仓库有以下类型: ### 配置 yum 仓库 -![](http://dunwu.test.upcdn.net/snap/20200403201609.png) +![img](http://dunwu.test.upcdn.net/snap/20200403201609.png) 推荐配置的 yum 代理仓库: @@ -146,7 +146,7 @@ Nexus 内置了多个定时任务,可以执行清理。 【示例】定期清理 SNAPSHOST -![](http://dunwu.test.upcdn.net/snap/20200403173030.png) +![img](http://dunwu.test.upcdn.net/snap/20200403173030.png) ## 三、开机自启动 From 326ebb2826e8dfe3d21c3036293f61158ab0ea8b Mon Sep 17 00:00:00 2001 From: dunwu Date: Thu, 13 May 2021 17:24:59 +0800 Subject: [PATCH 50/64] =?UTF-8?q?docs:=20=E6=96=87=E6=A1=A3=E6=95=B4?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- codes/linux/soft/fastdfs-install.sh | 6 +- docs/.vuepress/config.js | 127 ++++++++++++++++++++-------- docs/README.md | 5 +- docs/docker/docker-quickstart.md | 4 +- docs/docker/kubernetes.md | 2 +- docs/linux/cli/linux-cli-dir.md | 4 +- docs/linux/ops/crontab.md | 2 +- docs/linux/ops/samba.md | 2 +- docs/linux/ops/vim.md | 12 +-- docs/linux/soft/gitlab-ops.md | 10 +-- docs/linux/soft/jdk-install.md | 6 +- docs/linux/soft/jenkins-ops.md | 24 +++--- docs/linux/soft/nexus-ops.md | 14 +-- docs/linux/soft/svn-ops.md | 2 +- docs/linux/soft/yapi-ops.md | 12 +-- docs/package.json | 37 +++----- 17 files changed, 164 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index db9c6a26..45a0fa8c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- logo + logo

@@ -13,9 +13,8 @@ > 📚 **linux-tutorial** 是一个 Linux 教程。 > -> 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) -> -> 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) +> - 🔁 项目同步维护:[Github](https://github.com/dunwu/linux-tutorial/) | [Gitee](https://gitee.com/turnon/linux-tutorial/) +> - 📖 电子书阅读:[Github Pages](https://dunwu.github.io/linux-tutorial/) | [Gitee Pages](http://turnon.gitee.io/linux-tutorial/) ## 📖 内容 diff --git a/codes/linux/soft/fastdfs-install.sh b/codes/linux/soft/fastdfs-install.sh index 95cfb009..a7db9e44 100644 --- a/codes/linux/soft/fastdfs-install.sh +++ b/codes/linux/soft/fastdfs-install.sh @@ -59,7 +59,7 @@ yum install -y git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zl mkdir -p ${path} path=/opt/fdfs mkdir -p ${path}/libfastcommon -curl -o ${path}/libfastcommon.zip http://dunwu.test.upcdn.net/soft/fdfs/libfastcommon.zip +curl -o ${path}/libfastcommon.zip https://raw.githubusercontent.com/dunwu/images/dev/soft/fdfs/libfastcommon.zip if [[ ! -f ${path}/libfastcommon.zip ]]; then printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" exit 1 @@ -73,7 +73,7 @@ chmod +x -R ${path}/libfastcommon/*.sh printf "${GREEN}>>>>>>>>> install fastdfs${RESET}" mkdir -p ${path}/fastdfs -curl -o ${path}/fastdfs.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs.zip +curl -o ${path}/fastdfs.zip https://raw.githubusercontent.com/dunwu/images/dev/soft/fdfs/fastdfs.zip if [[ ! -f ${path}/fastdfs.zip ]]; then printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" fi @@ -84,7 +84,7 @@ chmod +x -R ${path}/fastdfs/*.sh printf "${GREEN}>>>>>>>>> install fastdfs-nginx-module${RESET}\n" mkdir -p ${path}/fastdfs-nginx-module -curl -o ${path}/fastdfs-nginx-module.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs-nginx-module.zip +curl -o ${path}/fastdfs-nginx-module.zip https://raw.githubusercontent.com/dunwu/images/dev/soft/fdfs/fastdfs-nginx-module.zip if [[ ! -f ${path}/fastdfs-nginx-module.zip ]]; then printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" fi diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index c5f617ba..f0c20a76 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -2,44 +2,105 @@ * @see https://vuepress.vuejs.org/zh/ */ module.exports = { - port: "4000", - dest: "dist", - base: "/linux-tutorial/", - title: "LINUX-TUTORIAL", - description: "数据库教程", - head: [["link", {rel: "icon", href: `/favicon.ico`}]], + port: '4000', + dest: 'dist', + base: '/linux-tutorial/', + title: 'LINUX-TUTORIAL', + description: '数据库教程', + head: [['link', { rel: 'icon', href: `/favicon.ico` }]], markdown: { externalLinks: { - target: "_blank", rel: "noopener noreferrer" - } + target: '_blank', + rel: 'noopener noreferrer', + }, }, themeConfig: { - logo: "images/dunwu-logo-100.png", - repo: "dunwu/linux-tutorial", - repoLabel: "Github", + logo: 'images/dunwu-logo-100.png', + repo: 'dunwu/linux-tutorial', + repoLabel: 'Github', + docsDir: 'docs', + docsBranch: 'master', editLinks: true, smoothScroll: true, locales: { - "/": { - label: "简体中文", selectText: "Languages", editLinkText: "帮助我们改善此页面!", lastUpdated: "上次更新", nav: [{ - text: "Linux 命令", link: "/linux/cli/", - }, { - text: "Linux 运维", link: "/linux/ops/", - }, { - text: "Linux 软件运维", link: "/linux/soft/", - }, { - text: "Docker 教程", link: "/docker/", - }, { - text: "🎯 博客", link: "https://github.com/dunwu/blog", target: "_blank", rel: "" - }], sidebar: "auto", sidebarDepth: 2 - } - } + '/': { + label: '简体中文', + selectText: 'Languages', + editLinkText: '帮助我们改善此页面!', + lastUpdated: '上次更新', + nav: [ + { + text: 'Linux 命令', + link: '/linux/cli/', + }, + { + text: 'Linux 运维', + link: '/linux/ops/', + }, + { + text: 'Linux 软件运维', + link: '/linux/soft/', + }, + { + text: 'Docker 教程', + link: '/docker/', + }, + { + text: '🎯 博客', + link: 'https://github.com/dunwu/blog', + target: '_blank', + rel: '', + }, + ], + sidebar: 'auto', + sidebarDepth: 2, + }, + }, }, - plugins: [["@vuepress/back-to-top", true], ["@vuepress/pwa", { - serviceWorker: true, updatePopup: true - }], ["@vuepress/medium-zoom", true], ["container", { - type: "vue", before: '
', after: "
" - }], ["container", { - type: "upgrade", before: info => ``, after: "" - }], ["flowchart"]] -}; + plugins: [ + [ + '@vuepress/active-header-links', + { + sidebarLinkSelector: '.sidebar-link', + headerAnchorSelector: '.header-anchor', + }, + ], + ['@vuepress/back-to-top', true], + [ + '@vuepress/pwa', + { + serviceWorker: true, + updatePopup: true, + }, + ], + [ + '@vuepress/last-updated', + { + transformer: (timestamp, lang) => { + // 不要忘了安装 moment + const moment = require('moment') + moment.locale(lang) + return moment(timestamp).fromNow() + }, + }, + ], + ['@vuepress/medium-zoom', true], + [ + 'container', + { + type: 'vue', + before: '
',
+        after: '
', + }, + ], + [ + 'container', + { + type: 'upgrade', + before: (info) => ``, + after: '', + }, + ], + ['flowchart'], + ], +} diff --git a/docs/README.md b/docs/README.md index bb3d6492..dd36841c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,13 +1,14 @@ --- home: true -heroImage: /images/dunwu-logo-200.png +heroImage: https://raw.githubusercontent.com/dunwu/images/dev/common/dunwu-logo-200.png heroText: LINUX-TUTORIAL tagline: 📚 linux-tutorial 是一个 Linux 教程。 actionLink: / footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu --- -# Linux 教程 +![license](https://badgen.net/github/license/dunwu/linux-tutorial) +![build](https://travis-ci.com/dunwu/linux-tutorial.svg?branch=master) > 📚 **linux-tutorial** 是一个 Linux 教程。 > diff --git a/docs/docker/docker-quickstart.md b/docs/docker/docker-quickstart.md index 6fe68340..f231afbc 100644 --- a/docs/docker/docker-quickstart.md +++ b/docs/docker/docker-quickstart.md @@ -31,7 +31,7 @@ Docker 将应用程序与该程序的依赖,打包在一个文件里面。运 - **更轻松的迁移** - 由于 `Docker` 确保了执行环境的一致性,使得应用的迁移更加容易。`Docker` 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。 - **更轻松的维护和扩展** - `Docker` 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,`Docker` 团队同各个开源项目团队一起维护了一大批高质量的 [官方镜像](https://hub.docker.com/search/?type=image&image_filter=official),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。 -![img](http://dunwu.test.upcdn.net/cs/os/docker/containers-and-vm.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/docker/containers-and-vm.png) ### Docker 的主要用途 @@ -354,4 +354,4 @@ $ sudo systemctl start docker ## 参考资料 - [Docker 入门教程](https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) -- [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) \ No newline at end of file +- [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) diff --git a/docs/docker/kubernetes.md b/docs/docker/kubernetes.md index d16b6c57..d600ea91 100644 --- a/docs/docker/kubernetes.md +++ b/docs/docker/kubernetes.md @@ -49,7 +49,7 @@ Kubernetes 主要由以下几个核心组件组成: K8S 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。 -![img](http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/kubernetes/pod.svg) - `Pod` - K8S 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 K8S 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。 - `Node` - Node 是 Pod 真正运行的主机,可以是物理机,也可以是虚拟机。为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 docker 或者 rkt)、`kubelet` 和 `kube-proxy` 服务。 diff --git a/docs/linux/cli/linux-cli-dir.md b/docs/linux/cli/linux-cli-dir.md index c5c9ad9c..d013dad0 100644 --- a/docs/linux/cli/linux-cli-dir.md +++ b/docs/linux/cli/linux-cli-dir.md @@ -8,7 +8,7 @@ linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -![img](http://dunwu.test.upcdn.net/cs/os/linux/linux-folders.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/linux-folders.png) ### 1.2. Linux 文件属性 @@ -35,7 +35,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot 每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 -![img](http://dunwu.test.upcdn.net/snap/20180920180927171909.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920180927171909.png) 从左至右用 0-9 这些数字来表示。 diff --git a/docs/linux/ops/crontab.md b/docs/linux/ops/crontab.md index 1fd9eca3..3ce26339 100644 --- a/docs/linux/ops/crontab.md +++ b/docs/linux/ops/crontab.md @@ -65,7 +65,7 @@ crontab 要执行的定时任务都被保存在 `/etc/crontab` 文件中。 crontab 的文件格式如下: -![img](http://dunwu.test.upcdn.net/snap/20200211113339.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200211113339.png) #### 标准字段 diff --git a/docs/linux/ops/samba.md b/docs/linux/ops/samba.md index dc0e4c50..71358f9a 100644 --- a/docs/linux/ops/samba.md +++ b/docs/linux/ops/samba.md @@ -149,7 +149,7 @@ Windows: 访问:`\\<你的ip>\<你的共享路径>` : -![img](http://dunwu.test.upcdn.net/snap/20180920180928161334.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920180928161334.png) Mac: diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 5fe92f4b..2f6d3942 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -308,33 +308,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet.png) ### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/basic-vim-cheat-sheet.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/basic-vim-cheat-sheet.png) ### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png) ### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-02.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-02.png) ### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-01.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-text-01.png) -![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-02.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-text-02.png) ## 4. 资料 diff --git a/docs/linux/soft/gitlab-ops.md b/docs/linux/soft/gitlab-ops.md index b90dbd35..ff942ad5 100644 --- a/docs/linux/soft/gitlab-ops.md +++ b/docs/linux/soft/gitlab-ops.md @@ -8,7 +8,7 @@ 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 -![img](http://dunwu.test.upcdn.net/snap/20190129155838.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190129155838.png) 以 CentOS7 为例: @@ -70,7 +70,7 @@ docker run -d \ gitlab/gitlab-ce ``` -![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190131150515.png) ### 自签名证书 @@ -175,7 +175,7 @@ sudo gitlab-runner register URL 和令牌信息在 Gitlab 的 Runner 管理页面获取: -![img](http://dunwu.test.upcdn.net/snap/20190129163100.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190129163100.png) ``` Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) @@ -292,11 +292,11 @@ sudo gitlab-ctl restart ### 创建项目 -![img](http://dunwu.test.upcdn.net/snap/20190131150658.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190131150658.png) 输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。 -![img](http://dunwu.test.upcdn.net/snap/20190131150759.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190131150759.png) ### 克隆项目到本地 diff --git a/docs/linux/soft/jdk-install.md b/docs/linux/soft/jdk-install.md index e4477193..4eed355c 100644 --- a/docs/linux/soft/jdk-install.md +++ b/docs/linux/soft/jdk-install.md @@ -7,7 +7,7 @@ - [JDK 安装步骤](#jdk-安装步骤) - [Windows 系统安装方法](#windows-系统安装方法) - [Linux 系统安装方法](#linux-系统安装方法) - - [RedHat 发行版本使用 rpm 安装方法](#redhat-发行版本使用-rpm-安装方法) + - [RedHat 发行版本使用 rpm 安装方法](#redhat-发行版本使用-rpm-安装方法) - [参考资料](#参考资料) @@ -22,13 +22,13 @@ a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/java b. 选择需要的版本: -![img](http://dunwu.test.upcdn.net/snap/20180920181010164121.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920181010164121.png) c. 选择对应操作系统的安装包: Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。 -![img](http://dunwu.test.upcdn.net/snap/20180920181010164308.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920181010164308.png) (2)运行安装包,按提示逐步安装 diff --git a/docs/linux/soft/jenkins-ops.md b/docs/linux/soft/jenkins-ops.md index 2495d792..6a000268 100644 --- a/docs/linux/soft/jenkins-ops.md +++ b/docs/linux/soft/jenkins-ops.md @@ -17,11 +17,11 @@ Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一 CI(Continuous integration,中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。借用网络图片对 CI 加以理解。 -![img](http://dunwu.test.upcdn.net/snap/20200310174528.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310174528.png) CD(Continuous Delivery, 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的 Staging 环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。下图反应的是 CI/CD 的大概工作模式。 -![img](http://dunwu.test.upcdn.net/snap/20200310174544.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310174544.png) ## Jenkins 安装 @@ -98,7 +98,7 @@ Jenkins 是一个强大的 CI 工具,虽然本身使用 Java 开发,但也 General 是构建任务的一些基本配置。名称,描述之类的。 -![img](http://dunwu.test.upcdn.net/snap/20200310221814.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310221814.png) 重要配置项: @@ -111,7 +111,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Source Code Management**,即源码管理,就是配置你代码的存放位置。 -![img](http://dunwu.test.upcdn.net/snap/20200310222110.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310222110.png) - **Git:** 支持主流的 Github 和 Gitlab 代码仓库。因我们的研发团队使用的是 gitlab,所以下面我只会对该项进行介绍。 - **Repository URL**:仓库地址。 @@ -124,7 +124,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Build Triggers**,即构建触发器,用于构建任务的触发器。 -![img](http://dunwu.test.upcdn.net/snap/20200310222608.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310222608.png) 配置说明: @@ -138,7 +138,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Build Environment**,即构建环境,配置构建前的一些准备工作,如指定构建工具。 -![img](http://dunwu.test.upcdn.net/snap/20200310223004.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310223004.png) ### Build @@ -146,7 +146,7 @@ Build,即构建。 点击下图中的 Add build step 按钮,会弹出一个构建任务菜单,可以根据实际需要来选择。 -![img](http://dunwu.test.upcdn.net/snap/20200310223241.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310223241.png) 【说明】 @@ -162,11 +162,11 @@ Build,即构建。 **Post-build Actions**,即构建后操作,用于构建完本项目的一些后续操作,比如生成相应的代码测试报告。 -![img](http://dunwu.test.upcdn.net/snap/20200310224106.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224106.png) -![img](http://dunwu.test.upcdn.net/snap/20200310224254.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224254.png) -![img](http://dunwu.test.upcdn.net/snap/20200310224331.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224331.png) 个人较常用的配置: @@ -182,13 +182,13 @@ Build,即构建。 ### 开始构建 -![img](http://dunwu.test.upcdn.net/snap/20200310224927.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224927.png) 如上图所示,一切配置好后,即可点击 **Build Now** 开始构建。 ### 构建结果 -![img](http://dunwu.test.upcdn.net/snap/20200310225234.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310225234.png) - **构建状态** - **Successful 蓝色**:构建完成,并且被认为是稳定的。 diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 73d3dffc..4f9df168 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -14,7 +14,7 @@ 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 -![img](http://dunwu.test.upcdn.net/snap/20181127203029.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20181127203029.png) 本人将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 @@ -69,7 +69,7 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} Nexus 的默认启动端口为 `8081`,启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: -![img](http://dunwu.test.upcdn.net/snap/20181127203131.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20181127203131.png) 点击右上角 Sign in 登录,默认用户名/密码为:`admin/admin123`。 @@ -82,7 +82,7 @@ Nexus 中的仓库有以下类型: - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -![img](http://dunwu.test.upcdn.net/cs/java/javalib/maven/nexus.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/java/javalib/maven/nexus.png) 建议配置如下: @@ -95,7 +95,7 @@ Nexus 中的仓库有以下类型: - **group 仓库** - **maven-public** - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 -![img](http://dunwu.test.upcdn.net/snap/20181127203156.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20181127203156.png) > 其中: > @@ -103,7 +103,7 @@ Nexus 中的仓库有以下类型: 参考配置如下: -![img](http://dunwu.test.upcdn.net/snap/20200403165258.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200403165258.png) 推荐配置的代理仓库: @@ -112,7 +112,7 @@ Nexus 中的仓库有以下类型: ### 配置 yum 仓库 -![img](http://dunwu.test.upcdn.net/snap/20200403201609.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200403201609.png) 推荐配置的 yum 代理仓库: @@ -146,7 +146,7 @@ Nexus 内置了多个定时任务,可以执行清理。 【示例】定期清理 SNAPSHOST -![img](http://dunwu.test.upcdn.net/snap/20200403173030.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200403173030.png) ## 三、开机自启动 diff --git a/docs/linux/soft/svn-ops.md b/docs/linux/soft/svn-ops.md index 31ebbc15..4f66396a 100644 --- a/docs/linux/soft/svn-ops.md +++ b/docs/linux/soft/svn-ops.md @@ -141,7 +141,7 @@ $ vi /etc/sysconfig/svnserve 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: -![img](http://dunwu.test.upcdn.net/snap/20190129175443.png) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190129175443.png) ## 参考资料 diff --git a/docs/linux/soft/yapi-ops.md b/docs/linux/soft/yapi-ops.md index 13802443..95e6b20b 100644 --- a/docs/linux/soft/yapi-ops.md +++ b/docs/linux/soft/yapi-ops.md @@ -4,17 +4,17 @@ > > 本文目的在于记录 svn 的安装、配置、使用。 -![img](http://dunwu.test.upcdn.net/snap/1562814562978.png!zp) +![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/1562814562978.png) - [1. 普通部署](#1-普通部署) - - [1.1. 环境要求](#11-环境要求) - - [1.2. 部署](#12-部署) - - [1.3. 升级](#13-升级) + - [1.1. 环境要求](#11-环境要求) + - [1.2. 部署](#12-部署) + - [1.3. 升级](#13-升级) - [2. Docker 部署](#2-docker-部署) - - [2.1. 环境要求](#21-环境要求) - - [2.2. 部署](#22-部署) + - [2.1. 环境要求](#21-环境要求) + - [2.2. 部署](#22-部署) - [3. 参考资料](#3-参考资料) diff --git a/docs/package.json b/docs/package.json index ade50f7a..cfccfcb4 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,35 +5,26 @@ "scripts": { "clean": "rimraf dist && rimraf .temp", "build": "npm run clean && vuepress build ./ --temp .temp", - "dev": "vuepress dev ./ --temp .temp", - "lint-md": "npm run lint-md:style && npm run lint-md:wording", - "lint-md:style": "remark --quiet --frail .", - "lint-md:wording": "textlint ./**/*.md", + "start": "vuepress dev ./ --temp .temp", + "lint": "markdownlint -r markdownlint-rule-emphasis-style -c ./.markdownlint.json **/*.md -i node_modules", + "lint:fix": "markdownlint -f -r markdownlint-rule-emphasis-style -c ./.markdownlint.json **/*.md -i node_modules", "show-help": "vuepress --help", "view-info": "vuepress view-info ./ --temp .temp" }, "devDependencies": { - "@textlint-rule/textlint-rule-no-unmatched-pair": "^1.0.7", - "@vuepress/plugin-back-to-top": "^1.3.0", - "@vuepress/plugin-medium-zoom": "^1.3.0", - "@vuepress/plugin-pwa": "^1.3.0", - "@vuepress/theme-vue": "^1.3.0", - "remark-cli": "^7.0.0", - "remark-lint": "^6.0.5", - "remark-preset-lint-consistent": "^2.0.3", - "remark-preset-lint-recommended": "^3.0.3", + "@vuepress/plugin-active-header-links": "^1.8.2", + "@vuepress/plugin-back-to-top": "^1.8.2", + "@vuepress/plugin-medium-zoom": "^1.8.2", + "@vuepress/plugin-pwa": "^1.8.2", + "@vuepress/theme-vue": "^1.8.2", + "markdownlint-cli": "^0.25.0", + "markdownlint-rule-emphasis-style": "^1.0.1", "rimraf": "^3.0.1", - "textlint": "^11.3.1", - "textlint-filter-rule-comments": "^1.2.2", - "textlint-rule-apostrophe": "^1.0.0", - "textlint-rule-common-misspellings": "^1.0.1", - "textlint-rule-diacritics": "^1.0.0", - "textlint-rule-en-capitalization": "^2.0.2", - "textlint-rule-stop-words": "^1.0.17", - "textlint-rule-terminology": "^1.1.30", - "textlint-rule-write-good": "^1.6.2", "vue-toasted": "^1.1.25", - "vuepress": "^1.3.0", + "vuepress": "^1.8.2", "vuepress-plugin-flowchart": "^1.4.2" + }, + "dependencies": { + "moment": "^2.29.1" } } From 6ab8b8753cb1f60a1a0d118f978699760b36747d Mon Sep 17 00:00:00 2001 From: dunwu Date: Wed, 6 Apr 2022 20:29:31 +0800 Subject: [PATCH 51/64] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7f7498fb..47463f91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ language: node_js sudo: required -node_js: stable +node_js: '16.13.0' branches: only: From 4c8a52e0dd823e00958d1eee26fce8bf0bd5b6b0 Mon Sep 17 00:00:00 2001 From: dunwu Date: Tue, 28 Jun 2022 10:24:33 +0800 Subject: [PATCH 52/64] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/README.md | 4 ++-- docs/docker/README.md | 2 +- docs/linux/cli/README.md | 4 ++-- docs/linux/ops/README.md | 2 +- docs/linux/soft/README.md | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 45a0fa8c..7f6cbfa7 100644 --- a/README.md +++ b/README.md @@ -154,4 +154,4 @@ ## 🚪 传送门 -◾ 🏠 [LINUX-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ +◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ diff --git a/docs/README.md b/docs/README.md index dd36841c..9e199165 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ --- home: true -heroImage: https://raw.githubusercontent.com/dunwu/images/dev/common/dunwu-logo-200.png +heroImage: https://raw.githubusercontent.com/dunwu/images/dev/common/dunwu-logo.png heroText: LINUX-TUTORIAL tagline: 📚 linux-tutorial 是一个 Linux 教程。 actionLink: / @@ -154,4 +154,4 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu ## 🚪 传送门 -◾ 🏠 [LINUX-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ +◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ diff --git a/docs/docker/README.md b/docs/docker/README.md index c77d748a..8653d515 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -33,4 +33,4 @@ ## 🚪 传送门 -◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ +◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ diff --git a/docs/linux/cli/README.md b/docs/linux/cli/README.md index 4c455df3..53a28666 100644 --- a/docs/linux/cli/README.md +++ b/docs/linux/cli/README.md @@ -12,7 +12,7 @@ - [Linux 系统管理](linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` - [Linux 网络管理](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-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` -- [Linux 软件管理](linux-cli-hardware.md) - 关键词:`rpm`, `yum`, `apt-get` +- [Linux 软件管理](linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get` ## 📚 资料 @@ -22,4 +22,4 @@ ## 🚪 传送门 -◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ +◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ diff --git a/docs/linux/ops/README.md b/docs/linux/ops/README.md index f9b0f0f9..658882f6 100644 --- a/docs/linux/ops/README.md +++ b/docs/linux/ops/README.md @@ -14,4 +14,4 @@ ## 🚪 传送门 -◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ +◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ diff --git a/docs/linux/soft/README.md b/docs/linux/soft/README.md index 7355bd46..b0df7bea 100644 --- a/docs/linux/soft/README.md +++ b/docs/linux/soft/README.md @@ -28,4 +28,4 @@ ## 🚪 传送门 -◾ 🏠 [DB-TUTORIAL 首页](https://github.com/dunwu/linux-tutorial) ◾ 🎯 [我的博客](https://github.com/dunwu/blog) ◾ +◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ From 64e3b95398518eb8923ae67072a9a87c5669dfc1 Mon Sep 17 00:00:00 2001 From: dunwu Date: Thu, 9 Feb 2023 21:19:59 +0800 Subject: [PATCH 53/64] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- docs/README.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7f6cbfa7..46655f2e 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ ### 其他 -- [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) -- [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) -- [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) +- [一篇文章让你彻底掌握 Python](https://dunwu.github.io/blog/pages/ef501b/) +- [一篇文章让你彻底掌握 Shell](https://dunwu.github.io/blog/pages/ea6ae1/) +- [如何优雅的玩转 Git](https://dunwu.github.io/blog/pages/2fc8b1/) ## ⌨ 脚本 diff --git a/docs/README.md b/docs/README.md index 9e199165..2d5f349d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -84,9 +84,9 @@ footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu ### 其他 -- [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md) -- [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md) -- [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md) +- [一篇文章让你彻底掌握 Python](https://dunwu.github.io/blog/pages/ef501b/) +- [一篇文章让你彻底掌握 Shell](https://dunwu.github.io/blog/pages/ea6ae1/) +- [如何优雅的玩转 Git](https://dunwu.github.io/blog/pages/2fc8b1/) ## ⌨ 脚本 From 506d864e4bd5b76b67b155bc29e5483e85bd661b Mon Sep 17 00:00:00 2001 From: 123456 Date: Fri, 31 Mar 2023 20:58:41 +0800 Subject: [PATCH 54/64] plus --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 46655f2e..64679b5b 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ - [东北大学](http://mirror.neu.edu.cn/) - [浙江大学](http://mirrors.zju.edu.cn/) - [东软信息学院](http://mirrors.neusoft.edu.cn/) + - [校园网联合镜像站](https://mirrors.cernet.edu.cn) ## 🚪 传送门 From e966ae7e3595c8afa168b9d76602c6371d5f8497 Mon Sep 17 00:00:00 2001 From: kid1412621 Date: Mon, 28 Aug 2023 10:28:15 +0800 Subject: [PATCH 55/64] Update linux-cli-system.md fix typo --- docs/linux/cli/linux-cli-system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/linux/cli/linux-cli-system.md b/docs/linux/cli/linux-cli-system.md index 5a1b4ea2..703a5905 100644 --- a/docs/linux/cli/linux-cli-system.md +++ b/docs/linux/cli/linux-cli-system.md @@ -276,7 +276,7 @@ systemctl status nfs-server.service systemctl restart nfs-server.service # 6.查看所有已启动的服务 -systemctl list -units --type=service +systemctl list-units --type=service # 7. 开启防火墙 22 端口 iptables -I INPUT -p tcp --dport 22 -j accept From f25d28fd1dc7c19d34dd1739733be6e717b170fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=9D=E6=82=9F?= Date: Mon, 4 Sep 2023 09:22:08 +0800 Subject: [PATCH 56/64] Update crontab.md --- docs/linux/ops/crontab.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/linux/ops/crontab.md b/docs/linux/ops/crontab.md index 3ce26339..8713136b 100644 --- a/docs/linux/ops/crontab.md +++ b/docs/linux/ops/crontab.md @@ -170,7 +170,7 @@ MAILTO=root #### 实例 11:每一小时重启 smb ```shell -* */1 * * * /etc/init.d/smb restart +0 * * * * /etc/init.d/smb restart ``` #### 实例 12:晚上 11 点到早上 7 点之间,每隔一小时重启 smb From 13ab6a64428bfd9b959300fc8c3582f056cd39d9 Mon Sep 17 00:00:00 2001 From: dunwu Date: Sat, 27 Jan 2024 23:10:11 +0800 Subject: [PATCH 57/64] =?UTF-8?q?feat:=20=E6=95=B4=E7=90=86=E5=9B=BE?= =?UTF-8?q?=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- codes/linux/soft/fastdfs-install.sh | 6 +++--- docs/README.md | 2 +- docs/docker/docker-quickstart.md | 2 +- docs/docker/kubernetes.md | 2 +- docs/linux/cli/linux-cli-dir.md | 4 ++-- docs/linux/ops/crontab.md | 2 +- docs/linux/ops/samba.md | 2 +- docs/linux/ops/vim.md | 12 ++++++------ docs/linux/soft/gitlab-ops.md | 10 +++++----- docs/linux/soft/jdk-install.md | 4 ++-- docs/linux/soft/jenkins-ops.md | 24 ++++++++++++------------ docs/linux/soft/nexus-ops.md | 14 +++++++------- docs/linux/soft/svn-ops.md | 2 +- docs/linux/soft/yapi-ops.md | 2 +- 15 files changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 64679b5b..328d4182 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- logo + logo

diff --git a/codes/linux/soft/fastdfs-install.sh b/codes/linux/soft/fastdfs-install.sh index a7db9e44..95cfb009 100644 --- a/codes/linux/soft/fastdfs-install.sh +++ b/codes/linux/soft/fastdfs-install.sh @@ -59,7 +59,7 @@ yum install -y git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zl mkdir -p ${path} path=/opt/fdfs mkdir -p ${path}/libfastcommon -curl -o ${path}/libfastcommon.zip https://raw.githubusercontent.com/dunwu/images/dev/soft/fdfs/libfastcommon.zip +curl -o ${path}/libfastcommon.zip http://dunwu.test.upcdn.net/soft/fdfs/libfastcommon.zip if [[ ! -f ${path}/libfastcommon.zip ]]; then printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" exit 1 @@ -73,7 +73,7 @@ chmod +x -R ${path}/libfastcommon/*.sh printf "${GREEN}>>>>>>>>> install fastdfs${RESET}" mkdir -p ${path}/fastdfs -curl -o ${path}/fastdfs.zip https://raw.githubusercontent.com/dunwu/images/dev/soft/fdfs/fastdfs.zip +curl -o ${path}/fastdfs.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs.zip if [[ ! -f ${path}/fastdfs.zip ]]; then printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" fi @@ -84,7 +84,7 @@ chmod +x -R ${path}/fastdfs/*.sh printf "${GREEN}>>>>>>>>> install fastdfs-nginx-module${RESET}\n" mkdir -p ${path}/fastdfs-nginx-module -curl -o ${path}/fastdfs-nginx-module.zip https://raw.githubusercontent.com/dunwu/images/dev/soft/fdfs/fastdfs-nginx-module.zip +curl -o ${path}/fastdfs-nginx-module.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs-nginx-module.zip if [[ ! -f ${path}/fastdfs-nginx-module.zip ]]; then printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" fi diff --git a/docs/README.md b/docs/README.md index 2d5f349d..8cf2000b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ --- home: true -heroImage: https://raw.githubusercontent.com/dunwu/images/dev/common/dunwu-logo.png +heroImage: https://raw.githubusercontent.com/dunwu/images/master/common/dunwu-logo.png heroText: LINUX-TUTORIAL tagline: 📚 linux-tutorial 是一个 Linux 教程。 actionLink: / diff --git a/docs/docker/docker-quickstart.md b/docs/docker/docker-quickstart.md index f231afbc..4c5434fb 100644 --- a/docs/docker/docker-quickstart.md +++ b/docs/docker/docker-quickstart.md @@ -31,7 +31,7 @@ Docker 将应用程序与该程序的依赖,打包在一个文件里面。运 - **更轻松的迁移** - 由于 `Docker` 确保了执行环境的一致性,使得应用的迁移更加容易。`Docker` 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。 - **更轻松的维护和扩展** - `Docker` 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,`Docker` 团队同各个开源项目团队一起维护了一大批高质量的 [官方镜像](https://hub.docker.com/search/?type=image&image_filter=official),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/docker/containers-and-vm.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/docker/containers-and-vm.png) ### Docker 的主要用途 diff --git a/docs/docker/kubernetes.md b/docs/docker/kubernetes.md index d600ea91..82daee1c 100644 --- a/docs/docker/kubernetes.md +++ b/docs/docker/kubernetes.md @@ -49,7 +49,7 @@ Kubernetes 主要由以下几个核心组件组成: K8S 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/kubernetes/pod.svg) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/kubernetes/pod.svg) - `Pod` - K8S 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 K8S 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。 - `Node` - Node 是 Pod 真正运行的主机,可以是物理机,也可以是虚拟机。为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 docker 或者 rkt)、`kubelet` 和 `kube-proxy` 服务。 diff --git a/docs/linux/cli/linux-cli-dir.md b/docs/linux/cli/linux-cli-dir.md index d013dad0..891234f8 100644 --- a/docs/linux/cli/linux-cli-dir.md +++ b/docs/linux/cli/linux-cli-dir.md @@ -8,7 +8,7 @@ linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/linux-folders.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/linux-folders.png) ### 1.2. Linux 文件属性 @@ -35,7 +35,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot 每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920180927171909.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920180927171909.png) 从左至右用 0-9 这些数字来表示。 diff --git a/docs/linux/ops/crontab.md b/docs/linux/ops/crontab.md index 8713136b..25d27650 100644 --- a/docs/linux/ops/crontab.md +++ b/docs/linux/ops/crontab.md @@ -65,7 +65,7 @@ crontab 要执行的定时任务都被保存在 `/etc/crontab` 文件中。 crontab 的文件格式如下: -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200211113339.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200211113339.png) #### 标准字段 diff --git a/docs/linux/ops/samba.md b/docs/linux/ops/samba.md index 71358f9a..19fe6d9f 100644 --- a/docs/linux/ops/samba.md +++ b/docs/linux/ops/samba.md @@ -149,7 +149,7 @@ Windows: 访问:`\\<你的ip>\<你的共享路径>` : -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920180928161334.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920180928161334.png) Mac: diff --git a/docs/linux/ops/vim.md b/docs/linux/ops/vim.md index 2f6d3942..ece7522d 100644 --- a/docs/linux/ops/vim.md +++ b/docs/linux/ops/vim.md @@ -308,33 +308,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet.png) ### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/basic-vim-cheat-sheet.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/basic-vim-cheat-sheet.png) ### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png) ### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-02.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-02.png) ### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-text-01.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-text-01.png) -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/os/linux/vim/vim-cheat-sheet-text-02.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-text-02.png) ## 4. 资料 diff --git a/docs/linux/soft/gitlab-ops.md b/docs/linux/soft/gitlab-ops.md index ff942ad5..3d5e564f 100644 --- a/docs/linux/soft/gitlab-ops.md +++ b/docs/linux/soft/gitlab-ops.md @@ -8,7 +8,7 @@ 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190129155838.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190129155838.png) 以 CentOS7 为例: @@ -70,7 +70,7 @@ docker run -d \ gitlab/gitlab-ce ``` -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190131150515.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190131150515.png) ### 自签名证书 @@ -175,7 +175,7 @@ sudo gitlab-runner register URL 和令牌信息在 Gitlab 的 Runner 管理页面获取: -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190129163100.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190129163100.png) ``` Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) @@ -292,11 +292,11 @@ sudo gitlab-ctl restart ### 创建项目 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190131150658.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190131150658.png) 输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190131150759.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190131150759.png) ### 克隆项目到本地 diff --git a/docs/linux/soft/jdk-install.md b/docs/linux/soft/jdk-install.md index 4eed355c..6c29acb4 100644 --- a/docs/linux/soft/jdk-install.md +++ b/docs/linux/soft/jdk-install.md @@ -22,13 +22,13 @@ a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/java b. 选择需要的版本: -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920181010164121.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920181010164121.png) c. 选择对应操作系统的安装包: Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20180920181010164308.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920181010164308.png) (2)运行安装包,按提示逐步安装 diff --git a/docs/linux/soft/jenkins-ops.md b/docs/linux/soft/jenkins-ops.md index 6a000268..3ed2e485 100644 --- a/docs/linux/soft/jenkins-ops.md +++ b/docs/linux/soft/jenkins-ops.md @@ -17,11 +17,11 @@ Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一 CI(Continuous integration,中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。借用网络图片对 CI 加以理解。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310174528.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310174528.png) CD(Continuous Delivery, 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的 Staging 环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。下图反应的是 CI/CD 的大概工作模式。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310174544.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310174544.png) ## Jenkins 安装 @@ -98,7 +98,7 @@ Jenkins 是一个强大的 CI 工具,虽然本身使用 Java 开发,但也 General 是构建任务的一些基本配置。名称,描述之类的。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310221814.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310221814.png) 重要配置项: @@ -111,7 +111,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Source Code Management**,即源码管理,就是配置你代码的存放位置。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310222110.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310222110.png) - **Git:** 支持主流的 Github 和 Gitlab 代码仓库。因我们的研发团队使用的是 gitlab,所以下面我只会对该项进行介绍。 - **Repository URL**:仓库地址。 @@ -124,7 +124,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Build Triggers**,即构建触发器,用于构建任务的触发器。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310222608.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310222608.png) 配置说明: @@ -138,7 +138,7 @@ General 是构建任务的一些基本配置。名称,描述之类的。 **Build Environment**,即构建环境,配置构建前的一些准备工作,如指定构建工具。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310223004.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310223004.png) ### Build @@ -146,7 +146,7 @@ Build,即构建。 点击下图中的 Add build step 按钮,会弹出一个构建任务菜单,可以根据实际需要来选择。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310223241.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310223241.png) 【说明】 @@ -162,11 +162,11 @@ Build,即构建。 **Post-build Actions**,即构建后操作,用于构建完本项目的一些后续操作,比如生成相应的代码测试报告。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224106.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224106.png) -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224254.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224254.png) -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224331.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224331.png) 个人较常用的配置: @@ -182,13 +182,13 @@ Build,即构建。 ### 开始构建 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310224927.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224927.png) 如上图所示,一切配置好后,即可点击 **Build Now** 开始构建。 ### 构建结果 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200310225234.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310225234.png) - **构建状态** - **Successful 蓝色**:构建完成,并且被认为是稳定的。 diff --git a/docs/linux/soft/nexus-ops.md b/docs/linux/soft/nexus-ops.md index 4f9df168..c68af2ab 100644 --- a/docs/linux/soft/nexus-ops.md +++ b/docs/linux/soft/nexus-ops.md @@ -14,7 +14,7 @@ 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20181127203029.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20181127203029.png) 本人将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 @@ -69,7 +69,7 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} Nexus 的默认启动端口为 `8081`,启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20181127203131.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20181127203131.png) 点击右上角 Sign in 登录,默认用户名/密码为:`admin/admin123`。 @@ -82,7 +82,7 @@ Nexus 中的仓库有以下类型: - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/java/javalib/maven/nexus.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/cs/java/javalib/maven/nexus.png) 建议配置如下: @@ -95,7 +95,7 @@ Nexus 中的仓库有以下类型: - **group 仓库** - **maven-public** - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20181127203156.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20181127203156.png) > 其中: > @@ -103,7 +103,7 @@ Nexus 中的仓库有以下类型: 参考配置如下: -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200403165258.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200403165258.png) 推荐配置的代理仓库: @@ -112,7 +112,7 @@ Nexus 中的仓库有以下类型: ### 配置 yum 仓库 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200403201609.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200403201609.png) 推荐配置的 yum 代理仓库: @@ -146,7 +146,7 @@ Nexus 内置了多个定时任务,可以执行清理。 【示例】定期清理 SNAPSHOST -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20200403173030.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200403173030.png) ## 三、开机自启动 diff --git a/docs/linux/soft/svn-ops.md b/docs/linux/soft/svn-ops.md index 4f66396a..3e27a4b5 100644 --- a/docs/linux/soft/svn-ops.md +++ b/docs/linux/soft/svn-ops.md @@ -141,7 +141,7 @@ $ vi /etc/sysconfig/svnserve 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20190129175443.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190129175443.png) ## 参考资料 diff --git a/docs/linux/soft/yapi-ops.md b/docs/linux/soft/yapi-ops.md index 95e6b20b..938cf73f 100644 --- a/docs/linux/soft/yapi-ops.md +++ b/docs/linux/soft/yapi-ops.md @@ -4,7 +4,7 @@ > > 本文目的在于记录 svn 的安装、配置、使用。 -![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/1562814562978.png) +![img](https://raw.githubusercontent.com/dunwu/images/master/snap/1562814562978.png) From 879ef3bf08b1638a7d6f36c2de32726f81552df8 Mon Sep 17 00:00:00 2001 From: ratio Date: Mon, 26 Feb 2024 22:56:56 +0800 Subject: [PATCH 58/64] Update linux-cli-dir.md --- docs/linux/cli/linux-cli-dir.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/linux/cli/linux-cli-dir.md b/docs/linux/cli/linux-cli-dir.md index 891234f8..5bdd9f1b 100644 --- a/docs/linux/cli/linux-cli-dir.md +++ b/docs/linux/cli/linux-cli-dir.md @@ -18,7 +18,7 @@ Linux 系统是一种典型的多用户系统,不同的用户处于不同的 ```bash $ ls -l total 64 -dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin +drwxr-xr-x 2 root root 4096 Dec 14 2012 bin dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot ``` @@ -52,7 +52,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot ```bash $ ls -l total 64 -dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin +drwxr-xr-x 2 root root 4096 Dec 14 2012 bin dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot ``` From 8f89d2ba22e65014acea0232afad4f9d9e25cc20 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 12 Mar 2024 18:34:04 +0800 Subject: [PATCH 59/64] Add pnpm lockfile to .gitignore --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 83948575..e3e2b4c2 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ hs_err_pid* # maven plugin temp files .flattened-pom.xml package-lock.json - +pnpm-lock.yaml # ------------------------------- javascript ------------------------------- # dependencies @@ -50,12 +50,10 @@ yarn-error.log* bundle*.js book.pdf - # ------------------------------- intellij ------------------------------- .idea *.iml - # ------------------------------- eclipse ------------------------------- .classpath .project From d66d86d85ccde3d66b47379b45d1523e7d593db3 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 12 Mar 2024 18:36:58 +0800 Subject: [PATCH 60/64] =?UTF-8?q?fix:=20Docker=E6=95=99=E7=A8=8B=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2`=F0=9F=93=96=E5=86=85=E5=AE=B9`=20=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docker/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docker/README.md b/docs/docker/README.md index 8653d515..7434bbcb 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -2,10 +2,10 @@ ## 📖 内容 -- [Docker 快速入门](docker/docker-quickstart.md) -- [Dockerfile 最佳实践](docker/docker-dockerfile.md) -- [Docker Cheat Sheet](docker/docker-cheat-sheet.md) -- [Kubernetes 应用指南](docker/kubernetes.md) +- [Docker 快速入门](docker-quickstart.md) +- [Dockerfile 最佳实践](docker-dockerfile.md) +- [Docker Cheat Sheet](docker-cheat-sheet.md) +- [Kubernetes 应用指南](kubernetes.md) ## 📚 资料 From 2920e96b0144a41cfdc8b53ce3d549ee94bf8af0 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 12 Mar 2024 18:38:55 +0800 Subject: [PATCH 61/64] =?UTF-8?q?fix:=20=E5=85=B6=E5=AE=83=E5=AD=90?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2logo=E6=98=BE=E7=A4=BA=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vuepress/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index f0c20a76..24645b9f 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -15,7 +15,7 @@ module.exports = { }, }, themeConfig: { - logo: 'images/dunwu-logo-100.png', + logo: '/images/dunwu-logo-100.png', repo: 'dunwu/linux-tutorial', repoLabel: 'Github', docsDir: 'docs', From 1b95fa90ccdfadcfb0e67dabf0a8e50efd30d6a1 Mon Sep 17 00:00:00 2001 From: dunwu Date: Tue, 17 Dec 2024 07:54:15 +0800 Subject: [PATCH 62/64] =?UTF-8?q?docs:=20=E4=BF=AE=E6=AD=A3=E6=8C=87?= =?UTF-8?q?=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/linux/soft/jdk-install.md | 2 +- docs/linux/soft/maven-install.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/linux/soft/jdk-install.md b/docs/linux/soft/jdk-install.md index 6c29acb4..594ec498 100644 --- a/docs/linux/soft/jdk-install.md +++ b/docs/linux/soft/jdk-install.md @@ -97,7 +97,7 @@ $ tar -zxf jdk-8u162-linux-x64.tar.gz (3)配置系统环境变量 -执行 `/etc/profile` 命令,添加以下内容: +执行 `vi /etc/profile` 命令,添加以下内容: ```bash # JDK 的根路径 diff --git a/docs/linux/soft/maven-install.md b/docs/linux/soft/maven-install.md index 5b41c115..1ea46d0e 100644 --- a/docs/linux/soft/maven-install.md +++ b/docs/linux/soft/maven-install.md @@ -32,7 +32,7 @@ ``` # MAVEN 的根路径 export MAVEN_HOME=/opt/maven/apache-maven-3.5.2 -export PATH=\$MAVEN_HOME/bin:\$PATH +export PATH=$MAVEN_HOME/bin:$PATH ``` 执行 `source /etc/profile` ,立即生效 From 7bc0d6b346cd48b95ee167971e7ef182d849cc3b Mon Sep 17 00:00:00 2001 From: huhuhang Date: Wed, 22 Jan 2025 14:07:13 +0800 Subject: [PATCH 63/64] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E7=A4=BE?= =?UTF-8?q?=E5=8C=BA=E7=BD=91=E7=AB=99=E9=93=BE=E6=8E=A5=E5=92=8C=E5=AD=A6?= =?UTF-8?q?=E4=B9=A0=E8=B5=84=E6=BA=90=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 328d4182..b5e9a3de 100644 --- a/README.md +++ b/README.md @@ -110,17 +110,16 @@ - [linux-command](https://github.com/jaywcjlove/linux-command) - **社区网站** - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 - - [实验楼](https://www.shiyanlou.com/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 + - [LabEx 中国](https://labex.io/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 - [鸟哥的 linux 私房菜](http://linux.vbird.org/) - 非常适合 Linux 入门初学者看的教程。 - [Linux 公社](http://www.linuxidc.com/) - Linux 相关的新闻、教程、主题、壁纸都有。 - [Linux Today](http://www.linuxde.net) - Linux 新闻资讯发布,Linux 职业技术学习!。 - **知识相关** - [Linux 思维导图整理](http://www.jianshu.com/p/59f759207862) - [Linux 初学者进阶学习资源整理](http://www.jianshu.com/p/fe2a790b41eb) - - [Linux 基础入门(新版)](https://www.shiyanlou.com/courses/1) + - [Linux 快速入门(动手实验)](https://labex.io/zh/courses/quick-start-with-linux) - [【译】Linux 概念架构的理解](http://www.jianshu.com/p/c5ae8f061cfe) [En](http://oss.org.cn/ossdocs/linux/kernel/a1/index.html) - [Linux 守护进程的启动方法](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html) - - [Linux 编程之内存映射](https://www.shiyanlou.com/questions/2992) - [Linux 知识点小结](https://blog.huachao.me/2016/1/Linux%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B0%8F%E7%BB%93/) - [10 大白帽黑客专用的 Linux 操作系统](https://linux.cn/article-6971-1.html) - **软件工具** From 5eef25e2e44a55f2500e4a9272429c12f2699a89 Mon Sep 17 00:00:00 2001 From: Katzura <80234066+Power-Kid@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:55:17 +0800 Subject: [PATCH 64/64] Update linux-cli-dir.md --- docs/linux/cli/linux-cli-dir.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/linux/cli/linux-cli-dir.md b/docs/linux/cli/linux-cli-dir.md index 5bdd9f1b..16f82d4a 100644 --- a/docs/linux/cli/linux-cli-dir.md +++ b/docs/linux/cli/linux-cli-dir.md @@ -47,6 +47,21 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot - 第 2、5、8 位表示写权限,如果用"w"字符表示,则有写权限,如果用"-"字符表示没有写权限。 - 第 3、6、9 位表示可执行权限,如果用"x"字符表示,则有执行权限,如果用"-"字符表示,则没有执行权限。 +```bash +d rwx r-x r-x +↑ ↑↑↑ ↑↑↑ ↑↑↑ +│ │││ │││ │││-其他用户执行权限 (x/-) +│ │││ │││ │└─ 其他用户写权限 (w/-) +│ │││ │││ └── 其他用户读权限 (r/-) +│ │││ ││└──── 属组用户执行权限 (x/-) +│ │││ │└───── 属组用户写权限 (w/-) +│ │││ └────── 属组用户读权限 (r/-) +│ ││└──────── 属主用户执行权限 (x/-) +│ │└───────── 属主用户写权限 (w/-) +│ └────────── 属主用户读权限 (r/-) +└──────────── 文件类型 (该文件是目录) +``` + #### 1.2.1. Linux 文件属主和属组 ```bash

- - logo - -

lI15Lss5q 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 z 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