使用 Paramiko SFTP 复制目录
Copying Directories using Paramiko SFTP
我想模仿著名的 Linux rsync 命令的行为,如果目录末尾没有指定“/”,它会从 "test" 复制整个目录并复制所有内容当存在“/”时,在 "test/" 内。我的本地 "test" 文件夹结构如下:
test
.
├── fileA.txt
├── fileB.txt
├── test1
│ └── test3
│ └── file3.txt
└── test2
└── file2.txt
在rsync中将整个本地测试文件夹复制到远程服务器:
rsync -avzP test username@remotehost:/home/
在远程主机的主目录中是
home
.
|__ test
├── fileA.txt
├── fileB.txt
├── test1
│ └── test3
│ └── file3.txt
└── test2
└── file2.txt
示例 A
要复制本地 "test" 文件夹中的所有内容 ,不包括 本身:
rsync -avzP test/ username@remotehost:/home/
主目录的结构为:
home/
.
├── fileA.txt
├── fileB.txt
├── test1
│ └── test3
│ └── file3.txt
└── test2
└── file2.txt
示例 B
我的代码不适用于示例 B。我考虑过拆分路径并摆脱 "test" 然后复制其中的所有内容,但这只会让我陷入无尽的嵌套, for 循环。另一个想法是使用 os.listdir。如果是目录,则再次列出该目录并复制该目录中的内容。这又是一个无限循环。上面的树结构是一个过度简化的例子,但在现实生活中,我们都知道目录可能有 5 层深。我如何实施示例 B?
def put (self, localpath, remotepath):
sftp = self.ssh.open_sftp ()
# Create remote directory if it doesn't exist
try:
sftp.stat (remotepath)
except FileNotFoundError:
sftp.mkdir (remotepath)
if os.path.isfile (localpath):
# Obtain file name from local path & append to remote path
path = os.path.split (localpath) # Returns a tuple (directory, filename)
remote_filename = os.path.join (remotepath, path [1])
print (' Copying %s' % remote_filename)
sftp.put (localpath, remote_filename)
elif os.path.isdir (localpath):
p = os.path.join (remotepath, localpath)
try:
sftp.stat (p)
except FileNotFoundError:
sftp.mkdir (p)
for dirpath, dirnames, filenames in os.walk (localpath):
# Traverse into each child directory and create sub directory if it doesn't exist
if dirnames:
for dirname in dirnames:
subdir = os.path.join (dirpath, dirname)
try:
sftp.stat (subdir)
except FileNotFoundError:
sftp.mkdir (subdir)
for filename in filenames:
local_filename = os.path.join (dirpath, filename)
remote_filename = os.path.join (remotepath, local_filename)
sftp.put (local_filename, remote_filename)
我明白了。它不是最漂亮但最实用的。如果有人有更好、更干净、更 pythonic 的方法,请分享。
def put (self, localpath, remotepath):
sftp = self.ssh.open_sftp ()
# Create remote directory if it doesn't exist
try:
sftp.stat (remotepath)
except FileNotFoundError:
sftp.mkdir (remotepath)
if os.path.isfile (localpath):
# Obtain file name from local path & append to remote path
path = os.path.split (localpath) # Returns a tuple (directory, filename)
remote_filename = os.path.join (remotepath, path [1])
print (' Copying %s' % remote_filename)
sftp.put (localpath, remote_filename)
elif os.path.isdir (localpath):
if localpath.endswith ('/'):
for dirpath, dirnames, filenames in os.walk (localpath):
# Change local dirpath to match remote path. Ex: local/dir/.. to remote/dir/...
remotedir = dirpath.split ('/') # remotedir = [local, dir1, dir2, ...]
remotedir [0] = remotepath.rstrip ('/') # remotedir = [/remote, dir1, dir2, ...]
remotedir = '/'.join (remotedir)
# Traverse into each child directory and create sub directory if it doesn't exist on remote host
if dirnames:
for dirname in dirnames:
subdir = os.path.join (remotedir, dirname)
try:
sftp.stat (subdir)
except FileNotFoundError:
sftp.mkdir (subdir)
for filename in filenames:
localdir = os.path.join (dirpath, filename)
remotefile = os.path.join (remotedir, filename)
print (' Copying %s' % localdir)
sftp.put (localdir, remotefile)
else:
# Create path /remote/local/dir1...
p = os.path.join (remotepath, localpath)
try:
sftp.stat (p)
except FileNotFoundError:
sftp.mkdir (p)
for dirpath, dirnames, filenames in os.walk (localpath):
if dirnames:
for dirname in dirnames:
subdir = os.path.join (dirpath, dirname)
remotedir = os.path.join (remotepath, subdir)
try:
sftp.stat (remotedir)
except FileNotFoundError:
sftp.mkdir (remotedir)
for filename in filenames:
local_filename = os.path.join (dirpath, filename)
remote_filename = os.path.join (remotepath, local_filename)
print (' Copying %s' % local_filename)
sftp.put (local_filename, remote_filename)
else:
print ('File or directory not found.')
最终结果:
示例 A:
>>> ssh.put ('test', '/home/user/')
Copying test/fileA.txt
Copying test/fileB.txt
Copying test/test1/test3/file3.txt
Copying test/test2/file2.txt
Completed!
-sh-4.1$ ls -lh test/*
-rw-r----- 1 user users 0 Sep 1 23:43 test/fileA.txt
-rw-r----- 1 user users 0 Sep 1 23:43 test/fileB.txt
test/test1:
total 4.0K
drwxr-x--- 2 user users 4.0K Sep 1 23:43 test3
test/test2:
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file2.txt
-sh-4.1$ ls -lh test/*/*
-rw-r----- 1 user users 0 Sep 1 23:43 test/test2/file2.txt
test/test1/test3:
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file3.txt
-sh-4.1$
示例 B:
>>> ssh.put ('test/', '/home/user/')
Copying test/fileA.txt
Copying test/fileB.txt
Copying test/test1/test3/file3.txt
Copying test/test2/file2.txt
Completed!
-sh-4.1$ pwd
/home/user
-sh-4.1$ ls -lh
total 108K
-rw-r----- 1 user users 0 Sep 1 23:43 fileA.txt
-rw-r----- 1 user users 0 Sep 1 23:43 fileB.txt
drwxr-x--- 3 user users 4.0K Sep 1 23:43 test1
drwxr-x--- 2 user users 4.0K Sep 1 23:43 test2
-sh-4.1$ ls -lh test1 test2
test1:
total 4.0K
drwxr-x--- 2 user users 4.0K Sep 1 23:43 test3
test2:
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file2.txt
-sh-4.1$ ls -lh test1/test3/
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file3.txt
-sh-4.1$
我想模仿著名的 Linux rsync 命令的行为,如果目录末尾没有指定“/”,它会从 "test" 复制整个目录并复制所有内容当存在“/”时,在 "test/" 内。我的本地 "test" 文件夹结构如下:
test
.
├── fileA.txt
├── fileB.txt
├── test1
│ └── test3
│ └── file3.txt
└── test2
└── file2.txt
在rsync中将整个本地测试文件夹复制到远程服务器:
rsync -avzP test username@remotehost:/home/
在远程主机的主目录中是
home
.
|__ test
├── fileA.txt
├── fileB.txt
├── test1
│ └── test3
│ └── file3.txt
└── test2
└── file2.txt
示例 A
要复制本地 "test" 文件夹中的所有内容 ,不包括 本身:
rsync -avzP test/ username@remotehost:/home/
主目录的结构为:
home/
.
├── fileA.txt
├── fileB.txt
├── test1
│ └── test3
│ └── file3.txt
└── test2
└── file2.txt
示例 B
我的代码不适用于示例 B。我考虑过拆分路径并摆脱 "test" 然后复制其中的所有内容,但这只会让我陷入无尽的嵌套, for 循环。另一个想法是使用 os.listdir。如果是目录,则再次列出该目录并复制该目录中的内容。这又是一个无限循环。上面的树结构是一个过度简化的例子,但在现实生活中,我们都知道目录可能有 5 层深。我如何实施示例 B?
def put (self, localpath, remotepath):
sftp = self.ssh.open_sftp ()
# Create remote directory if it doesn't exist
try:
sftp.stat (remotepath)
except FileNotFoundError:
sftp.mkdir (remotepath)
if os.path.isfile (localpath):
# Obtain file name from local path & append to remote path
path = os.path.split (localpath) # Returns a tuple (directory, filename)
remote_filename = os.path.join (remotepath, path [1])
print (' Copying %s' % remote_filename)
sftp.put (localpath, remote_filename)
elif os.path.isdir (localpath):
p = os.path.join (remotepath, localpath)
try:
sftp.stat (p)
except FileNotFoundError:
sftp.mkdir (p)
for dirpath, dirnames, filenames in os.walk (localpath):
# Traverse into each child directory and create sub directory if it doesn't exist
if dirnames:
for dirname in dirnames:
subdir = os.path.join (dirpath, dirname)
try:
sftp.stat (subdir)
except FileNotFoundError:
sftp.mkdir (subdir)
for filename in filenames:
local_filename = os.path.join (dirpath, filename)
remote_filename = os.path.join (remotepath, local_filename)
sftp.put (local_filename, remote_filename)
我明白了。它不是最漂亮但最实用的。如果有人有更好、更干净、更 pythonic 的方法,请分享。
def put (self, localpath, remotepath):
sftp = self.ssh.open_sftp ()
# Create remote directory if it doesn't exist
try:
sftp.stat (remotepath)
except FileNotFoundError:
sftp.mkdir (remotepath)
if os.path.isfile (localpath):
# Obtain file name from local path & append to remote path
path = os.path.split (localpath) # Returns a tuple (directory, filename)
remote_filename = os.path.join (remotepath, path [1])
print (' Copying %s' % remote_filename)
sftp.put (localpath, remote_filename)
elif os.path.isdir (localpath):
if localpath.endswith ('/'):
for dirpath, dirnames, filenames in os.walk (localpath):
# Change local dirpath to match remote path. Ex: local/dir/.. to remote/dir/...
remotedir = dirpath.split ('/') # remotedir = [local, dir1, dir2, ...]
remotedir [0] = remotepath.rstrip ('/') # remotedir = [/remote, dir1, dir2, ...]
remotedir = '/'.join (remotedir)
# Traverse into each child directory and create sub directory if it doesn't exist on remote host
if dirnames:
for dirname in dirnames:
subdir = os.path.join (remotedir, dirname)
try:
sftp.stat (subdir)
except FileNotFoundError:
sftp.mkdir (subdir)
for filename in filenames:
localdir = os.path.join (dirpath, filename)
remotefile = os.path.join (remotedir, filename)
print (' Copying %s' % localdir)
sftp.put (localdir, remotefile)
else:
# Create path /remote/local/dir1...
p = os.path.join (remotepath, localpath)
try:
sftp.stat (p)
except FileNotFoundError:
sftp.mkdir (p)
for dirpath, dirnames, filenames in os.walk (localpath):
if dirnames:
for dirname in dirnames:
subdir = os.path.join (dirpath, dirname)
remotedir = os.path.join (remotepath, subdir)
try:
sftp.stat (remotedir)
except FileNotFoundError:
sftp.mkdir (remotedir)
for filename in filenames:
local_filename = os.path.join (dirpath, filename)
remote_filename = os.path.join (remotepath, local_filename)
print (' Copying %s' % local_filename)
sftp.put (local_filename, remote_filename)
else:
print ('File or directory not found.')
最终结果:
示例 A:
>>> ssh.put ('test', '/home/user/')
Copying test/fileA.txt
Copying test/fileB.txt
Copying test/test1/test3/file3.txt
Copying test/test2/file2.txt
Completed!
-sh-4.1$ ls -lh test/*
-rw-r----- 1 user users 0 Sep 1 23:43 test/fileA.txt
-rw-r----- 1 user users 0 Sep 1 23:43 test/fileB.txt
test/test1:
total 4.0K
drwxr-x--- 2 user users 4.0K Sep 1 23:43 test3
test/test2:
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file2.txt
-sh-4.1$ ls -lh test/*/*
-rw-r----- 1 user users 0 Sep 1 23:43 test/test2/file2.txt
test/test1/test3:
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file3.txt
-sh-4.1$
示例 B:
>>> ssh.put ('test/', '/home/user/')
Copying test/fileA.txt
Copying test/fileB.txt
Copying test/test1/test3/file3.txt
Copying test/test2/file2.txt
Completed!
-sh-4.1$ pwd
/home/user
-sh-4.1$ ls -lh
total 108K
-rw-r----- 1 user users 0 Sep 1 23:43 fileA.txt
-rw-r----- 1 user users 0 Sep 1 23:43 fileB.txt
drwxr-x--- 3 user users 4.0K Sep 1 23:43 test1
drwxr-x--- 2 user users 4.0K Sep 1 23:43 test2
-sh-4.1$ ls -lh test1 test2
test1:
total 4.0K
drwxr-x--- 2 user users 4.0K Sep 1 23:43 test3
test2:
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file2.txt
-sh-4.1$ ls -lh test1/test3/
total 0
-rw-r----- 1 user users 0 Sep 1 23:43 file3.txt
-sh-4.1$