Hatena::ブログ(Diary)

はけの徒然日記 このページをアンテナに追加 RSSフィード

2005 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 04 | 05 | 08 | 09 | 10 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 | 11 |
2011 | 01 | 02 | 03 | 04 | 11 | 12 |
2012 | 02 | 03 | 05 |
2014 | 02 | 03 | 04 | 05 | 12 |
2015 | 05 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 04 | 05 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 |

2016-12-30(Fri)

PowerShell - 関数(値渡しと参照渡し)

参照渡しの場合、呼び出し側の引数に[ref]をつける、また引数に括弧がないとエラーになった。

関数定義の仮引数の[ref]は無くても良い、関数内では仮引数のValueプロパティアクセスする。

# 値渡し
function byVal($arg){
    $arg = "Good Bye"
    Write-Host "in Function : $arg"
}

# 参照渡し
function byRef([ref]$arg){
    $arg.Value= "Good Bye"  # Valueプロパティにアクセス
    Write-Host "in Function : $($arg.Value)"
}


$a = "Hello"
$a
byVal $a      # 値渡し
$a            # Hello


$a = "Hello"
byRef([ref]$a) # 参照渡し
$a             # Good Bye

2016-12-28(Wed)

PowerShell - 関数(パイプ)

パイプで渡されるてくるデータの処理用の関数

開始処理はbeginブロック、終了処理はendブロック、渡されてくるデータの処理はprocessブロック記述する。渡されてきたデータは変数$_に格納されている。

returnで値を返すと関数の出力になる。


even.ps1

function even {
    begin{ $cnt = 0 }
    process {
        $cnt++
        if($_ % 2 -eq 0){
            return "${cnt}番目のデータ:$_"
        } else {
            return
        }
    }
    end{ return "終了" }
}

実行結果

PS C:\> . C:\even.ps1

PS C:\> 1..10 | even
2番目のデータ:2
4番目のデータ:4
6番目のデータ:6
8番目のデータ:8
10番目のデータ:10
終了

2016-12-27(Tue)

PowerShell - 再帰関数

function fact($i) {
    if($i -eq 0) { 
        return 1
    } else {
        return $i * (fact ($i - 1))
    }
}

fact 5 # 120

2017/1/28追記 再帰回数に限界がある模様

Win10 PowerShell 5.1の環境で、以下の関数fooの引数が1000の場合は正常終了しましたが、5000の場合エラーになりました。

function foo {
    param($i)

    #Write-Host $i
    if($i -eq 0){return 1}
    return (foo($i - 1)) + 1
}


Write-Host (Get-Date)
Write-Host "Result: $(foo(5000))" 
Write-Host (Get-Date)
PS C:\> C:\powershell\test.ps1
2017/01/28 21:47:03
Result: 1001
2017/01/28 21:47:04

PS C:\> C:\powershell\test.ps1
2017/01/28 21:47:18
呼び出しの深さのオーバーフローのため、スクリプトが失敗しました。
    + CategoryInfo          : InvalidOperation: (0:Int32) []、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : CallDepthOverflow

PowerShell - 関数(可変長引数)

引数は変数$argsという配列にわたされる。

test.ps1

function foo {
    $args.GetType()

    foreach( $i in $args ) {
        Write-Host $i
    }
}

結果

PS C:\> . C:\test.ps1  # 関数読み込み
PS C:\> foo 1 2 3

IsPublic IsSerial Name                                     BaseType                    
-------- -------- ----                                     --------                    
True     True     Object[]                                 System.Array                
1
2
3

PS C:\> foo "a" "b" "c"

IsPublic IsSerial Name                                     BaseType                    
-------- -------- ----                                     --------                    
True     True     Object[]                                 System.Array                
a
b
c

2016-12-26(Mon)

PowerShell - 制御構造(ループ)

for

for($i = 0; $i -lt 5; $i++){
    Write-Host $i
}

0
1
2
3
4

foreach

foreach($i in (0..4)){
    Write-Host $i
}

0
1
2
3
4

while / do while

$i = 0
while($i -lt 5){
    Write-Host $i
    $i++
}

0
1
2
3
4


$i = 0
do {
    Write-Host $i
    $i++
}while($i -lt 5)

0
1
2
3
4

break/continue

for($i = 0; $i -lt 10; $i++){
    if($j -eq 2){break}
    Write-Host $i
}

0
1


for($i = 0; $i -lt 10; $i++){
    if($i -eq 2){continue}
    Write-Host $i
}

0
1
3
4
5
6
7
8
9

多重ループからの脱出

Write-Host "before loop"

:exit_all
for($i = 0; $i -lt 10; $i++){
    for($j = 0; $j -lt 10; $j++){
        Write-Host $i, $j
        if($j -eq 2){break exit_all}
    }
}
Write-Host "after loop"


before loop
0 0
0 1
0 2
after loop

ラベルをforループの下に移動したら、"after loop"が非表示になった。

ラベルが付いている文の次へジャンプするということ?

PowerShell - 制御構造(条件分岐)

if

if(条件){
    文
} elseif(条件){
    文
} else {
    文
}

switch

switch($v){
  1          {文; break}
  2          {文; break}
  ($_ -ge 3) {文; break}
  default    {文; break}
}

# 正規表現 (-matchでないことに注意)
$s = "abcd"
switch -Regex($s){
    "^a"       {Write-Host 1;break} # aで始まる
    "bc"       {Write-Host 2;break} # bcを含む
    default    {Write-Host 3;break}
}

# ワイルドカード (-likeでないことに注意)
switch -Wildcard($s){
    "a*"       {Write-Host 1;break} # aで始まる
    "bc"       {Write-Host 2;break} # bcを含む
    default    {Write-Host 3;break}
}

PowerShell - 正規表現

match演算子

変数$Matchesでマッチした文字列を取得できる。

ただし最初マッチしたものしか得られないみたい。

PS C:\> "abcde" -match "(.)(.)"
True
PS C:\> $Matches

Name            Value
----            -----
2               b
1               a
0               ab

PS C:\> $Matches[0]
ab

regexオブジェクト

正規表現オブジェクト使用するとマッチした全てが取得できる。



PS C:\> $re = New-Object regex("(.)(.)")
PS C:\> $m = $re.Matches("abcde")
PS C:\> $m

Groups   : {ab, a, b}
Success  : True
Captures : {ab}
Index    : 0
Length   : 2
Value    : ab

Groups   : {cd, c, d}
Success  : True
Captures : {cd}
Index    : 2
Length   : 2
Value    : cd

PS C:\> $m[1].Groups[1]

Success  : True
Captures : {c}
Index    : 2
Length   : 1
Value    : c

PS C:\> $m[1].Groups[1].Value
c

PowerShell - 文字列

変数展開

PS C:\> $a = "Hello "
PS C:\> "${a} world" # " "で囲まれた中の変数は展開される
Hello  world

PS C:\> '${a} world' # ' 'で囲まれた中の変数は展開されない
${a} world

部分式

PS C:\> $a = 100

PS C:\> "$($a + 1) is 101"
101 is 101

ヒアドキュメント

改行付き文字列の代入と参照

$s = @"
abdefg
hijklmn
opqrstu
"@

write-host $s 

連結

PS C:\> "a" + "b"
ab

繰り返し

PS C:\> "a" * 5
aaaaa

ワイルドカード

PS C:\> "abcdefg" -like "a*"
True

否定演算子は -notlike


正規表現

PS C:\> "abcdefg" -match "^a"
True

否定演算子は -notmatch


分割

PS C:\> "abcdefg" -split "\B"
a
b
c
d
e
f
g

置換

PS C:\> "abcdefg" -replace "b","z"
azcdefg

フォーマット

PS C:\> "{0:#0.00}, {1:00.00}" -f(0.123, 0.123)
0.12, 00.12

その他

PS C:\> "abcdefg".Length
7
PS C:\> "abcdefg".Contains("c")
True
PS C:\> "abcdefg".IndexOf("c")
2
PS C:\> "abcdefg"[2]
c
PS C:\> "abcdefg".Insert(2, "z")
abzcdefg
PS C:\> "abzcdefg".Remove(2,1)
abcdefg

2016-12-25(Sun)

PowerShell - 連想配列

代入と参照

PS C:\> $h = @{"a" = 1; "b" = 2}
PS C:\> $h["c"] = 3
PS C:\> $h

Name                           Value
----                           -----
c                              3
a                              1
b                              2

PS C:\> $h["a"]
1
PS C:\> $h["z"] -eq $null # 存在しないキーでは$nullが返る
True

キーおよび値の配列取得

PS C:\> $ks = $h.Keys
PS C:\> $ks
c
a
b
PS C:\> $vs = $h.Values
PS C:\> $vs
3
1
2

キーおよび値が存在するか

PS C:\> $h.ContainsKey("b")
True
PS C:\> $h.ContainsValue(3)
True

要素の削除

PS C:\> $h.Remove("a")
PS C:\> $h

Name                           Value
----                           -----
c                              3
b                              2

各要素の処理

PS C:\> $h = @{"a" = 1; "b" = 2; "c" = 3}
PS C:\> foreach($e in $h.GetEnumerator()){Write-Host $e.key, $e.value}
c 3
b 2
a 1

PowerShell - 配列

代入

PS C:\> $ary = 1,2,3,4,5
PS C:\> $ary = 1 .. 5

PS C:\> $ary = @() #空配列

PS C:\> $ary = @(1,2,3,4,5)
PS C:\> $ary = "abcdefg" -split "" # 1文字ずつに分割(前後に空文字の要素が入る)
PS C:\> $ary = "abcdefg" -split "\B" # 1文字ずつに分割(前後に空文字の要素が入らない)

要素アクセス

PS C:\> $a = 1..5
PS C:\> $a[2] = 300
PS C:\> $a
1
2
300
4
5

PS C:\> $a[0]
1
PS C:\> $a[0,2,4]
1
300
5
PS C:\> $a[0..3]
1
2
300
4
PS C:\> $a[4..0] # 逆順に並べ替え
5
4
300
2
1

要素追加

PS C:\> $a = 1..5
PS C:\> $a += 100 # 末尾に追加
PS C:\> $a
1
2
3
4
5
100
PS C:\> $a = @(-100) + $a # 先頭に追加
PS C:\> $a
-100
1
2
3
4
5
100

配列コピー

PS C:\> $a = 0..4
PS C:\> $b = $a # $bは$aと同じオブジェクトへの参照
PS C:\> $a[0] = 100
PS C:\> $b
100
1
2
3
4
PS C:\> $a.GetHashCode()
28959488
PS C:\> $b.GetHashCode()
28959488

PS C:\> $a = 0..4
PS C:\> $b = $a.Clone() # 別オブジェクトとしてコピー
PS C:\> $a[0] = 100
PS C:\> $b
0
1
2
3
4
PS C:\> $a.GetHashCode()
38658037
PS C:\> $b.GetHashCode()
45724662

その他

PS C:\> $a.Count # 要素数
7
PS C:\> $a.Contains(-100) # 要素に存在するか
True
PS C:\> -100 -in $a # 要素に存在するか(V3以降)
True

PowerShell - テキストファイルの読み書き

書き込みOut-Fileを使用する。文字列パイプで渡すか、-InputObjectでオブジェクトを渡す。-Appendで追加書き込み

読み込みはGet-Contentを使用する。各行毎の文字列配列として変数に読み込まれる。ただし1行のみのファイルStringになるため、GetType()のNameプロパティ確認必要となる。


$file = ".\test.txt"

# ファイルへの書き込み
"1行目” | Out-File $file -Encoding default
"2行目” | Out-File $file -Encoding default -Append # 追加書込モード
Out-File $file -Encoding default -Append -InputObject "3行目”



# ファイル読み込み(複数行)
$txt = Get-Content $file
$txt
$txt.GetType()
# IsPublic IsSerial Name        BaseType
# -------- -------- ----        --------
# True     True     Object[]    System.Array 

$txt[1] # 2行目


# ファイル読み込み(1行)
$txt = Get-Content $file
$txt.GetType()
#IsPublic IsSerial Name                                     BaseType              
#-------- -------- ----                                     --------              
#True     True     String                                   System.Object

1行ずつの読み取り

.NETStreamReader使用する。

StreamReaderは、System.IO.StreamReaderなのだけれども、Systemは省略できるっぽいです。ReadLineメソッドで1行ずつ読みとる動作を$nullになるまで繰り返す。




$path = Get-Location
$file = "test.txt"
$enc = [Text.Encoding]::Default # Shift_JIS
$reader = New-Object IO.StreamReader(($path.Path + "\" + $file), $enc)

while ( ($line = $reader.ReadLine()) -ne $null){
    Write-Host $line
}

$reader.close()

PowerShell - ファイル/フォルダリストの取得

バージョン 5.1

PS C:\> Get-ChildItem -File

PS C:\>Get-ChildItem -Directory

バージョン 2.0

PSIsContainerプロパティ?がTrueならフォルダ、FalseならファイルなのでWhere-Object抽出する。

バージョン5.1でも使用可能

PS C:\> Get-ChildItem | Where-Object{!$_.PSIsContainer}  # ファイル

PS C:\> Get-ChildItem | Where-Object{$_.PSIsContainer}   # フォルダ

PowerShell - データと変数の型

文字と数値が混ざった式は、左の型に合わせる? でにキャスト?すれば変換できるっぽい

PS C:\> 1 + "1"
2
PS C:\> "1" + 1
11
PS C:\> [long]"1" + 1
2

変数の型

最初使用するときに型の宣言をすれば、その型の変数になる。

使用をやめるときはRemove-Variable "$を付けないの変数名"

PS C:\> [long]$i = 1
PS C:\> $i = "abc"
値 "abc" を型 "System.Int64" に変換できません。エラー: "入力文字列の形式が正しくありません。"
発生場所 行:1 文字:1
+ $i = "abc"
+ ~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

PS C:\> Remove-Variable i
PS C:\> $i = "abc"
PS C:\> $i
abc

短縮形が登録されている型と正式型(Ver. 5.1の場合

続きを読む

PowerShell - 実行環境の設定、その他

次期のWindowsアップデートデフォルトコマンドプロンプトからPowerShellに代わるそうなので学習開始です。


実行ポリシーの変更

デフォルトでは実行できなようになっている為、実行ポリシーを変更する必要がある。

コマンドプロンプト上で以下の様に実行、foo.ps1がPowerShellスクリプト

C:\>powershell -ExecutionPolicy RemoteSigned .\foo.ps1

  • 恒久的な変更

管理者権限PowerShellを起動して

PS C:\> Get-ExecutionPolicy
Restricted
PS C:\> Set-ExecutionPolicy RemoteSigned

実行ポリシーの変更
実行ポリシーは、信頼されていないスクリプトからの保護に役立ちます。実行ポリシーを変更すると、about_Execution_Policies
のヘルプ トピック (http://go.microsoft.com/fwlink/?LinkID=135170)
で説明されているセキュリティ上の危険にさらされる可能性があります。実行ポリシーを変更しますか?
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ (既定値は "N"): y
PS C:\> Get-ExecutionPolicy
RemoteSigned


PowerShellバージョン確認

自宅のWindows 10ではバージョン5.1、職場Windows 7ではバージョン2.0

PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.14393.576
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.576
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\> $PSVersionTable.PSCompatibleVersions

Major  Minor  Build  Revision
-----  -----  -----  --------
1      0      -1     -1
2      0      -1     -1
3      0      -1     -1
4      0      -1     -1
5      0      -1     -1
5      1      14393  576


続きを読む