在不同主机间传输文件,常用的有三个命令:sftp、scp、rsync。

sftp、scp 与 ssh 这三个命令默认都使用 22 端口。

sftp/scp/rsync 三者连接远程主机时,使用的格式都相同:

[username@]{hostname|ip}:destination

username:
    若指定 username,表示以【username】帐号名登入到远程主机,若省略 username,表示以当前本地用户身份登入;

hostname|ip:
	可通过【hostname】或【IP】登入远程主机

destination:
	是指远程主机上文件位置 或 要将本地文件放到远程主机上的哪个目录下;

传输过程中文件权限与属性变化:

  • 当将本地文件传输到远程主机时,该文件【属主与属组】会发生变化,并改成登入远程主机的 username 的属主与属组;
  • 当从远程主机下载文件到本地时,该文件【属主与属组】也会发生变化,并改成当前本地用户的属主与属组;
  • 也就是说,本地与远程主机之间传输文件时,文件 user 与 group 属性会改变,且始终是【与登入主机的用户】的属性保持一致,除非用 root;
  • 另外除了 user/group 会改变外,【权限属性】也有可能会改变,这个主要是由【文件是否存在】决定;

1、scp

scp 与 cp 指令类似,但 cp 只能是本地拷贝,而 scp 能在不同主机间进行复制,且 scp 传输是加密的。

scp 基本不影响系统正常使用,而 rsync 会导致硬盘 I/O 变高。

scp [-rp] [-l 速率]  source  destination    从本地复制到远程主机
scp [-rp] [-l 速率]  destination  source    从远程主机复制到本地

选项:
    -p:保留原文件或目录权限属性
    -r:用于复制目录
    -l:可限制传输速度,单位为 Kbits/s,如 -l 800 代表传输速度为 100KB

如:
	[root@www ~]# scp root@172.25.0.11:/root/file1 /root 					#从远程主机上复制文件
	[root@www ~]# scp /root/file1 /root/file2 root@172.25.0.11:/root/		#从本地复制多个文件到远程主机
	[root@www ~]# scp -r root@172.25.0.11:/root/dir1 /root      			#从远程主机上复制某个目录到本地

2、rsync

rsync 是一个远程数据同步工具,用于同步不同主机间的文件。

它与 scp 区别是,如果两个系统间的两个文件或目录相似,rsync 仅传输两个文件或目录不同部分,而 scp 会复制所有内容。

rsync 优点是,首次传文件时,整个文件复制过去,而从第二次开始,只把“修改过内容”或“不同部分”同步过去,即增量传输。

rsync [-arvtogp]  source  destination      	从本地同步到远程主机
rsync [-arvtogp]  destination   source      从远程主机同步到本地 

选项:
	-r:以递归方式同步整个目录
	-v:显示详细信息
	-t:保留文件修改时间信息
	-l:用于同步 softlink 文件,不加该选项,默认会拒绝同步,提示“skipping non-regular file”
	
	--list-only:
		仅仅是列出同步的文件有哪些,实际不会执行同步操作,显示格式类似于 ll 命令
    -I, --ignore-times:
    	默认在同步时,若文件大小及修改时间没有变化,与原来一致,则rsync不会同步该文件
    	该选项表示在同步时,即使文件大小及修改时间一致,也要同步该文件
    -z,--compress:
    	在传输文件时进行压缩处理,压缩技术与 gzip 一样
    -p:perserve permission,保留文件权限
       	若不加该选项,rysnc 默认处理权限方式是:
           如果目的端没有此文件,则在同步后会将目的端文件的权限保持与源端一致;
           如果目的端已存在此文件,则只会同步文件内容,权限保持原有不变;
       	若使用了 -p,则无论如何,rysnc 都会让目的端保持与源端的权限一致的

    -o:保留文件属主(owner)
    -g:保留文件属组(group)
    -D:perserve devices(root only)  保持设备文件的原始信息
    
    -a:归档模式,表示以递归方式传输文件,并尽可能的保持各方面的一致性,等于 -【 r l p t g o D】 一堆命令组合
        但 -a 无法同步“硬链接”情况,需加上 -H 选项
    
    --delete:删除 DST 端中“SRC端”没有的文件,若使用该选项,必须结合 -r 使用
    --delete-excluded:删除 DST 端中被该选项指定的文件
    --delete-after:传输结束后再删除要清除的文件,默认情况下,rsync是先清理目的端的文件再开始数据同步;
    --exclude=PATTERN:过滤掉指定文件,可使用正则表达式

    -n:用于测试哪些文件会被传输,不是真实去执行。也可结合 delete 相关命令测试

如:
	[root@desktop0 ~]# rsync -azv file1 172.25.0.11:/tmp/dir1 				#一般使用av即可
	[root@desktop0 ~]# rsync -r student@172.25.0.11:/home/student /root

3、sftp

sftp 与 ftp 类似,但 sftp 传输的所有信息都使用 ssh 加密,比 ftp 更安全。

sftp [user@]host[:file ...]
sftp [user@]host[:dir[/]]

[root@www ~]# sftp root@124.172.235.134 		#登入远程主机,并进入到 root 工作目录
[root@www ~]# sftp root@124.172.235.134:/tmp 	#登入远程主机,并进入到 /tmp 目录

远程主机操作命令:
	sftp> pwd 							#查看当前工作目录
	sftp> cd path 						#切换工作目录
	sftp> mkdir path 					#创建目录
	sftp> rename oldpath newpath 		#重命名文件名
	sftp> ln oldpath newpath			#建立文件软连接
	sftp> rm path  						#删除文件
	sftp> rmdir path 					#删除目录
	sftp> ls [-1aflnrSt] [path] 		#查看文件
	sftp> chgrp grp path 				#修改文件属组
	sftp> chown own path 				#修改文件属主
	sftp> chmod mode path 				#修改文件权限

本机操作命令:
	sftp> lpwd
	sftp> lcd path	
	sftp> lls [ls-options [path]]
	sftp> lmkdir path
	sftp> !command 								#Execute 'command' in local shell
	sftp> ! 									#Escape to local shell(暂时跳到本地shell模式下执行命令,执行exit命令回到sftp模式下)

上传与下载:
	sftp> get [-P] remote-path [local-path]  	#下载文件,若不指定位置,会下载到本地当前工作位置
	sftp> put [-P] local-path [remote-path] 	#上传文件,若不指定位置,会上传到远程主机当前工作位置

4、expect

expect 是一个用来处理交互的命令。它的作用是,当我们在 SHELL 脚本中使用 ssh、scp、rsync 等交互式命令时,能免去我们手动输入密码的步骤,expect会自动填入我们预先设置好的密码信息。

安装:

yum install -y expect

 语法:

expect [[ -c cmds ] cmdfile]

cmds:
    send: 向程序发送字符串(Sends string to the current process)
    expect: 等待并接收程序返回的字符串(waits until one of the patterns matches the output of a spawned process)
    spawn:给程序传递交互指令(creates a new process running program args)
    interact:执行完成后保持交互状态,把控制权交回给用户,此时用户可进行手动输入操作(gives control of the current process to the user)

    set timeout {second|-1}:
	    设置超时时间,单位是秒,默认为10秒,-1为永不超时
	    若设为30秒,则表示在这30秒期间,都未能成功匹配expect命令定义的字符串,则将跳过该expect判断,执行后续内容

    $argv n:
	    expect脚本可接收从bash传递过来的参数
	    其中通过 [lindex $argv n] 可获得第 n 个参数的值,通过 [lrange $argv a b] 可获取 a~b 的参数值

示例:

  • send:用于执行交互动作,向程序发送字符串,代替手动输入,需在结尾处加上换行符。
语法:send [-flags] string

[root@alihost7 ~]$ expect
expect1.1> send "hello world\n"
hello world
expect1.2> send "123456\n"
123456

[root@alihost7 ~]$ expect -c 'send "hello world\n"'
hello world
  • expect:用于判断输入内容中,是否匹配expect后面定义的字符串,若匹配,则执行后面的语句体。expect后面指定接收的字符串参数也可使用正则表达式。
语法:expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]

1)基础用法
[root@alihost7 ~]$ expect
expect1.1> expect "abc" {send "your input is a letter\n"}  #若接收到的字符串为'abc',则执行后面send语句
abc                         #手动输入
youe input is a letter      #匹配expect后面定义的字符串,并输出结果

[root@alihost7 ~]$ expect -c 'expect "abc" {send "your input is a letter\n"}'
abc
youe input is a letter

[root@alihost7 ~]$ expect -c 'expect "abc*" {send "your input is a letter\n"}'
abcdefg
youe input is a letter


2)两个特殊变量
$expect_out(buffer):存储了所有对expect的输入
$expect_out(0,string):存储了匹配到expect定义的字符串的输入

[root@alihost7 ~]$ expect -c 'expect "end" {send "all input str: $expect_out(buffer)"}'
aaaaa
bbbbb
ccccc
end
all input str: aaaaa
bbbbb
ccccc
end

[root@alihost7 ~]$ expect -c 'expect "end" {send "only match str: $expect_out(0,string)"}'
aaaaa
bbbbb
ccccc
end
only match str: end

3)多个条件分支
expect最常用的语法是来自tcl语言的pattion-action。

[root@alihost7 ~]$ expect -c 'expect "a" {send "apple"} "b" {send "banana"}'
b
banana
  • spawn:上文的所有例子都是跟shell终端自身的stdin与stdout进行交互。相对于spawn命令,它可以和某一个进程进行交互,当与某个进程交互时,send输出和expect输入的字符串,都是与spawn打开的进程进行交互的。
示例1:通过expect完成ftp交互密码的输入

[root@alihost7 ~]$ cat << EOF > exp_ftp.sh
#!/usr/bin/expect
spawn ftp 192.168.100.128 23235
expect "Name*"
send "fashion01\r"
expect "Password:"
send "fashion01\r"
expect "ftp> "
send "binary\r"
expect "ftp> "
send "get file\r"
expect "ftp> "
send "exit"
EOF

执行脚本,自动完成交互过程中的所有输入,输出结果如下:
[root@alihost7 ~]$ expect exp_ftp.sh 
spawn ftp 192.168.100.128 23235
Connected to 192.168.100.128 (192.168.100.128).
Connected to 192.168.100.128 (192.168.100.128).
220 (vsFTPd 2.2.2)
Name (192.168.100.128:root): fashion01
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> binary
200 Switching to Binary mode.
ftp> get file
local: file remote: file
227 Entering Passive Mode (192,168,100,128,243,141).
150 Opening BINARY mode data connection for file (4 bytes).
226 Transfer complete.
4 bytes received in 5.9e-05 secs (67.80 Kbytes/sec)
  • interact:如何让人在适当的时候干预这个过程,比如下载完ftp文件时,仍然可以停留在ftp命令行状态,以便手动的执行后续命令,interact可以达到这些目的。
示例:
[root@alihost7 ~]$ cat << EOF > exp_ftp.sh
#!/usr/bin/expect
spawn ftp 192.168.100.128 23235
expect "Name*"
send "fashion01\r"
expect "Password:"
send "fashion01\r"
interact
EOF

执行脚本,登入进ftp后,就交由用户来操作:
[root@alihost7 ~]$ expect exp_ftp.sh 
spawn ftp 192.168.100.128 23235
Connected to 192.168.100.128 (192.168.100.128).
220 (vsFTPd 2.2.2)
Name (192.168.100.128:root): fashion01
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> 
  • $argv n:expect脚本可接收从bash传递过来的参数,其中通过 [lindex $argv n] 可获得第 n 个参数的值,通过 [lrange $argv a b] 可获取 a~b 的参数值。
  • exp_continue:该命令附加于某个 expect 判断项之后,可以使该项被匹配后,还能继续匹配该 expect 判断语句内的其他项。
示例:
[root@alihost7 ~]$ cat << FOE > exp_args.sh
#!/usr/bin/expect
set username [lindex $argv 0]
set password [lindex $argv 1]
set hostname [lindex $argv 2]
spawn ssh $username@$hostname
expect {
"yes/no"
{send "yes\r";exp_continue;}
"password:"
{send "$password\r";}
}
expect eof
FOE

执行脚本,并传入参数。行脚本后,会自动登入和退出机器,如下:
[root@alihost7 ~]$ expect exp_args.sh root zjm 192.168.100.128

免密码SSH登录:

[root@alihost7 ~]$ cat << EOF > exp_ssh.sh
#!/usr/bin/expect
set timeout 15
set hostname [lindex $argv 0]
spawn ssh -l root $hostname
expect "password*"
send "123456\r"
interact
EOF

执行脚本登录机器:
[root@alihost7 ~]$ ./exp_ssh.sh 192.168.1.100

 

5、综合案例

通过expect脚本方式实现ssh、rsync、scp等命令自动输入密码登录。

expect + scp:

示例:
[root@alihost7 ~]$ cat << EOF > exp_scp.sh
#!/bin/bash
filepath=/data/shell/dir/scpfile.txt
username=root
password=zjm
hostname=192.168.100.128

#写法1
/usr/bin/expect -c "
spawn scp ${filepath} $username@$hostname:/tmp
expect {
\"yes/no\" {send \"yes\r\"; exp_continue;}
\"*assword\" {set timeout -1; send \"$password\r\";}
}
expect eof
"

#写法2
/usr/bin/expect <<EOF
spawn scp ${filepath} $username@$hostname:/tmp
expect {
"yes/no" {send "yes\r"; exp_continue;}
"*assword" {set timeout -1; send "$password\r";}
}
expect eof
EOF

执行脚本:
[root@alihost7 ~]$ sh exp_scp.sh

说明:

expect eof:

如果省略expect eof,那脚本立即就结束了,可能得不到正确结果

这个是和spawn对应的,当spawn发送指令到终端执行时在返回时被expect捕捉时,在起始会有一个eof,
就好比在shell中 cat >>file <<OEF\r\r content \r\r EOF一样,在结束时也要有EOF


exp_continue:跟continue含义一样.继续下个最近的循环

timeout:设为-1,表示永不超时,若不指定,当传送大文件时会自动断开

expect + rsync:

[root@alihost7 ~]$ cat << EOF > exp_rsync.sh
#!/bin/bash
filepath=/data/shell/dir/rsyncfile.txt
username=root
password=zjm
hostname=192.168.100.128

/usr/bin/expect <<EOF
set timeout -1
spawn rsync -av ${filepath} $username@$hostname:/tmp
expect {
"*yes/no" { send "yes\r"; exp_continue }
"*assword:" { send "$password\r" }
}
expect eof
EOF

expect + ssh:

[root@alihost7 ~]$ cat << EOF > sleep1.sh
#!/bin/bash
sleep 12
echo 'this is a output file from sleep1.sh script' | tee /tmp/mydir1/sleep1.txt
EOF


[root@alihost7 ~]$ cat << EOF > exp_ssh.sh
#!/bin/bash
username=root
password=zjm
hostname=192.168.100.128

/usr/bin/expect <<EOF
set timeout 15
spawn ssh $username@$hostname
expect {
"*yes/no" { send "yes\r"; exp_continue }
"*assword:" { send "$password\r" }
}
expect "*#"
send "cd /tmp/mydir1\r"
expect "*#"
send "sh sleep1.sh\r"
expect "*#"
send "exit\r"
expect eof
EOF

说明:

timeout:由于sleep1.sh脚本执行时间需要12秒,当执行expect时,在10秒内若没反馈信息,则会自动退出,所以要设置timeout

expect + ftp:

[root@alihost7 ~]$ cat << EOF > exp_ftp.sh
#!/bin/bash
host=192.168.100.128
user=fashion01
port=23235
password=fashion01
localpath=/tmp/mydir2/

/usr/bin/expect <<EOF
set timeout -1
spawn ftp -ivn $host $port
expect "ftp>"
send "user $user $password\r"
expect "ftp>"
send "lcd $localpath\r"
expect "ftp>"
send "binary\r"
expect "ftp>"
send "mget file1 file2\r"
expect "ftp>"
send "exit\r"
expect eof
EOF

expect + sftp:

[root@alihost7 ~]$ cat << EOF > exp_sftp.sh
#!/bin/bash
username=root
hostname=124.172.235.134
password=Zjm.339224046

/usr/bin/expect <<EOF
set timeout -1
spawn sftp $username@$hostname
expect {
"*yes/no" { send "yes\r"; exp_continue }
"*assword:" { send "$password\r" }
}
expect "sftp>"
send "cd /data/backup\r"
expect "sftp>"
send "lcd /tmp\r"
expect "sftp>"
send "get /data/backup/* /tmp/mydir1\r"
expect "sftp>"
send "exit\r"
expect eof
EOF

expect + mysql:

[root@alihost7 ~]$ cat << EOF > exp_mysql.sh
#!/bin/bash
user=root
password=admin
host=127.0.0.1
dbname=test01
dbpwd=test01

/usr/bin/expect <<EOF
set timeout -1
spawn /usr/local/mysql/bin/mysql -u${user} -p -h ${host}
expect {
"*assword:" { send "$password\r" }
}
expect "mysql>"
send "show databases;\r"
expect "mysql>"
send "create database IF not EXISTS ${dbname};\r"
expect "mysql>"
send "GRANT ALL PRIVILEGES ON $dbname.* TO '$dbname'@'$host' IDENTIFIED BY '$dbpwd' WITH GRANT OPTION;\r"
expect "mysql>"
send "exit\r"
expect eof
EOF

Logo

更多推荐