Powershell – Remoting EnableCompatibilityHttpListener

화요일, 1 11월 2011

원격지의 서버에 Powershell Remote를 활성화 시키는 작업중 해당 네트워크가 80번 포트를 제외한 거의 모든 포트가 윈도우즈 방화벽이 아닌 네트워크 상에서 막혀 있었다. 즉 Default port 인 5985 / 5986 을 사용 할 수 없어 원격 접속이 불가능한 경우 였다.

이때 포트를 직접 80번으로 바꾸어 주었는데 WSMan:\localhost\Service\EnableCompatibilityHttpListener 를 활성화 시키면 자동으로 80번 포트가 추가로 사용 가능 하였다.

1
2
Set-Item WSMan:\localhost\Service\EnableCompatibilityHttpListener -Value true
Set-Item WSMan:\localhost\Service\EnableCompatibilityHttpsListener -Value true

그리하여 현제품의 Powershell Remote 관련 셋팅은 다음과 같다. (붙혀넣기용)

1
2
3
4
Enable-PSRemoting -Force
Set-item WSMan:\localhost\Client\TrustedHosts -Value * -Force
Set-Item WSMan:\localhost\Service\EnableCompatibilityHttpListener -Value true
Set-Item WSMan:\localhost\Service\EnableCompatibilityHttpsListener -Value true

Powershell – 원격 접속을 위한 Credential 만들기

금요일, 7 10월 2011

원격 접속을 위해 Enter-PSSession 또는 Invoke-Command 를 수행 할 때 -Credential 파라메터가 필요하다. 사용자 이름만 적으면 자동으로 대화 상자가 뜨면서 입력 할 수 있다.

Enter-PSSession -ComputerName [대상] -Credential [대상의 사용자]

직접 Password 를 입력 할 수 없고 대화상자가 뜨는 이유는 -Credential 파라메터에 PSCredential 타입의 개체가 필요한데 이것을 만드는데 사용되는 SecureString 개체가 대화상자를 통해서만 만들수 있다.

SecureString 만들기

아래와 같이 직접 만들 수 있다.

$secureString = Read-Host -AsSecureString

명령을 입력하면 사용자의 입력을 기다릴 것이다.
이 입력은 파라메터나 파이프라인으로 전달 할 수 없고 오로지 직접 입력해야 하는 것이다.

그러나 스크립트로 자동화 하기 위해서는 사용자의 입력없이 자동으로 돌아가야 하기 때문에 일반적으로 다음과 같이 처리한다.

$secureString = Read-Host -AsSecureString
$secureString | ConvertFrom-SecureString

ConvertFrom-SecureString를 사용하면 SecureString 개체를 문자열로 볼 수 있는데 물론 원본 string은 아니고 알아 볼수 없는 암호화된 string 이다.

ConvertFrom-SecureString 명령을 통해서 만들어진 결과물은 다시 ConvertTo-SecureString 으로 다시 SecureString 타입으로 만들 수 있다.

따라서 암화화된 string 을 파일(또는 어디든) 에 저장 해 두고 필요할때 ConvertTo-SecureString 으로 다시 SecureString 개체로 만들고 사용하는 것이다.

Read-Host -AsSecureString | ConvertFrom-SecureString | Set-Content "credential.txt"
# creadential.txt 에 암호화된 문자열이 저장되어 있다. 이것을 재사용함.
$secureString = Get-Content "credential.txt" | ConvertTo-SecureString

이 방식의 단점은 $secureString을 저장한 파일을 미리 만들어놔야 한다는 것이다. 즉, 동적으로 스크립트 상에서 SecureString을 만들어 낼 수 없다.

때문에 나는 직접 다음과같은 스크립트를 작성하여 사용한다.

New-SecureString

1
2
3
4
5
6
7
8
9
10
11
12
Function New-SecureString {
    param
    (
    [parameter(Mandatory=$true)]
    [string]$Value
    )

    $secureString = New-Object System.Security.SecureString
    $Value.ToCharArray() | %{ $secureString.AppendChar($_) }

    return $secureString
} Export-ModuleMember -Function New-SecureString

파라메터로 일반 String을 받아 SecureString으로 만든다.

# 즉시 만들어짐.
$secureString1 = New-SecureString -Value "1234qwer"

# 입력과정이 한번더 들어감.
$secureString2 = Read-Host -AsSecureString
"1234qwer"

# $secureString1 과 $secureString2 는 같다.

PSCredential를 생성하는 스크립트도 만들어두면 편하다.

New-PSCredential

1
2
3
4
5
6
7
8
9
10
11
Function New-PSCredential {
    param
    (
    [parameter(Mandatory=$true)]
    [string]$Username,
    [parameter(Mandatory=$true)]
    [string]$Password
    )

    New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $(New-SecureString -Value $Password)
} Export-ModuleMember -Function New-PSCredential

SecureString 을 만들어 PSSession을 만드는 예

# 접속에 필요한 정보를 아래와 같이 가정함.
# computername - talsu.net
# username - Administrator
# password - 1234qwer

$cred = New-PSCredential -Username "Administrator" -Password "1234qwer"
$session = New-PSSession -ComputerName talsu.net -Credential $cred

#session 이 만들어 졌다. Invoke-Command 하거나 Enter-PSSession 할 수 있다.

# Invoke-Command
Invoke-Command -Session $session -ScriptBlock {Get-Process}

# Enter-PSSession
Enter-PSSession -Session $session

이것들을 잘 활용하면 수백 수천대의 서버를 장난감처럼 다룰수 있다.


Powershell – Remoting (원격 접속)

금요일, 7 10월 2011

원격지에 있는 Powershell 에 접속이 가능하다 정확히는 Session 을 만들수 있는데 이 Session을 이용하여 원격지에 있는 Powershell 과 상호작용이 가능한것이다.
자주쓰게 되는 기능인데 물어보는 분들이 많아 정리 해 둔다.
자세한 규칙이나 프로토콜, 원리등을 설명하면 20페이지정도 문서가 나올꺼 같지만 여기선 필요한 과정들에 대한 설명만 기록한다.

바쁜사람들은 아래를 보고 따라하자.

A 가 B 에 원격 접속 하고 싶을 때.

초간단 설정

정말 바쁘거나 세부내용을 알기 싫고 모르겠으면 A와 B 양쪽에 아래 명령을 입력하고 “사용하기” 로 넘어가자.

1
2
Enable-PSRemoting -Force
Set-item WSMan:\localhost\Client\TrustedHosts -Value * -Force

A 측에 할 작업

Enable-PSRemoting

Enable-PSRemoting

다음과 같은 셋팅을 한꺼번에 하려고 한단다.

1. WinRM 서비스 시작 또는 다시 시작(이미 시작된 경우)
2. WinRM 서비스 유형을 자동 시작으로 설정
3. 모든 IP 주소에 대한 요청을 허용하는 수신기 만들기
4. WS-Management 트래픽에 대한 방화벽 예외 사용(http 전용)

그뒤 SDDL 별로 Set-PSSessionConfiguration 을 수행 하면서 질문을 하는데, 필요한것 것들에 대해 Y를 눌려 수행한다. (잘 모르겠으면 A – Yes to All)



이해가 잘 안가거나 다 Yes 하고 싶으면 -Force 옵션을 붙히자.

Enable-PSRemoting -Force

A 와 B 에 모두 할 작업

WSMan:\localhost\Client\TrustedHosts 설정

Powershell 원격제어를 하는 Protocol 은 상호간에 TrustedHost 가 설정 되어 있어야 한다.
다음 명령으로 현재 값을 확이해 보면 Value 가 공란으로 있을 것이다.

Get-Item WSMan:\localhost\Client\TrustedHosts

이 값을 상대방의 ComputerName, IP, Domain 등으로 설정하면된다.
값은 ,로 구분해 Array로 넣을 수 있고 * 를 사용할 수 있다.

예를 들어.

모든 대상에 대해서 허용.

Set-item WSMan:\localhost\Client\TrustedHosts -value *

컴퓨터 이름이 Alpha, Beta 인 두 대상을 허용.

Set-item WSMan:\localhost\Client\TrustedHosts -value Alpha,Beta

IP 가 192.168.0.10 인 대상을 허용.

Set-item WSMan:\localhost\Client\TrustedHosts -value 192.168.0.10

역시 잘 모르겠으면 다음을 입력하자.

Set-item WSMan:\localhost\Client\TrustedHosts -value *

사용 하기

ssh 나 telnet 처럼 원격으로 접속하여 사용하길 원하면 다음 명령을 이용한다.

Enter-PSSession [대상]


명령하나를 원격지에 수행하고 싶다면 Invoke-Command 를 이용한다.

1
Invoke-Command -ComputerName [대상] -ScriptBlock {[수행할 명령, 스크립트]}

1
Invoke-Command -ComputerName talsu.net -ScriptBlock {Get-Process}

ComputerName 대신 미리 New-PSSession 명령을 이용해 Session 을 만들어 그것을 사용할 수도 있다. 말그대로 같은Session에서 수행 되기 때문에 연속적인 작업을 나누어 할수 있고 여러번 수행할때 속도도 빠르다.

1
2
$session = New-PSSession [대상]
Invoke-Command -Session $session -ScriptBlock {[수행할 명령, 스크립트]}

1
2
$session = New-PSSession talsu.net
Invoke-Command -Session $session -ScriptBlock {Get-Process}


.NET – WPF JumpList

일요일, 17 7월 2011

JumpList 란 Windows 7 부터 지원 되는 기능으로 작업 표시줄에 등록된 아이콘을 마우스 오른쪽 버튼을 누를때 표시 되는 메뉴이다.

Windows Media Player의 JumpList

Internet Explorer, 탐색기, Windows Media Player 등에서 지원 되고 있고 최근의 많은 프로그램들도 지원 하고 있는 기능이다.

JumpList 메뉴를 보면 항목들이 Category로 나뉘어져 있는데, 일반적으로 다음과 같이 나뉜다.

    자주 사용하는 항목 Category
    최근에 사용한 항목 Category
    사용자가 직접 Pin 버튼을 눌려 고정된 항목 Category
    Custom 하게 구성된 항목 Category

WPF 에서 JumpList 를 구현하는것은 간단하다. (그리고 한정적이다.)

System.Windows.Shell 네임스페이스 아래에 있는 다음 클래스들을 주로 사용한다.

    System.Windows.Shell.JumpList
    System.Windows.Shell.JumpTask (System.Windows.Shell.JumpItem 클래스 상속받음)
    System.Windows.Shell.JumpPath (System.Windows.Shell.JumpItem 클래스 상속받음)

클래스 이름만으로 알 수 있듯이 JumpList 클래스가 실제 Application 에 설정되는 JumpList 이고 JumpItem 이라는 추상클래스를 항목을 상속 받은 JumpTask, JumpPath 를 JumpList에 추가하는 형식이다.

간단한 JumpList sample

먼저 JumpList를 어플리케이션에 설정하고 Notepad를 실행하는 JumpTask를 추가하는 코드를 보자.
WPF 어플리케이션을 만들고 App 클래스에 구현한다. (App.xaml.cs)

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
using System.Windows;
using System.Windows.Shell;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            var runNotepadTask = new JumpTask
            {
                Title = "Notepad",
                Description = "Run Notepad.",
                CustomCategory = "Actions",
                Arguments = " ",
                IconResourcePath = @"C:\Windows\notepad.exe",
                ApplicationPath = @"C:\Windows\notepad.exe",
            };

            var jumpList = new JumpList();
            jumpList.JumpItems.Add(runNotepadTask);
            jumpList.ShowFrequentCategory = true;
            jumpList.ShowRecentCategory = true;

            JumpList.SetJumpList(Application.Current, jumpList);

            base.OnStartup(e);
        }
    }
}

JumpList의 static 메서드인 SetJumpList 로 Application에 JumpList를 설정하는 것에 주목하자.

이것은 완전히 동일하게 XAML 로 작정할 수 있다. (App.xaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Application x:Class="WpfApplication1.App"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            StartupUri="MainWindow.xaml">
    <JumpList.JumpList>
        <JumpList ShowRecentCategory="True"
                 ShowFrequentCategory="True">
            <JumpTask Title="Notepad"
                     Description="Run Notepad."
                     CustomCategory="Actions"
                     Arguments=" "
                     ApplicationPath="C:\Windows\notepad.exe"
                     IconResourcePath="C:\Windows\notepad.exe"/>
        </JumpList>
    </JumpList.JumpList>
</Application>

작업표시줄의 아이콘에 오른쪽 버튼을 눌렸을때 다음과 같이 확인 할 수 있다.


참고로 JumpList 클래스는 여러개 생성 할 수 있지만, Application에 연결 할수 있는 것은 오로지 한번에 하나 이다.

코드를 보면 매우 간단 함을 느낄 수 있다. JumpItem을 상속 받은 JumpTask 를 하나 만들고 JumpList에 추가 하기만 했다.

JumpTask 클래스의 값에 따라 표시되는 형식이나 동작이 달라 진다.

다음은 JumpItem 클래스와 JumpTask 클래스의 내부이다.

JumpItem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**************************************************************************\
    Copyright Microsoft Corporation. All Rights Reserved.
\**************************************************************************/


namespace System.Windows.Shell
{
    public abstract class JumpItem
    {
        // This class is just provided to strongly type the JumpList's contents.
        // It's not externally extendable.
        internal JumpItem()
        {
        }
 
        public string CustomCategory { get; set; }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

CustomCategory 항목은 카테고리를 나타 내는데 위 예제에서는 Actions 로 입력하여 Actions 카테고리가 생성 되었다. 만약 설정하지 않는다면 Tasks 라는 기본값 Category 에 들어 갈 것이다. (Windows 7 영문판 기준)

JumpTask

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
/**************************************************************************\
    Copyright Microsoft Corporation. All Rights Reserved.
\**************************************************************************/


namespace System.Windows.Shell
{
    public class JumpTask : JumpItem
    {
        public JumpTask() : base()
        {}

        public string Title { get; set; }

        public string Description { get; set; }

        public string ApplicationPath { get; set; }
 
        public string Arguments { get; set; }
 
        public string WorkingDirectory { get; set; }

        public string IconResourcePath { get; set; }
 
        public int IconResourceIndex { get; set; }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

메서드 하나 없이 단순한 속성 들만 존재 하고 이름만으로도 이해가된다. 속성들에 대한 자세한 설명은 MSDN을 참고 하자

MSDN – JumpTask

JumpTask 사용시 주의 사항 – Argument를 설정하지 않거나 String.Empty로 할 때, Pin to List 나 AddToRecentCategory 가 제대로 동작하지 않는다. 공백 하나라도 넣는것이 좋다. 이것이 버그인지는 아직 파악하지 못했다.

JumpPath 항목은 FilePath를 이용하여 실행한 Application 이 File을 로드하게 한다. (물론 새 Task로.)

JumpPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**************************************************************************\
    Copyright Microsoft Corporation. All Rights Reserved.
\**************************************************************************/


namespace System.Windows.Shell
{
    public class JumpPath : JumpItem
    {
        public JumpPath() : base()
        {}

        public string Path { get; set; }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

혼돈 하지 말아야 할것은 .txt 파일 경로를 입력 했다 해서 notepad 가 열리는 것이 아니라 JumpList를 설정한 Application으로 .txt 파일경로가 전달 되는데 Registry 에 Application 이 .txt 파일에 연결되지 않았다면 JumpList 에 보이지도 않는다.

자주 사용하는 항목(Frequent), 최근 항목(Recent)

마지막으로 기본 카테고리인 자주 사용하는 항목(Frequent), 최근 항목(Recent) 에 JumpItem을 추가 하기 위해서는 JumpList 클래스의 static 메서드인 AddToRecentCategory() 를 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void AddTask(object sender, RoutedEventArgs e)
{
    var runNotepadTask = new JumpTask
    {
        Title = "Notepad",
        Description = "Run Notepad.",
        Arguments = " ",
        IconResourcePath = @"C:\Windows\notepad.exe",
        ApplicationPath = @"C:\Windows\notepad.exe"
    };

    JumpList jumpList = JumpList.GetJumpList(App.Current);
    JumpList.AddToRecentCategory(runNotepadTask);
    jumpList.Apply();
}

추가할 List에 .AddToRecentCategory() 를 하고 .Apply()를 잊지 말자.

자주 사용하는 항목 (Frequent)는 따로 추가하는 메서드가 없고 AddToRecentCategory()의 빈도에따라 자동적으로 나타 나는데, MSDN 에 따르면 일반적으로 Recent 와 Frequent를 동시에 표시 하지 않고 하나만 표시 한다고 한다. (물론 2개다 표시해도 아무 이상 없다.)

Recent 에 추가된 항목은 사용자가 직접 오른쪽 클릭후 삭제할 수 있다. 어플리케이션에서 JumpList 자체를 리셋해도 Recent는 남아 있게 되는데 이것은 Windows 에서 별도로 저장하고 있기 때문이다. 다음 경로에 저장 된다.
%AppData%\Microsoft\Windows\Recent\AutomaticDestinations


.NET – Command Queue 구현

금요일, 15 7월 2011

즐겨쓰는 Command Queue 방식.

Polling 하는 Thread를 따로 두지 않는다.

Command Interface

1
2
3
4
interface IExecutable
{
    void Execute();
}

Command Executer

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
/// <summary>
/// The executer with queue.
/// </summary>
internal class ExecuterWithQueue
{
    /// <summary>
    ///   The is executing.
    /// </summary>
    private bool isExecuting;

    /// <summary>
    ///   The queue.
    /// </summary>
    private ConcurrentQueue<IExecutable> queue;

    /// <summary>
    /// Gets Queue.
    /// </summary>
    private ConcurrentQueue<IExecutable> Queue
    {
        get
        {
            if (this.queue == null)
            {
                this.queue = new ConcurrentQueue<IExecutable>();

                Logger.Write("Queue", string.Format("{0} Queue Created.", "IExecutable"), TraceEventType.Verbose);
            }

            return this.queue;
        }
    }

    /// <summary>
    /// The enqueue.
    /// </summary>
    /// <param name="command">
    /// The command.
    /// </param>
    public void Enqueue(IExecutable command)
    {
        Queue.Enqueue(command);

        Logger.Write("Enqueue", string.Format("{0} Queue length : {1}", "IExecutable", Queue.Count), TraceEventType.Verbose);

        DequeueAndExecute();
    }

    /// <summary>
    /// The dequeue and execute.
    /// </summary>
    private void DequeueAndExecute()
    {
        if (this.isExecuting)
        {
            return;
        }

        IExecutable command;

        if (!Queue.TryDequeue(out command))
        {
            return;
        }

        try
        {
            this.isExecuting = true;

            command.Execute();
        }
        catch (Exception ex)
        {
            Logger.Write(ex);
        }
        finally
        {
            this.isExecuting = false;

            DequeueAndExecute();
        }
    }
}

Powershell – Ping-MultipleTarget

목요일, 24 3월 2011

ICMP 패킷을 이용하여 Ping 결과를 bool 로 리턴하는 Function을 작성하고, 이를 이용하여 지정된 범위 의 모든 주소에 Ping을 시도하여 Host들의 상태를 확인 한다.

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
param
(
    [Parameter(Mandatory=$true)]
    $BaseIp,
    $Start = 1,
    $End = 255,
    $Timeout = 100
)

Function Ping-Target
{
    param (
        [Parameter(Mandatory=$true)]
        [string]$HostNameOrAddress,
        [int]$Timeout = 100
        )
   
    $pingObject = New-Object System.Net.NetworkInformation.Ping
    $pingReply = $pingObject.Send($HostNameOrAddress, $Timeout)
       
    return $pingReply.Status -eq [System.Net.NetworkInformation.IPStatus]::Success 
}

$Start..$End | %{ $BaseIp + $_ } |
    %{
        if ($(Ping-Target $_ $Timeout))
        {
            Write-Host $($_ + " .. Success") -ForegroundColor Green
        }
        else
        {
            Write-Host $($_ + " .. Fail") -ForegroundColor Red
        }
    }
Ping-MultipleTarget

Ping-MultipleTarget Sample.


Powershell – Resize Powershell Window size

월요일, 22 11월 2010

Powershell 창을 사용 할 때 가끔 폭이 너무 작아 불편 할 때가 있다. Height는 창을 늘리면 늘어 나지만 Width는 특정 크기 이상 최대화를 해도 늘어 나지 않는다. Property가 많은 개체를 열어 보거나 긴 text line을 봐야 하는 경우 갑갑 하기도 하다.

이때는 $Host.UI.RawUI.WindowSize 값을 조절 해 줌으로써 넓은 화면을 사용 할 수 있다.

단 변경할때 $Host.UI.RawUI.BufferSize 도 함께 조절 해야 실제 창에 맞게 text 가 표시 됨을 명심하고 특히 ‘Buffer Width는 Window Width 보다 같거나 커야 한다’ 라는 규칙만 주의 하여 조절 하자.

아래 스크립트는 -WidthPersent Parameter로 지정된 폭으로 창으로 확장 하거나. 값을 입력하지 않을 때 창 크기를 최대화 한다.

Set-WindowSize

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
function Set-WindowSize
{
    param ($WidthPercent)

    if ($WidthPercent -gt 100)
    {  
        $WidthPercent = 100
    }

    $WindowSize = $Host.UI.RawUI.WindowSize
    $BufferSize = $Host.UI.RawUI.BufferSize

    $CurrentWindowWidth = $WindowSize.Width
    $Width = $Host.UI.RawUI.MaxPhysicalWindowSize.Width

    if ($WidthPercent -ne $null)
    {
        $Width = $Width * $WidthPercent / 100  
    }
    else
    {  
        $WindowSize.Height = $Host.UI.RawUI.MaxPhysicalWindowSize.Height
    }

    $WindowSize.Width = $Width
    $BufferSize.Width = $Width

    if ($CurrentWindowWidth -gt $WindowSize.Width)
    {
        $Host.UI.RawUI.WindowSize = $WindowSize
        $Host.UI.RawUI.BufferSize = $BufferSize
    }
    else
    {
        $Host.UI.RawUI.BufferSize = $BufferSize
        $Host.UI.RawUI.WindowSize = $WindowSize
    }
}

$Host.UI.RawUI.MaxPhysicalWindowSize 또는 $Host.UI.RawUI.MaxWindowSize 값으로 현재 위치에서의 최대 창 크기를 파악 할 수 있다.


Powershell – Invoke-BatchCommand

화요일, 2 11월 2010

일반 cmd 에서 잘 수행되던 명령이 Powershell 에서 똑같이 입력 했을 때 오류가 나는 경우가 있다. 대부분 특수문자가 있을때 Powershell이 특수 문자를 특정 연산자로 인식하면서 의도하지 않은 동작이 일어나는 경우 이다.
예를 들어 svn Dump file을 load하는 경우 다음과 같은 명령을 사용한다.

svnadmin load C:\svn\test < .\test.Dump

cmd 에서는 잘 실행되지만 Powershell 에서는 다음과 같은 Error가 날 것이다.

‘<' 연산자는 나중에 사용하도록 예약되어 있습니다.
위치 줄:1 문자:21
+ svnadmin load test < <<<< .\test.Dump
+ CategoryInfo : ParserError: (<:OperatorToken) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : RedirectionNotSupported

문제를 해결 하기 위해서는 Error 를 발생시킨 연산자를 Powershell이 올바르게 사용 할 수 있도록 수정 해 줘야 하는데, 번거롭고 정확히 파악 하지 못했을때에는 귀찮은 작업이 된다.

이럴 때 사용할 명령을 실제 cmd로 실행 시키는 Invoke-BatchCommand 라는 Function을 만들어 쓴다. 과정은 function parameter로 받은 문자열을 가지는 임시의 .bat 파일을 만들고 이 파일을 실행 한뒤 작업이 종료되면 .bat 파일을 지운다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function Invoke-BatchCommand
{
    param (
        [Parameter(Mandatory=$true)]
        $Command,
        $Path=$(Get-Location)
        )

    $TempFileName = "{0}.bat" -f [System.IO.Path]::GetRandomFileName()
    $TempFilePath = Join-Path $Path $TempFileName

    Set-Content -Path $TempFilePath -Value $Command -Force

    if (Test-Path $TempFilePath)
    {
        & $TempFilePath
        Remove-Item $TempFilePath -Force
    }
}

사용예

1
2
$executionCommand = "svnadmin load C:\svn\test < .\test.Dump"
Invoke-BatchCommand $executionCommand

Powershell – Parameter Attribute

월요일, 13 9월 2010

Powershell Cmdlet, Function을 이용 하다 보면 명령 뒤의 Parameter를 필수 적으로 입력을 요구 할 때가 있다. 또 명령어 뒤에 -[parameter명] 을 붙히지 않고 공백으로 구분하여 입력 하면 자동으로 순서대로 Parameter를 인식 하는데 이런 동작들은 Cmdlet, Function 내부에 진입 하기 전에 Parameter Attribute 에 의해서 조절 된다.

즉 꼭 필요로 하는 Parameter를 Cmdlet 안에서 $null 체크를 하는 것이 아니라 (할 수도 있겠지만), Parameter Attribute 에 의해서 값이 체크 되고 통과 하지 않으면 자동으로 Parameter를 물어 본다.

예를 들어

Set-ExecutionPolicy

를 입력 하면 다음과 같이 ExecutionPolicy 를 물어 볼 것이다.

PS D:\myScripts> Set-ExecutionPolicy

cmdlet Set-ExecutionPolicy(명령 파이프라인 위치 1)
다음 매개 변수에 대한 값을 제공하십시오.
ExecutionPolicy:

물론 당연하게 느껴 진다. 실행 정책(Execution Policy)을 설정(Set) 한다고 했는데 어떤 값으로 Set 할지 알려 주지 않았기 때문에 당연히 Cmdlet 이 ExecutionPolicy 값을 요구 한다.

이것은 Set-ExecutionPolicy Cmdlet 내부 Process 에 정의 되어 있는 것이 아니고 Parameter Attribute 에 의해서 체크 되고 또 입력이 요구 되는 것이다.

예제로 간단한 스크립트를 작정하면서 어떻게 Parameter Attribute를 사용 하는지 알아 보자.

다음은 입력한 이름과 일치하는 파일 또는 디렉토리를 찾는 스크립트 Search-Item.ps1 이다.

1
2
3
param ($Name)

Get-ChildItem -Include $Name -Recurse

스크립트를 실행 해 보자. 만약 스크립트를 실행하는 현재 위치, 또는 하위 디렉토리에 test 라는 파일이 있다면 다음과 같이 검색 할 수 있다.

.\Search-Item.ps1 -Name test

test 라는 파일이 존재 하지 않으면 아무 결과도 볼 수 없을 것이고 있다면 다음과 비슷한 결과를 볼 수 있을 것이다.

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----      2010-03-24   오후 3:27            test

이번엔 Parameter로 아무 것도 입력 하지 않고 스크립트를 실행 해 보자.

.\Search-Item.ps1

아마도 현재 위치를 포함한 하위의 모든 파일, 디렉토리가 나올 것이다. 분명 잘 못된 동작이다.

그렇다면 Search-Item.ps1 의 -Name Parameter를 강제로 입력 하도록 하는것이 해결 방법중 하나가 되겠다. 이 때 Parameter Attribute를 유용하게 쓸 수 있다.

Search-Item.ps1을 다음과 같이 고쳐 보자.

1
2
3
param ([Parameter(Mandatory=$true)]$Name)

Get-ChildItem -Include $Name -Recurse

Mandatory (필수) Property를 true 로 설정 했다. 즉 $Name Parameter는 필수로 입력 해야 된다는 이야기 이다. 물론 $false로 설정하면 반대의 효과이다.

그리고 또 다시 Parameter를 입력하지 않고 스크립트를 실행 시키면, 의도 대로 Parameter를 요구 한다.

PS D:\myScripts> .\Search-Item.ps1

cmdlet Search-Item.ps1(명령 파이프라인 위치 1)
다음 매개 변수에 대한 값을 제공하십시오.
Name:

Parameter Attribute 에는 Mandatory 이외에도 몇가지 Property가 더 있는데 다음 링크를 참고 한다.


MSDN – ParamerterAttribute Properties


내가 즐겨 쓰는 Property 로는

    HelpMessage

    : 파라메터 설명 넣기

    Mandatory

    : 필수 Parameter

    Position

    : Parameter 이름을 명시 하지 않을 때 (-Name 과 같은.) Parameter의 위치

    ValueFromPipeline

    : true로 하면 Pipeline로 받은 값을 해당 Parameter의 입력으로 넣을 수 있다.

이 옵션들을 적당히 사용했을 때 Search-Item.ps1은 다음과 같다.

Search-Item.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
param (
[Parameter(
    HelpMessage="Item Name",
    Mandatory=$true,
    Position=0,
    ValueFromPipeline=$true
)]
$Name,

[Parameter(
    HelpMessage="Base Path",
    Position=1 
)]
$BasePath = $(Get-Location)
)

Get-ChildItem -Path $BasePath -Include $Name -Recurse

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으로 하였다.