Shell編程

計算機編程技術

shell 是操作系統的最外層。shell 合併編程語言以控制進程和文件,以及啟動和控制其它程序。shell 通過提示您輸入,向操作系統解釋該輸入,然後處理來自操作系統的任何結果輸出來管理您與操作系統之間的交互。

基本概述


shell 提供了與操作系統通信的方式。此通信以交互的方式(來自鍵盤的輸入立即操作)或作為一個 shell 腳本執行。shell 腳本是 shell 和操作系統命令的序列,它存儲在文件中。
當登錄到系統中時,系統定位要執行的 shell 的名稱。在它執行之後,shell 顯示一個命令提示符。普通用戶的此提示符通常是一個 $(美元符)。當提示符下輸入命令並按下 Enter 鍵時,shell 對命令進行求值,並嘗試執行它。取決於命令說明,shell 將命令輸出寫到屏幕或重定向到輸出。然後它返回命令提示符,並等待您輸入另一個命令。
命令行是輸入所在的行。它包含 shell 提示符。每行的基本格式如下:
$ 命令參數(一個或多個)
shell 視命令行的第一個字(直到第一個空白空格)為命令,所有後繼字為自變數
在Windows環境下,不論是使用Visual C++還是Delphi或是其他一些軟體開發工具開發的應用程序,儘管存在著差別,但有一點是相同的:都是運行於Windows操作系統之下的。在程序開發過程中也經常要在自己的應用程序中加入一些Windows系統本身就有的功能,比如文件的拷貝、刪除、查找以及運行程序等等。而這些功能在Windows操作系統下都是具備的,顯然如果能直接從系統中調用這些功能將不僅僅減少程序的大小和開發人員的工作量,而且由於是直接通過操作系統來完成這些功能,將會大大減小這部分程序出現異常錯誤的概率。Windows系統雖說也存在不少錯誤,但常用功能的錯誤還是比較少的,而且通過補丁程序可以更低限度減少系統錯誤,因此程序員可以將調試檢錯的注意力放在應用程序的其他地方,對於調用系統功能這部分代碼則可以不必投入太大的精力去調試,因為這部分調試的工作在操作系統發布的時候就已經由微軟做好了。本文通過外殼編程,實現了搜尋文件、運行程序、控制工具條、最大最小化窗口的功能。

實現方法


前面所說的直接使用Windows操作系統部分功能的編程方法就是針對Windows操作系統外殼的編程,可以通過對操作系統提供的幾個編程介面對操作系統的部分功能進行調用,甚至可以按照自己的意圖在應用程序中對部分功能進行修改、擴展。但這方面的資料介紹不是特別多,講的也大都語焉不詳,而且用通常的編程方法去進行外殼編程是非常麻煩的,動輒就要對相關的結構對象進行設置,而這樣的結構里的數據成員少則十來個多則幾十個,因此配置起來非常煩瑣,下面就以一個比較簡單的外殼操作--拷貝文件進行舉例說明:
……
SHFILEOPSTRUCT FileOp; //外殼的文件操作結構
FileOp.hwnd=m_hWnd; //設置句柄
//設置操作方式,拷貝用FO_COPY,刪除用 FO_DELETE
FileOp.wFunc=FO_COPY;
FileOp.pFrom=m_source; //源文件路徑
FileOp.pTo=m_detect; //目標文件路徑
FileOp.fFlags=FOF_ALLOWUNDO; //允許恢復
FileOp.hNameMappings=NULL;
FileOp.lpszProgressTitle=strTitle; //設置標題
SHFileOperation(&FileOp); //執行外殼拷貝
if(FileOp.fAnyOperationsAborted) //監測有無中止
TRACE("An Operation was aborted!!!\n");
……
上述代碼實現起來雖然效果還是不錯的,但然實現起來卻是比較麻煩的,這僅僅是一個比較簡單的外殼操作,對於一些比較複雜的外殼操作比如系統托盤、任務條等等的編程,更是尤為嚴重,而且象此類編程,MFC里並沒有提供封裝好的程序類庫,提供的只有系統的WinAPI 應用程序介面,因此在程序開發過程中往往會有一種在進行SDK編程的感覺。
COM (Component Object Model,組件對象模型)是Microsoft創建的一種二進位和網路標準,也是Microsoft大力推廣並已取得廣泛認可的一種組件標準。在COM標準中,COM對象被很好的封裝起來,客戶無法訪問對象的實現細節,提供給用戶的唯一的訪問途徑是通過COM介面來訪問。對於COM介面有兩方面的含義:首先它是一組可供調用的函數,由此客戶可以讓該對象做某些事情;其次,也是更為重要的,介面是組件及其客戶程序之間的協議。也就是說介面不但定義了可用什麼函數,也定義了當調用這些函數時對象要做什麼。Windows操作系統本身作為一個大的COM組件對象,也提供了一些必要的COM介面給客戶程序,因此我們可以通過這些COM介面來直接對Windows外殼進行編程。
在程序進行正式編寫設計之前有一點是肯定的:程序里需要用到COM介面,要對COM對象進行操作。因此首先要加入初始化COM和終止COM的代碼。一般是在應用程序類的InitInstance()函數的開始處和返回前添加初始化COM和終止COM代碼的:
……
CoInitialize(NULL); //初始化COM
……
CoUninitialize(); //終止COM代碼
……
以上兩個函數在MFC程序和非MFC程序中都可以很好的使用。另外,如果程序框架是以MFC為基礎的,那麼只需簡單的調用AfxOleInit()函數就可以達到同樣的目的。而且不必顯式調用終止COM的代碼。在COM標準中,訪問COM對象的唯一途徑是COM介面,因此在編寫操縱Windows 系統外殼程序首先要得到其提供的COM介面。所用的COM介面是IShellDispatch,它是從IDispatch介面派生來的,在VC安裝目錄的VC98\Include\Exdisp.h頭文件中有定義,下面節選了一些將要用到的介面定義:
……
EXTERN_C const IID IID_IShellDispatch;
#if defined(__cplusplus) && !defined(CINTERFACE)
interface DECLSPEC_UUID("D8F015C0-C278-11CE-A49E-444553540000")
IShellDispatch : public Idispatch
{
public:
……
virtual HRESULT STDMETHODCALLTYPE MinimizeAll( void) = 0;
virtual HRESULT STDMETHODCALLTYPE UndoMinimizeALL( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FileRun( void) = 0;
virtual HRESULT STDMETHODCALLTYPE CascadeWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileVertically( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileHorizontally( void) = 0;
virtual HRESULT STDMETHODCALLTYPE ShutdownWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Suspend( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetTime( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TrayProperties( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Help( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindFiles( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindComputer( void) = 0;
};
……
該介面在CoCreateInstance()函數創建COM對象時將會得到指向其的指針,通過這個函數客戶程序可以避免顯式同類廠打交道,其實該函數內部也調用了CoGetClassObject()函數來獲取COM對象的類廠,只不過它把通過類廠創建對象的過程封裝起來了,只需用戶指定對象類的CLSID和待輸出的介面指針及介面ID,顯然這樣直接創建COM對象是非常便捷的,在獲取到COM對象指針之後就可以通過這個指針去訪問調用COM對象里的方法來實現Windows 外殼的種種功能調用了。下面是實現該功能的部分關鍵代碼:
……
HRESULT sc;//返回結果
IShellDispatch *pShellDisp = NULL; //初始化介面指針
//直接創建COM對象
sc = CoCreateInstance( CLSID_Shell,//指定待創建的COM對象標識符
NULL, //指定被聚合時的外部對象的介面指針
CLSCTX_SERVER, //指定組件類別,可以指定進程內組件進程外組件或者進程內控制對象。
IID_IDispatch, //指定介面ID,需要注意的是這裡指的是待
//創建的COM對象的介面ID,而非類廠對象的介面標識符
(LPVOID *) &pShellDisp );//存放函數返回的對象的介面指針
if( FAILED(sc) )//必須用FAILED 或SUCCECCED來判斷COM對象是否創建成功
return;
pShellDisp->FindFiles(); //調用COM對象里的方法
pShellDisp->Release(); //釋放申請到的介面指針
……
在這裡通過pShellDisp介面指針調用了COM對象的FindFiles()方法去進行查找文件的系統外殼操作。同樣,可以根據實際需要靈活調用響應的方法來執行相應的外殼操作,主要有以下幾個方法:MinimizeAll:所有窗口最小化、UndoMinimizeALL:恢復窗口最小化、 FileRun:開始菜單的"運行…"、CascadeWindows:層疊窗口、TileVertically:垂直平鋪、TileHorizontally:水平平鋪、ShutdownWindows:關閉Windows、Suspend 掛起計算機、SetTime:設定時間、TrayProperties:任務欄屬性、Help Windows:幫助、FindFiles:查找文件、FindComputer:查找計算機等。
這些介面均在VC安裝目錄的VC98\Include\Exdisp.h頭文件中有定義,可以通過對該文件的查看來編寫響應的外殼操作代碼。

編程步驟


1、啟動Visual C++6.0,生成一個Win32應用程序,項目命名為"Shell";
2、添加應用程序圖標資源APP_ICON和對話框資源DLG_MAIN,對話框界面按上圖一設計;
3、使用Class Wizard為對話框上的各個按鈕添加滑鼠單擊處理函數;
4、添加代碼,編譯運行程序。

常用技巧


1. 字元串的截取
shell字元串的截取的問題:
一、Linux shell 截取字元變數的前8位,有方法如下:
1) expr substr “$a” 1 8
2) echo $a|awk ‘{print substr(,1,8)}’
3) echo $a|cut -c1-8
4) expr $a : ‘\(.\\).*’
5) echo $a|dd bs=1 count=8 2>/dev/null
二、按指定的字元串截取
1) 第一種方法:
${varible##*string} 從左向右截取最後一個string后的字元串
${varible#*string}從左向右截取第一個string后的字元串
${varible%%string*}從右向左截取最後一個string后的字元串
${varible%string*}從右向左截取第一個string后的字元串
“*”只是一個通配符可以不要
例子:
$ MYVAR=foodforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg
2) 第二種方法:${varible:n1:n2}:截取變數varible從n1到n2之間的字元串。
可以根據特定字元偏移和長度,使用另一種形式的變數擴展,來選擇特定子字元串。試著在 bash 中輸入以下行:
$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga
這種形式的字元串截斷非常簡便,只需用冒號分開來指定起始字元和子字元串長度。
三、按照指定要求分割:
比如獲取後綴名
ls -al | cut -d “.” -f2
2. 大小寫轉換
echo $uppercase |tr [A-Z] [a-z]
echo $lowercase |tr [a-z] [A-Z]

編程過程


/////////////////////////////////////
#define INC_OLE2
#define WIN32_LEAN_AND_MEAN
#define STRICT
#include
#include
#include
#include
#include
#include “taskbar.h”
#include “resource.h”
// data
static WNDPROC g_pfnOldProc;
static HWND g_hwndButton=NULL;
static HWND g_hDlg=NULL;
static HINSTANCE g_hInstance;
static HICON g_hIconLarge;
static HICON g_hIconSmall;
// functions
static VOID OnRunPrograms( VOID );
static VOID OnFindFiles( VOID );
static VOID OnMinimizeAll( VOID );
static VOID OnUndoMinimize( VOID );
static VOID OnTaskbarProperties( VOID );
static VOID OnAddTab( HWND );
static VOID OnDeleteTab( VOID );
static VOID OnInitDialog( HWND );
static VOID OnButtonActivation( VOID );
// callbacks
LRESULT CALLBACK APP_DlgProc( HWND, UINT, WPARAM, LPARAM );
LRESULT CALLBACK ButtonProc( HWND, UINT, WPARAM, LPARAM );
INT APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevious,
LPSTR lpsz, INT iCmd )
{
BOOL b;
g_hIconLarge = (HICON) LoadImage( hInstance, “APP_ICON”, IMAGE_ICON,
GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON), 0 );
g_hIconSmall = (HICON) LoadImage( hInstance, “APP_ICON”, IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), 0 );
// initialize OLE libraries
CoInitialize(NULL);
InitCommonControls();
// run main dialog
g_hInstance = hInstance;
b = DialogBox( hInstance, “DLG_MAIN”, NULL, (DLGPROC)APP_DlgProc );
// exit
DestroyIcon( g_hIconLarge );
DestroyIcon( g_hIconSmall );
// free the objects used by ITaskbarList
DestroyWindow( g_hwndButton );
OnDeleteTab();
CoUninitialize();
return b;
}LRESULT CALLBACK APP_DlgProc( HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam )
{
switch( uiMsg )
{
case WM_INITDIALOG:
OnInitDialog( hDlg );
break;
case WM_COMMAND:
switch( wParam )
{
case IDC_FINDFILES:
OnFindFiles();
return 1;
case IDC_RUNPROGRAMS:
OnRunPrograms();
return 1;
case IDC_MINIMIZE:
OnMinimizeAll();
return 1;
case IDC_UNDOMINIMIZE:
OnUndoMinimize();
return 1;
case IDC_PROPERTIES:
OnTaskbarProperties();
return 1;
case IDC_ADDTAB:
OnAddTab( hDlg );
return 1;
case IDC_DELETETAB:
OnDeleteTab();
return 1;
case IDCANCEL:
EndDialog( hDlg, FALSE );
return 0;
}
break;
}
return 0;
}
VOID OnFindFiles( VOID )
{
HRESULT sc;
IShellDispatch *pShellDisp = NULL;
sc = CoCreateInstance( CLSID_Shell, NULL, CLSCTX_SERVER, IID_IDispatch, (LPVOID *) &pShellDisp );
if( FAILED(sc) )
return;
pShellDisp->FindFiles();
pShellDisp->Release();
return;
}
VOID OnTaskbarProperties( VOID )
{
HRESULT sc;
IShellDispatch *pShellDisp = NULL;
sc = CoCreateInstance( CLSID_Shell, NULL, CLSCTX_SERVER,IID_IDispatch, (LPVOID *) &pShellDisp );
if( FAILED(sc) )
return;
pShellDisp->TrayProperties();
pShellDisp->Release();
return;
}
VOID OnRunPrograms( VOID )
{
HRESULT sc;
IShellDispatch *pShellDisp = NULL;
sc = CoCreateInstance( CLSID_Shell, NULL, CLSCTX_SERVER, IID_IShellDispatch, (LPVOID *) &pShellDisp );
if( FAILED(sc) )
return;
pShellDisp->FileRun();
pShellDisp->Release();
return;
}
VOID OnMinimizeAll( VOID )
{
HRESULT sc;
IShellDispatch *pShellDisp = NULL;
sc = CoCreateInstance( CLSID_Shell, NULL, CLSCTX_SERVER,
IID_IDispatch, (LPVOID *) &pShellDisp );
if( FAILED(sc) )
return;
pShellDisp->MinimizeAll();
pShellDisp->Release();
return;
}
VOID OnUndoMinimize( VOID )
{
HRESULT sc;
IShellDispatch *pShellDisp = NULL;
sc = CoCreateInstance( CLSID_Shell, NULL, CLSCTX_SERVER,
IID_IDispatch, (LPVOID *) &pShellDisp );
if( FAILED(sc) )
return;
pShellDisp->UndoMinimizeALL();
pShellDisp->Release();
return;
}
VOID OnInitDialog( HWND hDlg )
{
// set the icons (T/F as to Large/Small icon)
g_hDlg = hDlg;
SendMessage( hDlg, WM_SETICON, FALSE, (LPARAM)g_hIconSmall );
SendMessage( hDlg, WM_SETICON, TRUE, (LPARAM)g_hIconLarge );
}
VOID OnAddTab( HWND hWnd )
{
static BOOL g_bFirstTime=TRUE;
HRESULT sc;
ITaskbarList *pDisp = NULL;
sc = CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_SERVER, IID_ITaskbarList, (LPVOID *) &pDisp );
if( FAILED(sc) )
return;
// call the first time only
if( g_bFirstTime )
{
g_bFirstTime = FALSE;
pDisp->HrInit();
// create a new button window
g_hwndButton = CreateWindow( “button”, “My Button”, WS_CLIPSIBLINGS|BS_PUSHBUTTON,0, 0, 58, 14, hWnd, NULL, g_hInstance, NULL );
g_pfnOldProc = (WNDPROC) SubclassWindow( g_hwndButton, ButtonProc );
}
pDisp->AddTab( g_hwndButton );
pDisp->Release();
return;
}
VOID OnDeleteTab( VOID )
{
HRESULT sc;
ITaskbarList *pDisp = NULL;
sc = CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_SERVER, IID_ITaskbarList, (LPVOID *) &pDisp );
if( FAILED(sc) )
return;
pDisp->DeleteTab( g_hwndButton );
pDisp->Release();
return;
}
LRESULT CALLBACK ButtonProc( HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam )
{
switch( uiMsg )
{
case WM_NCACTIVATE:
if( wParam==TRUE )
OnButtonActivation();
break;
}
return CallWindowProc( g_pfnOldProc, hwnd, uiMsg, wParam, lParam );
}
VOID OnButtonActivation( VOID )
{
HMENU hmenu;
RECT r;
LONG x, y;
// get some window handles
HWND h0=FindWindow(“Shell_TrayWnd”, NULL );
HWND h1=FindWindowEx(h0,NULL,”RebarWindow32″, NULL);
HWND h2=FindWindowEx(h1,NULL,”MSTaskSwWClass”, NULL);
HWND h3=FindWindowEx(h2,NULL,”SysTabControl32″, NULL);
GetWindowRect( h3, &r );
// get the currently selected button and
// create a new popup menu
hmenu = CreatePopupMenu();
INT i=TabCtrl_GetCurSel( h3 );
if( i==-1 )
{
AppendMenu( hmenu, MF_STRING, IDC_DELETETAB,”&Close” );
}
else
{
AppendMenu( hmenu, MF_STRING, IDC_MINIMIZE,”&Minimize All” );
AppendMenu( hmenu, MF_STRING, IDC_UNDOMINIMIZE,”&Undo Minimize All” );
AppendMenu( hmenu, MF_SEPARATOR, 0, NULL );
AppendMenu( hmenu, MF_STRING, IDC_PROPERTIES,”&Taskbar Properties” );
}
// set and immediately reset its size to get
// the current width and height
LONG l = TabCtrl_SetItemSize( h3, 0, 0 );
TabCtrl_SetItemSize( h3, LOWORD(l), HIWORD(l) );
// have the menu to appear just above the button
if( i==-1 )
{
POINT pt;
GetCursorPos( &pt );
x = pt.x;
y = pt.y;
}
else
{
x = r.left + LOWORD(l)*i+3;
y = GetSystemMetrics(SM_CYSCREEN)-(HIWORD(l)+1);
}
TrackPopupMenu( hmenu, TPM_BOTTOMALIGN, x, y, 0, g_hDlg, 0);
DestroyMenu( hmenu );
return;
}

課程大綱


第1章 簡介及基礎

shell簡介及基礎
shell是什麼,為什麼要學習shell
shell發展史及分類
bash功能簡介
部署設置bash環境
shell腳本組成、運行模式及執行方法
bash排錯方法
父shell和子shell
常用快捷鍵

第2章 變數

變數分類
內部變數
預定義變數
自定義變數及基本用法
數組
特殊字元
別名
Here Document
列印特殊字元

第3章 循環結構與流程式控制制

if條件判斷
case條件判斷
for循環
while循環
until循環
select命令
break和continue

第4章 函數

函數用法
函數作用範圍
參數使用
建立函數庫
遞歸函數

第5章 算術運算

算術式簡介
算術擴展
利用expr做算術運算
使用$[]做算術運算
使用內置變數declare、let做算術運算
利用bc做浮點運算
進位轉換

第6章 重定向與轉向

文件代碼
操作文件
輸入輸出轉向
轉向附加
標準錯誤伴隨輸出轉向
Here Document轉向

第7章 高級變數與字元串操作

變數擴展:測試存在性及空值
變數擴展:取字元串切片,字元串長度
變數擴展:對比樣式
變數擴展:取變數名稱列表,數組索引列表
命令替換

第8章 正則表達式

入門介紹
元字元
字條轉義
重複
字元類
分支條件
反義
分組
後向引用
零寬斷言與負向零寬斷言
貪婪與懶惰
註釋
處理選項
平衡組/遞歸匹配

第9章 sed編輯器

sed介紹
sed基本語法
sed調用方式
sed高級用法

第10章 awk學習

awk原理介紹
awk基本語法
awk調用方式
awk循環語句
awk函數
awk數組
awk的BEGIN和END模式
awk數學運算和字元串操作
awk重定向輸出
awk與bash之間傳參調用
awk調試方法

第11章 文字與圖形介面編程

dialog文本介面編程
xdialog圖形介面編程

第12章 bash網路編程

bash網路轉向
遠程執行命令
expect與自動登錄

第13章 trap陷阱觸發

信號signal
trap運用

第14章 shell腳本加密與包裝

加密shell腳本
生成二進位可執行文件

第15章 shell中常用命令介紹

grep命令詳解
find與xargs
sort和uniq命令詳解
cut命令詳解
curl與wget命令詳解
cat、tac與rev
tr命令詳解
paste命令詳解
join命令詳解

第16章 一些shell技巧

利用腳本修改密碼
判斷奇偶
添加行號與刪除行號
刪除行首空格,刪除重複行
合併行操作
逐行讀取操作
字元與ASCII碼轉換操作
連接mysql資料庫操作

第17章 shell十三問

PS1和CR的關係
echo知多少
“”(雙綽號)與”(單引號)差在哪兒
export前後差在哪兒
exec跟source差在哪兒
()與{}差在哪兒
$(())和$()、${}差在哪兒
$@和$*差在哪兒
&&和||差在哪兒
>和<差在哪兒>
你要if還是case
for what?while和until差在哪兒?
[^ ]和[! ]差在哪兒

第18章 綜合實戰講解一

系統信息收集腳本
備份腳本
日誌切割腳本
nagios監控插件腳本
發郵件腳本

第19章 綜合實戰講解二

系統初始化腳本
基礎軟體安裝腳本
iptables腳本
線上操作記錄審核腳本
文本完整性審核腳本
kickstart配置腳本