Привычные вещи отнюдь не статичны как о них положено думать, и если на них посмотреть под иным углом, причем не обязательно с высоким градусом и вызывающим похмелье, можно открыть для себя нечто новое, способное во многом повысить эффективность работы.
Если проводить аналогии, профили PowerShell — те же конфигурационные файлы терминалов *nix, основное назначение которых хранить пользовательские настройки. Более подродно о профилях можно почитать во встроенном руководстве:
PS C:> man about_Profiles
Совершенно очевидно, что пихать в профиль все что ни поподя не следует — чем больше вес файла, тем менее скорость загрузки хоста, если конечно не был указан параметр /noprofile при вызове последнего. Обычно профили применяются для упреждения неоднозначностей (о них чуть позже), настройки окружения и определения часто используемых функций. Попробуем пояснить это на примере.
PS C:> ni -Type File -Path $Profile -Force
PS C:> vim $Profile #vim - сугубо личное предпочтение
Было бы неплохо иметь постоянно доступ к ускорителям типов.
if (($ta = [PSObject].Assembly.GetType(
'System.Management.Automation.TypeAccelerators'
))::Get.Keys -notcontains 'accelerators') {
$ta::Add('accelerators', $ta)
}
Для того, чтобы изменения вступили в силу, требуется перезапустить хост.
PS C:> ii (ls $env:allusersprofile -r -ea 0).Where({$_.Name -match 'shell.lnk'}).FullName;kill $pid
...
PS C:> [accelerators]::Get
Key Value
--- -----
Alias System.Management.Automation.AliasAttribute
AllowEmptyCollection System.Management.Automation.AllowEmptyCollectionAttribute
AllowEmptyString System.Management.Automation.AllowEmptyStringAttribute
AllowNull System.Management.Automation.AllowNullAttribute
array System.Array
bool System.Boolean
byte System.Byte
char System.Char
CmdletBinding System.Management.Automation.CmdletBindingAttribute
datetime System.DateTime
...
Ранее упомяналось о неких неоднозначностях, самая пора к ним вернуться. Дело в том, что от использования переменных env: следует отказаться по причине возможности их изменения.
PS C:> gc env:allusersprofile
C:ProgramData
PS C:> $old = gc env:allusersprofile
PS C:> sc env:allusersprofile C:
PS C:> gc env:allusersprofile
C:
PS C:> sc env:allusersprofile $old
PS C:> gc env:allusersprofile
C:ProgramData
Иными словами перезапуск хоста способом выше без предварительной проверки переменной allusersprofile — не самая лучшая идея, и, если в том возникает необходимость, лучше использовать переменные окружения определенные в Environment+SpecialFolder или просто предопределить некоторые переменные env: в виде констант.
Так как Корзина используется некоторыми как пункт промежуточного хранения файлов, почему бы не упростить себе задачу и в этом случае?!
Set-Content function:trash {
param(
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_})]
[String]$Path
)
(New-Object -ComObject Shell.Application).NameSpace(0xA).MoveHere(
(Convert-Path $Path)
)
}
В итоге помещать файлы в Корзину станет проще:
PS C:> trash E:docfoo
Требуется возможностью перевода незнакомых слов? Денег на покупку дорогостоящих приложений у нас нет, поэтому будем использовать онлайн-переводчик, а точнее — Яндекс.Перевод, благо у того вменяемое API, да и качество переводов неплохое. Устанавливаем API-ключ.
PS C:> $key = '...' #API-ключ
PS C:> $bin = "$([Environment]::GetFolderPath('UserProfile'))yatrans.bin"
PS C:> Add-Type -AssemblyName System.Security
PS C:> [IO.File]::WriteAllBytes(
>> $bin,
>> [Security.Cryptography.ProtectedData]::Protect(
>> [Text.Encoding]::Unicode.GetBytes($key),
>> $null,
>> [Security.Cryptography.DataProtectionScope]::CurrentUser
>> ))
>>
PS C:> attrib +h $bin
Ключ установили, теперь можно приступать к чтению документации API. Если же совсем невтерпеж, то в самом простом варианте, функция перевода может выглядеть так.
function Get-Translation {
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[String]$Data
)
begin {
Add-Type -AssemblyName System.Security
# декодирование ключа
if (Test-Path ($key = "$([Environment]::GetFolderPath('UserProfile'))yatrans.bin")) {
$key = [Text.Encoding]::Unicode.GetString(
[Security.Cryptography.ProtectedData]::Unprotect(
[IO.File]::ReadAllBytes($key),
$null,
[Security.Cryptography.DataProtectionScope]::CurrentUser
)
)
}
else {
throw 'API-ключ Яндекс.Перевод не найден.'
}
# url-root
$url = 'https://translate.yandex.net/api/v1.5/tr/'
# определение языка оригинала
$detect = "$($url)detect?key=$key&text="
# перевод
$transl = "$($url)translate?key=$key&text=%t&lang=%l-ru&format=plain"
# user agent
$usr = 'Mozilla/5.0 (Windows NT 6.3; rv:37.0.1) Gecko/20100101 Firefox/37.0.1'
}
process {
# для перевода текстовых файлов
$Data = if (Test-Path $Data) { gc $Data } else { $Data }
$res = [xml](wget "$detect$Data" -DisableKeepAlive -UseBasicParsing -UserAgent $usr).Content
if ($res.DetectedLang.code -ne 200) {
throw 'Невозможно определить язык.'
}
$transl = $transl -replace '%t', $Data
$transl = $transl -replace '%l', $res.DetectedLang.lang
$res = [xml](wget $transl -DisableKeepAlive -UseBasicParsing -UserAgent $usr).Content
if ($res.Translation.code -ne 200) {
throw 'Невозможно перевести текст.'
}
$res.Translation.text
}
}
Функция в действии:
PS C:> Get-Translation 'Die Zeit ist auf!'
- Время!
PS C:> vim foo
Hey, teacher! Leave this kids alone!
...
PS C:> Get-Translation foo
Эй, учитель! Оставить детей в покое!
PS C:> ri foo
PS C:>
К слову, Яндекс предоставляет возможность узнать свой публичный IP — также может сгодится в хозяйстве:
$par = @{
Uri = 'http://ipv4.internet.yandex.ru/internet/api/v0/ip'
DisableKeepAlive = $true
UseBasicParsing = $true
UserAgent = 'Mozilla/5.0 (Windows NT 6.3; rv:37.0.1) Gecko/20100101 Firefox/37.0.1'
}
(wget @par).Content -replace [Char]34, ''
Все, что не планируется использовать в повседневной работе, лучше выносить в отдельные модули.
Автор: gregzakharov