You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Python中使用Paramiko和SCPClient传输目录时忽略隐藏文件与符号链接(需经跳板机连接)

Absolutely! You can absolutely exclude hidden files (those starting with .) and symbolic links when transferring directories with Paramiko and SCPClient—no rsync required. Let’s adjust your script to implement this, and even optimize it to skip the local temporary folder (which saves disk space and time).

Instead of downloading files to your local machine first, you can transfer directly from target_host_1 to target_host_2 using Paramiko’s SFTP capability. This is faster and avoids cluttering your local disk. Here’s how to modify your script:

Step 1: Add SFTP Transfer Function

Add this recursive function to handle directory traversal, filter out hidden files/symlinks, and transfer content directly:

def transfer_directory(sftp_src, sftp_dst, src_dir, dst_dir):
    """
    Recursively transfer files/dirs from src_dir (sftp_src) to dst_dir (sftp_dst),
    skipping hidden files and symbolic links.
    """
    # List all items in the source directory with attributes
    items = sftp_src.listdir_attr(src_dir)
    
    for item in items:
        item_name = item.filename
        
        # Skip hidden files (names starting with .)
        if item_name.startswith('.'):
            continue
        
        # Skip symbolic links (check S_IFLNK flag in file mode)
        if item.st_mode & 0o120000:
            continue
        
        src_path = os.path.join(src_dir, item_name)
        dst_path = os.path.join(dst_dir, item_name)
        
        if item.st_mode & 0o040000:
            # It's a directory: create it on destination if missing
            try:
                sftp_dst.stat(dst_path)
            except FileNotFoundError:
                sftp_dst.mkdir(dst_path)
            # Recurse into the subdirectory
            transfer_directory(sftp_src, sftp_dst, src_path, dst_path)
        else:
            # It's a regular file: transfer it directly
            with sftp_src.open(src_path, 'rb') as src_file:
                with sftp_dst.open(dst_path, 'wb') as dst_file:
                    dst_file.write(src_file.read())

Step 2: Replace Local Transfer Logic

Remove the local folder creation, scp_1.get()/scp_2.put(), and shutil.rmtree() parts. Instead, use SFTP clients and call the transfer function:

# Open SFTP sessions for both target servers
sftp_1 = ssh_1.open_sftp()
sftp_2 = ssh_2.open_sftp()

# Delete old files on target_host_2 (more reliable than sleep)
command = "find /filepath -mindepth 1 -delete"
stdin, stdout, stderr = ssh_2.exec_command(command)
exit_status = stdout.channel.recv_exit_status()
if exit_status != 0:
    print(f"Warning: Failed to delete old backup files: {stderr.read().decode()}")

# Transfer directly from target_host_1 to target_host_2
transfer_directory(sftp_1, sftp_2, '/filepath/', '/filepath/')

# Close SFTP sessions
sftp_1.close()
sftp_2.close()

Option 2: Modified Local Transfer (If You Need to Keep Local Copy)

If you still want to maintain a local temporary copy, adjust your script to filter files during download/upload:

Step 1: Get Filtered File List from Target 1

First, fetch the list of non-hidden, non-symlink files/directories from target_host_1:

def get_approved_files(ssh_client, remote_dir):
    """Fetch relative paths of files/dirs to transfer (exclude hidden, symlinks)"""
    # Use find command to list valid items
    find_cmd = f'''find "{remote_dir}" -type f ! -name ".*" ! -type l -printf "%P\\n"; 
                   find "{remote_dir}" -type d ! -name ".*" ! -type l -printf "%P\\n" | grep -v "^$"'''
    stdin, stdout, stderr = ssh_client.exec_command(find_cmd)
    return stdout.read().decode().splitlines()

remote_source_dir = '/filepath/'
local_target_dir = '/target_folder_local/'
files_to_transfer = get_approved_files(ssh_1, remote_source_dir)

Step 2: Download Only Approved Files

Create the local directory structure and download each valid file:

# Create local directory structure
for item in files_to_transfer:
    local_path = os.path.join(local_target_dir, item)
    if os.path.basename(item):
        # It's a file: create parent directory
        os.makedirs(os.path.dirname(local_path), exist_ok=True)
    else:
        # It's a directory: create it
        os.makedirs(local_path, exist_ok=True)

# Download each file
for item in files_to_transfer:
    remote_path = os.path.join(remote_source_dir, item)
    local_path = os.path.join(local_target_dir, item)
    if os.path.isfile(local_path):  # Only download files (dirs already created)
        scp_1.get(remote_path, local_path)

Step 3: Upload Approved Files to Target 2

Upload the filtered local files to target_host_2:

# Upload to target_host_2
remote_dest_dir = '/filepath/'
for item in files_to_transfer:
    local_path = os.path.join(local_target_dir, item)
    remote_path = os.path.join(remote_dest_dir, item)
    if os.path.isfile(local_path):
        scp_2.put(local_path, remote_path)
    else:
        # Create directory on target if missing
        ssh_2.exec_command(f"mkdir -p '{remote_path}'")

Key Notes

  • Symlink Detection: We check the file mode flag 0o120000 (S_IFLNK) to identify symbolic links on the remote server.
  • Hidden Files: We skip any item whose name starts with . (except . and .., which are handled automatically by directory traversal).
  • Reliable Cleanup: Replaced time.sleep(5) with checking the command exit status to ensure old files are deleted before starting the backup.

内容的提问来源于stack exchange,提问作者Pheonix

火山引擎 最新活动