rsync error: protocol incompatibility / mismatch

一、問題

今日在維護集羣環境的時候,遇到了一個小問題,rsync 向集羣中的機器傳輸文件的時候報錯:html

protocol version mismatch -- is your shell clean?
(see the rsync man page for an explanation)
rsync error: protocol incompatibility (code 2) at compat.c(171) [sender=3.0.6]

即便打開調試選項 -vv,也沒能獲得更多的有用信息,不過看提示,應該是跟 shell 環境有關。linux

又翻了下 man rsync,發現官方對這個問題有以下的解釋:shell

DIAGNOSTICS編程

rsync occasionally produces error messages that may seem a little cryptic. The one that seems to cause the most confusion is "protocol version mismatch -- is your shell clean?".bash

This message is usually caused by your startup scripts or remote shell facility producing unwanted garbage on the stream that rsync is using for its transport. The way to diagnose this problem is to run your remote shell like this:ssh

ssh remotehost /bin/true > out.dat

then look at out.dat. If everything is working correctly then out.dat should be a zero length file. If you are getting the above error from rsync then you will probably find that out.dat contains some text or data. Look at the contents and try to work out what is producing it. The most common cause is incorrectly configured shell startup scripts (such as .cshrc or .profile) that contain output statements for non-interactive logins.編程語言

同時 google 這個問題你會發現答案也都是來源於官方的幫助文檔,那麼問題初步肯定是 shell 環境的問題。測試

按照提示,ssh remotehost /bin/true > out.dat 執行事後輸出的正是遠程機器上 .bashrc 設置裏的一條 echo 提示語句。this

註釋掉,而後再次測試,便可正常運行了。google

二、緣由

那麼,這兒問題就來了(問題固然不是挖掘機哪家強 - _ - 。。。),rsync 和 .bashrc 有半毛錢關係呢?

哈哈,欲聽後事如何,且聽我慢慢道來~

緣由是 rsync 在傳輸數據以前,會先與遠端進行一次 ssh 登陸認證,而當 .bashrc文件有輸出的時候,rsync 客戶端解析返回的數據包會出現混亂,因而乎就會出現文中開頭提到的報錯:客戶端和遠端的協議版本不兼容/不匹配了。

須要說明的是:

遠端 sshd 進程是經過「bash –c」的方式來執行命令(即"非交互式的非登陸shell")

但在執行命令以前,ssh的那一次登陸自己是「非交互式的登陸shell」,非交互式的登陸shell (bash –l xxx.sh)載入的信息列表及順序以下:

/etc/profile
[~/.bash_profile || ~/.bash_login || ~/.profile]
$BASH_ENV

對於Bash來講,登陸shell(包括交互式登陸shell和使用「–login」選項的非交互shell),它會首先讀取和執行/etc/profile全局配置文件中的命令,而後依次查找~/.bash_profile、~/.bash_login 和 ~/.profile這三個配置文件,讀取和執行這三個中的第一個存在且可讀的文件中命令。除非被「–noprofile」選項禁止了。
非登陸shell裏,只讀取 ~/.bashrc (和 /etc/bash.bashrc、/etc/bashrc )文件,不一樣的發行版裏面可能有所不一樣,如RHEL6.3中非登陸shell僅執行了「~/.bashrc」文件(沒有執行/etc/bashrc),而KUbuntu10.04中卻依次執行了/etc/bash.bashrc 和 ~/.bashrc 文件。
對於這些規則,能夠直接在相應的配置文件中加一些echo命令來驗證其真實性。

因此 ssh 的時候會載入「~/.bash_profile」,

讓咱們再來看一下 .bash_profile 的內容:

cat ~/.bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc  # .bashrc 默認又會加載 /etc/bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

export PATH

看到這兒,我想你大概明白了爲何 .bashrc 裏有輸出流會致使 rsync 傳輸失敗了。

三、解決方案

既然都找到緣由了,那怎麼解決呢?

有同窗會問,我原本就是要讓用戶登陸的時候有登陸提示的呀?如今爲了 rsync 豈不是得禁掉?

那這裏就又引入了另外一個知識點:交互式shell和飛交互式shell、登陸shell和非登陸shell 的區別

什麼是交互式的呢?就像咱們平時經過命令行作事同樣,你敲一個命令,終端解析執行完以後給你一個結果,這樣一種交互式的形式。那麼,平時咱們接觸的Shell基本上都是交互式的,如gnome-terminal打開一個Shell以及經過Ctrl+alt+1等切換過去的文本終端。交互式Shell下, "echo $-"返回的字符串中包含i,不然不包含。也能夠經過在bash後面加-i參數打開一個交互式的Shell,具體能夠看man bash。bash後面加-c參數執行命令打開的是非交互式Shell,能夠用如下命令驗證:

bash -c 'echo $-'  # 返回hBc

解釋完交互式以後,繼續解析本小節後半部分中的登陸二字。登陸Shell其實很好理解,就是咱們平時經過用戶名/密碼才能登陸的Shell,最典型的就是用Ctrl+alt+1切換過去的文本終端。如何區分登陸Shell和非登陸Shell呢,能夠經過查看$0的值,登陸Shell返回-bash,而非登陸Shell返回的是bash。平時gnome-terminal打開的Shell就是非登陸Shell。也能夠經過在bash後面加--login參數打開一個登陸Shell。

回到本小節開頭的問題,我們能夠用以下方式去區分一個命令是處於登陸shell仍是非登陸shell:

[[ $- == *i* ]] && echo 'This is interactive shell.'

其中,$-中包含i意思是指當前的Shell是一個交互式(interactive)的Shell。

針對文中開頭的問題,我們加上如上的判斷,就能夠作到登陸的時候有提示,同時 rsync 也不會報錯了,可謂一箭雙鵰。

好了,今天的內容就到這兒了,其實 shell 做爲一門古老的編程語言以及隨着 linux 版本的多樣化發展、不斷的演變,」坑「不少,卻也值得讓人細細探索~

四、Refer:

[1] 什麼是交互式登陸 Shell [[ $- != *i* ]] && return

http://kodango.com/what-is-interactive-and-login-shell

[2] linux下的bash與sh 詳解以及例子

http://bbs.chinaunix.net/thread-1068678-1-1.html

[3] 登陸shell,交互式非登陸shell,非交互式shell

http://bbs.chinaunix.net/thread-2018339-1-1.html

[4] Shell 默認選項 himBH 的解釋

http://kodango.com/explain-shell-default-options

[5] 交互式SHELL和非交互式SHELL、登陸SHELL和非登陸SHELL的區別

http://smilejay.com/2012/10/interactive-shell-login-shell/

相關文章
相關標籤/搜索