Bashrunner или PowerShell для юниксоида

в 16:06, , рубрики: bash, powershell, powershell2, Программирование

Какой смысл ограничивать себя чем-то одним, если существует вероятность возникновения задачи непосильной для этого чего-то? Кудряво сказано, а если проще, то есть такая штука, называется кругозор. И потом, какие-то вещи проще познавать на примерах, какие-то в сравнении с чем-то. Но все же лучшим стартом в изучении чего-либо является чтение документации и опыт предшественников, и если первое дает некоторые представление о предмете, второе позволяет избежать повторения ошибок и способствует улучшению решений уже существующих.

Причины, согласно которым Bash'исты могут использовать PowerShell (ровно как и наоборот), могут быть самыми разными, но все их мы оставим за кадром. Куда больший интерес представляет то, насколько быстро можно освоить PowerShell юниксоиду. Пожалуй, наиболее удачным было бы такое начало:

PS C:> man man

ИМЯ
    Get-Help

ОПИСАНИЕ
    Отображает сведения о командах и концепция Windows PowerShell.

...

PS C:>

Помимо того, что man в контексте PowerShell является лишь псевдонимом (альясом) командлета Get-Help, языком разметки страницы справочного руководства является xml, в то время как в Bash — это troff, имеющий долгую и увлекательную историю, предшествующую UNIX. Сходство работы man в Bash и PowerShell заканчивается после того, как найдена нужная страница руководства. Далее, в Bash, нужная страница распаковывается, а ее содержимое выводится в терминал, иными словами, запись:

$ man proc

будет эквивалентна:

$ gzip -dc /usr/share/man/man5/proc.5.gz | groff -man -Tascii | less

В PowerShell содержимое справки выводится сразу же после считывания данных с диска. Впрочем, эти и многие другие нюансы становятся очевидны по мере изучения как самой справки, так и механизмов заложенных в PowerShell.

Наряду с man, в PowerShell есть знакомые каждому юниксоиду ls, cat, echo, history и некоторые другие команды, вывод и функциональные возможности которых продиктованы либо устройством операционной системы, либо логикой самого хоста PowerShell. Но некоторых юниксоидов PowerShell отпугивает потому, что какие-то вещи кажутся неочевидными или избыточными. Например:

$ head foo

в PowerShell выглядит так:

PS C:> cat foo | select -f 10

Но ничто не мешает определить head самостоятельно.

PS C:> sc function:head {param([String]$File, [Int32]$Number=10) cat $File | select -f $Number}
PS C:> head foo

Если же требуется нечто более продвинутое, следует сперва ознакомиться с такими страницами руководства:

PS C:> man about_function
...

PS C:>

Вот вам и страницы руководств в виде простых текстовых файлов, — это лишь к слову.

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

tail

PS C:> cat foo | select -l 10

tail -f

PS C:> cat foo -Wait

less

PS C:> cat foo | oh -Paging

cat -n

PS C:> cat foo | % {$i=0}{$i++;"$i $_"}

tr -d

PS C:> "`"test`"" -replace [Char]34, ''

grep

PS C:> Set-Alias grep Select-String
PS C:> cat foo | grep bar

Или:

PS C:> cat foo | ? {$_ -match 'bar'}

sed

PS C:> cat foo | % {$_ -replace 'bar', 'foo'}

wc

PS C:> (cat foo | measure -Word).Words

wc -l

PS C:> (cat foo | measure).Count

top

PS C:> while(1){cls;ps;sleep -s 3}

which

PS C:> Set-Alias which Get-Command
PS C:> which man

whoami

PS C:> ('UserDomainName', 'UserName' | % {[Environment]::$_}) -join ''

time

PS C:> sc function:time {param($Command) Measure-Command {$Command}}
PS C:> time Get-Help

touch

PS C:> Out-File foo -Encoding ASCII

touch -a

PS C:> (gi foo).LastAccessTime = [DateTime]'12.31.2014 23:00:00'

touch -m

PS C:> (gi foo).LastWriteTime = [DateTime]'12.31.2014 23:00:00'

who

PS C:> gp 'HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionProfileList*' |
>> % {(New-Object Security.Principal.SecurityIdentifier($_.PSChildName)).Translate(
>> [Security.Principal.NTAccount]).Value}
>>
...
PS C:>

whatis

PS C:> sc function:whatis {param($Command) (Get-Command $Command).FileVersionInfo.FileDescription}
PS C:> whatis netsh

Можно продолжать в таком ключе еще долго, — суть, полагаю, ясна. При этом если у нас имеется более продвинутая реализация некоторой команды, которую планируется использовать довольно часто, можно поместить ее в профиль (man about_Profiles) или же вынести в отдельный модуль (man about_Modules) с каким-нибудь говорящим названием, скажем, bash или unix, или cal, если модуль реализует одноименную команду.

cal.psm1

#requires -version 2.0
if (!(Test-Path alias:cal)) {Set-Alias cal Get-Calendar}

function Get-Calendar {
  param(
    [Parameter(Position=0)]
    [Alias('m')]
    [ValidateRange(1, 12)]
    [Int32]$Month = (Get-Date -u %m),
    
    [Parameter(Position=1)]
    [ValidateRange(2000, 9999)]
    [Int32]$Year = (Get-Date -u %Y),
    
    [Parameter(Position=2)]
    [Alias('mf')]
    [Switch]$MondayFirstly
  )
  
  begin {
    [Globalization.DateTimeFormatInfo]::CurrentInfo.ShortestDayNames | % {$arr = @()}{$arr += $_}
    $cal = [Globalization.CultureInfo]::CurrentCulture.Calendar
    $dow = [Int32]$cal.GetDayOfWeek([DateTime]([String]$Month + '.1.' + [String]$Year))
    
    if ($MondayFirstly) {
      $arr = $arr[1..$arr.Length] + $arr[0]
      if (($doe = --$dow) -lt 0) { $dow = 6 }
    }
  }
  process {
    $loc = [Globalization.DateTimeFormatInfo]::CurrentInfo.MonthNames[$Month - 1] + ' ' + $Year
    $loc = [String]((' ' * [Math]::Round((20 - $loc.Length) / 2)) + $loc)
    
    if ($dow -ne 0) { for ($i = 0; $i -lt $dow; $i++) { $arr += (' ' * 2) } }
    1..$cal.GetDaysInMonth($Year, $Month) | % {
      if ($_.ToString().Length -eq 1) { $arr += ' ' + [String]$_ }
      else { $arr += [String]$_ }
    } #foreach
  }
  end {
    Write-Host $loc -fo Magenta
    for ($i = 0; $i -lt $arr.Length; $i += 6) {
      Write-Host $arr[$i..($i + 6)]
      $i++
    }
    ''
  }
}

Export-ModuleMember -Alias cal -Function Get-Calendar

После установки модуля:

PS C:> Import-Module cal
PS C:> cal -mf

    Апрель 2015
Пн Вт Ср Чт Пт Сб Вс
       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

PS C:>

Вот таким вот незатейливым способом можно превратить PowerShell в Bash, а стоит ли оно того — дело сугубо личное.

Автор: gregzakharov

Источник

Поделиться новостью

* - обязательные к заполнению поля