Powershell – Resize Image Files

목요일, 12 8월 2010

Powershell 로 이미지 파일을 Resize 해 보자.

Parameter는 입력 파일 경로, 출력 파일 경로 그리고 Width, Height 사이즈만 있으면 된다.

System.Drawing을 사용하였는데 기본적으로 Load 되어 있는 Assembly가 아니므로 Load 해 줘야 한다.

아래는 여러가지 방법중 하나 이다. .Net 을 사용한 Image Resize 코드들을 찾아 보면 여러 가지 방법이 있으니 다른 방법도 Powershell로 다시 써보면 재미 있을 것이다.

Resize-Image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Function Resize-Image
{  
    param(
        [String]$InputFile,
        [String]$OutputFile,
        [int]$Width,
        [int]$Height
    )
   
    [reflection.assembly]::LoadWithPartialName("System.Drawing")

    $OriginImage = [System.Drawing.Bitmap]::FromFile($InputFile)

    $ResizedImage = New-Object System.Drawing.Bitmap @($Width, $Height)

    $graphics = [System.Drawing.Graphics]::FromImage($ResizedImage)

    $graphics.DrawImage($OriginImage, 0, 0, $Width, $Height)

    $graphics.Dispose()

    $ResizedImage.Save($OutputFile)
}

이 Function을 이용하여 원하는 Directory 내에 있는 File들을 모두 Resize 하기 위해서는 다음과 같이 응용한다.

Reset-ImageFilesSize.ps1

1
2
3
4
5
6
param($InputDirectory, $OutputDirectory, $Width, $Height)

New-Item -ItemType Directory -Path $OutputDirectory -Force

$InputFiles = ls $InputDirectory | ? {$_.Extension -eq ".jpg"} |
    %{Resize-Image $_.FullName (Join-Path $OutputDirectory $_.Name) $Width $Height}

Extension을 .jpg로 고정 했는데 Parameter로 받아도 좋을 것이다.

Verb 를 Resize로하고 싶었는데 Get-Verb 범위에서 벗어나 Reset으로 하였다.


Powershell – Customize prompt

월요일, 26 7월 2010

Powershell 의 Prompt는 기본적으로 PS {현재경로}> 로 되어 있다.

이 모양은 Powershell 에서 명령을 수행하고 Prompt 라는 Function을 수행한 결과이다.

믿기지 않는 다면 다음 명령으로 확인 해 보자.

1
Get-Command prompt

다음과 같이 Fuction임을 확인 할 수 있을 것이다.

1
2
3
CommandType Name   Definition
----------- ----   ----------
Function    prompt $(if (test-path variable:/PSDebugCon...

Definition을 자세히 보면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(
    if (test-path variable:/PSDebugContext)
    {
        '[DBG]: '
    }
    else
    {
        ''
    }
) + 'PS ' + $(Get-Location) +
$(
    if ($nestedpromptlevel -ge 1)
    {
        '>>'
    }
) + '> '

위의 코드가 Powershell의 Prompt를 쓰는 것이다.

그러므로 똑같은 이름의 Prompt Function을 만들면 직접 만든 Prompt를 사용 할 수 있다.

예를 들어 PS 라는 문자 대신에 자신의 Username 을 넣고 싶다면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Function Prompt{
    $(
        if (test-path variable:/PSDebugContext)
        {
            '[DBG]: '
        }
        else
        {
            ''
        }
    ) + $Env:USERNAME + ' ' + $(Get-Location) +
    $(
        if ($nestedpromptlevel -ge 1)
        {
            '>>'
        }
    ) + '> '
}

Powershell Prompt Username

사용자 이름이 잘 보인다.

한가지만 더 해보자, 응용하면 더욱 재미 있게 쓸 수 있다. 꼭 Prompt 만 변경 할수 있다고 생각 하지 말자 $host.UI 를 이용하여 Window Title 을 현재 경로로 할 수도 있다.

1
2
3
4
5
6
7
8
9
function Prompt
{
    $Colors = [System.Enum]::GetValues([System.ConsoleColor])
    $Color = $Colors[$(Get-Random -Minimum 0 -Max ($Colors.Count))]
    Write-Host -ForegroundColor $Color "PS" -NoNewline
    Write-Host -NoNewLine " $(Get-Location)>  "
    $host.UI.RawUI.WindowTitle = "$(Get-Location)"
    "`b"
}

Prompt Color

재미 삼아 PS 글자의 색을 Random 하게 넣어 봤는데 별로 이쁘지도 않고 멍청해 보인다.


Powershell – FTP List Parsing

월요일, 19 7월 2010

Powershell 을 이용해 FTP Server를 상대로 자동화 작업을 하다보니 File 업로드, 다운로드 뿐 만 아니라. 특정 FTP 경로 아래에 있는 File 과 Directory 들의 정보가 필요 했다. 이때 FTP Server를 상대로 WebRequest를 보내는데 이때 Request Method를 “List”로 한다.

이전에 올린 포스트인 Get-WebResponseString 을 이용하여 List를 요청 해 보자.



참고 Get-WebResponseString

1
2
3
4
5
6
7
$Url = "ftp://Use-Powershell.com"
$Username = "talsu"
$Password = "pass1234"

$credential = New-Object System.Net.NetworkCredential @($Username, $Password)  

Get-WebResponseString -Url $Url -Credential $credential -Method "List"

다음과 같은 결과가 나온다. 내가 타겟으로 하고 있는 FTP Server는 Ubuntu에 설치 되어 있는 vsFTPd 이다. 만약 다른 FTP Server라면 출력 포멧이 다를 수도 있다.

drwxrwxrwx   10 1000     1000         4096 Jul 16 09:53 Storage
-rw-r--r--    1 1000     1000          179 Jun 16 21:19 examples.desktop
drwx------    2 1000     1000         4096 Jul 01 17:55 test
drwxr-xr-x    2 1000     1000         4096 Jun 16 21:29 공개
drwxr-xr-x    2 1000     1000         4096 Jul 05 12:18 다운로드
drwxr-xr-x    2 1000     1000         4096 Jun 16 21:29 문서
drwxr-xr-x    2 1000     1000         4096 Jul 01 22:52 바탕화면
drwxr-xr-x    2 1000     1000         4096 Jun 16 21:29 비디오
drwxr-xr-x    3 1000     1000         4096 Jul 01 22:29 사진
drwxr-xr-x    2 1000     1000         4096 Jun 16 21:29 음악
drwxr-xr-x    2 1000     1000         4096 Jun 16 21:29 템플릿

이 출력은 하나의 문자열이다. 이것을 Parsing 해야 원하는 정보로 정렬하고 사용 할 수 있다. 다음은 FTP List를 Parsing 하는 Function 인데 추출하는 Regex는 자신의 Server와 다를수 있으니 알맞게 수정 하도록 하자.

Convert-FtpList

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
Function Convert-FtpList
{
    param ([String]$ListString)
   
    $FileCache = [System.IO.Path]::GetTempFileName()
    Set-Content -Path $FileCache -Value $ListString
   
    $FTPListRegex = "(?<dir>[\-ld])(?<permission>([\-r][\-w][\-xs]){3})\s+(?<filecode>\d+)\s+(?<owner>\w+)\s+(?<group>\w+)\s+(?<size>\d+)\s+(?<timestamp>(\w+\s+\d+\s+\d{2}:\d{2})|\w+\s+\d+\s+\d{4})\s+(?<name>.+)"
   
    $PSObjects = (Get-Content $FileCache) | % {
        if ($_ -match $FTPListRegex )
        {
            New-Object PSObject -Property @{
                Name = [String]$Matches["name"];
                Size = [Int64]$Matches["size"];
                Type = $(if($Matches["dir"] -eq "d"){ "Directory" }else{"File"});
                LastWriteTime = [DateTime]$Matches["timestamp"];
            }
        }
    }
   
    Remove-Item $FileCache
   
    return $PSObjects | Sort Type
}

임시 파일을 만들어 String을 File에 쓰고 Get-Content 의 출력을 Pipeline 으로 보내면 한줄씩 접근 할 수 있다. 한줄씩 접근 하기 위해 여러가지 방법이 있을 수 있지만 편한 방법으로 했다. (효율은 별로 좋지 못하다.)

한줄씩 접근하여 Regex로 값을 추출 해내고 그것을 Hash Table을 만든다. 이때 형 변환을 같이 하는데 DataTime 타입이 편리하게도 별다른 과정 없이 Parsing이 잘 되었다.

최종 출력은 HashTable을 Property로 옮기는 Custom PSObject 들이다. 이것으로 원하는 정보로 정렬 작업을 할 수 있게 되었다.

사용 예

1
2
3
4
5
6
7
8
9
$Url = "ftp://Use-Powershell.com"
$Username = "talsu"
$Password = "pass1234"

$credential = New-Object System.Net.NetworkCredential @($Username, $Password)  

$ListString = Get-WebResponseString -Url $Url -Credential $credential -Method "List"

Convert-FtpList $ListString

출력은 다음과 같다. (Format-Table Type, LastWriteTime, Size, Name -AutoSize)

Type      LastWriteTime               Size Name
----      -------------               ---- ----
Directory 2010-07-19 오후 4:21:00     4096 공개
Directory 2010-07-19 오전 5:12:00     4096 다운로드
Directory 2010-07-19 오후 4:21:00     4096 템플릿
Directory 2010-07-19 오후 4:21:00     4096 문서
Directory 2010-07-19 오전 1:22:00     4096 사진
Directory 2010-07-19 오후 4:21:00     4096 음악
Directory 2010-07-19 오전 1:22:00     4096 바탕화면
Directory 2010-07-19 오후 4:21:00     4096 비디오
Directory 2010-07-19 오후 4:09:00     4096 Storage
Directory 2010-07-19 오전 1:17:00     4096 test
File      2010-07-19 오후 4:21:00      179 examples.desktop
File      2010-07-19 오후 11:01:00 2964966 wordpress-3.0.zip

Powershell – Web (FTP) Request, Response

월요일, 19 7월 2010

Powershell 을 이용하여 웹 요청을 해보자. 웹 사이트를 관리 하거나 Rest 방식의 서비스를 사용 할 때 유용하다.

Get-WebResponseString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function Get-WebResponseString
{
    param (
        [Parameter(Mandatory=$true)]
        [String]$Url,      
        [Parameter(Mandatory=$true)]
        [String]$Method,
        [Parameter(Mandatory=$false)]
        [System.Net.NetworkCredential]$Credential
    )
   
    $Request = [System.Net.WebRequest]::Create($Url)   
    $Request.Method = $Method
   
    if ($Credential -ne $null)
    {
        $Request.Credentials = $credential
    }
   
    $Response = $Request.GetResponse()
   
    $StreamReader = New-Object System.IO.StreamReader $Response.GetResponseStream()
    $StreamReader.ReadToEnd()
}

사용 예

1
2
3
4
5
6
7
$Url = "http://Use-Powershell.com"
$Username = "talsu"
$Password = "pass1234"

$credential = New-Object System.Net.NetworkCredential @($Username, $Password)  

Get-WebResponseString -Url $Url -Credential $credential -Method "GET"

$Response 에서 결과 String을 반환 하지 않고 .StatusCode 등 다른 정보를 활용 할 수 있다.

Web 뿐만 아니라 FTP 에서도 동일하게 사용 할 수 있다.


Powershell – Hash Tables (해시 테이블)

금요일, 16 7월 2010

Powershell 에서의 Hash Table은 .NET 의 System.Collections.Hashtable 타입이다. 따라서 동일한 Property와 Method 들을 가지고 있다.

Hash Table은 Key-Value Pair(쌍) 의 Collection 이다. Key 와 Value 쌍으로 있어야 입력 할 수 있다. Value는 null 값이 가능 하지만 Keys는 null 값이 허용되지 않는다.

Hash Table 생성

Hash Table 개체는 New-Object cmdlet 또는 @{ } 로 생성 할 수 있다.

다음은 동일한 동작이다.

1
2
$HashA = New-Object -TypeName System.Collections.Hashtable
$HashB = @{}

@{ }을 이용해 생성과 함께 초기 값들을 입력 할 수 있다.
@{“Key”=”Value”;”Key”=”Value”} 형식으로 입력 한다.

1
2
# @{"Key"="Value";"Key"="Value"}
$Hash = @{"Alpha"=1111; "Bravo"=2222; "Charlie"=3333}

생성한 $Hash를 확인 해 보자.

PS > $Hash

Name                           Value
----                           -----
Charlie                        3333
Alpha                          1111
Bravo                          2222

Hash Table 의 특성상 순서는 신경 쓰지 않는다. 필요하면 정렬해서 쓰도록 하자.

사용 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$Hash = @{"Alpha"=1111; "Bravo"=2222; "Charlie"=3333}

$Hash.Count                 # 3

$Hash.Add("Delta", 4444)    # Element 추가.

$Hash.Count                 # 4

$Hash.Add("Bravo", 5555)    # Error - 이미 존재 하는 Key.

$Hash.Remove("Bravo")       # Bravo Key를 가진 Element 삭제.

$Hash.Contains("Charlie")   # True - Charlie 라는 Key를 포함하고 있다.

$Hash.Contains("Bravo")     # False - Bravo 라는 Key는 존재하지 않는다.

Key, Value의 접근

Key에 해당하는 Value를 얻기 위해서는 [ ]연산자를 이용하여 접근 한다.

$Value = $Hash["Key"]

Powershell 에서 Hash Table 개체는 포함하고 있는 Key들의 Collection과 Value들의 Collection을 제공 한다.

.Values를 이용하여 Value들을 순회

foreach ($value in $Hash.Values)
{
    Write-Host $value
}

.Keys를 이용하여 key들을 순회

foreach ($key in $Hash.Keys)
{
    Write-Host $key
}

.Keys를 이용하여 key와 value를 순회

foreach ($key in $Hash.Keys)
{
    Write-Host ("{0} - {1}" -f $key, $Hash[$key])
}

Hash Table 순회시 주의 사항

.Net과 다르게 Powershell 에서는 .Keys 나 .Values를 사용하지 않고 직접 HashTable 개체에 순회를 시도 할 때에는 Collection으로 인식하지 않는다. foreach 또는 pipeline 에서 HashTable 타입을 Collection으로 착각하지 말자.

예를 들어 .NET C# 에서는

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Hashtable openWith = new Hashtable();

openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");

int count = 0;

foreach( DictionaryEntry de in openWith )
{
            Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
            count++;
}

Console.WriteLine(count);            // 4

foreach로 HashTable을 접근하여 DictionaryEntry에서 .Key와 .Value에 접근하였다.
count 변수는 4번 증가 하였다.

하지만 Powershell 에서는

1
2
3
4
5
6
7
8
9
10
11
$Hash = @{"Alpha"=1111; "Bravo"=2222; "Charlie"=3333}

[int]$counter = 0

foreach ($item in $Hash)
{
    Write-Host ("Key = {0}, Value = {1}" -f $item.Key , $item.Value)
    $counter++
}

$counter            # 1

다음과 같은 출력을 볼 것이다.

Key = , Value =
1

Key와 Value 가 비어 있는 것은 .Key .Value 맴버가 없기 때문에 $null 이 출력 된 것이다. 이 말은 곧 $item 변수가 DictionaryEntry 타입이 아니라는 뜻이다. 게다가 count 가 한번만 증가 한것으로 보아 HashTable 타입의 개체는 Collection으로 인식 하지 않는다. 즉 위 코드에서 foreach 내부의 $item은 HashTable 타입이다.

다음과 같이 확인해보면 더욱 명확하게 이해 할 수 있다.

1
2
3
4
5
$Hash = @{"Alpha"=1111; "Bravo"=2222; "Charlie"=3333}
foreach ($item in $Hash)
{
    $item.GetType();
}

Powershell – Arrays (배열)

화요일, 13 7월 2010

Powershell 의 모든 Variable(변수)는 .NET의 Type을 가지는데 지금 부터 설명할 Array는 Powershell에서 기본 값으로 System.Object[] 타입으로 생성된다.

배열 선언

간단한 Array부터 만들어 보자.

1
2
$Arr = "A", "B", "C"
$Arr  # A B C

다음과 같이 출력 될 것이다.

A
B
C

원소가 3개인 Object 배열 (Object[]) 이 생성되었다. 확인을 위해 다음을 하나씩 입력 해 보자.

1
2
3
4
5
6
$Arr.GetType()  # Object[]
$Arr.Count      # 3
$Arr.Length     # 3
$Arr[0]         # A
$Arr[2]         # C
Get-Member -InputObject $Arr

$Arr 개체가 타입이 Object[] 즉 배열로 생성이 되었고, Property들이 정상 작동 한다.

Powershell 에서 배열은 @( ) 연산자 를 이용하여 선언 할 수도 있다. 다음 선언 되는 2개의 변수는 동일 한 것이다(Compare-Object 기준).

1
2
3
4
5
6
$Arr1 = @("A1", "A2", "A3")
$Arr2 = "A1", "A2", "A3"
$Arr1.GetType()
$Arr2.GetType()

Compare-Object $Arr1 $Arr2


인덱스를 이용한 원소 접근

[] 또는 메서드와 인덱스를 통해 원소에 접근 할 수 있다.
인덱스는 0 부터 할당되어 원소가 3개인 경우 인덱스는 0 ~ 2 이다.

다음은 인덱스를 이용해 원소에 접근하고 값을 변경 한다.

1
2
3
4
5
6
$Arr = "A", "B", "C"
$Arr.GetValue(1)    # B
$Arr[1] = "X"       # 2 번째 원소의 값을 X로 변경
$Arr.Get(1)         # X
$Arr.Set(0, "Y")    # 1 번째 원소의 값을 Y로 변경
$Arr[0]             # Y


배열 순회

배열을 순회하는 방법에는 여러가지가 있다. 익숙한 for문과 인덱스를 이용하는 방법 이외에도 C#의 그것과 유사한 foreach 문, 그리고 Powershell 의 개체 파이프라인을 이용하는 방법이 있다. 다음 예제들은 모두 같은 출력을 가진다.

1
$Numbers = @( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
1
2
3
4
for ($index = 0; $index -lt $Numbers.Length; $index++)
{
    $Numbers[$index] + 1
}
1
2
3
4
foreach ($item in $Numbers)
{
    $item + 1
}
1
$Numbers | ForEach-Object { $_ + 1 }


배열 순회시 주의 사항

foreach 문이나 Pipeline 의 출력을 받는 ForEach-Object cmdlet 내부에서 배열의 원소값을 변경하는 실수를 범하지 말자. 이 것들은 iteration variable 이기 때문에 실제 원소들이 변경 되지 않는다.

1
2
3
4
5
6
7
8
$Numbers = @( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )

foreach ($item in $Numbers)
{
    $item = 0
}

$Numbers  # 변함 없이 0 1 2 3 4 5 6 7 8 9
1
2
3
4
5
$Numbers = @( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )

$Numbers | ForEach-Object { $_++ }

$Numbers  # 변함 없이 0 1 2 3 4 5 6 7 8 9

순회 하면서 원소를 변경 할 때에는 for 문과 index를 사용 하는 것이 방법중 하나가 될 수 있다.

1
2
3
4
5
6
7
8
$Numbers = @( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )

for ($index = 0; $index -lt $Numbers.Length; $index++)
{
    $Numbers[$index] ++
}

$Numbers  # 변경됨 : 1 2 3 4 5 6 7 8 9 10


배열 연산자

Powershell 에서의 배열은 몇가지 연산자를 지원 한다. 실제 .NET Object[] 에서는 지원하지 않는 기능인데 Powershell 에서는 편리하게 사용 할 수 있다.

+ : 하나의 원소를 또는 다른 배열을 합친 새로운 배열을 리턴한다.

1
2
3
4
5
6
7
$Alphabet = "Alpha", "Bravo"

$Result = $Alphabet + "Charlie"  
$Result  # "Alpha", "Bravo", "Charlie"

$Result = $Alphabet + @("Delta", "Echo")
$Result  # "Alpha", "Bravo", "Delta", "Echo"

+= : 기존의 배열에 하나의 원소 또는 다른 배열을 추가한다. List 개체의 .Add() , .AddRange() 와 비슷 하다.

1
2
3
4
5
6
7
$Alphabet = "Alpha", "Bravo"

$Alphabet += "Charlie"  
$Alphabet  # "Alpha", "Bravo", "Charlie"

$Alphabet += @("Delta", "Echo")
$Alphabet  # "Alpha", "Bravo", "Charlie", "Delta", "Echo"

* : 곱한 값 만큼 반복되는 배열을 리턴한다. (int 값만 가능하다.)

1
2
3
4
$Alphabet = "Alpha", "Bravo"

$Result = $Alphabet * 2
$Result  # "Alpha", "Bravo", "Alpha", "Bravo"

*= : 기존의 배열에 곱한 값 만큼 반복되는 배열(기존의 배열 포함)을 추가 시킨다. (int 값만 가능하다.)

1
2
3
4
$Alphabet = "Alpha", "Bravo"

$Alphabet *= 2
$Alphabet  # "Alpha", "Bravo", "Alpha", "Bravo"

Powershell – Web File Download , Upload

목요일, 8 7월 2010

Powershell을 이용하여 Linux의 wget과 같이 Web (http, ftp) 에서 File을 다운로드하고 업로드하는 스크립트를 만들어 보자.

.Net의 WebClient를 사용하면 간단하다.

Get-WebFile.ps1

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
param(
    [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
    [String[]]$FileURLs,
    [String]$SavePath = (Get-Location),
    [String]$Username,
    [String]$Password
)

if ( -not (Test-Path $SavePath))
{  
    return
}

foreach ($FileURL in $FileURLs)
{
    $Pieces = $FileURL.Split("/")
    $FileName = $Pieces[$Pieces.Count - 1]
    $FilePath = Join-Path $SavePath $FileName
   
    $Client = New-Object System.Net.WebClient
   
    if (($Username -ne $null) -and ($Password -ne $null))
    {      
        $Client.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)        
    }
   
    Write-Host ("[Download] {0} -> {1} ..." -f $FileURL, $FilePath) -NoNewline
    $Client.DownloadFile($FileURL, $FilePath)
    Write-Host " Finish"
}

핵심은 WebClient 개체를 만들고 DownloadFile 메서드를 호출 하는 것.

Upload도 마찬가지로 UploadFile 메서드를 호출 하면 된다.

Add-WebFile.ps1

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
param(
    [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
    [String[]]$Files,
    [Parameter(Mandatory=$true, Position=1)]
    [String]$TargetPath,
    [String]$Username,
    [String]$Password
)

foreach ($File in $Files)
{
    if (Test-Path $File)
    {
        $FileName = (Get-ChildItem $File).Name
        if ($TargetPath[$TargetPath.Length - 1] -ne "/") { $TargetPath += "/" }
       
        $TargetFullPath = ("{0}{1}" -f $TargetPath, $FileName)
        $Client = New-Object System.Net.WebClient
   
        if (($Username -ne $null) -and ($Password -ne $null))
        {      
            $Client.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
        }
        Write-Host ("[Upload] {0} -> {1} ..." -f $File, $TargetFullPath) -NoNewline
        $Client.UploadFile($TargetFullPath, $File)
        Write-Host " Finish"
    }
    else
    {
        Write-Host ("Wrong File Path : {0}" -f $File)
    }
}

이름 정하기가 까다롭다. Get-Verb 안에서 동사를 선택 하는데 Download, Upload 는 없으므로 Get , Add를 사용 했는데 적절 한지 모르겠다. Import, Export도 고려 해 봤지만 wget 명령이 생각 나서 Get은 쓰고 싶었고, 그렇다고 Upload를 Set으로 하는것도 마음에 안든다. Update 가 더 가까운 의미 인것 같기도 하다.


Powershell – Network Adapter Enable, Disable

수요일, 7 7월 2010

WmiObject 를 이용하여 네트워크 장치(랜카드, NIC)를 활성화, 비 활성화 시킨다.

핵심은 win32_networkadapter WmiObject 를 이용하여 해당 Network Adapter Object를 추출한뒤 .Enable() .Disable() 메서드를 호출 하는 것이다.

Set-NetworkAdapterStatus.ps1

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
$Adapters = gwmi win32_networkadapter | ?{$_.PhysicalAdapter}
$Adapters | select index, name, NetEnabled | Format-Table -AutoSize

[int]$SelectedIndex = Read-Host "Select Network Adapter index "

$SelectedAdapter = $Adapters | ?{$_.index -eq $SelectedIndex}

if ($SelectedAdapter -eq $null)
{
    Write-Error "Wrong index"
    return
}

$SelectedAdapter | select Name, NetEnabled | Format-Table -AutoSize

Write-Host "1: Enable"
Write-Host "2: Disable"
[int]$Status = Read-Host "Select Status "

if ($Status -eq 2)
{
    $SelectedAdapter.Disable()
}
elseif ($Status -eq 1)
{
    $SelectedAdapter.Enable()
}

Powershell Test-Connection 으로 네트워크상의 Host들 확인하기

일요일, 27 6월 2010

Powershell cmdlet 중 하나인 Test-Connection 은 네트워크상의 Host와 ICMP 패킷을 이용하여 연결을 테스트 할 수 있다. 이 cmdlet은 흔히 cmd 에서 사용하는 ping.exe와 매우 유사하다.

Test-Connection [-ComputerName] <string[]> [[-Source] <string[]>] [-AsJob] [-Authentication {Default | None | Connect | Call | Packet | PacketIntegrity | PacketPrivacy | Unchanged}] [-BufferSize <int>] [-Count <int>] [-Credential <PSCredential>] [-Delay <int>] [-Impersonation {Default | Anonymous | Identify | Impersonate | Delegate}] [-Quiet] [-ThrottleLimit <int>] [-TimeToLive <int>] [<CommonParameters>]

ping.exe 로 할 수 있는 것은 거의 다 할 수있고, Powershell Cmdlet인 만큼 다른 cmdlet과 조합해 쓰면 더욱 유용할 것이다.

간단한 것부터 시작 해 보자.
IP가 192.168.0.10인 Host 로 Test-Connection 한다.

Test-Connection 192.168.0.10    # Test-Connection -ComputerName 192.168.0.10

성공적이라면 다음과 같은 결과를 볼 것이다.

Source   Destination  IPV4Address  IPV6Address                  Bytes Time(ms)
------   -----------  -----------  -----------                  ----- --------
TALSU-PC 192.168.0.20 192.168.0.20 fe80::3d95:e8c9:298f:33dc%13 32    0      
TALSU-PC 192.168.0.20 192.168.0.20 fe80::3d95:e8c9:298f:33dc%13 32    0      
TALSU-PC 192.168.0.20 192.168.0.20 fe80::3d95:e8c9:298f:33dc%13 32    0      
TALSU-PC 192.168.0.20 192.168.0.20 fe80::3d95:e8c9:298f:33dc%13 32    0

성공적으로 ICMP 패킷을 보내고 응답을 받은 모습이다. (자신의 IP로 하면 100% 성공할 수 있다.)

실패하면 다음과 같은 Error 가 발생 한다.

Test-Connection : ’192.168.0.10′ 컴퓨터에 대한 연결 테스트가 실패했습니다. 데이터베이스를 검색하는 동안 복구할 수 없는 오류가 발생했습니다
위치 줄:1 문자:16
+ Test-Connection <<<< 192.168.0.10 | clip
+ CategoryInfo : ResourceUnavailable: (192.168.0.10:String) [Test-Connection], PingException
+ FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand

ICMP 패킷을 보내고 응답이 없을때마다. 같은 에러가 계속 발생 한다.

Error가 보고 싶지 않고 접속 결과만 얻기 위해서는 -Quiet Parameter를 사용면 그 결과에 따라 $true, $false (Bool)를 반환 한다.

Test-Connection 192.168.0.10 -Quiet     # Error 없이 [bool] 타입을 반환한다.

이번에는 한번에 같은 Class에 있는 Host 들에게 모두 Test-Connection을 시도해 보자.

1..255 | Foreach-Object { Test-Connection "192.168.0.$_" }

192.168.0.1 ~ 192.168.0.255 까지 모두 Test-Connection을 시도 하면서 결과를 보일 것이다. 그런데 ICMP패킷을 4번씩 보내고 응답이 없는 Host에는 Error 메세지가 4번씩 나오기 때문에 보기도 쉽지 않고 출력을 다른곳에 활용하기도 불편해 보인다.

ICMP 패킷을 하나만 보내고 결과는 Bool 타입으로 받아 응답하는 Host 번호만 보면 좋을 것 같다.

1..255 | Where-Object { Test-Connection "192.168.0.$_" -Count 1 -Quiet }

예상대로 동작한다.

보기 좋게 출력 되도록 조금더 손봐서 스크립트로 만들어두면 편리 하다.

Test-MultipleConnections1.ps1

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
param (
    [string]$Gateway,
    [int]$StartPosition = 1,
    [int]$EndPosition = 255
    )

if ($Gateway -match "(\d{1,3}\.){2}\d{1,3}")
{
    $StartPosition..$EndPosition |
    Foreach-Object {
        $Name = "{0}.{1}" -f $Matches[0], $_
               
        if (Test-Connection $Name -count 1 -quiet)
        {
            $Color = "Green"
            $Name += " - Success"
        }
        else
        {
            $Color = "Red"
            $Name += " - Fail"
        }
       
        Write-host $Name -ForegroundColor $Color       
    }
}

Test-MultipleConnections1.ps1

컬러풀 하고 보기 좋게 나온다. 하지만 PSObject 출력이 필요 할 때도 있다.

Test-MultipleConnections2.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
param (
    [string]$Gateway,
    [int]$StartPosition = 1,
    [int]$EndPosition = 255
    )

if ($Gateway -match "(\d{1,3}\.){2}\d{1,3}")
{
    $ResultObject = New-Object -TypeName PSObject
    Add-Member -InputObject $ResultObject -MemberType NoteProperty -Name Address -Value $null
    Add-Member -InputObject $ResultObject -MemberType NoteProperty -Name IsSuccess -Value $false
   
    $StartPosition..$EndPosition |
    Foreach-Object {
        $ResultObject.Address = "{0}.{1}" -f $Matches[0], $_
       
        $ResultObject.IsSuccess = Test-Connection $ResultObject.Address -count 1 -quiet
       
        $ResultObject
    }
}

Test-MultipleConnections2.ps1



Add-Member가 부담되고 좀더 쉽게 가려면

Test-MultipleConnections3.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
param (
    [string]$Gateway,
    [int]$StartPosition = 1,
    [int]$EndPosition = 255
    )

if ($Gateway -match "(\d{1,3}\.){2}\d{1,3}")
{
    $StartPosition..$EndPosition |
    Foreach-Object {
        $ResultHash = @{}
        $ResultHash.Address = "{0}.{1}" -f $Matches[0], $_     
        $ResultHash.IsSuccess = Test-Connection $ResultHash.Address -count 1 -quiet
       
        New-Object PSObject -Property $ResultHash
    }
}

출력은 Test-MultipleConnections2.ps1 와 유사하다.


Powershell 에서의 정규 표현식 (Regular Expressions)

목요일, 24 6월 2010

Powershell 에서도 쉽게 Regex(정규 표현식)을 사용할 수 있다. shell 환경인 만큼 그 활용 범위가 매우 크며 여러 상황에서 응용이 가능하다.

Regex를 사용하는 경우를 크게 2가지로 보면 검색(일치 확인) 과 치환작업으로 볼 수 있는데 Powershell은 두가지를 연산자 -match 와 -replace로 제공한다.

기본적으로 대소문자를 구분하지 않는 Powershell 환경이기 때문에 -match와 -replace도 대소문자를 구분 하지 않는다. 대소문자를 구분하기 위해서는 다른 연산자들과 같이 앞에 ‘c’를 붙혀 -cmatch, -creplace를 사용 하면 된다. (‘i’ 를 붙혀 -imatch -ireplace 도 있는데 이것은 -match -replace와 동일 하다.)


-match

[Source] -replace [Find Target] 형식으로 찾을 수 있다.

$text = "Windows PowerShell is a task-based command-line shell and scripting language designed especially for system administration."

$text -match "powershell"       # true

$text -cmatch "powershell"      # false

$text -cmatch "PowerShell"      # true

-match 연산자를 사용한 뒤에는 $Matches 라는 변수가 새로 생성 되는데 이 변수는 -match 연산자가 true를 반환 했을때 생성되며 match로 찾은 대상들이 Collection으로 담겨 있다.

현 상태에서 $Matches를 확인 해 보자.

PS C:\> $Matches
Name                           Value
----                           -----
0                              PowerShell

PowerShell이라는 문자열을 찾았으니 당연히 PowerShell 이 들어 있다.

그렇다면 이번엔 g로 끝나는 단어를 찾아 보자.

$text -match "\w+g"

결과는 True로 이지만 어떤 문자열이 match 되었는지는 알 수 없다. 이때 $Matches를 이용해 확인 해 보자.

PS C:\> $Matches
Name                           Value
----                           -----
0                              scripting

scripting 이라는 문자열이 match 됨을 확인 할 수 있다.

추가 적으로 Group 을 이용하여 검색 할때 다음과 같이 활용 할 수 있다.

$text -match '(\w+)-(\w+)'

문자열 중앙에 – 이 들어간 문자열을 찾을 것이다.

PS C:\> $Matches

Name                           Value
----                           -----
2                              based
1                              task
0                              task-based

Group으로 검색된 결과와 전체 결과까지 같이 포함된다.
$Matches[0] 이 전체 문자열이고 $Matches[1] 번부터 Group 1번이다.

위에서 눈여겨 볼 점은 “command-line” 문자열도 발견될 조건인데 “task-based” 만 발견 되었다. 이유는 -match 연산자는 bool 값을 리턴하기 때문에 전체 문자열을 앞에서 부터 탐색하여 한번이라도 발견 되면 즉시 True를 리턴하고 종료 하기 때문이다.


이번에는 -replace 연산자로 치환을 해 보자. “Powershell” 이라는 문자열을 줄임 말인 “Posh” 로 바꿔보자.

[Source] -replace [Find Target], [Replace What] 형식이다.

$text = "Windows PowerShell is a task-based command-line shell and scripting language designed especially for system administration."

$text -replace "Powershell", "Posh"

즉시 다음과 같은 치환이 이루어진 문자열이 보일 것이다.

Windows Posh is a task-based command-line shell and scripting language designed especially for system administration.

문자열이 출력 되었다는 것은 -replace 연산자가 문자열을 return 했다는 뜻이다. 원본 문자열인 $text는 아무 변화가 없고 오로지 치환된 문자열이 return 될뿐이다.

이번에는 Group을 이용한 -replace를 해 보자.

$text -replace "(\w+)-(\w+)", "$2-$1"
Windows PowerShell is a - - shell and scripting language designed especially for system administration.

잠시 놀랄 것이다. 기대 했던 것은 -양쪽의 문자열을 서로 바꾸는 동작인데 결과는 빈칸이 들어가 버렸다. 이유는 특수문자 $는 Powershell 에서 변수 앞에 붙힌다. 그런데 이것은 “(쌍따옴표)로 감싼 문자열에서도 유효하다. 문자열에서 Powershell 특수문자를 일반 문자로 인식 시키기 위해서는 2가지 방법이 있다.

  • “(쌍따옴표) 대신 ‘(따옴표)로 감싸기
  • 특수문자 앞에 escape 문자인 `(GRAVE ACCENT)를 붙히기 (한글 이름을 잘 모르겠고 키보드 상에서 1(!) 키 왼쪽에 있는 것)
  • 그러면 이제 다음 예를 보자.

    나쁜 예

    $text -replace "(\w+)-(\w+)", "$2-$1"       # " 안에서는 $ 는 문자가 아니다.
    $text -replace "(\w+)-(\w+)", '`$2-`$1'     # ' 안에서는 `로 $를 escape 할 필요 없다.

    좋은 예

    $text -replace "(\w+)-(\w+)", '$2-$1'       # ' 를 이용해 $가 일반 문자로 취급됨
    $text -replace "(\w+)-(\w+)", "`$2-`$1"     # " 안에서 `로 $를 적절히 escape 하였다.

    올바르게 치환 되면 다음과 같은 결과를 얻을 수 있다.

    Windows PowerShell is a based-task line-command shell and scripting language designed especially for system administration.