ssh登录与shellcoding

ssh登录最基本用法

# 假定你要以用户名user,登录远程主机host
ssh user@host
# 如果本地用户名与远程用户名一致,可以省去用户名
ssh host
# SSH的默认端口是22,也就是说,你的登录请求会送进远程主机的22端口。使用p参数,可以修改这个端口。
ssh -p 8081 user@host

第一次登录对方主机,系统会提示信息,让你确认host主机的真实性,然后要求输入密码,当远程主机的公钥被接受以后,它就会被保存在文件$HOME/.ssh/known_hosts之中,下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。
每个SSH用户都有自己的known_hosts文件,此外系统也有一个这样的文件,通常是/etc/ssh/ssh_known_hosts,保存一些对所有用户都可信赖的远程主机的公钥。

公钥登录(免密登录)

  1. 使用密码登录,每次都必须输入密码,非常麻烦。好在SSH还提供了公钥登录,可以省去输入密码的步骤。
  2. 所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。
  3. 这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用ssh-keygen生成一个
# 系统会出现一系列提示,可以一路回车。其中有一个问题是,要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。
# 运行结束以后,在$HOME/.ssh/目录下,会新生成两个文件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥。
ssh-keygen -t rsa -C 350442340@qq.com
  1. 将公钥传送到远程主机host上面
ssh-copy-id user@host

解释ssh-copy-id原理,公钥保存过程

远程主机将用户的公钥,保存在登录后的用户主目录的$HOME/.ssh/authorized_keys文件中。公钥就是一段字符串,只要把它追加在authorized_keys文件的末尾就行了。
这里不使用ssh-copy-id

ssh user@host 'mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

依次分解开来看:

  1. $ ssh user@host表示登录远程主机;
  2. mkdir .ssh && cat >> .ssh/authorized_keys,表示登录后在远程shell上执行的命令:
  3. $ mkdir -p .ssh的作用是,如果用户主目录中的.ssh目录不存在,就创建一个;
  4. 'cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub的作用是,将本地的公钥文件~/.ssh/id_rsa.pub,重定向追加到远程文件authorized_keys的末尾。
  5. 写入authorized_keys文件后,公钥登录的设置就完成了。

SFTP,使用Transmit客户端,添加服务器,左右拖动即可

brew search transmit
brew install transmit
brew info transmit

SFTP命令

# 连接远程linux,输入密码即可
sftp user@host
# 查看远程当前工作路径
pwd 
cd ../
# 查看本地当前工作路径 location
lpwd
lcd ../
# 上传文件 
put -r [本地路径] [远程路径]
# 上传文件夹(在远程新建文件夹)
put -r [本地路径/文件夹/*]  [远程路径/新建文件夹/]
# 下载文件
get -r [远程路径] [本地路径]
# 下载文件夹(在本地新建文件夹)
get -r [远程路径/文件夹/*]  [本地路径/新建文件夹/]
# 退出
exit

scp使用

# 远程拷贝到本地
scp -r user@host:[远程路径] [本地路径]
# 本地拷贝到远程
scp -r [本地路径]  user@host:[远程路径]

sftp和scp。2者的最大不同之处在于「SFTP在文件传输过程中中断的话,连接后还可以继续传输,但SCP不行」。

scp相对轻量一点

变量

  1. 常用系统变量
$HOME
$PWD
$SHELL
$USER
  1. 自定义变量: 变量=值 。中间不能有空格
A=2
echo $A
# 撤销变量
unset A
  1. 变量默认类型都是字符串类型,无法直接进行数值运算
  2. 如果有空格,需要用双引号或单引号括起来
  3. 可以把变量提升为全局变量,可供其他Shell程序使用
A=hahaha
export A

特殊变量:

  1. $n, n为数字,$0代表脚本名称, $1-$9 代表第一个到第九个参数
  2. $#, 获取所有输入参数个数,常用于循环
  3. $*, 代表命令行中所有的参数,把所有参数看成一个整体
  4. $@, 代表命令行中所有的参数,把每个参数区分对待
  5. $?, 最后一次执行的命令的返回状态。=0表示正确执行,非0表示执行错误

运算符

  1. $((表达式))$[表达式]
  2. expr +,-,\*,/,%加减乘除,取模. expr运算符间要有空格
expr `expr 2 + 3` \* 4
result=$[(2+3)*4]

条件判断

  1. [ condition ], condition前后必需要有空格,条件非空就为true. []返回false
  2. 常用的判断条件
  3. 整数之间的比较
    1. =字符串比较
    2. -lt 小于(less than)
    3. -le 小于(less equal)
    4. -eq 小于(equal)
    5. -gt 小于(greater than)
    6. -ge 小于(greater equal)
    7. -ne 小于(not equal)
  4. 按文件权限进行判断
    1. -r 有读的权限(read)
    2. -w 有写的权限(write)
    3. -x 有执行的权限(execute)
  5. 按文件类型进行判断
    1. -f文件存在并且是一个常规的文件(file)
    2. -e文件存在(existence)
    3. -d文件存在,并且是一个目录(directory)
[ 23 -ge 22 ]

流程控制

if [ 条件判断式 ]; then
    coding
fi
# 或者
if [ 条件判断式 ]
    then
    coding
fi

注意事项:

  1. 条件判断式和中括号之间必需有空格
  2. if 后要有空格

case语句

case $var in
"value1")
    coding
;;
"value2")
    coding
;;

# 结尾相当于 default, 双分号相当于 break
*)  
;;
esac  
# case 倒过来写,表示结束

for 循环

echo "===== for循环方式1"
s=0
for ((i=1;i<=100;i++))
do
    s=$[$s + $i]
done
echo "for循环方式1 is $s"
# 第二种方式
echo "===== for循环方式2"
s2=0
for i in $*
do
    s2=$[ $s2 + $i ]
done
echo "for循环方式2 is $s2"

switch语句

case 值 in
模式1)
    command
    ;;
模式2)
    command
    ;;
*)
    command
    ;;
esac

取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。;; 与其他语言中的 break 类似,意思是跳到整个 case 语句的最后。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

函数

function function_name () {
    list of commands
    echo "$1 $2 $* $@ [$#]个数"
    [ return value ]
}
function_name 3 9

function可以不写,函数返回值,可以显式增加 return 语句;如果不加,会将最后一条命令运行结果作为返回值。函数返回值只能是整数

字符串

字符串是 shell 编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟 PHP 类似。

  1. 单引号: 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的; 单引号字串中不能出现单引号(对单引号使用转义符后也不行)
  2. 双引号:双引号的优点: 双引号里可以有变量 双引号里可以出现转义字符
# 拼接字符串
your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 获取字符串长度
string="alibaba is a great company"
echo ${#string}
# 提取子字符串
echo ${string:1:4} #输出 liba
# 查找子字符串
echo `expr index "$string" is`

数组

bash 支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与 C 语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。
在 Shell 中,用括号来表示数组,数组元素用“空格”符号分割开。定义数组的一般形式为:

array_name=(value1 ... valuen)
# 还可以单独定义数组的各个分量: 可以不使用连续的下标,而且下标的范围没有限制。
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
# 读取数组元素值的一般格式是:
valuen=${array_name[2]}
# 使用@或*可以获取数组中的所有元素,例如:
${array_name[*]}
${array_name[@]}
# 获取数组长度的方法与获取字符串长度的方法相同
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

awk

  1. 读(Read),AWK 从输入流(文件、管道或者标准输入)中读入一行然后将其存入内存中。
  2. 执行(Execute), 对于每一行输入,所有的 AWK 命令按顺执行。 默认情况下,AWK 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。
  3. 重复(Repeate),一直重复上述两个过程直到文件结束。
  4. 程序的结构
BEGIN {awk-commands} # 开始块 可选, 在整个过程中只执行一次
/pattern/ {awk-commands} # 主体块
END {awk-commands} # 结束块 可选, 在整个过程中只执行一次

awk基本语法

awk [options] file ...
# 从文件中读取awk指令
awk -f command.awk marks.txt

AWK 标准选项

  1. -v选项,可以为变量赋值。它允许在程序执行之前为变量赋值。
awk -v name=Jerry 'BEGIN{printf "Name = %s\n", name}' # Name = Jerry