天天看點

Python實作linux/windows通用批量‘指令/上傳/下載下傳’小工具

這陣子一直在學python,碰巧最近想把線上伺服器環境做一些規範化/統一化,于是便萌生了用python寫一個小工具的沖動。就功能方面來說,基本上是在“重複造輪子”吧,但是當我用這小工具完成了30多台伺服器從系統層面到應用層面的一些規範化工作之後,覺得效果還不算那麼low(高手可忽略這句話~~),這才敢拿出來跟小夥伴們分享一下。

(注:筆者所用為python版本為3.5,其他版本未經測試~~)

其實很簡單,就"一個腳本"+"server資訊檔案"實作如題目所述的功能,能夠像使用linux系統指令一樣拿來即用。來看一些基本的使用截圖吧

幫助資訊:

<a href="http://s3.51cto.com/wyfs02/M02/85/35/wKioL1ecuRjhZOcbAAB726fGrJ8967.png" target="_blank"></a>

批量執行遠端指令:

<a href="http://s3.51cto.com/wyfs02/M00/85/35/wKiom1ecuU3DWCOkAAAcECLpQRM543.png" target="_blank"></a>

上傳目錄:

<a href="http://s1.51cto.com/wyfs02/M00/85/35/wKioL1ecuWWz6RXOAABgZwSs0fs274.png" target="_blank"></a>

下載下傳單個檔案:

<a href="http://s4.51cto.com/wyfs02/M01/85/35/wKioL1ectNCy078nAAAZGxGlF6s564.png" target="_blank"></a>

下載下傳目錄:

<a href="http://s2.51cto.com/wyfs02/M01/85/36/wKiom1ecwVOyS14CAACGrT3hr7w463.png" target="_blank"></a>

接下來直接看代碼吧(我的老(lan)習慣,代碼裡注釋還算詳細,是以我就懶得再解釋那麼多喽)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

<code>#!/bin/env python3</code>

<code># coding:utf-8</code>

<code>"""</code>

<code>by ljk 20160704</code>

<code>from</code> <code>paramiko </code><code>import</code> <code>SSHClient, AutoAddPolicy</code>

<code>from</code> <code>os </code><code>import</code> <code>path, walk, makedirs</code>

<code>from</code> <code>re </code><code>import</code> <code>split, match, search</code>

<code>from</code> <code>sys </code><code>import</code> <code>exit</code>

<code>from</code> <code>argparse </code><code>import</code> <code>ArgumentParser, RawTextHelpFormatter</code>

<code># ----------</code>

<code># get_args()函數通過argparse子產品的ArgumentParser類來生成幫助資訊并擷取指令行參數</code>

<code># 生成一個全局變量字典對象args,儲存處理過的指令行參數</code>

<code>def</code> <code>get_args():</code>

<code>    </code><code>"""執行個體化類,formatter_class參數允許help資訊以自定義的格式顯示"""</code>

<code>    </code><code>parser </code><code>=</code> <code>ArgumentParser(description</code><code>=</code><code>"This is a tool for execute command(s) on remote server(s) or get/put file(s) from/to the remote server(s)\nNotice: please always use '/' as path separater!!!"</code><code>,formatter_class </code><code>=</code><code>RawTextHelpFormatter,epilog</code><code>=</code><code>"Notice:\n  If any options use more than once,the last one will overwrite the previous"</code><code>)</code>

<code>    </code><code>parser.add_argument(</code><code>'-u'</code><code>,metavar</code><code>=</code><code>'USER'</code><code>,dest</code><code>=</code><code>'user'</code><code>,</code><code>help</code><code>=</code><code>"remote username"</code><code>,required</code><code>=</code><code>True</code><code>)</code>

<code>    </code><code>parser.add_argument(</code><code>'-p'</code><code>,metavar</code><code>=</code><code>'PASSWORD'</code><code>,dest</code><code>=</code><code>'passwd'</code><code>,</code><code>help</code><code>=</code><code>" user's password"</code><code>)</code>

<code>    </code><code>parser.add_argument(</code><code>'--pkey'</code><code>,nargs</code><code>=</code><code>'?'</code><code>,metavar</code><code>=</code><code>'PRIVATE KEY'</code><code>,dest</code><code>=</code><code>'pkey'</code><code>,</code><code>help</code><code>=</code><code>"local private key,if value not followed by this option,the default is: ~/.ssh/id_rsa"</code><code>,default</code><code>=</code><code>None</code><code>,const</code><code>=</code><code>'%s/.ssh/id_rsa'</code> <code>%</code> <code>path.expanduser(</code><code>'~'</code><code>))</code>

<code>    </code><code>parser.add_argument(</code><code>'--server'</code><code>, metavar</code><code>=</code><code>'SERVER_INFO_FILE'</code><code>, </code><code>help</code><code>=</code><code>"file include the remote server's information\nwith the format of 'name-ip:port',such as 'web1-192.168.1.100:22',one sever one line"</code><code>, required</code><code>=</code><code>True</code><code>)</code>

<code>    </code><code>remote_command </code><code>=</code> <code>parser.add_argument_group(</code><code>'remote command'</code><code>,</code><code>'options for running remote command'</code><code>)</code>

<code>    </code><code>remote_command.add_argument(</code><code>'--cmd'</code><code>,metavar</code><code>=</code><code>'“COMMAND”'</code><code>,dest</code><code>=</code><code>'cmd'</code><code>,</code><code>help</code><code>=</code><code>"command run on remote server,multiple commands sperate by ';'"</code><code>)</code>

<code>    </code><code>sftp </code><code>=</code> <code>parser.add_argument_group(</code><code>'sftp'</code><code>,</code><code>'options for running sftp'</code><code>)</code>

<code>    </code><code>sftp.add_argument(</code><code>'--put'</code><code>,metavar</code><code>=</code><code>'',</code><code>help</code><code>=</code><code>"transfer from local to remote"</code><code>,nargs</code><code>=</code><code>2</code><code>)</code>

<code>    </code><code>sftp.add_argument(</code><code>'--get'</code><code>,metavar</code><code>=</code><code>'',</code><code>help</code><code>=</code><code>"transfer from remote to local"</code><code>,nargs</code><code>=</code><code>2</code><code>)</code>

<code>    </code><code># 全局字典 鍵(add_argument()中的dest):值(使用者輸入)</code>

<code>    </code><code># vars将Namespace object轉換成dict object</code>

<code>    </code><code>global</code> <code>args</code>

<code>    </code><code>args </code><code>=</code> <code>vars</code><code>(parser.parse_args())</code>

<code>    </code><code># 判斷 --cmd  --put  --get 三個參數的唯一性</code>

<code>    </code><code># 清除掉args字典中值為None的項.argparse預設給不出現的值指派None</code>

<code>    </code><code>n </code><code>=</code> <code>0</code>

<code>    </code><code>for</code> <code>i </code><code>in</code> <code>(</code><code>'cmd'</code><code>,</code><code>'put'</code><code>,</code><code>'get'</code><code>):</code>

<code>        </code><code>if</code> <code>i </code><code>in</code> <code>args:</code>

<code>            </code><code>if</code> <code>args[i] </code><code>is</code> <code>None</code><code>:</code>

<code>                </code><code>del</code> <code>args[i]</code>

<code>            </code><code>else</code><code>:</code>

<code>                </code><code>n</code><code>+</code><code>=</code><code>1</code>

<code>    </code><code>if</code> <code>n &gt; </code><code>1</code><code>:</code>

<code>        </code><code>print</code><code>(</code><code>'\n  Only one of the "--cmd --put --get" can be used!'</code><code>)</code>

<code>        </code><code>exit(</code><code>10</code><code>)</code>

<code>def</code> <code>get_ip_port(fname):</code>

<code>    </code><code>"""從制定檔案(特定格式)中,取得主機名/主機ip/端口"""</code>

<code>    </code><code>try</code><code>:</code>

<code>        </code><code>fobj </code><code>=</code> <code>open</code><code>(fname,</code><code>'r'</code><code>)</code>

<code>    </code><code>except</code> <code>Exception as err:</code>

<code>        </code><code>print</code><code>(err)</code>

<code>    </code><code>for</code> <code>line </code><code>in</code> <code>fobj.readlines():</code>

<code>        </code><code>if</code> <code>line !</code><code>=</code> <code>'\n'</code> <code>and</code> <code>not</code>   <code>match(</code><code>'#'</code><code>,line):    # 過濾空行和注釋行</code>

<code>            </code><code>list_tmp </code><code>=</code>   <code>split(</code><code>'[-:]'</code><code>,line)</code>

<code>            </code><code>server_name </code><code>=</code> <code>list_tmp[</code><code>0</code><code>]</code>

<code>            </code><code>server_ip </code><code>=</code> <code>list_tmp[</code><code>1</code><code>]</code>

<code>            </code><code>port </code><code>=</code> <code>int</code><code>(list_tmp[</code><code>2</code><code>])</code>

<code>            </code><code>yield</code> <code>(server_name,server_ip,port)</code>

<code>def</code> <code>create_sshclient(server_ip,port):</code>

<code>    </code><code>"""根據指令行提供的參數,建立到遠端server的ssh連結.這裡本在run_command()函數内部。</code>

<code>    </code><code>摘出來的目的是為了讓sftp功能也通過sshclient對象來建立sftp對象,因為初步觀察t.connect()方法在使用key時有問題"""</code>

<code>    </code><code>global</code> <code>client</code>

<code>    </code><code>client </code><code>=</code> <code>SSHClient()</code>

<code>    </code><code>client.set_missing_host_key_policy(AutoAddPolicy())</code>

<code>        </code><code>client.connect(server_ip,port</code><code>=</code><code>port,username</code><code>=</code><code>args[</code><code>'user'</code><code>],password</code><code>=</code><code>args[</code><code>'passwd'</code><code>],key_filename</code><code>=</code><code>args[</code><code>'pkey'</code><code>])</code>

<code>    </code><code>except</code> <code>Exception as err:    </code><code># 有異常,列印異常,并傳回'error'</code>

<code>        </code><code>print</code><code>(</code><code>'{}----{} error: {}'</code><code>.</code><code>format</code><code>(</code><code>' '</code><code>*</code><code>4</code><code>,server_ip,err))</code>

<code>        </code><code>return</code> <code>'error'</code>

<code># run_command()執行遠端指令</code>

<code>def</code> <code>run_command():</code>

<code>    </code><code>"""執行遠端指令的主函數"""</code>

<code>    </code><code># stdout 假如通過分号提供單行的多條指令,所有指令的輸出(在linux終端會輸出的内容)都會存儲于stdout</code>

<code>    </code><code># 據觀察,下面三個變量的特點是無論"如何引用過一次"之後,其内容就會清空</code>

<code>    </code><code># 有readlines()的地方都是流,用過之後就沒有了</code>

<code>    </code><code>stdin,stdout,stderr </code><code>=</code> <code>client.exec_command(args[</code><code>'cmd'</code><code>])</code>

<code>    </code><code>copy_out,copy_err </code><code>=</code> <code>stdout.readlines(),stderr.readlines()</code>

<code>    </code><code>if</code> <code>len</code><code>(copy_out) !</code><code>=</code> <code>0</code><code>:</code>

<code>        </code><code>print</code><code>(</code><code>'%s----result:'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>8</code><code>))</code>

<code>        </code><code>for</code> <code>i </code><code>in</code> <code>copy_out:</code>

<code>            </code><code>print</code><code>(</code><code>'%s%s'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>12</code><code>,i),end</code><code>=</code><code>'')</code>

<code>    </code><code>elif</code> <code>len</code><code>(copy_err) !</code><code>=</code> <code>0</code><code>:</code>

<code>        </code><code>print</code><code>(</code><code>'%s----error:'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>8</code><code>))</code>

<code>        </code><code>for</code> <code>i </code><code>in</code> <code>copy_err:</code>

<code>    </code><code>client.close()</code>

<code># sftp_transfer() 遠端傳輸檔案的主函數</code>

<code>def</code> <code>sftp_transfer(source_path,destination_path,method):</code>

<code>    </code><code>"""檔案傳輸的 主函數"""</code>

<code>    </code><code>sftp </code><code>=</code> <code>client.open_sftp()</code>

<code>    </code><code># 下面定義sftp_transfer()函數所需的一些子函數</code>

<code>    </code><code>def</code> <code>str_to_raw(s):</code>

<code>        </code><code>"""</code>

<code>        </code><code>!!此函數暫未使用,參數中的目錄強制使用'/'作為分隔符!!</code>

<code>        </code><code>借用網友的代碼,将會被反斜杠轉義的字元做轉換.将\轉換為\\,這裡的轉換還不全,比如對'\123'這樣的還無法轉換成'\\123'</code>

<code>        </code><code>raw_map </code><code>=</code> <code>{</code><code>8</code><code>:r</code><code>'\b'</code><code>, </code><code>7</code><code>:r</code><code>'\a'</code><code>, </code><code>12</code><code>:r</code><code>'\f'</code><code>, </code><code>10</code><code>:r</code><code>'\n'</code><code>, </code><code>13</code><code>:r</code><code>'\r'</code><code>, </code><code>9</code><code>:r</code><code>'\t'</code><code>, </code><code>11</code><code>:r</code><code>'\v'</code><code>}</code>

<code>        </code><code>return</code> <code>r''.join(i </code><code>if</code> <code>ord</code><code>(i) &gt; </code><code>32</code> <code>else</code> <code>raw_map.get(</code><code>ord</code><code>(i), i) </code><code>for</code> <code>i </code><code>in</code> <code>s)</code>

<code>    </code><code>def</code> <code>process_arg_dir(src,dst):</code>

<code>        </code><code>"""處理目錄時,自動檢查使用者輸入,并在s_path和d_path後面都加上/"""</code>

<code>        </code><code>if</code> <code>not</code> <code>src.endswith(</code><code>'/'</code><code>):</code>

<code>            </code><code>src </code><code>=</code> <code>src </code><code>+</code> <code>'/'</code>

<code>        </code><code>if</code> <code>not</code> <code>dst.endswith(</code><code>'/'</code><code>):</code>

<code>            </code><code>dst </code><code>=</code> <code>dst </code><code>+</code> <code>'/'</code>

<code>        </code><code>return</code> <code>src,dst</code>

<code>    </code><code>def</code> <code>sftp_put(src, dst, space):</code>

<code>        </code><code>"""封裝sftp.put"""</code>

<code>        </code><code>try</code><code>:</code>

<code>            </code><code>sftp.put(src, dst)</code>

<code>            </code><code>print</code><code>(</code><code>'%s%s'</code> <code>%</code> <code>(</code><code>' '</code> <code>*</code> <code>space, src))</code>

<code>        </code><code>except</code> <code>Exception as err:</code>

<code>            </code><code>print</code><code>(</code><code>'%s----Uploading %s Failed'</code> <code>%</code> <code>(</code><code>' '</code> <code>*</code> <code>space, src))</code>

<code>            </code><code>print</code><code>(</code><code>'{}----{}'</code><code>.</code><code>format</code><code>(</code><code>' '</code> <code>*</code> <code>space, err))</code>

<code>            </code><code>exit(</code><code>10</code><code>)</code>

<code>    </code><code>def</code> <code>sftp_get(src, dst, space):</code>

<code>        </code><code>"""封裝sftp.get"""</code>

<code>            </code><code>sftp.get(src, dst)</code>

<code>            </code><code>print</code><code>(</code><code>'%s----Downloading %s Failed'</code> <code>%</code> <code>(</code><code>' '</code> <code>*</code> <code>space, src))</code>

<code>    </code><code>def</code> <code>sftp_transfer_rcmd(cmd</code><code>=</code><code>None</code><code>,space</code><code>=</code><code>None</code><code>):</code>

<code>        </code><code>"""在sftp_transfer()函數内部執行一些遠端指令來輔助其完成功能"""</code>

<code>        </code><code>stdin,stdout,stderr </code><code>=</code> <code>client.exec_command(cmd)</code>

<code>        </code><code>copy_out, copy_err </code><code>=</code> <code>stdout.readlines(), stderr.readlines()</code>

<code>        </code><code>if</code> <code>len</code><code>(copy_err) !</code><code>=</code> <code>0</code><code>:</code>

<code>            </code><code>for</code> <code>i </code><code>in</code> <code>copy_err:</code>

<code>                </code><code>print</code><code>(</code><code>'%s----%s'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>space,i),end</code><code>=</code><code>'')</code>

<code>        </code><code>else</code><code>:</code>

<code>            </code><code>return</code> <code>copy_out</code>

<code>    </code><code>def</code> <code>check_remote_path(r_path):</code>

<code>        </code><code>"""通過client對象在遠端linux執行指令,來判斷遠端路徑是否存在,是檔案還是目錄"""</code>

<code>        </code><code>check_cmd </code><code>=</code> <code>'if [ -e {0} ];then if [ -d {0} ];then echo directory;elif [ -f {0} ];then echo file;fi;else echo no_exist;fi'</code><code>.</code><code>format</code><code>(r_path)</code>

<code>        </code><code># check_cmd指令會有三種‘正常輸出’directory  file  no_exist</code>

<code>        </code><code>check_result </code><code>=</code> <code>sftp_transfer_rcmd(cmd</code><code>=</code><code>check_cmd)[</code><code>0</code><code>].strip(</code><code>'\n'</code><code>)</code>

<code>        </code><code>if</code> <code>check_result </code><code>=</code><code>=</code> <code>'directory'</code><code>:</code>

<code>            </code><code>return</code> <code>'directory'</code>

<code>        </code><code>elif</code> <code>check_result </code><code>=</code><code>=</code> <code>'file'</code><code>:</code>

<code>            </code><code>return</code> <code>'file'</code>

<code>            </code><code>return</code> <code>'no_exist'</code>

<code>    </code><code># 子函數定義完畢</code>

<code>    </code><code># 上傳邏輯</code>

<code>    </code><code>if</code> <code>method </code><code>=</code><code>=</code> <code>'put'</code><code>:</code>

<code>        </code><code>print</code><code>(</code><code>'%s----Uploading %s TO %s'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>4</code><code>,source_path,destination_path))</code>

<code>        </code><code>if</code>  <code>path.isfile(source_path):    </code><code># 判斷是檔案</code>

<code>            </code><code>if</code> <code>destination_path.endswith(</code><code>'/'</code><code>):</code>

<code>                </code><code>"""</code>

<code>                </code><code>put和get方法預設隻針對檔案,且都必須跟上檔案名,否則會報錯.</code>

<code>                </code><code>這裡多加一層判斷實作了目标路徑可以不加檔案名</code>

<code>                </code><code>sftp_transfer_rcmd(cmd</code><code>=</code><code>'mkdir -p {}'</code><code>.</code><code>format</code><code>(destination_path),space</code><code>=</code><code>4</code><code>)    </code><code># 若目标路徑不存在,則建立</code>

<code>                </code><code>destination_path </code><code>=</code> <code>destination_path </code><code>+</code>  <code>path.basename(source_path)</code>

<code>                </code><code>sftp_put(source_path, destination_path, </code><code>8</code><code>)</code>

<code>                </code><code>sftp_transfer_rcmd(cmd</code><code>=</code><code>'mkdir -p {}'</code><code>.</code><code>format</code><code>( path.dirname(destination_path)), space</code><code>=</code><code>4</code><code>)</code>

<code>        </code><code>elif</code>  <code>path.isdir(source_path):   </code><code># 判斷是目錄</code>

<code>            </code><code>source_path,destination_path </code><code>=</code> <code>process_arg_dir(source_path,destination_path)</code>

<code>            </code><code>for</code> <code>root, dirs, files </code><code>in</code>  <code>walk(source_path):</code>

<code>                </code><code>"""通過 os.walk()函數取得目錄下的所有檔案,此函數預設包含 . ..的檔案/目錄,需要去掉"""</code>

<code>                </code><code>for</code> <code>file_name </code><code>in</code> <code>files:</code>

<code>                    </code><code>s_file </code><code>=</code>  <code>path.join(root,file_name).replace(</code><code>'\\','</code><code>/</code><code>')    </code><code># 逐級取得每個sftp client端檔案的全路徑,并将路徑中的\換成/</code>

<code>                    </code><code>if</code> <code>not</code>   <code>search(</code><code>'.*/\..*'</code><code>, s_file):</code>

<code>                        </code><code>"""過濾掉路徑中包含以.開頭的目錄或檔案"""</code>

<code>                        </code><code>d_file </code><code>=</code> <code>s_file.replace(source_path,destination_path,</code><code>1</code><code>)    </code><code># 由local_file取得每個遠端檔案的全路徑</code>

<code>                        </code><code>d_path </code><code>=</code>  <code>path.dirname(d_file)</code>

<code>                        </code><code>check_remote_path_result </code><code>=</code> <code>check_remote_path(d_path)</code>

<code>                        </code><code>if</code> <code>check_remote_path_result </code><code>=</code><code>=</code> <code>'directory'</code><code>:</code>

<code>                            </code><code>sftp_put(s_file, d_file, </code><code>12</code><code>)  </code><code># 目标目錄存在,直接上傳</code>

<code>                        </code><code>elif</code> <code>check_remote_path_result </code><code>=</code><code>=</code> <code>'no_exist'</code><code>:</code>

<code>                            </code><code>print</code><code>(</code><code>'%s----Create Remote Dir: %s'</code> <code>%</code> <code>(</code><code>' '</code> <code>*</code> <code>8</code><code>,  path.dirname(d_file)))</code>

<code>                            </code><code>sftp_transfer_rcmd(cmd</code><code>=</code><code>'mkdir -p {}'</code><code>.</code><code>format</code><code>(d_path))</code>

<code>                            </code><code>sftp_put(s_file, d_file, </code><code>12</code><code>)</code>

<code>                        </code><code>else</code><code>:</code>

<code>                            </code><code>print</code><code>(</code><code>'{}----the {} is file'</code><code>.</code><code>format</code><code>(</code><code>' '</code> <code>*</code> <code>8</code><code>, d_path))</code>

<code>                            </code><code>exit(</code><code>10</code><code>)</code>

<code>            </code><code>print</code><code>(</code><code>'%s%s is not exist'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>8</code><code>,source_path))</code>

<code>    </code><code># 下載下傳邏輯</code>

<code>    </code><code>elif</code> <code>method </code><code>=</code><code>=</code> <code>'get'</code><code>:</code>

<code>        </code><code>print</code><code>(</code><code>'%s----Downloading %s TO %s'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>4</code><code>, source_path, destination_path))</code>

<code>        </code><code>check_remote_path_result </code><code>=</code> <code>check_remote_path(source_path)</code>

<code>        </code><code>if</code> <code>check_remote_path_result </code><code>=</code><code>=</code> <code>'file'</code><code>:    </code><code># 判斷是檔案</code>

<code>            </code><code>if</code> <code>path.isfile(destination_path):</code>

<code>                </code><code>sftp_get(source_path, destination_path, </code><code>8</code><code>)</code>

<code>            </code><code>else</code><code>:    </code><code># 參數中的'目标路徑'為目錄或不存在</code>

<code>                </code><code>try</code><code>:</code>

<code>                    </code><code>makedirs(destination_path)</code>

<code>                    </code><code>sftp_get(source_path,  path.join(destination_path, path.basename(source_path)).replace(</code><code>'\\','</code><code>/</code><code>'), </code><code>8</code><code>)</code>

<code>                </code><code>except</code> <code>Exception as err:</code>

<code>                    </code><code>print</code><code>(</code><code>'%s----Create %s error'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>4</code><code>,destination_path))</code>

<code>                    </code><code>print</code><code>(</code><code>'{}{}'</code><code>.</code><code>format</code><code>(</code><code>' '</code><code>*</code><code>8</code><code>,err))</code>

<code>                    </code><code>exit(</code><code>10</code><code>)</code>

<code>            </code><code>sftp_get(source_path,destination_path,</code><code>8</code><code>)</code>

<code>        </code><code>elif</code> <code>check_remote_path_result </code><code>=</code><code>=</code> <code>'directory'</code><code>:    </code><code># 判斷是目錄</code>

<code>            </code><code>source_path, destination_path </code><code>=</code> <code>process_arg_dir(source_path, destination_path)</code>

<code>            </code><code>def</code> <code>process_sftp_dir(path_name):</code>

<code>                </code><code>此函數遞歸處理sftp server端的目錄和檔案,并在client端建立所有不存在的目錄,然後針對每個檔案在兩端的全路徑執行get操作.</code>

<code>                </code><code>path_name第一次的引用值應該是source_path的值</code>

<code>                </code><code>d_path </code><code>=</code> <code>path_name.replace(source_path,destination_path,</code><code>1</code><code>)</code>

<code>                </code><code>if</code> <code>not</code>  <code>path.exists(d_path):    </code><code># 若目标目錄不存在則建立</code>

<code>                    </code><code>print</code><code>(</code><code>'%s----Create Local Dir: %s'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>8</code><code>,d_path))</code>

<code>                    </code><code>try</code><code>:</code>

<code>                         </code><code>makedirs(d_path)    </code><code># 遞歸建立不存在的目錄</code>

<code>                    </code><code>except</code> <code>Exception as err:</code>

<code>                        </code><code>print</code><code>(</code><code>'%s----Create %s Failed'</code> <code>%</code> <code>(</code><code>' '</code><code>*</code><code>8</code><code>,d_path))</code>

<code>                        </code><code>print</code><code>(</code><code>'{}----{}'</code><code>.</code><code>format</code><code>(</code><code>' '</code><code>*</code><code>8</code><code>,err))</code>

<code>                        </code><code>exit(</code><code>10</code><code>)</code>

<code>                </code><code>for</code> <code>name </code><code>in</code> <code>(i </code><code>for</code> <code>i </code><code>in</code> <code>sftp.listdir(path</code><code>=</code><code>path_name) </code><code>if</code> <code>not</code> <code>i.startswith(</code><code>'.'</code><code>)):</code>

<code>                    </code><code>"""去掉以.開頭的檔案或目錄"""</code>

<code>                    </code><code>s_file </code><code>=</code>  <code>path.join(path_name,name).replace(</code><code>'\\','</code><code>/</code><code>')    # 在win環境下組合路徑所用的'</code><code>\\</code><code>'換成'</code><code>/</code><code>'</code>

<code>                    </code><code>d_file </code><code>=</code> <code>s_file.replace(source_path,destination_path,</code><code>1</code><code>)    </code><code># 目标端全路徑</code>

<code>                    </code><code>chk_r_path_result </code><code>=</code> <code>check_remote_path(s_file)</code>

<code>                    </code><code>if</code> <code>chk_r_path_result </code><code>=</code><code>=</code> <code>'file'</code><code>:    </code><code># 檔案</code>

<code>                        </code><code>sftp_get(s_file,d_file,</code><code>12</code><code>)</code>

<code>                    </code><code>elif</code> <code>chk_r_path_result </code><code>=</code><code>=</code> <code>'directory'</code><code>:    </code><code># 目錄</code>

<code>                        </code><code>process_sftp_dir(s_file)    </code><code># 遞歸調用本身</code>

<code>            </code><code>process_sftp_dir(source_path)</code>

<code>            </code><code>print</code><code>(</code><code>'%s%s is not exist'</code> <code>%</code> <code>(</code><code>' '</code> <code>*</code> <code>8</code><code>, source_path))</code>

<code>if</code> <code>__name__ </code><code>=</code><code>=</code> <code>"__main__"</code><code>:</code>

<code>        </code><code>get_args()</code>

<code>        </code><code>for</code> <code>server_name,server_ip,port </code><code>in</code> <code>get_ip_port(args[</code><code>'server'</code><code>]):    </code><code>#循環處理每個主機</code>

<code>            </code><code>print</code><code>(</code><code>'\n--------%s'</code> <code>%</code> <code>server_name)</code>

<code>            </code><code>if</code> <code>create_sshclient(server_ip,port) </code><code>=</code><code>=</code> <code>'error'</code><code>:</code>

<code>                </code><code>continue</code>

<code>            </code><code># 差別處理 --cmd --put --get參數</code>

<code>            </code><code>if</code> <code>'cmd'</code> <code>in</code> <code>args:</code>

<code>                </code><code>run_command()</code>

<code>            </code><code>elif</code> <code>'put'</code> <code>in</code> <code>args:</code>

<code>                </code><code>sftp_transfer(args[</code><code>'put'</code><code>][</code><code>0</code><code>],args[</code><code>'put'</code><code>][</code><code>1</code><code>],</code><code>'put'</code><code>)</code>

<code>            </code><code>elif</code> <code>'get'</code> <code>in</code> <code>args:</code>

<code>                </code><code>sftp_transfer(args[</code><code>'get'</code><code>][</code><code>0</code><code>],args[</code><code>'get'</code><code>][</code><code>1</code><code>],</code><code>'get'</code><code>)</code>

<code>    </code><code>except</code> <code>KeyboardInterrupt:</code>

<code>        </code><code>print</code><code>(</code><code>'\n-----bye-----'</code><code>)</code>

其實之是以想造這個“簡陋”的輪子,一方面能鍛煉python coding,另一方面當時确實有這麼一個需求。而且用自己的工具完成工作也是小有成就的(請勿拍磚~)。

另外,在使用paramiko子產品的過程中,又促使我深入的了解了一些ssh登陸的詳細過程。是以作為一枚運維,現在開始深刻的了解到“運維”和“開發”這倆概念之間的互相促進。

     本文轉自kai404 51CTO部落格,原文連結:http://blog.51cto.com/kaifly/1832200,如需轉載請自行聯系原作者