學習SAS的時候,input和put函數(shù)是非常常用的兩個功能,而且初學者還容易搞混或者分不清楚。
成都創(chuàng)新互聯(lián)主營永寧網(wǎng)站建設的網(wǎng)絡公司,主營網(wǎng)站建設方案,app軟件定制開發(fā),永寧h5微信小程序定制開發(fā)搭建,永寧網(wǎng)站營銷推廣歡迎永寧等地區(qū)企業(yè)咨詢
上次我講了input中加問號的作用,今天我就再細講一下這兩個函數(shù),以及我發(fā)現(xiàn)的一個錯誤。
你只要記?。?/p>
input是將SAS變量值從字符型轉換成數(shù)值型;而put是將數(shù)值型轉換成字符型 就夠了。
data test;
a=1;
b="sas";
c="3";
run;
sas變量類型就兩個,一個數(shù)值型,一個字符型??聪聢D
可以看到數(shù)值型是在方框的右邊;字符型是頂格靠近方框的左邊。
我們在將數(shù)值型變量轉換成字符型的時候,最好和strip函數(shù)一起使用,要不然的話轉換后值前后會有很多空格,而不是頂格輸出。就像下面的圖片那樣:
data test2;
set test;
d=put(a,best.);
run;
加上strip之后,顯示正常。
d=strip(put(a,best.));
當然put和input還能用于日期格式的轉換,后面專門寫一篇文章介紹SAS中的日期格式。這次就簡單舉個日子。
讀入數(shù)據(jù):
data dat;
input dat $19.;
cards;
2018-02-13
2021-5-18
;
run;
data dat2;
set dat;
dat2=input(dat,yymmdd10.);
run;
如果input和put一起用:
dat2=strip(put(input(dat,yymmdd10.),yymmdd10.));
可能你會覺得dat和dat2不是長得一樣嗎?有必要做這些轉換嗎?其實dat只是普通的字符串,而dat2在SAS看來卻是日期,相當于帶有日期format的字符串。
下面講一下我發(fā)現(xiàn)的一個錯誤:
有時候會遇到字符型也存在空格的情況,導致字符串拼接的時候達不到我們的要求,于是我也會用上strip,就像下面這樣。
e=strip(b)||strip(c);
這樣是不會報錯的,但是如果我想將c轉換成數(shù)值型,這時候問題就出現(xiàn)了。
d=strip(input(c,best.));
雖然顯示出來了,但是卻是字符型,而且報錯了
但是如果只是簡單的轉換成數(shù)值型就沒問題
d=input(c,best.);
用拼接符將變量d和b拼接,
f=strip(d)||strip(b);
還是報上面的錯,但是如果用catx函數(shù)連接,就不會報錯。所以我很喜歡用catx函數(shù)。
e=catx("-",d,b);
寫了這么多,就是想講大家在轉換字符型的數(shù)字的時候,這時候就不能strip和input一起用,可以先轉換成數(shù)值型再用strip函數(shù),分開處理,而且更不要想著把字符"sas"轉換成數(shù)值型,sas都懶得理你,字符型的數(shù)字還好說。
源程序:自己可以復制到SAS跑跑看。
data test2;
set test;
d=input(c,best.);
e=catx("-",d,b);
f=strip(d)||strip(b);
run;
日常工作中需要大量、頻繁地使用ssh到服務器查看、拉取相關的信息或者對服務器進行變更。目前公司大量使用的shell,但是隨著邏輯的復雜化、腳本管理的精細化,shell已經(jīng)不滿足日常需求,于是我嘗試整合工作中的需求,制作適合的工具。 由于管理制度的缺陷,我以工作流程為核心思考適合自己的運維方式,提升工作效率,把時間留給更有價值的事情。 完整代碼在最后,請大家參考。
生產(chǎn):4000+物理服務器,近 3000 臺虛擬機。
開發(fā)環(huán)境:python3.6、redhat7.9,除了paramiko為第三方模塊需要自己安裝,其他的直接import即可。
批量執(zhí)行操作是一把雙刃劍。批量執(zhí)行操作可以提升工作效率,但是隨之而來的風險不可忽略。
風險案例如下:
掛載很多數(shù)據(jù)盤,通常先格式化硬盤,再掛載數(shù)據(jù)盤,最后再寫入將開機掛載信息寫入/etc/fstab文件。在批量lsblk檢查硬盤信息的時候發(fā)現(xiàn)有的系統(tǒng)盤在/sda有的在/sdm,如果不事先檢查機器相關配置是否一致直接按照工作經(jīng)驗去執(zhí)行批量操作,會很容易造成個人難以承受的災難。
在執(zhí)行批量操作時按照慣例:格式化硬盤-掛載-開機掛載的順序去執(zhí)行,假設有的機器因為某些故障導致格式化硬盤沒法正確執(zhí)行。在處理這類問題的時候通常會先提取出失敗的ip,并再按照慣例執(zhí)行操作。運維人員會很容易忽略開機掛載的信息已經(jīng)寫過了,導致復寫(這都是血和淚的教訓)。
所以,為了避免故障,提升工作效率,我認為應當建立團隊在工作上的共識,應當遵守以下原則:
當然,代碼的規(guī)范也應當重視起來,不僅是為了便于審計,同時也需要便于溯源。我認為應當注意以下幾點:
1、ssh no existing session,sftp超時時間設置:
在代碼無錯的情況下大量ip出現(xiàn)No existing session,排查后定位在代碼的寫法上,下面是一個正確的示例。由于最開始沒考慮到ssh連接的幾種情況導致了重寫好幾遍。另外sftp的實例貌似不能直接設置連接超時時間,所以我采用了先建立ssh連接再打開sftp的方法。
2、sftp中的get()和put()方法僅能傳文件,不支持直接傳目錄:
不能直接傳目錄,那換個思路,遍歷路徑中的目錄和文件,先創(chuàng)建目錄再傳文件就能達到一樣的效果了。在paramiko的sftp中s方法可以獲取遠程路徑中的文件、目錄信息。那么我們可以寫一個遞歸來遍歷遠程路徑中的所有文件和目錄(傳入一個列表是為了接收遞歸返回的值)。
python自帶的os模塊中的os.walk()方法可以遍歷到本地路徑中的目錄和文件。
3、多線程多個ip使用s方法時無法并發(fā)。
改成多進程即可。
4、多個ip需要執(zhí)行相同命令或不同的命令。
由于是日常使用的場景不會很復雜,所以借鑒了ansible的playbook,讀取提前準備好的配置文件即可,然后再整合到之前定義的ssh函數(shù)中。
同時,我們還衍生出一個需求,既然都要讀取配置,那同樣也可以提前把ip地址準備在文件里。正好也能讀取我們返回的執(zhí)行程序的結果。
參數(shù)說明:
密碼認證:
公鑰認證:
可以配合 grep,awk 等命令精準過濾。
個人認為 Python 在初中級運維工作中的性質更像是工具,以提升工作效率、減少管理成本為主。可以從當前繁瑣的工作中解脫出來,去 探索 更有價值的事情。python 本質上并不會減少故障的產(chǎn)生,所以在不同的階段合理利用自身掌握的知識解決當前最重要的痛點,千萬不要本末倒置。
上篇文章簡單介紹了multiprocessing模塊,本文將要介紹進程之間的數(shù)據(jù)共享和信息傳遞的概念。
在多進程處理中,所有新創(chuàng)建的進程都會有這兩個特點:獨立運行,有自己的內(nèi)存空間。
我們來舉個例子展示一下:
這個程序的輸出結果是:
在上面的程序中我們嘗試在兩個地方打印全局列表result的內(nèi)容:
我們再用一張圖來幫助理解記憶不同進程間的數(shù)據(jù)關系:
如果程序需要在不同的進程之間共享一些數(shù)據(jù)的話,該怎么做呢?不用擔心,multiprocessing模塊提供了Array對象和Value對象,用來在進程之間共享數(shù)據(jù)。
所謂Array對象和Value對象分別是指從共享內(nèi)存中分配的ctypes數(shù)組和對象。我們直接來看一個例子,展示如何用Array對象和Value對象在進程之間共享數(shù)據(jù):
程序輸出的結果如下:
成功了!主程序和p1進程輸出了同樣的結果,說明程序中確實完成了不同進程間的數(shù)據(jù)共享。那么我們來詳細看一下上面的程序做了什么:
在主程序中我們首先創(chuàng)建了一個Array對象:
向這個對象輸入的第一個參數(shù)是數(shù)據(jù)類型:i表示整數(shù),d代表浮點數(shù)。第二個參數(shù)是數(shù)組的大小,在這個例子中我們創(chuàng)建了包含4個元素的數(shù)組。
類似的,我們創(chuàng)建了一個Value對象:
我們只對Value對象輸入了一個參數(shù),那就是數(shù)據(jù)類型,與上述的方法一致。當然,我們還可以對其指定一個初始值(比如10),就像這樣:
隨后,我們在創(chuàng)建進程對象時,將剛創(chuàng)建好的兩個對象:result和square_sum作為參數(shù)輸入給進程:
在函數(shù)中result元素通過索引進行數(shù)組賦值,square_sum通過 value 屬性進行賦值。
注意:為了完整打印result數(shù)組的結果,需要使用 result[:] 進行打印,而square_sum也需要使用 value 屬性進行打?。?/p>
每當python程序啟動時,同時也會啟動一個服務器進程。隨后,只要我們需要生成一個新進程,父進程就會連接到服務器并請求它派生一個新進程。這個服務器進程可以保存Python對象,并允許其他進程使用代理來操作它們。
multiprocessing模塊提供了能夠控制服務器進程的Manager類。所以,Manager類也提供了一種創(chuàng)建可以在不同流程之間共享的數(shù)據(jù)的方法。
服務器進程管理器比使用共享內(nèi)存對象更靈活,因為它們可以支持任意對象類型,如列表、字典、隊列、值、數(shù)組等。此外,單個管理器可以由網(wǎng)絡上不同計算機上的進程共享。
但是,服務器進程管理器的速度比使用共享內(nèi)存要慢。
讓我們來看一個例子:
這個程序的輸出結果是:
我們來理解一下這個程序做了什么:首先我們創(chuàng)建了一個manager對象
在with語句下的所有行,都是在manager對象的范圍內(nèi)的。接下來我們使用這個manager對象創(chuàng)建了列表(類似的,我們還可以用 manager.dict() 創(chuàng)建字典)。
最后我們創(chuàng)建了進程p1(用于在records列表中插入一條新的record)和p2(將records打印出來),并將records作為參數(shù)進行傳遞。
服務器進程的概念再次用下圖總結一下:
為了能使多個流程能夠正常工作,常常需要在它們之間進行一些通信,以便能夠劃分工作并匯總最后的結果。multiprocessing模塊支持進程之間的兩種通信通道:Queue和Pipe。
使用隊列來回處理多進程之間的通信是一種比較簡單的方法。任何Python對象都可以使用隊列進行傳遞。我們來看一個例子:
上面這個程序的輸出結果是:
我們來看一下上面這個程序到底做了什么。首先我們創(chuàng)建了一個Queue對象:
然后,將這個空的Queue對象輸入square_list函數(shù)。該函數(shù)會將列表中的數(shù)平方,再使用 put() 方法放入隊列中:
隨后使用 get() 方法,將q打印出來,直至q重新稱為一個空的Queue對象:
我們還是用一張圖來幫助理解記憶:
一個Pipe對象只能有兩個端點。因此,當進程只需要雙向通信時,它會比Queue對象更好用。
multiprocessing模塊提供了 Pipe() 函數(shù),該函數(shù)返回由管道連接的一對連接對象。 Pipe() 返回的兩個連接對象分別表示管道的兩端。每個連接對象都有 send() 和 recv() 方法。
我們來看一個例子:
上面這個程序的輸出結果是:
我們還是來看一下這個程序到底做了什么。首先創(chuàng)建了一個Pipe對象:
與上文說的一樣,該對象返回了一對管道兩端的兩個連接對象。然后使用 send() 方法和 recv() 方法進行信息的傳遞。就這么簡單。在上面的程序中,我們從一端向另一端發(fā)送一串消息。在另一端,我們收到消息,并在收到END消息時退出。
要注意的是,如果兩個進程(或線程)同時嘗試從管道的同一端讀取或寫入管道中的數(shù)據(jù),則管道中的數(shù)據(jù)可能會損壞。不過不同的進程同時使用管道的兩端是沒有問題的。還要注意,Queue對象在進程之間進行了適當?shù)耐剑鷥r是增加了計算復雜度。因此,Queue對象對于線程和進程是相對安全的。
最后我們還是用一張圖來示意:
Python的multiprocessing模塊還剩最后一篇文章:多進程的同步與池化
敬請期待啦!
Queue 叫隊列,是數(shù)據(jù)結構中的一種,基本上所有成熟的編程語言都內(nèi)置了對 Queue 的支持。
Python 中的 Queue 模塊實現(xiàn)了多生產(chǎn)者和多消費者模型,當需要在多線程編程中非常實用。而且該模塊中的 Queue 類實現(xiàn)了鎖原語,不需要再考慮多線程安全問題。
該模塊內(nèi)置了三種類型的 Queue,分別是 class queue.Queue(maxsize=0) , class queue.LifoQueue(maxsize=0) 和 class queue.PriorityQueue(maxsize=0) 。它們?nèi)齻€的區(qū)別僅僅是取出時的順序不一致而已。
Queue 是一個 FIFO 隊列,任務按照添加的順序被取出。
LifoQueue 是一個 LIFO 隊列,類似堆棧,后添加的任務先被取出。
PriorityQueue 是一個優(yōu)先級隊列,隊列里面的任務按照優(yōu)先級排序,優(yōu)先級高的先被取出。
如你所見,就是上面所說的三種不同類型的內(nèi)置隊列,其中 maxsize 是個整數(shù),用于設置可以放入隊列中的任務數(shù)的上限。當達到這個大小的時候,插入操作將阻塞至隊列中的任務被消費掉。如果 maxsize 小于等于零,則隊列尺寸為無限大。
向隊列中添加任務,直接調用 put() 函數(shù)即可
put() 函數(shù)完整的函數(shù)簽名如下 Queue.put(item, block=True, timeout=None) ,如你所見,該函數(shù)有兩個可選參數(shù)。
默認情況下,在隊列滿時,該函數(shù)會一直阻塞,直到隊列中有空余的位置可以添加任務為止。如果 timeout 是正數(shù),則最多阻塞 timeout 秒,如果這段時間內(nèi)還沒有空余的位置出來,則會引發(fā) Full 異常。
當 block 為 false 時,timeout 參數(shù)將失效。同時如果隊列中沒有空余的位置可添加任務則會引發(fā) Full 異常,否則會直接把任務放入隊列并返回,不會阻塞。
另外,還可以通過 Queue.put_nowait(item) 來添加任務,相當于 Queue.put(item, False) ,不再贅述。同樣,在隊列滿時,該操作會引發(fā) Full 異常。
從隊列中獲取任務,直接調用 get() 函數(shù)即可。
與 put() 函數(shù)一樣, get() 函數(shù)也有兩個可選參數(shù),完整簽名如下 Queue.get(block=True, timeout=None) 。
默認情況下,當隊列空時調用該函數(shù)會一直阻塞,直到隊列中有任務可獲取為止。如果 timeout 是正數(shù),則最多阻塞 timeout 秒,如果這段時間內(nèi)還沒有任務可獲取,則會引發(fā) Empty 異常。
當 block 為 false 時,timeout 參數(shù)將失效。同時如果隊列中沒有任務可獲取則會立刻引發(fā) Empty 異常,否則會直接獲取一個任務并返回,不會阻塞。
另外,還可以通過 Queue.get_nowait() 來獲取任務,相當于 Queue.get(False) ,不再贅述。同樣,在隊列為空時,該操作會引發(fā) Empty 異常。
Queue.qsize() 函數(shù)返回隊列的大小。注意這個大小不是精確的,qsize() 0 不保證后續(xù)的 get() 不被阻塞,同樣 qsize() maxsize 也不保證 put() 不被阻塞。
如果隊列為空,返回 True ,否則返回 False 。如果 empty() 返回 True ,不保證后續(xù)調用的 put() 不被阻塞。類似的,如果 empty() 返回 False ,也不保證后續(xù)調用的 get() 不被阻塞。
如果隊列是滿的返回 True ,否則返回 False 。如果 full() 返回 True 不保證后續(xù)調用的 get() 不被阻塞。類似的,如果 full() 返回 False 也不保證后續(xù)調用的 put() 不被阻塞。
queue.Queue() 是 FIFO 隊列,出隊順序跟入隊順序是一致的。
queue.LifoQueue() 是 LIFO 隊列,出隊順序跟入隊順序是完全相反的,類似于棧。
優(yōu)先級隊列中的任務順序跟放入時的順序是無關的,而是按照任務的大小來排序,最小值先被取出。那任務比較大小的規(guī)則是怎么樣的呢。
注意,因為列表的比較對規(guī)則是按照下標順序來比較的,所以在沒有比較出大小之前 ,隊列中所有列表對應下標位置的元素類型要一致。
好比 [2,1] 和 ["1","b"] 因為第一個位置的元素類型不一樣,所以是沒有辦法比較大小的,所以也就放入不了優(yōu)先級隊列。
然而對于 [2,1] 和 [1,"b"] 來說即使第二個元素的類型不一致也是可以放入優(yōu)先級隊列的,因為只需要比較第一個位置元素的大小就可以比較出結果了,就不需要比較第二個位置元素的大小了。
但是對于 [2,1] 和 1 [2,"b"] 來說,則同樣不可以放入優(yōu)先級隊列,因為需要比較第二個位置的元素才可以比較出結果,然而第二個位置的元素類型是不一致的,無法比較大小。
綜上,也就是說, 直到在比較出結果之前,對應下標位置的元素類型都是需要一致的 。
下面我們自定義一個動物類型,希望按照年齡大小來做優(yōu)先級排序。年齡越小優(yōu)先級越高。
本章節(jié)介紹了隊列以及其常用操作。因為隊列默認實現(xiàn)了鎖原語,因此在多線程編程中就不需要再考慮多線程安全問題了,對于程序員來說相當友好了。
.put 不清楚
str() 數(shù)據(jù)類型轉換,轉換成字符串。如:str(123)='123'