shell编程

ssh

  1. 免密登录
# 先生成私钥 公钥
ssh-keygen
ssh-copy-id [-p 端口号] user@host 
  1. 配置服务器别名
# 1. 编辑 ~/.ssh/config
# 输入内容
Host server-alias           # server-alias为SSH链接的服务器别名
  HostName server-ip  # 服务器地址
  Port 22
  User username           # 目标服务器端用户名
  PreferredAuthentications publickey 
  # 私钥地址,默认为 ~/.ssh/id_rsa
  IdentityFile ~/.ssh/id_rsa

ssh server-alias 进行连接

shell脚本编程

  1. 变量
# 执行器定义
# 在脚本的第一行定义 #!/bin/bash  # 定义脚本执行器,表示使用bash来执行当前脚本

# 变量
# 1. 变量的 k=v 之间不能有空格
# 2. 变量名只能由英、数字以及下划线组成,而且不能以数字开头
# 3. 变量名区分大小写
# 4. 单引号和双引号都可以表示一个字符串。但是双引号可以引用变 "${VARNAME}其他内容"
#    但是单引号的优势是不论什么字符,都不会被转义,除了单引号自己
# 5. 用反引号表示命令执行结果,`ls` `pwd` `date` `echo $?` 可以赋值给一个变量 lsres=`ls`
# 本地变量:作用域为整个bash进程
set VARNAME="value"

# 局部变量: 作用域为当前代码段
local VARNAME="value"

# 环境变量: 作用域为当前shell进程及子进程
export VARNAME="value"

# 位置变量:$1, $2 
# 当前脚本或者函数的位置参数 按顺序

# 特殊变量 , $#, $?, $!
$0: 当前脚本名
$#: 参数个数
#?: 退出状态
$!: 最后一个后台进程的进程号
$RANDOM: 随机数,系统内置的

# declare 定义
# 定义一个数字类型的变量 
declare -i a=1 
# 定义一个只读变量
declare -r b=1
# 显示变量定义
declare -p a b
# 定义一个数组
declare -a arr=("a" "b" "c")
# 声明一个环境变量 和 export 一样
declare -x PATH="/usr/local/bin:$PATH"
# 将变量值存储到目标变量(引用变量)
# declare -n 用于创建引用变量 ref_var,它将指向 var。修改 var 的值,ref_var 会自动反映这个变化。
declare -n ref_var=var
var="This is a reference"
echo $ref_var  # 输出 'This is a reference'
# 显示或设置函数属性。
function my_function() {
    echo "This is a function"
}
# 他会把这个函数的全部内容打印出来
declare -f my_function


# 这个东西多少年也没用过了
file=/dir1/dir2/dir3/my.file.txt
${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
${file##*/}:删掉最后一个 /  及其左边的字符串:my.file.txt
${file#*.}:删掉第一个 .  及其左边的字符串:file.txt
${file##*.}:删掉最后一个 .  及其左边的字符串:txt
${file%/*}:删掉最后一个  /  及其右边的字符串:/dir1/dir2/dir3
${file%%/*}:删掉第一个 /  及其右边的字符串:(空值)
${file%.*}:删掉最后一个  .  及其右边的字符串:/dir1/dir2/dir3/my.file
${file%%.*}:删掉第一个  .   及其右边的字符串:/dir1/dir2/dir3/my
记忆的方法
# 是 去掉左边(键盘上#在 $ 的左边)
%是去掉右边(键盘上% 在$ 的右边)
单一符号是最小匹配;两个符号是最大匹配
${file:5}:删除最左边的 5 个字节:/dir2/dir3/my.file.txt
${file:0:5}:提取最左边的 5 个字节:/dir1
${file:5:5}:提取第 5 个字节右边的连续5个字节:/dir2
也可以对变量值里的字符串作替换
${file/dir/path}:将第一个dir 替换为path:/path1/dir2/dir3/my.file.txt
${file//dir/path}:将全部dir 替换为 path:/path1/path2/path3/my.file.txt
${parameter:-word}:如果parameter为空或未定义,则变量展开为“word”;否则,展开为parameter的值;
${parameter:+word}:如果parameter为空或未定义,不做任何操作;否则,则展开为“word”值;
${parameter:=word}:如果parameter为空或未定义,则变量展开为“word”,并将展开后的值赋值给parameter;
  1. 数据类型
数值、
num=1
字符串、
str="hello"
布尔、
bool=true
数组、
arr=(1 2 3)
字典
declare -A dict=([name]=zhangsan [age]=18)

遍历数组
for i in ${arr[@]}
do
    echo $i
done

遍历字典
for key in "${!dict[@]}"; do
    echo "$key = ${dict[$key]}"
done
  1. 代码块
# 函数 先声明后使用
function func_name(args) {

}
# 函数调用
func_name(args)
# 对于无参的函数可以不用加括号 直接写函数名
 

# {}代码块 实际上是一个匿名函数。其内部的变量会提升作用域到当前shell进程


# ()代码块,将作为一个子进程来执行,所以其内部的变量只在()内部有效
  1. 条件测试
整数测试
    -eq: 测试两个整数是否相等;比如 $A -eq $B
    -ne: 测试两个整数是否不等;不等,为真;相等,为假;
    -gt: 测试一个数是否大于另一个数;大于,为真;否则,为假;
    -lt: 测试一个数是否小于另一个数;小于,为真;否则,为假;
    -ge: 大于或等于
    -le:小于或等于
字符测试
    ==
    !=
    >
    <
    -n:字符串非空
    -z:字符串为空
文件测试
    -e: 文件存在
    -f: 是文件
    -d: 是目录
    -r: 是可读文件
    -w: 是可写文件
    -x: 是可执行文件
-a: 与关系
-o: 或关系
!: 非关系 -not

判断java命令是否存在
hasjava=`command -v java &> /dev/null`

if [ $? -eq 0 ]; then
    echo "java 信息"
    java -version
else
    echo "java command has found"
fi

判断 hostname 为空或者等于'(none)' 就赋值为 localhost 
这个功能有点类似于c语言定义常量的 检查后赋值的语法
[ -z "$hostname" -o "hostname" == '(none)' ] && hsotname=localhost
  1. 分支
# while循环
declare -i I=1
declare -i SUM=0
while [ 判断 ]; do
    循环体
done


# for循环
for 变量 in 列表; do 
    循环体
done
        
for (( expr1 ; expr2 ; expr3 )); do 
  循环体
done

支持 break continue

case "$varname" in
    [a-z]) echo "abc";;
    [0-9]) echo "123";;
esac

case $1 in
'status')
    echo "`basename $0` stats" ;;
'start')
    echo "`basename $0` start..." ;;
'stop')
    echo "`basename $0` stop ..." ;;
*)
  echo "Unkonw options" ;;
esac

#表示把第n+1个参数移到第1个参数, 即命令结束后$1的值等于$n+1的值 #
#有点像二进制的左移指令。把第n+1 移动n个位置到最左侧。
# 不写n 默认为1
shift n
# shift 应用的一个案例
function main(){
    # arg parsing
    while [ $# -gt 0 ]; do
    echo '当前参数1='$1
        case $1 in
            -h|--help)
                echo './bootstrap [-r|--region <region>] [-p|--path <pkg>] [-y|--yes] [-d|--debug]'
                exit 0;;
            
            # 通过 -d 或 --debug 开启 DEBUG 模式 这个不要删 是此模板的默认参数
            -d|--debug) 
            DEBUG=1
        ;; 
            # 以下两个位置参数自己随便改
            # 接收 "-r 值" 或 "--region 值" 赋值给变量 REGION
            -r|--region) 
            REGION="$2"
               shift 1
        ;; 
            # 接收 "-p 值" 或 "--path 值" 赋值给变量 PKG_PATH
            -p|--path) 
            PKG_PATH="$2"
               shift 1
        ;; #同上
            (--) shift; break;;
            (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
            (*) break;;
        esac
        shift
    done

    echo ${DEBUG}
    echo ${REGION}
    echo ${PKG_PATH}
}
  1. shell expect
Expect是一个基于Tcl语言的工具,主要用于自动化控制那些需要交互的命令行程序。它通过模拟标准输入来与程序进行交互,从而自动化执行那些通常需要人工干预的任务。Expect的主要功能包括:

‌spawn‌:启动一个进程。
‌expect‌:等待特定的输出(字符串)出现。
‌send‌:发送响应或输入到进程中。
‌interact‌:允许用户与进程交互。
‌timeout‌:设置超时时间。

expect一般需要单独安装 sudo apt-get install expect   sudo yum install expect

它存在的意义
expect可以让我们实现自动登录远程机器,并且可以实现自动远程执行命令。当然若是使用不带密码的密钥验证同样可以实现自动登录和自动远程执行命令。
但当不能使用密钥验证的时候,我们就没有办法了。所以,这时候只要知道对方机器的账号和密码就可以通过expect脚本实现登录和远程命令。

例如脚本 expect_command.sh
使用方法 chmod +x expect_command.sh
expect_command.sh 用户名 密码 目标主机ip 要执行的命令
#!/usr/bin/expect
set user [lindex $argv 0]
set password [lindex $argv 0]
set host [lindex $argv 1]
set cmd [lindex $argv 2]

spawn ssh $user@$host

expect {
    "*yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$password\r" }
}
expect "]*"
send "$cmd\r"
expect "]*"
send "exit\r"

其他示例参考脚本和示例.md

控制台操作和一些命令

清空历史命令 ----- history -c
强制正在执行的程序中断 ----- ctrl+c
清空当前屏幕 ----- clear
清空当前命令行 ----- ctrl+u
命令补全 ----- tab
列出可补全的全部 ----- 双击tab
调用历史命令中离你最近一次的以ser开头的命令 ----- !ser
给命令起别名 ----- alias ddd='df -Th'
取消命令别名 ----- unalias ddd
查看当前系统中所有的别名 ----- alias

查找某个命令的绝对路径 ----- 
   [root@localhost ~]# which rmdir
   /bin/rmdir
    [root@localhost ~]# command -v rmdir
   /bin/rmdir
    [root@localhost ~]# whereis rmdir
   rmdir: /usr/bin/rmdir /usr/share/man/man1/rmdir.1.gz
查看用户所在的终端 ----- w 或者 who 命令 (本地终端一般是ttyn,ssh连接的终端是pts/n)
踢出pts/4用户 ----- pkill -kill -t pts/4
远程同步目录 ----- rsync -avP '-e ssh -p 1024' root@155.254.33.138:/data/admin /data
    rsync 也可以用于同步本地目录 用法和cp差不多
ssh免密码登录 ----- ssh-copy-id -i id_rsa.pub "-p 1024 root@155.254.33.138"
密码交互重定向 ----- echo "密码" | passwd --stdin 用户名  (设置用户名的密码)
将运行的脚本 变成父进程 ----- exec 
查看当前目录下一级子目录的大小 du -h  --max-depth=1
使用(;): 可以在一行运行多个命令。 
使用(&&): 可以在一行运行多个命令,并且只有前面命令执行成功,后面命令才会执行
使用(||): 可以在一行运行多个命令,并且只有前面命令执行失败,后面命令才会执行。它和 && 刚好相反
使用(&):  命令后添加& ,可以让命令到后台执行。一个正在运行的命令可以用 ctrl+z 将其放入到后台。
    然后使用 jobs 查看这些后台进程。系统会为每个后台进程分配一个编号。使用 fg 编号,可以将 后台命令调到前台
    czhn@czhn:~/download$ vim 123 &
    [1] 97676
    czhn@czhn:~/download$ jobs
    [1]+  Stopped                 vim 123
    czhn@czhn:~/download$ fg 1 # 将vim 123激活到前台进入vim窗口进行编辑

花括号的使用{}
{a,b}        代表a和b
{a..b}       代表a到b
{a..b..c}    a到b,步长为c
    [root@arthur test]# echo {1,10}          输出1和10
    [root@arthur test]# mkdir -p /data/{1..10} # 则创建 /data/1 /data/2 ... /data/10 一共10个目录


启动|重启|停止名称为aaa的系统服务 ----- service aaa start|restart|stop
                                      systemctl  start|stop|restart aaa

查看系统当前时间 ----- date
按照固定格式0000-00-00 00:00显示系统时间 ----- date "%Y-%m-%d %H:%M:%S"
设置系统时间 ----- date -s "2010-10-10 10:10:10"
 
显示2008年的日历表 ----- cal 2008 (需要安装ncal)
将时间修改保存到 BIOS ----- clock -w 

查看当前主机名称 ----- hostname
临时修改主机名称为myPc ----- hostname myPc
永久修改主机名称 ----- vi /etc/sysconfig/network
查看当前运行级别 ----- runlevel
修改当前运行级别 ----- telinit 3 或 init 3
        0表示关机,
        1表示单用户,
        2表示没有网络的命令行级别,
        3命令行级别(大多服务器都用这个级别),
        4为保留级别,
        5为图形化级别,
        6为重启。

查看计算机ip信息 ifconfig -a(-a可以显示所有网卡地址)
临时修改计算机eth0网卡ip地址 ifconfig eth0 192.168.100.136
实时查看系统详细信息 ----- top 
                        htop (需要单独安装)
查看系统一分钟,五分钟,十五分钟平均负载 ----- uptime
查看在线用户 ----- who
查看最近一段时间谁操作过系统重要命令,并有ip信息 ----- last
查看系统内存使用情况 ----- free -m   -- (-m参数显示以兆为单位)

查看环境变量 ----- echo $PATH #这个环境变量决定了你能在那些路径下直接找到执行程序
向屏幕输出 hello word ----- echo "hello word"
查看httpd相关进程表 ----- ps -ef |grep httpd
查看进程树 ----- pstree |grep httpd
查看pid号(进程号) ----- pstree -p |grep httpd
查看80端口是否开放 ----- netstat -tunpl |grep :80
查看httpd程序占用端口号 ----- netstat -tunpl |grep httpd*
强制杀掉一个pid ----- kill -9 pid
一次性杀掉所有httpd进程 ----- pkill httpd
                              kelladd httpd
关机 ----- 
  shutdown -h now 关闭系统(1)
  init 0 关闭系统(2)
  telinit 0 关闭系统(3)
  shutdown -h hours:minutes & 按预定时间关闭系统
  shutdown -c 取消按预定时间关闭系统
  shutdown -r now 重启(1)
  reboot 重启(2)
  logout 注销

显示机器的处理器架构 ----- uname -m 
显示正在使用的内核版本 ----- uname -r 
罗列一个磁盘的架构特性 ----- hdparm -i /dev/hda 
在磁盘上执行测试性读取操作系统信息 ----- hdparm -tT /dev/sda 
显示机器的处理器架构 ----- arch 
显示硬件系统部件 ----- dmidecode -q 
显示CPU info的信息 ----- cat /proc/cpuinfo 
显示中断 ----- cat /proc/interrupts 
校验内存使用 ----- cat /proc/meminfo 
显示哪些swap被使用 ----- cat /proc/swaps 
显示内核的版本 ----- cat /proc/version 
显示网络适配器及统计 ----- cat /proc/net/dev 
显示已加载的文件系统 ----- cat /proc/mounts 
罗列 PCI 设备 ----- lspci -tv 
显示 USB 设备 ----- lsusb -tv 
显示PID进程占用得文件句柄 ----- lsof – p PID

bash中设置PS1(是数字1而不是字母l)

每个版本bash的PS1变量内的特殊符号可能有些小的差异,你可以先man bash 一下。下面是FC4环境下默认的特殊符号所代表的意义:
\d   :代表日期,格式为weekday month date,例如:"Mon Aug 1"
\H :完整的主机名称。例如:我的机器名称为:fc4.linux,则这个名称就是fc4.linux
\h :仅取主机的第一个名字,如上例,则为fc4,.linux则被省略
\t :显示时间为24小时格式,如:HH:MM:SS
\T :显示时间为12小时格式
\A :显示时间为24小时格式:HH:MM
\u :当前用户的账号名称
\v :BASH的版本信息
\w :完整的工作目录名称。家目录会以 ~代替
\W :利用basename取得工作目录名称,所以只会列出最后一个目录
\# :下达的第几个命令
\$ :提示字符,如果是root时,提示符为:#   ,普通用户则为:$
默认的PS1内容为: '[\u@\h \W]\$ ' ,所以默认的提示符就是: [root@localhost ~]# 。
但设置PS1的时候需要稍微处理一下
PS1="[\\u@\\h \\W]\\$ "  这样显示的结果才是正确的。
设置颜色
颜色表  
前景(F)     背景(B)   颜色
---------------------------------------
30         40        黑色
31         41        红色
32         42        绿色
33         43        黄色
34         44        蓝色
35         45        紫红色
36         46        青蓝色
37         47        白色

模式(M)    意义
-------------------------
0        OFF
1        高亮显示
4        underline
5        闪烁
7        反白显示
8        不可见
使用前缀  "\e[M;F;Bm" 可更改其后的所有内容,MFB非必填
M的默认值是0,则原始样式为\e[m