Contents of this directory is archived and no longer updated.

Posts on this page:

2 недели назад я выступал на рижском IT Pro с темой бэкапа в Windows Server 2008 R2 с помощью PowerShell. Доклад получится немного скомканный и на ряд вопров из зала я не смог ответить, т.к. эти вопросы требуют достаточно много времени на формулировку ответа. Поэтому я здесь подниму этот вопрос снова и постараюсь ответить на неотвеченные вопросы.

Я думаю, что уже все знают про отсутствие ntbackup.exe в системах начиная с Windows Server 2008. Его теперь заменяет отдельный системный компонент Windows Backup (или Server Backup) и, который, устанавливается как компонент сервера в оснастке Server Manager. Первая версия Server Backup была достаточно грубой и примитивной. Она позволяла бэкапить только тома целиком блочным методом без возможности выбора отдельных файлов и папок. Блочный метод работает на уровень ниже, чем файловая система, поэтому никакой речи о файлах быть не могло. Так же ему требовался выделенный том под хранение бэкапа и при инициализации тома под бэкапы, он форматировался. Бэкап SystemState производился отдельно и его нельзя было включить в состав бэкапа отдельного тома. Иными словами, первая версия была непотребна чуть более чем полностью, поэтому в отношении него говорить просто не о чем.

С выходом Windows Server 2008 R2 ситуация немного улучшилась. После тысяч жалоб от покупателей и срачей на форумах в новой системе добавился бэкап на уровне файловой системы (как и ntbackup), сохранив и улучшив первоначальную версию Server Backup. Новая версия бэкапа отличается следующими характеристиками по сравнению с первой версией:

  • Добавлена возможность бэкапа файлов и папок
  • Добавлена возможность комбинирования объектов бэкапа. Например, к бэкапу отдельных папок и файлов можно добавлять бэкап SystemState
  • Теперь можно делать инкрементальный бэкап для SystemState (включено по умолчанию)
  • Улучшенная поддержка PowerShell

Как и раньше, командлеты для Server Backup поставляются в виде отдельной оснастки, которая подключается командой:

Add-PSSnapin Windows.ServerBackup

И все командлеты для бэкапа будут иметь префикс WB (от Windows Backup):



Мы видим достаточно приличное количество командлетов и часть из них мы используем для работы. Все задания бэкапов в ServerBackup являются политиками бэкапа. Следовательно, командлетом New-WBPolicy мы будем создавать каждое задание бэкапа:

[↑] [Administrator] $pol = New-WBPolicy
[↑] [Administrator] $pol


Schedule            :
BackupTargets       :
VolumesToBackup     :
FilesSpecsToBackup  :
FilesSpecsToExclude :
BMR                 : False
SystemState         : False
VssBackupOptions    : VssCopyBackup



[↑] [Administrator]

Мы создали объект новой политики бэкапа, который имеет ряд свойств. Эти свойства по названиям уже отражают своё назначение. Давайте сначала выберем объекты бэкапа. Например, добавим какую-нибудь папку, которую мы захотим бэкапить. Новые объекты бэкапа создаются в 2 этапа:

  1. Создание самого объекта командой New-WBFileSpec
  2. Добавление этого объекта в политику бэкапа командой Add-WBFileSpec

Будь то отдельная папка или отдельный том. Это не относится только к Bare Metal Recovery (полный бэкап системного тома, SystemState и всех системных файлов) и к самому SystemState. Они могут сразу добавляться в политику.

[↑] [Administrator] $source1 = New-WBFileSpec -FileSpec "C:\Users"
[↑] [Administrator] $source1 | ft -a

FilePath  FileName IsRecursive IsIncludeSpec
--------  -------- ----------- -------------
C:\Users\ *               True          True


[↑] [Administrator] $exclusion = New-WBFileSpec -FileSpec "C:\Users\vpodans" -Exclude
[↑] [Administrator] $exclusion | ft -a

FilePath          FileName IsRecursive IsIncludeSpec
--------          -------- ----------- -------------
C:\Users\vpodans\ *               True         False


[↑] [Administrator] $source2 = New-WBFileSpec -FileSpec "D:\Users" -NonRecursive
[↑] [Administrator] $source2 | ft -a

FilePath  FileName IsRecursive IsIncludeSpec
--------  -------- ----------- -------------
D:\Users\ *              False          True


[↑] [Administrator] $source1, $source2, $exclusion | Add-WBFileSpec -Policy $pol
[↑] [Administrator] $pol


Schedule            :
BackupTargets       :
VolumesToBackup     :
FilesSpecsToBackup  : {C:\Users\*, D:\Users\*}
FilesSpecsToExclude : {C:\Users\vpodans\*}
BMR                 : False
SystemState         : False
VssBackupOptions    : VssCopyBackup



[↑] [Administrator]

Первой командой мы задали бэкап всей папки C:\Users. Второй командой мы посмотрели объект этой точки. Как видно из таблички, эта папка будет бэкапить полностью включая все подпапки. Но я не хочу бэкапить папку профиля одного из пользователей. Для этого в командлете New-WBFileSpec есть ключ –Exclude, который исключит эту папку из бэкапа. В следующей строке это видно по состоянию свойства IsIncludeSpec = $false. И чтобы исключить рекурсивную обработку папки (т.е. нам нужно бэкапить содержимое только текущей папки не трогая подпапки совсем) для неё достаточно указать ключ –NonRecursive. под каждую категорию вы можете добавлять пути для бэкапа через запятую в одной команде. Но для каждой категории надо писать новую команду. В конце мы видим, что наша политика уже обросла какими-то данными. Если мы захотим сюда добавить ещё бэкап целого тома или физического диска, то нам уже придётся работать с командлетами Get/Add-WBDisk и Get/Add-WBVolume:

[↑] [Administrator] Get-WBDisk


DiskName       : WDC WD3200JS-00PDB0 ATA Device
DiskNumber     : 1
DiskId         : d5fae841-0000-0000-0000-000000000000
TotalSpace     : 320072933376
FreeSpace      : 3591163904
Volumes        : {New Volume (S:\VM\Core), Camelot Share-2 (F:)}
ContainsBackup : False
BackupVolumeId : 00000000-0000-0000-0000-000000000000
Properties     : Dynamic, ValidTarget

<...>
[↑] [Administrator] $disk = Get-WBDisk | ?{$_.disknumber -eq 1}
[↑] [Administrator] Get-WBVolume -Disk $disk


VolumeLabel : New Volume
MountPath   : S:\VM\Core
MountPoint  : \\?\Volume{06d04bb0-1949-11de-a731-001fd08fc2f1}
FileSystem  : NTFS
Property    : ValidSource
FreeSpace   : 2133585920
TotalSpace  : 18875416576

VolumeLabel : Camelot Share-2
MountPath   : F:
MountPoint  : \\?\Volume{62bda1e6-b515-4102-b03f-40b7896ab0f3}
FileSystem  : NTFS
Property    : ValidSource
FreeSpace   : 1453031424
TotalSpace  : 301192970240



[↑] [Administrator]

Командлет Get-WBDisk отобразит нам все физические диски, которые подключены к системе (с учётом аппаратного рейда, разумеется). Если захотим добавить этот диск в бэкап, то отфильтровываем через Where-Object (или просто вопросительный знак) и добавляем его в политику командой Add-WBDisk. Get-WBDisk нам так же потребуется и для просмотра логических томов, поскольку Get-WBVolume в качестве аргумента принимает только объекты, полученные от команды Get-WBDisk. Т.е. сначала выбираем диск и только потом просматриваем нужные тома. И отфильтровав нужный том добавляем в политику, например:

[↑] [Administrator] Get-WBVolume -Disk $disk


VolumeLabel : New Volume
MountPath   : S:\VM\Core
MountPoint  : \\?\Volume{06d04bb0-1949-11de-a731-001fd08fc2f1}
FileSystem  : NTFS
Property    : ValidSource
FreeSpace   : 2133585920
TotalSpace  : 18875416576

VolumeLabel : Camelot Share-2
MountPath   : F:
MountPoint  : \\?\Volume{62bda1e6-b515-4102-b03f-40b7896ab0f3}
FileSystem  : NTFS
Property    : ValidSource
FreeSpace   : 1453031424
TotalSpace  : 301192970240



[↑] [Administrator] $volume = Get-WBVolume -Disk $disk | ?{$_.volumelabel -eq "new volume"}
[↑] [Administrator] Add-WBVolume -Policy $pol -Volume $volume


VolumeLabel : New Volume
MountPath   : S:\VM\Core
MountPoint  : \\?\Volume{06d04bb0-1949-11de-a731-001fd08fc2f1}
FileSystem  : NTFS
Property    : ValidSource
FreeSpace   : 2133585920
TotalSpace  : 18875416576



[↑] [Administrator] $pol


Schedule            :
BackupTargets       :
VolumesToBackup     : {New Volume (S:\VM\Core)}
FilesSpecsToBackup  : {C:\Users\*, D:\Users\*}
FilesSpecsToExclude : {C:\Users\vpodans\*}
BMR                 : False
SystemState         : False
VssBackupOptions    : VssCopyBackup



[↑] [Administrator]

Если я вдруг не захочу уже бэкапить добавленный том, то его можно спокойно удалить (фактически командами Remove-WB* можно удалить что угодной из политики):

Remove-WBVolume -Policy $pol -Volume $volume

Теперь настало время выбрать точки, в которые мы будем копировать наш бэкап. Точки назначения добавляются в бэкап тоже задаются в 2 этапа:

  1. New-WBBackupTarget — создаёт объект точки назначения бэкапа
  2. Add-WBBackupTarget — добавляет эту точку в политику бэкапа
[↑] [Administrator] $target = New-WBBackupTarget -VolumePath "F:"
[↑] [Administrator] $target = New-WBBackupTarget -VolumePath "E:"
[↑] [Administrator] Add-WBBackupTarget -Policy $pol -Target $target


Label                  : Camelot Share-1
WBDisk                 :
WBVolume               : Camelot Share-1 (E:)
Path                   : \\?\Volume{ca6dbf07-14ad-11de-937f-806e6f6e6963}
TargetType             : Volume
InheritAcl             : False
PreserveExistingBackup : False



[↑] [Administrator] $pol


Schedule            :
BackupTargets       : {E:}
VolumesToBackup     : {}
FilesSpecsToBackup  : {C:\Users\*, D:\Users\*}
FilesSpecsToExclude : {C:\Users\vpodans\*}
BMR                 : False
SystemState         : False
VssBackupOptions    : VssCopyBackup



[↑] [Administrator]

Вы так же можете указать бэкап сразу в сеть. Для этого в команде New-WBBackupTarget вместо параметра –VolumePath использовать параметр –NetworkPath и за ним уже указывать UNC путь к сетевой папке. Однако, следует учесть несколько нюансов:

  • При бэкапе в сеть исходный размер копируемых данных неизвестен, в связи с чем будет катастрофически расти фрагментация в сетевой папке
  • В сети вы можете поддерживать только одну актуальную копию бэкапа. Последующие бэкапы в эту же папку будут перезаписывать предыдущие архивы
  • Вы не можете одновременно делать бэкап на локальный том или диск и в сеть.

Примечание: хелп заявляет, что бэкап можно одновременно копировать на несколько томов/дисков. Мне ещё неизвестна полностью природа всего происходящего и только могу констатировать факт, что физически бэкап копируется только на первый указанный том или диск.

Теперь у нас есть 2 варианта: начать выполнение бэкапа немедленно, или регистрация нашей политике в системе для периодического выполнения в автоматическом режиме (по внутреннему шедулеру). В первом случае достаточно выполнить команду:

Start-WBBackup –Policy $pol

или зашедулить командой Set-WBSchedule:

[↑] [Administrator] Set-WBSchedule -Policy $pol -Schedule "10.08.2009 21:00"

tresdiena, 2009. gada 7. oktobri 21:00:00


[↑] [Administrator] $pol


Schedule            : {2009.10.07. 21:00:00}
BackupTargets       : {E:}
VolumesToBackup     : {}
FilesSpecsToBackup  : {C:\Users\*, D:\Users\*}
FilesSpecsToExclude : {C:\Users\vpodans\*}
BMR                 : False
SystemState         : False
VssBackupOptions    : VssCopyBackup



[↑] [Administrator] Set-WBPolicy –Policy $pol

И командой Set-WBPolicy наша политика регистрируется в системе. Шедулинг следует указывать в следующей форме:

Month.Day.Year Hours:Minutes

И теперь каждый день в 21:00 будет выполняться наше задание.

Сегодня мы рассмотрели основные моменты создания политики бэкапа в Windows Server 2008 R2 с использованием PowerShell. В следующей (или следующих) рассмотрим вопросы управления этими политиками и вопросы каталогизации/ротации архивов.

А знаете ли вы как можно легко получить список всех Enterprise CA в текущем домене? А в текущем лесу? Оказывается это очень легко! ADSI — самый лучший способ, если вы хотите пошариться в своей базе AD. Список таких CA находится по пути:

CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, DC=Domain, DC=COM

Последние 2 значения уже будут отличаться в зависимости от имени домена. Чтобы получить список объектов по этому пути нужно просто создать соответствующий LDAP объект. Объект делается просто, сначала указывается тип объекта [ADSI], следом идёт префикс ссылки LDAP:// (почти как HTTP://) и после префикса уже этот путь (который называется Distinguished Name или просто DN):

[Administrator] $CA = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, DC=co
ntoso,DC=COM"
[Administrator] $CA


distinguishedName : {CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com}
Path              : LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, DC=contoso,DC
                    =COM
[Administrator] $CA.distinguishedName
CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com

Если посмотреть этот путь в ADSIEdit.msc, то мы увидим, что это контейнер. А раз это контейнер, то нам нужно в него заглянуть. Здесь, к сожалению, нельзя сделать dir $CA.distinguishedName, а так хочется. Чтобы посмотреть содержимое нужно использовать свойство Children (ворненк, дети отаке!):

[Administrator] $ca.Children


distinguishedName : {CN=contoso-DC2-CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=co
                    ntoso,DC=com}
Path              : LDAP://CN=contoso-DC2-CA,CN=Enrollment Services,CN=Public Key Services, CN=Services, CN=Configurati
                    on, DC=contoso,DC=COM

distinguishedName : {CN=Contoso CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contos
                    o,DC=com}
Path              : LDAP://CN=Contoso CA,CN=Enrollment Services,CN=Public Key Services, CN=Services, CN=Configuration,
                    DC=contoso,DC=COM



[Administrator]

Уже отсюда невооружённым глазом видны имена CA. Собственно, можно показать только имя самого CA и компьютера, на котором работает этот CA:

[Administrator] $ca.Children | ft Name, DNSHostName

Name                                                        DNSHostName
----                                                        -----------
{contoso-DC2-CA}                                            {dc2.contoso.com}
{Contoso CA}                                                {DC1.contoso.com}


[Administrator]

Здесь есть один важный нюанс. Если получить этот LDAP объект в PowerShell 1.0, то он будет содержать только Distinguished Name, а свойство Children будет отсутствовать в нём. Для этого нужно воспользоваться свойством PSBase, в котором уже будет Children. Командой Select можете выводить на экран и другие свойства, какие вы захотите:

PS C:\> $ca.children


MemberType          : Method
OverloadDefinitions :
TypeNameOfValue     : System.Management.Automation.PSMethod
Value               :
Name                : children
IsInstance          : True



PS C:\> $ca.psbase.children

distinguishedName
-----------------
{CN=contoso-DC2-CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com}
{CN=Contoso CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com}


PS C:\> $ca.psbase.children | select Name, DNSHostname

Name                                                        DNSHostname
----                                                        -----------
{contoso-DC2-CA}                                            {dc2.contoso.com}
{Contoso CA}                                                {DC1.contoso.com}


PS C:\>

Поэтому для обратной совместимости между версиями лучше использовать PSBase. В этом объекте будут содержаться не только CA вашего домена, а во всех доменах вашего леса, поскольку эта часть AD реплицируется как Forest naming context, т.е. между всеми контроллерами в лесу. Жизнь была бы неинтересной, если в каждом новом домене приходилось бы переписывать хвост (которая определяет домен, в котором следует искать) каждый раз. Для универсальности можно пойти на военную хитрость — раздобыть FQDN текущего домена, разобрать его и воткнуть в LDAP запрос. Получить имя текущего домена можно очень просто, с использованием статического метода GetCurrentDomain() класса System.DirectoryServices.ActiveDirectory.Domain. На самом деле у этого класса есть ещё куча других полезных методов, поэтому не лишним будет заглянуть по ссылке.

PS C:\> [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()


Forest                  : contoso.com
DomainControllers       : {DC1.contoso.com}
Children                : {}
DomainMode              : Windows2003Domain
Parent                  :
PdcRoleOwner            : DC1.contoso.com
RidRoleOwner            : DC1.contoso.com
InfrastructureRoleOwner : DC1.contoso.com
Name                    : contoso.com



PS C:\>

И свойство Name будет содержать имя нашего домена. Что дальше? А дальше, вполне очевидно, что нам надо заменить все точки на строку вида ", DC=". Вот так:

PS C:\> "contoso.com" -replace "\.", ", DC="
contoso, DC=com

А перед первым именем эту часть можно написать ручками. В итоге универсальная часть кода получится вот такая:

$domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
$domain = "DC=" + $domain -replace '\.', ", DC="
$CA = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, $domain"

Вот теперь у нас есть всё необходимое, чтобы написать простеньку функцию:

function Get-CertificationAuthority ([string]$CAName) {
    $domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
    $domain = "DC=" + $domain -replace '\.', ", DC="
    $CA = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, $domain"
    $CAs = $CA.psBase.Children | %{
        $current = "" | Select CAName, Computer
        $current.CAName = $_ | %{$_.Name}
        $current.Computer = $_ | %{$_.DNSHostName}
        $current
    }
    if ($CAName) {$CAs = @($CAs | ?{$_.CAName -eq $CAName})}
    if ($CAs.Count -eq 0) {throw "Sorry, here is no CA that match your search"}
    $CAs
}

Если выполнить эту функцию без аргументов, то она вернёт все CA в лесу. Но можно указать и какой-то один для каких-то других целей.

По мотивам предыдущего поста и срача в ньюсгруппе microsoft.public.windows.powershell перепубликовываю скрипт Роберта Робело для определения кодировки, в которой сохранён файл.

filter Get-TextEncoding {
#requires -Version 2.0
    begin {
        [string]$BOM_Unicode = [Text.Encoding]::Unicode.GetPreamble()
        [string]$BOM_UTF7 = [Text.Encoding]::UTF7.GetPreamble()
        [string]$BOM_UTF8 = [Text.Encoding]::UTF8.GetPreamble()
        [string]$BOM_BigEndian = [Text.Encoding]::BigEndianUnicode.GetPreamble()
        [string]$BOM_UTF32 = [Text.Encoding]::UTF32.GetPreamble()
    }
    process {
        if ($_ -is 'IO.FileInfo') {
            $bytes = Get-Content -Literal $_.pspath -Encoding Byte -Total 4 -ErrorAction SilentlyContinue
            $value = if ($bytes) {
                if ($bytes[0..1] -as 'String' -eq $BOM_Unicode) {'Unicode'}
                elseif ($bytes[0..2] -as 'String' -eq $BOM_UTF7) {'UTF7'}
                elseif ($bytes[0..2]  -as 'String' -eq $BOM_UTF8) {'UTF8'}
                elseif ($bytes[0..1] -as 'String' -eq $BOM_BigEndian) {'BigEndian'}
                elseif ($bytes[0..3] -as 'String' -eq $BOM_UTF32) {'UTF32'}
                # undetermined, no BOM
                else {'Unknown'}
            # undetermined, zero bytes
            } else {'Unknown'}
            $_ | Add-Member NoteProperty Encoding $value -PassThru
        # not an IO.FileInfo
        } else {$_}
    }
}

ну и использование достаточно простое:

dir *.ps1 | Get-TextEncoding | Format-Table Name, Encoding

Уже тут видно мелкий косячок подсветки в PowerGUI. Name должно быть такого же цвета, что и Encoding. А выглядеть это будет примерно так:

[↓] [vPodans] dir desktop\*.ps1 | Get-TextEncoding | ft name, encoding

Name                                                        Encoding
----                                                        --------
Untitled1.ps1                                               BigEndian
untitled2.ps1                                               UTF8


[↓] [vPodans]

Не забудьте, что это будет работать только в V2. Для PowerShell 1.0 придётся чуточку подпилить его.

Когда пользователь начинает работать с PowerShell, то со временем перед ним возникает вопрос — какой редактор (вернее сказать, среду разработки) выбрать? Решений уже достаточно много, чтобы было из чего выбирать:

  • PowerShell ISE
  • PowerGUI
  • PrimalScript
  • PowerShellPlus

в большинстве случаев выбор делают между первыми двумя продуктами. Иногда споры об этом переходят в разряд религиозных войн. Истинные фанаты PowerShell выбирают ISE. В принципе, как просто редактор он вполне неплох, т.к. уже есть в коробке (начиная с первых CTP версий PowerShell V2) и для работы с ним никаких телодвижений делать не надо. Однако с ним есть ряд трудностей:

  • IntelliSense

В ISE отсутствует IntelliSense (автозавершение команд и свойств/методов объекта). А это весьма необходимая функция в среде разработки. Мне кажется, что это поняли уже все, кроме разработчиков PowerShell ISE. Вот как это может выглядеть:

PowerGUI IntelliSense support

  • Подсказки (ToolTips)

При наведении курсора на команду, не отображаются подсказки (Tooltips). В PowerGUI это выглядит действительно классно, точь-в-точь как в Visual Studio:

PowerGUI ToolTips

Причём, на этой картинке можно нажимать на стрелочки вверх/вниз для просмотра типов данных, которые принимаются в качестве аргумента. При наведении на переменную PowerGUI показывает даже тип и содержимое переменной (в разумных пределах).

  • Multiple runspaces

В ISE включена поддержка одноврменного редактирования нескольких скриптов (за счёт табов). Однако, все табы выполняются в одном runspace, что часто приводит к негативным последствиям, когда в двух разных табах используются одинаковые имена переменных. В связи с этим, данные из определённой переменной одного таба будут мигировать во второй таб, если эта переменная ещё не определена. Это может привести к неожиданным результатам работы скрипта.

  • Кодировка сохраняемых файлов

ISE по умолчанию сохраняет файлы в Big Endian кодировке. Вроде бы ничего криминального, но... Set-Authenticodesignature не умеет подписывать скрипты в Big Endian кодировке! Поэтому вы не сможете подписать штатными средствами ни один скрипт, который был сохранён в ISE! Для этого нужно прибегать к грязным хакам. Т.е. использовать такую строку, которая переопределит кодировку, в которой скрипт будет сохранён:

$psISE.CurrentFile.Save([Text.Encoding]::UTF8)
  • Исполнение сохранённых скриптов и Execution Policy

Вы можете в нём исполнять свой сценарий до тех пор, пока не сохраните его в файл. А вот когда вы редактируете уже сохранённый скрипт, то получаете бонус в виде того, что скрипт не будет исполняться внутри ISE, если у вас политика исполнения скриптов выставлена в AllSigned. ISE об этом честно предупреждает:

The script you are about to run will be saved 
Т.е. он сохраняет файл и пытается его запустить. В PowerGUI сделано куда более гуманно. Из основного редактора код условно копируется и вставляется в консоль PowerShell. Т.е. вы можете спокойно редактировать и отлаживать свой скрипт и только когда он будет готов к работе — подписывать скрипт. В случае с ISE вам придётся либо после каждого изменения сохранять и переподписывать скрипт (а это дико неудобно), либо выделять весь код и выбирать Run Selection. А это тоже не очень удобно. Плюс, сохранение скрипта перед исполнением ведёт к другой проблеме. Вы не сможете отменить изменения на более раннее состояние.

  • Поддержка профиля $Profile

В ISE вы не можете использовать свой профиль (который в консоли содержится в переменной $Profile), а только поддерживать дополнительный профиль, который находится в той же папке, что и основной, но под именем Microsoft.PowerShellISE_profile.ps1

  • Отслеживание изменений в файле

ISE не поддерживает проверку файла на изменения, хотя это должно быть удобно. Я часто редактирую свои файлы дома на нотебуке и на работе. Папка со скриптами синхронизируется между домом и работой через Live Sync. Я достаточно редко закрываю редактор, поэтому вечером сохраняю файл (который уже открыт в редакторе на работе) и всё. Утром, придя на работу я могу без переоткрытия файла могу продолжать его редактировать. PowerGUI просто сообщит, что файл был изменён и сам предложит загрузить последнюю сохранённую версию. С ISE придётся вручную переоткрывать файл.

  • About

Я не могу вспомнить программ, которые бы имели меню Help, но внутри не имели подменю About. но это просто мелочи уже, которые не влияют на удобство разработки скриптов.


Updated 04.10.2009

Если подвести это всё в табличку, то получится примерно так:

  PowerShell ISE PowerGUI PowerShell Plus
Built-In :yes: :no: :no:
Is free :yes: :yes: :no:
Fast start :no: :no: :no:
Powershell Support 100% <100% <100%
External PowerShell window :yes: :yes: :yes:
Remote PowerShell tab :yes: :no: :no:
IntelliSense :no: :yes: :yes:
Multiple Runspaces :yes: :yes: :no:
ToolTips :no: :yes: :yes:
Syntax highlighting :yes: :yes: :yes:
Error syntax highlighting :no: :yes: :yes:
Error autocorrection :no: :no: :yes:
Changed lines highlighting :no: :yes: :yes:
Outline support :no: :yes: :yes:
Support for signing :no: :yes: :yes:
Can sign within IDE :no: :no: :yes:
Run signed scripts in external window :yes: :no: :yes:
Readable command help :yes: :yes: :yes:
Configurable editor panes :yes: :yes: :yes:
Variable pane :no: :yes: :yes:
PS $Profile support :no: :yes: :yes:
BreakPoints :yes: :yes: :yes:
Code templates :no: :yes: :yes:
Print from editor :no: :yes: :yes:
Script autosave :no: :yes: :yes:

Примечание: последнее изменение таблицы 16.03.2010

Вобщем, я обозначил те вещи, которые я хотел бы видеть в хорошем редакторе. И значительное большинство моих хотелок уже есть в PowerGUI. О достоинствах PowerGUI можно говорить сколько угодно, но не буду смущать Диму Сотникова, поэтому скажу, что их продукт очень крутой. Но, в то же время, есть к чему стремиться. :-)

Однако, хочу ещё раз напомнить, что PowerGUI никогда не закроет первую строчку в таблице, что для религиозных фанатиков будет самым главным преимуществом и, который, затмит остальные недостатки ISE. Но мой выбор в этом вопросе достаточно очевиден.

После небольшого перерыва продолжаю допиливать свой вариант FCIV на PowerShell. И радостно могу сообщить, что уже есть версия 1.0, т.е. полностью отвечающая нашим требованиям. Что изменилось в новой версии?

  • Основная команда переименована с Get-PsFCIV в Start-PsFCIV;
  • Включена предварительная проверка файлов на блокировку;
  • В связи с предыдущим пунктом, к  параметру Show добавлен аргумент – Locked. В это свойство помещаются заблокированные файлы (а для них нельзя подсчитать хеш);
  • Включена проверка пути. Если путь к исходной папке не является путём файловой системы, то скрипт будет генерировать ошибку;
  • Исправлена ошибка невозврата на исходный путь, если в процессе работы произошла фатальная ошибка;
  • Параметр Show теперь выводит файлы из указанных категорий в красивое графическое окошко с использованием Out-GridView. Однако, следует учесть, что использование Out-GridView требует, чтобы был установлен .NET Framework 3.5 SP1.
  • Добавлен режим Quiet, который ничего не выводит на экран, а только генерирует коды возврата (LastExitCode). Коды возврата обновлены.
  • Пофиксены сообщения режимов Verbose и Debug.
  • Исправлены мелкие неточности в коде и произведена небольшая оптимизация кода.

А теперь и on-line справка по всем параметрам по просьбе трудящихся.

  • Path <String> — путь к папке, файлы которой следует посчитать или проверить. Допускаются относительные пути. Данный параметр обязателен.
  • XML <String> — путь к XML файлу, который содержит сведения о файлах. Если указанный файл не существует, то после пересчёта файлов по этому пути будет создан новый файл БД со сведениями о файлах. Параметр обязателен.
  • Include <String> — опциональный параметр в котором вы можете указать только конкретный файл из папки. В таком случае будет проверена не вся папка, а только указанный файл. В настоящее время параметр не поддерживает подстановочные знаки (wildcard), типа '*.ext' из-за обеспечения поддержки файлов с метасимволами (например, в имени содержатся квадратные скобки).
  • Action <String> — опциональный параметр, который указывает на действие над файлами, у которых не совпадают хеши или сведения о дате изменения и размере файла. Может принимать значение Rename или Delete. В первом случае к проблемным файлам добавляется расширение .BAD, а во втором файл просто удаляется.
  • Show <String[]> — опциональный параметр, в котором указываются категории файлов для дальнейшего анализа. В зависимости от результата проверки каждый файл (его имя) помещается в ту или иную категорию. Требует установленного .Net Framework 3.5 SP1. Может принимать один или несколько аргументов из списка:
    • Bad — содержит имена всех файлов, у которых обнаружен несовпадающий хеш или не совпадает дата/время изменеия и/или размер файла;
    • Locked — в эту категорию помещаются файлы хеш которых проверить не удалось по причине блокировки файла в монопольном режиме каким-то приложением;
    • Missed — содержит имена файлов, для которых есть запись в XML файле БД, но самого файла уже не существует (по любым причинам);
    • New — в эту категорию попадают только имена новых файлов, для которых соответствующей записи ещё нет в XML файле. Новые файлы добавляются только в режиме Rebuild или когда файл БД создаётся с нуля.
    • Ok — сюда попадают файлы с успешным статусом проверки, т.е. хеш, дата/время изменения и размер файла совпадают со значениями в XML файле;
    • Total — содержит имена всех обработанных файлов вне зависимости от результата проверки;
    • Unknown — содержит имена файлов, для которых нельзя сопоставить хеш. Такая ситуация возможна, если в параметрах функции указан только хеш SHA1, но для файла в БД записан только MD5 хеш и наоборот.
  • Recurse <Switch> — ключ, который включает проверку файлов не только в указанной папке, но и во всех вложенных папках.
  • Rebuild <Swtich> — ключ, который задаёт особый режим работы скрипта — «освежение». Если этот ключ указан, то производится сверка файлов из БД с реальными файлами. Если файл более не существует, то соответствующая запись для него удаляется из БД. После чего целевая папка (и подпапки при указанном ключе –Recurse) проверяется на наличие новых файлов. Для каждого нового файла добавляется соответствующая запись в XML файл.
  • SHA1 <Switch> — задаёт алгоритм хеширования, который будет использоваться для подсчёта новых файлов и/или для проверки уже существующих записей в XML файле.
  • MD5 <Switch> — задаёт алгоритм хеширования, который будет использоваться для подсчёта новых файлов и/или для проверки уже существующих записей в XML файле.
  • Quiet <Switch> — включает несопровождаемый режим работы скрипта. Если ключ указан, то итоговая статистическая информация не выводится на экран, а в зависимости от результата проверки генерируется код возврата (LastExitCode). Код возврата может принимать одно из следующих значений:
    • 0 — в процессе работы скрипта все файлы были успешно проверены и их хеш, дата/время изменения и размер соответствуют записям в БД. Так же, данный код возврата будет сгенерирован, если создавался новый файл БД и все файлы были успешно в него добавлены;
    • 1 — в процессе проверки были обнаружены файлы с несовпадающим хешем и/или датой/временем и размером;
    • 2 — в процессе проверки было обнаружено, что для записи в БД нет соответствующего файла;
    • 3 — в процессе проверки были обнаружены файлы, для которых не удалось сопоставить алгоритм хеширования с хранимым в БД алгоритмом хеширования для файла. Такая ситуация возможна, если в параметрах функции указан только хеш SHA1, но для файла в БД записан только MD5 хеш и наоборот;
    • 4 — в процессе работы скрипта для некоторых файлов не удалось подсчитать хеш по причине блокировки файла в монопольном режиме другим приложением;
    • 5 — скрипт выполнялся в режиме Rebuild (освежения файла БД)
  • Verbose <Switch> — включает отображение дополнительной информации о ходе проверки файлов.
  • Debug <Switch> — включает отображение отладочной информации о ходе работы скрипта.

Несколько примеров использования:

Start-PsFCIV C:\Files db.xml -SHA1 -Recurse -Show Bad, Missed

будет проверена папка C:\Files и все вложенные папки. Файл db.xml должен быть размещён непосредственно в этой папке. Если файл не существует, то будет создан с нуля. После проверки будет показано графическое окно с именами файлов, которые попали в категорию Bad и Missed. Для каждой категории будет отдельное графическое окно.

Start-PsFCIV C:\Files db.xml -SHA1 -MD5 -Include data.dat

будет проверен только файл data.dat в папке C:\Files с использованием SHA1 алгоритмом хешиования. Если для файла в БД записан только MD5 хеш, то проверка будет произведена с использованием MD5. Если файл БД (db.xml) не существует, то создастся новый файл БД со сведениями о файле data.dat. Файл будет подсчитан с использованием как SHA1, так и MD5.

Start-PsFCIV C:\Files db.xml -SHA1 -MD5 -Rebuild

будет произведено освежение файла БД для папки C:\Files. Все записи, для которых соответствующего файла не обнаружено, будут удалены. Если в папке есть файлы, для которых нет соответствующей записи, то они будут обсчитаны с использованием алгоритмов SHA1 и MD5 и будут добвлены в XML файл. Файл db.xml должен существовать, иначе скрипт вернёт фатальную ошибку.

Start-PsFCIV C:\Files db.xml -SHA1 -Quiet

Папка C:\Files будет проверена в несопровождаемом режиме с использованием алгоритма SHA1. По умолчанию никакой информации на экране не будет. После окончания работы, в зависимости от результатов проверки, скрипт сгенерирует соответствующий код возврата (0-5).

И, собственно, сам скрипт:

И как обычно, любые замечания, комментарии постить в каменты.