160204 파워쉘 함수, cmdlet, 외부모듈사용
160204 파워쉘 함수, cmdlet, 외부모듈사용
황현동 노트북
powershell
study
작년 가을부터 파워쉘에 관심이 생겨서 공부를 하기 시작했는데
그다지 파워쉘을 활용할 일이 많이 없었다.
그다지 파워쉘을 활용할 일이 많이 없었다.
하지만 올해부터는 Windows 10 IoT Core 에서 개발이 예정되어 있고,
원격으로 붙을수 있는 쉘이 파워쉘뿐이다보니 아주 손발처럼 써야 할 상황이 되었다.
원격으로 붙을수 있는 쉘이 파워쉘뿐이다보니 아주 손발처럼 써야 할 상황이 되었다.
이번 글에서는 파워쉘을 파워풀하게 사용하기 위해 커스톰 function, 커스톰 dll 을 제작하여 활용하는 방법을 소개한다.
본론에 앞서 필자의 입장을 미리 밝히자면 필자는 파워쉘이 상당히 익숙해 지고 좋아졌지만 실용적으로는 간단한 스크립팅만 파워쉘로 구현하며 복잡한 기능은 커스톰 닷넷 dll제작해서 붙일것을 추천드린다.
독자분들도 이글을 읽고 파워쉘 코딩의 즐거움을 함께 느낄수 있었으면 좋겠다.
본론에 앞서 필자의 입장을 미리 밝히자면 필자는 파워쉘이 상당히 익숙해 지고 좋아졌지만 실용적으로는 간단한 스크립팅만 파워쉘로 구현하며 복잡한 기능은 커스톰 닷넷 dll제작해서 붙일것을 추천드린다.
독자분들도 이글을 읽고 파워쉘 코딩의 즐거움을 함께 느낄수 있었으면 좋겠다.
파워쉘이 처음이거나 기본 문법에 자신이 없으신 독자들은 필자의 파워쉘 기본문법 글을 먼저 선행하시길 권장드린다.
http://hhd2002.blogspot.kr/2015/11/151119.html
http://hhd2002.blogspot.kr/2015/11/151119.html
alias
alias 는 이름이 긴 function, cmdlet, path를 짧게 줄이는 역할을 한다.
주의할점은 파워쉘의 alias로는 bash처럼 파라미터까지 포함해서 만들수는 없다.
예제 : alias 정의
vim, sublime을 alias로 잡아서 사용
Set-Alias vim "c:\hhdcommand\vim74\vim.exe"
Set-Alias sublime "c:\hhdcommand\Sublime Text 2.0.2\sublime.exe"
PS C:\temp> sublime dir.txt
PS C:\temp> vim dir.txt
PS C:\temp>
function
function은 파워쉘의 스크립트를 확장할때 가장 기본이 되는 단위다.
가장 간단하게는 입출력이 없이 긴 스크립트 문을 축약하느라 사용하는 경우도 있고,
복잡하게 사용할때는 입력, 출력, 강타입, 파이프라인, 도움말주석처리 등을 모두 적용해서 사용할 수 있다.
여기서는 간단한 function부터 function의 풀스펙까지 모두 활용해 본다.
예제 : function을 입출력 없는 축약파라미터 대체재로 사용
ls -Force 와 같이 자주 사용하는 파라미터를 축약하는 용도로 입력파라미터 정의없이 사용할 수 있다.
function lsforce
{
Get-ChildItem -Force
}
PS C:\temp> lsforce
디렉터리: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2016-02-04 오후 4:22 790 dir.txt
PS C:\temp>
예제 : 복잡한 파이프라이닝 된 스크립트
ps 명령어는 너무 많은 정보를 리스팅해주기 때문에,
ps2 를 만들어서 WorkingSet(사용중 메모리) 기준으로 상위10개만 노출되며 WS는 메가바이트단위로 표시하며,
전체적으로 리스트를 예쁘게 보이게 했다.
ps2 도 역시 입력파라미터가 없는데 이 경우 ps2() 처럼 명시적으로 정의해도 아무 문제 없다.
function ps2()
{
ps | sort WS -Descending | select Id, Name, @{l="WS(M)"; e={[int]($_.WS / 1024 / 1024)}}, Path -First 10 | ft -Au
toSize -Wrap
}
PS C:\temp\abcd1234qwer> ps2
Id Name WS(M) Path
-- ---- ----- ----
4 System 376
6624 devenv 284 C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe
15640 marxico 198 C:\Program Files (x86)\Marxico\marxico.exe
3348 MsMpEng 166
15680 powershell 155 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
18068 chrome 128 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
16776 chrome 95 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
7660 Microsoft.VsHub.Server.HttpHost 90 C:\Program Files (x86)\Common Files\Microsoft Shared\VsHub\1.0.0.0\Microsof
t.VsHub.Server.HttpHost.exe
4460 explorer 86 C:\WINDOWS\Explorer.EXE
21784 chrome 79 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
PS C:\temp\abcd1234qwer>
예제 : 축약파라미터 버전에서 중요한 파라미터를 입력으로 받아줌.
cd는 정확히 디렉토리를 입력했을때만 동작하기에 cd2를 만들어서 패턴으로 매칭되면 디렉토리 이동되도록 개선했다.
function cd2($keyword)
{
cd *$keyword*
}
PS C:\temp> mkdir abcd1234qwer
PS C:\temp> cd2 1234
PS C:\temp>
예제 : function의 헤더주석을 붙여서 요약, 파라미터, 예제 표시
파워쉘을 이용하다보면 구글링보다는 help xxx -full 로 파라미터를 확인하고, example을 확인하는것이 더 정신건강에 좋다.
커스톰 function 들도 주석의 규칙을 이용해서 요약, 파라미터, 예제 를 help 에 뜨게 할 수 있다.
#
.SYNOPSIS
rm -Recurse -Force $path
.PARAMETER path
파일, 디렉토리 경로
.EXAMPLE
rmforce aaa
#>
function rmforce($path)
{
rm -Recurse -Force $path
}
PS C:\temp> help rmforce -Full
이름
rmforce
개요
rm -Recurse -Force $path
구문
rmforce [[-path] <Object>] [<CommonParameters>]
매개 변수
-path <Object>
파일, 디렉토리 경로
필수 여부 false
위치 1
기본값
파이프라인 입력 적용 여부 false
와일드카드 문자 적용 여부 false
<CommonParameters>
이 cmdlet은 Verbose, Debug, ErrorAction, ErrorVariable,
WarningAction, WarningVariable, OutBuffer, PipelineVariable 및
OutVariable과 같은 일반 매개 변수를 지원합니다. 자세한 내용은
about_CommonParameters(http://go.microsoft.com/fwlink/?LinkID=113216)를 참조하십시오.
입력
출력
-------------------------- 예 1 --------------------------
PS C:\>rmforce aaa
PS C:\temp\abcd1234qwer>
<
예제 : 모든 기능을 다 사용한 풀버전 function
입력파라미터의 강타입선언, cmdlet바인딩, 파이프라인적용, help주석 등
모든 기능을 전부 사용해서 만든 예제하지만 기능은 별거 없다.
프로세스 리스트를 prefix, 이름, 프로세스ID만 선택하는 기능이다.
#
.SYNOPSIS
시놉시스
.PARAMETER prefix
프레픽스
.EXAMPLE
PS C:\WINDOWS\system32> $DebugPreference = "continue"
PS C:\WINDOWS\system32> ps | select -First 5 | getcustomprocessinfo -prefix 프레픽스 -strArray @("하나", "둘", "셋")
디버그: strArray.Length : 3
디버그: idx : 0
디버그: randValue : 하나
디버그: strArray.Length : 3
디버그: idx : 1
디버그: randValue : 둘
디버그: strArray.Length : 3
디버그: idx : 1
디버그: randValue : 둘
디버그: strArray.Length : 3
디버그: idx : 1
디버그: randValue : 둘
디버그: strArray.Length : 3
디버그: idx : 2
디버그: randValue : 셋
prefix ProcessName ProcessId randValue
------ ----------- --------- ---------
프레픽스 ApplicationFrameHost 14912 하나
프레픽스 ccSvcHst 3508 둘
프레픽스 chrome 2220 둘
프레픽스 chrome 3076 둘
프레픽스 chrome 6288 셋
#>
function getcustomprocessinfo
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelinebyPropertyName=$true)]
[System.Diagnostics.Process]
$process,
[System.String]
$prefix,
[System.String[]]
$strArray
)
begin
{
}
process
{
$obj = New-Object -typename PSObject
$obj | Add-Member -MemberType NoteProperty -Name prefix -Value $prefix
$obj | Add-Member -MemberType NoteProperty -Name ProcessName -Value $process.Name
$obj | Add-Member -MemberType NoteProperty -Name ProcessId -Value $process.Id
Write-Debug ("strArray.Length : " + $strArray.Length)
if ($strArray.Length -gt 0)
{
$rand = New-Object -TypeName System.Random
$idx = $rand.Next($strArray.Length)
Write-Debug ("idx : " + $idx)
$randValue = $strArray[$idx]
Write-Debug ("randValue : " + $randValue)
$obj | Add-Member -MemberType NoteProperty -Name randValue -Value $randValue
}
Write-Output $obj
}
}
PS C:\temp> ps | where {$_.Name -eq "chrome"} | mycomplexfunc -prefix "프레픽스" -strArray @("일", "이", "삼")
디버그: strArray.Length : 3
디버그: idx : 2
디버그: randValue : 삼
디버그: strArray.Length : 3
디버그: idx : 2
디버그: randValue : 삼
디버그: strArray.Length : 3
디버그: idx : 0
디버그: randValue : 일
디버그: strArray.Length : 3
디버그: idx : 0
디버그: randValue : 일
디버그: strArray.Length : 3
디버그: idx : 1
디버그: randValue : 이
prefix ProcessName ProcessId randValue
------ ----------- --------- ---------
프레픽스 chrome 1340 삼
프레픽스 chrome 7004 삼
프레픽스 chrome 10656 일
프레픽스 chrome 14808 일
프레픽스 chrome 21212 이
<
profile.ps1
이 모든 function 들과 이외의 수많은 유용한 function들을 파워쉘 세션마다 로드되게 하고 싶으면
profile.ps1 파일을 수정하면 된다.이 파일의 위치는 아래와 같다.
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1아래 파일을 다운로드받고 독자들 각각의 환경에 맞추어 하드코딩된 파일경로들을 수정하길 바란다.
https://raw.githubusercontent.com/HyundongHwang/PsDev/master/PsScripts/profile.ps1
외부모듈 사용
사실 파워쉘의 함수들은 닷넷dll을 모두 사용할 수 있으므로 필요한 기능이 아무리 복잡해 보여도 파워쉘 구문을 꿰뚫고 있으면 그냥 스크립트만 가지고 모두 파워쉘 function 내부에 구현하면 된다.
하지만 필자가 실제로 파워쉘 스크립트 코딩을 하다보니 코드자동완성기능 빈약, 컴파일 없음, 정지점 디버깅 안됨 등 스크립트코딩의 태생적인 한계때문에 조금 복잡한 기능이 될것 같으면 그냥 커스톰 닷넷 dll만들어서 버리고 그 기능 파워쉘에서 사용하거나 이 모듈들을 스크립트로 접착해서 사용하는게 더 낫다는 생각에 이르렀다.
step1. 닷넷 라이브러리 dll 구성
먼저 닷넷 라이브러리 dll을 만들어서 파워쉘홈 디렉토리에 배포한다.
C:\Windows\System32\WindowsPowerShell\v1.0\
using System.IO.Compression;
namespace HPsUtils
{
public static class HPsUtils
{
public static void Zip(string dir, string zipFile)
{
try
{
ZipFile.CreateFromDirectory(dir, zipFile);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public static string DoubleStr(string str)
{
return str + str;
}
}
step2. 라이브러리 사용등록
profile.ps1 파일에 커스톰dll의 사용등록을 한다.
필자는 파워쉘홈 디렉토리에 dll을 복사해 두고 세션시작시마다 등록해서 사용한다.
if (Test-Path "C:\hhdcommand\PsDev\HPsUtils\bin\Debug\HPsUtils.dll")
{
Write-Debug "HPsUtils.dll 업데이트 ..."
cp -Force C:\hhdcommand\PsDev\HPsUtils\bin\Debug\HPsUtils.dll $PSHOME
Add-Type -Path "$PSHOME\HPsUtils.dll"
}
else
{
Write-Debug "HPsUtils.dll 업데이트 스킵 ..."
}
step3. 라이브러리 실제 사용
mytestdir 디렉토리를 만들고 여기에 난수로 100개의 파일을 만든다.
이후 닷넷 압축함수를 이용하여 파워쉘에서 압축을 수행한다.zip, unzip 함수 말고도 다른 함수가 있는지 help 함수를 만들어 두었다.
help 함수는 reflection을 사용하여 전체 함수의 리스트를 리턴하여 도움말과 같은 기능을 한다.
for($i=0; $i -lt 100; $i++){ $rand = Get-Random; $rand > "mytestdir\$rand.txt" }
PS C:\temp> ls .\mytestdir\ | select -First 10
디렉터리: C:\temp\mytestdir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2016-02-04 오후 9:42 24 100781744.txt
-a---- 2016-02-04 오후 9:42 26 1012567270.txt
-a---- 2016-02-04 오후 9:42 26 1033485104.txt
-a---- 2016-02-04 오후 9:42 26 1050876069.txt
-a---- 2016-02-04 오후 9:42 26 1068604014.txt
-a---- 2016-02-04 오후 9:42 26 1112232718.txt
-a---- 2016-02-04 오후 9:42 26 1113412666.txt
-a---- 2016-02-04 오후 9:42 26 1121581891.txt
-a---- 2016-02-04 오후 9:42 26 1176129291.txt
-a---- 2016-02-04 오후 9:42 26 1206878537.txt
PS C:\temp> [HPsUtils.HPsUtils]::help()
ReturnType FuncName ParamList
---------- -------- ---------
List`1 help {}
Void Zip {String dir, String zipFile}
Void Unzip {String dir, String zipFile}
String DoubleStr {String str}
Type GetType {}
PS C:\temp> [HPsUtils.HPsUtils]::Zip("$pwd\mytestdir", "$pwd\my.zip")
PS C:\temp> ls
디렉터리: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2016-02-04 오후 9:42 mytestdir
-a---- 2016-02-04 오후 9:44 12665 my.zip
PS C:\temp> mkdir mytestdir
PS C:\temp>
step4. 파이프라인 사용하여 연결
파이프라이닝 예제를 위해서 문자열을 복제해서 붙여주는 DoubleStr 함수를 만들었다.
a, b, c 3개의 문자열 배열을 준비해서 다음파이프로 보내고,
파이프에 도착한 문자열을 루프돌면서 더블치고
다음파이프를 연결하면 계속 파이프라이닝 코딩을 할 수 있다.
"a", "b", "c") | % { [HPsUtils.HPsUtils]::DoubleStr($_) } | % { [HPsUtils.HPsUtils]::DoubleStr
($_) }
aaaa
bbbb
cccc
PS C:\WINDOWS\system32> @(
예제모음 git
이제까지 설명한 예제는 git으로 오픈되어 있다.
https://github.com/HyundongHwang/PsDev독자여러분은 연습삼아 git clone 조차 파워쉘에서 실습해 보기 바란다.
clone git@github.com:HyundongHwang/hhdcommand.git hhdcommand
Cloning into 'hhdcommand'...
remote: Counting objects: 1003, done.
remote: Compressing objects: 100% (16/16), done.
PS C:\temp> git
이 글은 Evernote에서 작성되었습니다. Evernote는 하나의 업무 공간입니다. Evernote를 다운로드하세요. |
댓글
댓글 쓰기