You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含空格文件名加单引号后,Ansible的rsync/synchronize执行失败

问题描述

在Jenkins流水线中按StackOverflow建议调用ansible-playbook:

sh '''
ansible-playbook /web/playbooks/getfiles.yml -e "dest_host='$dest_host'" -e "dest_user='$dest_user'" -e "source_file_new='$files'" -vv
'''

Jenkins执行日志显示已传递带空格的文件名:

+ ansible-playbook /web/playbooks/getfiles.yml -e 'dest_host='\''remhost'\''' -e 'dest_user='\''remuser'\''' -e 'source_file_new='\''/web/3ACF0000316KMFLKO8KG_Letter.pdf
/web/000000BV0000 Insurance - Hambrose .pdf'\''' -vv

为处理含空格的文件名,在Playbook的synchronize模块中给文件名添加单引号后,Playbook执行出现冻结:

- name: 从{{ ansible_user }}@{{ inventory_hostname }}复制文件到Ansible控制节点
  synchronize:
    src: "'{{ item }}'"
    dest: "{{ playbook_dir }}/tmpfiles/{{ inventory_hostname }}/"
    mode: pull
    copy_links: yes
  with_items:
    - "{{ source_file_new.splitlines() }}"

随后尝试用raw模块结合rsync,同时试过shell和command模块均失败:

- debug:
    msg: "rsync -avzP  {{ ansible_user }}@{{ inventory_hostname }}:'{{ item }}' {{ playbook_dir }}/tmpfiles/{{ inventory_hostname }}/"
  with_items:
    - "{{ source_file_new.splitlines() }}"

- name: 从{{ ansible_user }}@{{ inventory_hostname }}复制文件到Ansible控制节点
  raw: "rsync -avzP  {{ ansible_user }}@{{ inventory_hostname }}:'{{ item }}' {{ playbook_dir }}/tmpfiles/{{ inventory_hostname }}/"
  with_items:
    - "{{ source_file_new.splitlines() }}"

Debug输出的rsync命令手动执行正常,但Playbook中执行时出现SSH连接异常:

TASK [Copying from "remuser" at "remhost" to this ansible server.] *************************************************************************************
task path: /web/playbooks/getfiles.yml:120
Tuesday 10 January 2023  10:53:15 -0600 (0:00:00.042)       0:00:03.553 *******
<remhost> ESTABLISH SSH CONNECTION FOR USER: remuser
<remhost> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="remuser"' -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o 'ControlPath="/home/remuser/.ansible/cp/fcf90ac50d"' -tt remhost 'rsync -avzP  remuser@remhost:'"'"'/web/3ACF0000316KMFLKO8KG_Letter.pdf'"'"' /web/playbooks/automation/getfiles/tmpfiles/remhost/'

使用ansible.posix.synchronize模块时,出现rsync连接意外关闭错误(错误码255):

failed: [remhost] (item=/web/000000BV0000 Insurance - Hambrose .pdf) => {
    "ansible_loop_var": "item",
    "changed": false,
    "cmd": "/bin/rsync --delay-updates -F --compress --copy-links --archive --rsh=/usr/share/centrifydc/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L remuser@remhost:'/web/000000BV0000 Insurance - Hambrose .pdf' /web/playbooks/automation/getfiles/tmpfiles/remhost/",
    "invocation": {
        "module_args": {
            "_local_rsync_password": null,
            "_local_rsync_path": "rsync",
            "_substitute_controller": false,
            "archive": true,
            "checksum": false,
            "compress": true,
            "copy_links": true,
            "delete": false,
            "dest": "/web/playbooks/automation/getfiles/tmpfiles/remhost/",
            "dest_port": null,
            "dirs": false,
            "existing_only": false,
            "group": null,
            "link_dest": null,
            "links": null,
            "mode": "pull",
            "owner": null,
            "partial": false,
            "perms": null,
            "private_key": null,
            "recursive": null,
            "rsync_opts": [],
            "rsync_path": null,
            "rsync_timeout": 0,
            "set_remote_user": true,
            "src": "remuser@remhost:'/web/000000BV0000 Insurance - Hambrose .pdf'",
            "ssh_args": null,
            "ssh_connection_multiplexing": false,
            "times": null,
            "verify_host": false
        }
    },
    "item": "/web/000000BV0000 Insurance - Hambrose .pdf",
    "msg": "Warning: Permanently added 'remhost' (ED25519) to the list of known hosts.\r\n\nThis system is for the use by authorized users only. All data contained\non all systems is owned by the company and may be monitored, intercepted,\nrecorded, read, copied, or captured in any manner and disclosed in any\nmanner, by authorized company personnel. Users (authorized or unauthorized)\nhave no explicit or implicit expectation of privacy. Unauthorized or improper\nuse of this system may result in administrative, disciplinary action, civil\nand criminal penalties. Use of this system by any user, authorized or\nunauthorized, constitutes express consent to this monitoring, interception,\nrecording, reading, copying, or capturing and disclosure.\n\nIF YOU DO NOT CONSENT, LOG OFF NOW.\n\n##################################################################\n# *** This Server is using Centrify                          *** #\n# *** Remember to use your Active Directory account          *** #\n# ***    password when logging in                            *** #\n##################################################################\n\nConnection closed by 192.168.112.2 port 22\r\nrsync: connection unexpectedly closed (0 bytes received so far) [Receiver]\nrsync error: unexplained error (code 255) at io.c(226) [Receiver=3.1.2]\n",
    "rc": 255
}

复现步骤
在目标主机执行以下命令创建测试文件:

touch '/web/3ACF0000316KMFLKO8KG_Letter.pdf'
touch '/web/000000BV0000 Insurance - Hambrose .pdf'
解决方案

1. 修复Jenkins变量传递方式

Jenkins传递带换行、空格的变量时,避免手动拼接引号导致转义混乱,推荐两种方式:

  • 直接传递变量,让Ansible自动处理引用:
sh '''
ansible-playbook /web/playbooks/getfiles.yml \
  -e dest_host="$dest_host" \
  -e dest_user="$dest_user" \
  -e "source_file_new=$files" \
  -vv
'''
  • 使用JSON格式传递extra-vars,彻底规避引号问题:
sh '''
ansible-playbook /web/playbooks/getfiles.yml \
  --extra-vars '{
    "dest_host": "'"$dest_host"'",
    "dest_user": "'"$dest_user"'",
    "source_file_new": "'"$files"'"
  }' \
  -vv
'''

2. 正确使用synchronize模块处理带空格路径

synchronize模块本身会自动处理带空格的路径,手动加单引号会让路径被错误解析(变成包含单引号的无效路径),修改任务如下:

- name: 从远程主机拉取文件到控制节点
  ansible.posix.synchronize:
    src: "{{ item }}"
    dest: "{{ playbook_dir }}/tmpfiles/{{ inventory_hostname }}/"
    mode: pull
    copy_links: yes
  with_items: "{{ source_file_new.splitlines() }}"

注意:mode: pull时,src是远程主机路径,Ansible会自动通过SSH传递正确转义后的路径,无需额外处理。

3. 若必须用rsync+raw/shell模块,正确处理转义

如果因环境限制必须用raw/shell模块执行rsync,需避免嵌套引号错误,推荐用Ansible的quote过滤器自动处理路径转义,同时让命令在控制节点执行(避免远程主机二次SSH连接):

- name: 用rsync拉取文件(shell模块)
  shell: |
    rsync -avzP {{ ansible_user }}@{{ inventory_hostname }}:{{ item | quote }} {{ playbook_dir }}/tmpfiles/{{ inventory_hostname }}/
  delegate_to: localhost
  with_items: "{{ source_file_new.splitlines() }}"

4. 排查Centrify环境SSH问题

错误日志显示目标主机用Centrify,可能是认证或SSH配置问题:

  • 先手动测试控制节点到目标主机的SSH连接:ssh remuser@remhost,确保能正常登录
  • 在synchronize模块中指定Centrify相关SSH参数:
ansible.posix.synchronize:
  # 保留其他参数
  ssh_args: '-o UserKnownHostsFile=/dev/null'
  rsync_path: 'sudo rsync' # 如果需要sudo权限执行rsync

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 12:35:21