【未解決】PHPのDirectoryIterator::isDotの理解できない挙動についてのメモ

Windows10 HOMEのPCでPHPのDirectoryIteratorでディレクトリの中のファイルやディレクトリを列挙するコードを書いていた時、理解できない現象に遭遇したことについてメモしておく。
'.'や'..'は表示して欲しくないので、最初はisDotで判定しようとした。

<?php
if ( isset($_GET['dir']) ){
    $dir = $_GET['dir'];
} else {
    $dir = './';
}
$this_page = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://').$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];

$iterator = new DirectoryIterator($dir);
foreach($iterator as $file){
    if ($file->isFile()){
        $pathname = $file->getPathname();
        print "<li><a href='{$pathname}'>{$pathname}</a></li>";
    }
}
foreach($iterator as $file){
    if ($file->isDir()) {
        $pathname = $file->getPathname();
        $sub1 = substr($pathname,-2);
        $sub2 = substr($pathname,-3);
//        if ( !file->isDot() ){
        if( $sub1 != '\.' && $sub2 != '\..' ){
            print "<li><a href='{$this_page}?dir={$pathname}'>{$pathname}</a></li>";
        }
    }
}

ファイルなどは以下の通りだが、なぜか、isDot()を使ったときは'dir11'と'dir12'が表示されなくなってしまう。

    ディレクトリ: C:\WebApplication\php\Documents\dir1


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2022/11/08     11:19                dir11
d-----        2022/11/08     11:20                dir12
-a----        2022/11/08     11:18             26 doca.txt

仕方なく部分文字列で判定するように変更したのだが・・・。
なぜ、'dir11'と'dir12'とはisDotなのだろう?

Let's Try Qiita API via curl of Ubuntu 20.04(WSL)

Let's Try Qiita API via curl of Ubuntu 20.04(WSL)

WSL(Ubuntu 20.04)のcurlでQiita APIを使ってみた。
powershellのときはpowershellのscriptだけで済んだが、
Let's try Qiita API v2 via curl of powershell - willwealth’s diary
シェルでJSONを扱うのは厳しそうだったので、外側にpythonをかぶせ python でdictに変換して処理した。

qiita.sh
#!/bin/bash
url='https://qiita.com/api/v2/items?page=1&per_page=10'
curl $url

powershellcurlはcontentをStringで返してきたが、このケースでは'bytes'で返される。

>>> res = subprocess.run(['/home/user1/qiita.sh'],capture_output=True)
>>> bs = res.stdout
>>> type(bs)
<class 'bytes'>

そこで、'bytes'をdecode()で'str'に変換してからjson.loads()する。
JSON文字列をdictに変換するloads()とは別に、ファイルからdictに変換するload()という関数があり躓いてしまった。
初心者からするとちょっと紛らわしかったので注意が必要だ。

qiita.py
#!/usr/bin/python3
import subprocess
import json

res = subprocess.run(['/home/user1/qiita.sh'],capture_output=True)
bs = res.stdout
str = bs.decode()
arr = json.loads(str)
for i,a in enumerate(arr):
    title = a['title']
    print(f"[{i}] {title}")

invoke-commandのcomputernameにpowershellの配列を渡してみた!ただ、Get-Hostの挙動がちょっと変?

invoke-commandはcomputernameを配列で指定できる。
以下のリンクの「例 7: 複数のコンピューター上のホスト プログラムのバージョンを取得する」を試してみた。

$version = Invoke-Command -ComputerName (Get-Content Machines.txt) -ScriptBlock {(Get-Host).Version}

learn.microsoft.com

invoke-commandの-computernameは配列を受け取ってくれるのか試してみたら、本当に受け取ってくれた。
(credencialは一致している前提で)
ただ、Get-Hostの結果がlocalhostで実行した場合と異なっていた。
invoke-commandだとversionが全て1で同じになってしまう。
なぜだろう?
’[system.environment]::osversion’で試してみたら異なる結果が得られた。

PS >(get-host).version

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14409  1027


PS >$h = ( get-content .\hosts.txt )
PS >$h
PCXX
PCYY
localhost
PS >invoke-command -computername $h -scriptblock { (get-host).version }

Major  Minor  Build  Revision PSComputerName
-----  -----  -----  -------- --------------
1      0      0      0        localhost
1      0      0      0        PCXX
1      0      0      0        PCYY


PS >invoke-command -computername $h -scriptblock { [system.environment]::osversion }


PSComputerName : PCXX
RunspaceId     : **********
Platform       : Win32NT
ServicePack    :
Version        : 10.0.22621.0
VersionString  : Microsoft Windows NT 10.0.22621.0

PSComputerName : localhost
RunspaceId     : ***********
Platform       : Win32NT
ServicePack    :
Version        : 6.3.9600.0
VersionString  : Microsoft Windows NT 6.3.9600.0

PSComputerName : PCYY
RunspaceId     : **********
Platform       : Win32NT
ServicePack    :
Version        : 10.0.19044.0
VersionString  : Microsoft Windows NT 10.0.19044.0

PSRemotingでget-hostを実行しても同様だったので、実行するcmdletによっては注意が必要ということだろうか。
それよりも、powershell自体が古いバージョンだということが問題なのかもしれないが、もうすぐ退役なので・・・。

Name                           Value                                                                                                                                                                             
----                           -----                                                                                                                                                                             
PSVersion                      5.1.14409.1027                                                                                                                                                                    

【確認】PSRemotingやinvoke-commandで管理者権限が必要なcmdletを試す

localhostで管理者powershellを起動するときはユーザーアカウント制御が作動し、
「このアプリがデバイスに変更を加えることを許可しますか」と聞いてくる。
PSRemotingやinvoke-commandはどうなっているのだろうと気になり試してみた。

【結論】

  • PSRemotingでは管理者として認知してくれる
  • invoke-commandでも管理者として認知してくれる

get-websiteは管理者権限が必要なため、管理者権限を持っていても通常(localhost)のpowershellでは実行できない。

PS >get-website 'default web site'
PS >終了エラー(Get-Website): "名前 'WebAdministration' を持つプロバイダーが見つかりません。"
get-website : 名前 'WebAdministration' を持つプロバイダーが見つかりません。
発生場所 行:1 文字:1
+ get-website 'default web site'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (WebAdministration:String) [Get-Website], ProviderNotFoundException
    + FullyQualifiedErrorId : ProviderNotFound,Microsoft.IIs.PowerShell.Provider.GetWebsiteCommand
get-website : 名前 'WebAdministration' を持つプロバイダーが見つかりません。
発生場所 行:1 文字:1
+ get-website 'default web site'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (WebAdministration:String) [Get-Website], ProviderNotFoundException
    + FullyQualifiedErrorId : ProviderNotFound,Microsoft.IIs.PowerShell.Provider.GetWebsiteCommand

勿論、管理者権限のpowershellからは実行できるが、管理者権限のpowershellを起動するにはGUI操作が必要になる。
一方、

PSRemotingでは管理者権限者として認知してくれているようだった。
[PCXXX]: PS C:\Users\user1\Documents> get-website 'default web site'

Name             ID   State      Physical Path                  Bindings
----             --   -----      -------------                  --------
Default Web Site 1    Stopped    %SystemDrive%\inetpub\wwwroot  http *:80:
invoke-commandでも管理者権限者として認知してくれているようだった。
PS >invoke-command -computername PCXXX -scriptblock { get-website 'default web site'}

Name             ID   State      Physical Path                  Bindings                                              PSComputerName
----             --   -----      -------------                  --------                                              --------------
Default Web Site 1    Stopped    %SystemDrive%\inetpub\wwwroot  http *:80:                                            PCXXX

PS >invoke-command -computername PCXXX -scriptblock { start-website 'default web site'}
PS >invoke-command -computername PCXXX -scriptblock { get-website 'default web site'}

Name             ID   State      Physical Path                  Bindings                                              PSComputerName
----             --   -----      -------------                  --------                                              --------------
Default Web Site 1    Started    %SystemDrive%\inetpub\wwwroot  http *:80:                                            PCXXX

PSRemotingではGUI操作はできないので管理者ユーザーが管理者権限で実行できるのは当然なのかもしれない。
localhostでのハードルが高いのは、たぶんユーザーへの安全性への配慮なのだろう。

余談

invoke-commandはlocalhostのscript fileを指定して実行することもできる。

PS >invoke-command localhost -filepath D:\Learning\powershell\qiita.ps1              

qiita.ps1

$url = 'https://qiita.com/api/v2/items?page=1&per_page=20'
$r = curl -URI $url
$c = ($r.content | convertfrom-json )
$i = 0
foreach ( $a in $c ){
    Write-Host "[${i}]"  ${a}.title
    $i ++
}

関数を実行することもできるが、

PS >invoke-command localhost -scriptblock $function:testfunc

2022102211:24:31

関数名に'-'が含まれている場合や

PS >invoke-command localhost -scriptblock $function:test-function -argumentlist"2"
Invoke-Command : パラメーター 'ScriptBlock' をバインドできません。"-function" の値を "System.String" 型から "System.Management.Automation.ScriptBlock" 型に変換できません。
...

引数を必要とする関数の場合のやり方が分からず諦めた。

PS >invoke-command localhost -scriptblock $function:testfunction -argumentlist"2"
Invoke-Command : パラメーター 'ScriptBlock の引数を確認できません。引数が null です。引数に有効な値を指定して、コマンドを再度実行してください。
...

invoke-commandのScriptBlockをMySQLから読み込み実行させてみたときのメモ

パーソナルなIT雑務を想定して、Windowsリモートホストに定型的なコマンドをmysqlから読み込み発行するシナリオでスクリプトを書いてみた。

Table Schema

oid(operation id)はscriptをグループ化させる想定で追加してある(not null制約がないのは失敗)。

mysql> desc operation;
+--------+---------------+------+-----+---------+-------+
| Field  | Type          | Null | Key | Default | Extra |
+--------+---------------+------+-----+---------+-------+
| id     | int           | NO   | PRI | NULL    |       |
| host   | varchar(128)  | YES  |     | NULL    |       |
| uid    | varchar(128)  | NO   |     | NULL    |       |
| oid    | int           | YES  |     | NULL    |       |
| script | varchar(1024) | YES  |     | NULL    |       |
| secure | varchar(1024) | YES  |     | NULL    |       |
+--------+---------------+------+-----+---------+-------+
6 rows in set (0.02 sec)

データ検索はpowershellでは少々、厳しそうだったので素直にpython scriptに任せた。
python scriptは結果をdictの配列で返し、powershell側でjson形式の文字列をconvertfrom-jsonでPSCustomObjectに変換する。

Python Script
import MySQLdb
import sys
import os

av = sys.argv
if len(av) < 2:
    print('oid is Mandatory')
    exit()
else:
    oid = av[1]

mysql_user   = os.environ['mysql_user']
mysql_passwd = os.environ['mysql_passwd']
computername = 'localhost'
dbname = 'db1'

conn = MySQLdb.connect(user=mysql_user,passwd=mysql_passwd,host=computername,db=dbname)
cur = conn.cursor()

placeholder = "select * from operation where oid = {} order by id"
sql = placeholder.format(oid)
cur.execute(sql)
rows = cur.fetchall()
conn.close()

keys = ['id','host','uid','oid','script','secure']
arr = []
for row in rows:
    d = {}
    for k,v in zip(keys,row):
        d[k] = v
    arr.append(d)

print(arr)


リモートホストには invoke-command で発行するが、scriptblock はべた打ちのときは { } の中にコマンドの文字列を書き込めばよい。しかし、コマンド文字列を変数に格納している場合は、{ }の中にコマンドの文字列を表示させても失敗する。
型変換はできないので、[Scriptblock]::Create()関数で文字列からscriptblockを生成しなければならない。
OperationIDは'ValueFromPipeline=$True'としてパイプラインから受け取れるようにしている、必要性はないのだが・・・。なお、不要でもbegin{},end{}を入れているのはadvanced functionを書く時の癖としている。

PowerShell Script
#
# Invoke Operation  
# e.g. invoke-operation -operationid 10
#
Function Invoke-Operation {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True,
                   ValueFromPipeline=$True)]
        [ValidateNotNullOrEmpty()]
        [Int]$OperationID
    )
    begin {
    }
    process {
        $cmd = "python '${home}\Documents\Python Scripts\get_data_mysql.py' $OperationID"
        $ret = ( invoke-expression  $cmd )
        $ops = ( convertfrom-json $ret )
        
        foreach ( $h in $ops ){
            $pcname = ${h}.host
            $uid    = ${h}.uid
            $cmdstr = ${h}.script
            $cmdstr
            $script = [Scriptblock]::Create($cmdstr)
            $secure = ${h}.secure
        
            $pw = ConvertTo-SecureString -String $secure
        
            $c = New-Object System.Management.Automation.PSCredential($uid,$pw)
            invoke-command  -computername $pcname -credential $c -scriptblock $script
        }
    }
    end {
    }
}

ConvertFrom-SecureStringでセキュリティで保護された文字列 (System.Security.SecureString) を暗号化された標準文字列 (System.String) に変換して格納してあるので、ConvertTo-SecureStringで元に戻す。
ネットで調べると以下の関数で平文への復号が可能なようなので単なる気休めに過ぎないのかもしれない。しかし、結構、気休めになるのでは。

  • [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR()
  • [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR()

AsJobオプションを使用する場合は以下が参考になるかも。私には読み込む気力はなかったが・・・
www.vwnet.jp

script data
mysql> select oid,script from operation;
+------+---------------------------------------------------------------------------
| oid  | script                                                                    
+------+---------------------------------------------------------------------------
|   11 | docker container start python1                                      
|   10 | $c = docker container ls -aq -f name=test-.+; docker container start $c   
|   20 | docker container ls --format="table{{.ID}}     {{.Names}}      {{.Ports}}"
|   30 | 1..3 | % { $n = 'test-' + $_;$n;$c = docker container ls -q -f name=$n;...
|   50 | $c = docker container ls -q; docker container stop $c                    
|   90 | stop-computer -force                                                     
|   99 |                                                                          
+------+--------------------------------------------------------------------------
execution example
PS >invoke-operation -operationid 10
$c = docker container ls -aq -f name=test-.+; docker container start $c
d57707775ada
4b4ca2b65be2
b76c836b59fc
PS >invoke-operation -operationid 20
docker container ls --format="table{{.ID}}	{{.Names}}	{{.Ports}}"
CONTAINER ID   NAMES     PORTS
d57707775ada   test-3    0.0.0.0:8093->80/tcp
4b4ca2b65be2   test-2    0.0.0.0:8092->80/tcp
b76c836b59fc   test-1    0.0.0.0:8091->80/tcp
PS >invoke-operation -operationid 30
1..3 | % { $n = 'test-' + $_;$n;$c = docker container ls -q -f name=$n; docker container exec $c hostname -i }
test-1
172.17.0.4
test-2
172.17.0.3
test-3
172.17.0.2
PS >invoke-operation -operationid 50
$c = docker container ls -q; docker container stop $c
d57707775ada
4b4ca2b65be2
b76c836b59fc
PS >invoke-operation -operationid 90
stop-computer -force

PowerShellで文字列から[ScriptBlock]::Create()関数でScriptBlockを作ったときのメモ。

今まで invoke-command のscriptblockはベタ打ちでしか使ったことがなかったが、
「PSRemotingでDockerのコンテナの中に入れないのであれば、invoke-commandとさほど変わらないのでは?」
と思い、scriptblockの文字列を変数化してinvoke-commandを試してみて躓いてしまった。
そのとき以下の記事が参考になった。
yomon.hatenablog.com

べたで打ってるときと同じ調子で、コマンドの文字列を変数に格納し、{$cmdstr}としてみたが駄目だった。
[System.Management.Automation.ScriptBlock]型に変換もできない。(前後してしまうが・・・)

PS >$t = [System.Management.Automation.ScriptBlock]$c
"{date}" の値を "System.String" 型から "System.Management.Automation.ScriptBlock" 型に変換できません。
発生場所 行:1 文字:1
+ $t = [System.Management.Automation.ScriptBlock]$c
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) []、RuntimeException
    + FullyQualifiedErrorId : ConvertToFinalInvalidCastException

どうも、[Scriptblock]::Create()関数でちゃんと作らなければならないようだ。

PS >$c = 'date'
PS >$s = [Scriptblock]::Create($c)
PS >$s.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ScriptBlock                              System.Object


PS >$s.invoke()

202210913:59:29


PS >$c = '{date}'
PS >$s = [Scriptblock]::Create($c)
PS >$s.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ScriptBlock                              System.Object


PS >$s.invoke()
date
PS >$s.invoke().invoke()

202210914:00:41


PS >stop-transcript

コマンド文字列に'{ }'を入れてしまうと、'$s.invoke().invoke()'と2回ほどinvoke()しないと意図する結果が得られなかった。なので文字列としては'{ }'は付けない方が良いのだろう。

=============

2022-10-10 p.s.

ベタ打ちのときは文字化けしないが、[scriptblock]::create()で生成したscriptblockをinvokeするとなぜか文字化けする。

PS >$cmd = 'docker container ls -a -f name=test-.+'
PS >$x = [scriptblock]::create($cmd)
PS >$x.invoke()
CONTAINER ID   IMAGE                 COMMAND                  CREATED       STATUS                  PORTS     NAMES
9bbd5b2256b6   nginx:1.17.6-alpine   "nginx -g 'daemon of窶ヲ"   12 days ago   Exited (0) 2 days ago             test-3
34410af55b73   nginx:1.17.6-alpine   "nginx -g 'daemon of窶ヲ"   12 days ago   Exited (0) 2 days ago             test-2
d892fccc011b   nginx:1.17.6-alpine   "nginx -g 'daemon of窶ヲ"   12 days ago   Exited (0) 2 days ago             test-1

一方、
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command "chcp 65001"
で起動したpowershellではencodingはus-asciiのままだったが、文字化けはしていなかった。
PowerShell起動時、文字コードをUTF-8に変える方法 - Qiita

なぜだろう?何が違うのだろう?

PS >$cmd = 'docker container  ls -a -f name=test-.+'
PS >$x = [scriptblock]::create($cmd)
PS >$x.invoke()
CONTAINER ID   IMAGE                 COMMAND                  CREATED       STATUS                  PORTS     NAMES
9bbd5b2256b6   nginx:1.17.6-alpine   "nginx -g 'daemon of…"   12 days ago   Exited (0) 2 days ago             test-3
34410af55b73   nginx:1.17.6-alpine   "nginx -g 'daemon of…"   12 days ago   Exited (0) 2 days ago             test-2
d892fccc011b   nginx:1.17.6-alpine   "nginx -g 'daemon of…"   12 days ago   Exited (0) 2 days ago             test-1
PS >$outputencoding


IsSingleByte      : True
BodyName          : us-ascii
EncodingName      : US-ASCII
HeaderName        : us-ascii
WebName           : us-ascii
WindowsCodePage   : 1252
IsBrowserDisplay  : False
IsBrowserSave     : False
IsMailNewsDisplay : True
IsMailNewsSave    : True
EncoderFallback   : System.Text.EncoderReplacementFallback
DecoderFallback   : System.Text.DecoderReplacementFallback
IsReadOnly        : True
CodePage          : 20127

eof

【整理】PSRemotingの制約をついつい忘れてしまうのでとりあえずメモ

PSRemotingを使っていると何かと躓くことが多いので、この際、リストアップしておこう。

  • Enter-PSSessionはネストできない。
  • WSLにsshできない。
  • Dockerのコンテナの中に入ることができない。

Windows PowerShell PSSession の実行中は、Enter-PSSession コマンドレットを使用して別の PSSession を実行することはできない。

管理者権限powershellだとlocalhostにEnter-PSSessionできるが、さらにlocalhostにEnter-PSSessionしようとすると怒られる。
これは、異なるPC間でも同じであった。

PS >Enter-PSSession -computername  localhost
[localhost]: PS C:\Users\user1\Documents> Enter-PSSession -computername  localhost
Enter-PSSession : 現在 Windows PowerShell PSSession の実行中のため、Enter-PSSession コマンドレットを使用して別の PSSession を実行することはできません。
    + CategoryInfo          : InvalidArgument: (:) [Enter-PSSession]、ArgumentException
    + FullyQualifiedErrorId : RemoteHostDoesNotSupportPushRunspace,Microsoft.PowerShell.Commands.EnterPSSessionCommand

WSLの中に入れない

WSLを入れているPCにEnter-PSSessionしても、WSLにはsshで入ることはできない。
'Pseudo-terminal will not be allocated'と断られてしまうが、何やらメッセージは表示される。

[localhost]: PS C:\Users\user1\Documents> ssh 192.168.150.210
ssh : Pseudo-terminal will not be allocated because stdin is not a terminal.
    + CategoryInfo          : NotSpecified: (Pseudo-terminal...not a terminal.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.10.60.1-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Oct  2 15:52:34 JST 2022

  System load:  0.06               Processes:             13
  Usage of /:   1.0% of 250.98GB   Users logged in:       0
  Memory usage: 3%                 IPv4 address for eth0: 192.168.150.210
  Swap usage:   0%


253 updates can be applied immediately.
182 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

New release '22.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.


[localhost]: PS C:\Users\user1\Documents>

Dockerのコンテナの中に入ることができない。

コンテナの起動・停止はできるが、exec -it で中に入ろうとすると'docker : the input device is not a TTY'と怒られる。
これも、きっとPseudo-terminalであることが制約になっているのだろう。

[localhost]: PS C:\Users\user1\Documents>>$c = docker container ls -q
[localhost]: PS C:\Users\user1\Documents> docker container exec -it $c /bin/sh
docker : the input device is not a TTY.  If you are using mintty, try prefixing the command with 'winpty'
発生場所 行:1 文字:1
+ docker container exec -it $c /bin/sh
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (the input devic...d with 'winpty':String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

[localhost]: PS C:\Users\user1\Documents>

'mintty'なるものを使っていれば可能性があるのかもしれないが、独立しているソフトではなく前提条件としてインストールしなければならないソフトがあるようなので追求するのは断念した。
コンテナの中に入れないのでは、面倒だが外からコマンドを発行するしかなさそうだ。

なお、起動・停止だけなら文字化けする程度でCONTAINER IDの配列を渡して問題なく実行できた。

[localhost]: PS C:\Users\user1> docker container ls -a -f name=test-.*
CONTAINER ID   IMAGE                 COMMAND                  CREATED      STATUS                  PORTS     NAMES
9bbd5b2256b6   nginx:1.17.6-alpine   "nginx -g 'daemon of窶ヲ"   5 days ago   Exited (0) 3 days ago             test-3
34410af55b73   nginx:1.17.6-alpine   "nginx -g 'daemon of窶ヲ"   5 days ago   Exited (0) 3 days ago             test-2
d892fccc011b   nginx:1.17.6-alpine   "nginx -g 'daemon of窶ヲ"   5 days ago   Exited (0) 3 days ago             test-1
[localhost]: PS C:\Users\user1> $c = docker container ls -a --quiet -f name=test-.*
[localhost]: PS C:\Users\user1> $c
9bbd5b2256b6
34410af55b73
d892fccc011b
[localhost]: PS C:\Users\user1> docker container start $c
9bbd5b2256b6
34410af55b73
d892fccc011b