ssCAROのブログ

色んなとこで見つけたプログラムのメモ置き場っぽい

PowerShellのスクリプトをタスクスケジューラで実行させる

環境:Windows 10 Pro
PowerShellスクリプトのパス:C:\Task\HogeHoge.ps1

新しい操作で設定する項目は次のようにする。
プログラム/スクリプト:C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
引数の追加:-ExecutionPolicy Bypass "C:\Task\HogeHoge.ps1"
開始:C:\Task
*スクリプト絶対パスを指定しているのであれば開始のパスは無くて良い。

-ExecutionPolicy Bypassを指定しないと権限がない!とかで実行してくれない。

参考URL
PowerShellをタスクスケジューラーに登録して実行する方法│Soy Pocket

バッチファイルやPowerShellでSQL ServerのデータをCSV出力する

SQL Server 2017のデータを定期的(タスクスケジューラ等で)にCSV(タブ区切り)のShift-JISで出力したい。
*SQLiteに取り込みたいのでヘッダーは"なし"にしている。

バッチファイル (sqlcmdを使用)

sample1.bat

@echo off
set db_server=HogeSql
set db_database=HogeDb
set db_user=sa
set db_password=PassW0rd
set db_param='りんご'
sqlcmd -S %db_server% -U %db_user% -P %db_password% -d %db_database% -i .\sample1.sql -v Param1=%db_param% -b -s"<tab>" -h -1 -W -o .\sample1.csv

<tab>と書いている箇所には、タブ記号(タブキー)を入力すること

sample1.sql

SET NOCOUNT ON
SELECT * FROM ITEM_TBL WHERE ITEM_NAME = $(Param1)
SET NOCOUNT OFF

SET NOCOUNT ONSET NOCOUNT OFFを記入しないと"(1 行処理されました)"等のメッセージも出力される。
$(Param1)に代入されている値には、' (シングルクォーテーション)の記号も含まれる。

PowerShell (Invoke-Sqlcmdを使用) その1

sample2.ps1

$db_server = "HogeSql"
$db_database = "HogeDb"
$db_user = "sa"
$db_password = "PassW0rd"
$db_params = @("Param1='りんご'")
$ret = Invoke-Sqlcmd -InputFile ".\sample2.sql" -Variable $db_params -ServerInstance $db_server -Database $db_database -Username $db_user -Password $db_password | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation
$ret | Select-Object -Skip 1 | % {$_ -replace '"', ""} | Out-File (".\sample2.csv") -Force -Encoding oem

1行目にヘッダーがあり、データも" (ダブルクオーテーション)で囲まれているので、それらを削除している。
出力フォーマットはShift-JISにする。

sample2.sql

SELECT * FROM ITEM_TBL WHERE ITEM_NAME = $(Param1)

SET NOCOUNT ONSET NOCOUNT OFFは必要でない。

PowerShell (Invoke-Sqlcmdを使用) その2

sample3.ps1

$db_server = "HogeSql"
$db_database = "HogeDb"
$db_user = "sa"
$db_password = "PassW0rd"
$db_params = @("Param1='りんご'")
$sql_command = "SELECT * FROM ITEM_TBL WHERE ITEM_NAME = `$(Param1)"
$ret = Invoke-Sqlcmd -Query $sql_command -Variable $db_params -ServerInstance $db_server -Database $db_database -Username $db_user -Password $db_password | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation
$ret | Select-Object -Skip 1 | % {$_ -replace '"', ""} | Out-File (".\sample3.csv") -Force -Encoding oem

SQLスクリプトファイルは使用せず、直接SQLを書いている。
このとき、SQLの変数`$(Param1)の"$"の前には` (バッククォート)を付加すること。
そうしないとPowerShellの変数として扱われ展開されるので。

参考URL
powershell - Invoke-sqlcmd output without "quotes" and headers - Stack Overflow

SQL ServerでDBの復元に失敗する

バックアップしたDBを別のパソコンに復元しようとしたら次のエラーが発生して失敗した。
DBの互換性レベルは、SQL Server 2017(140)にしている。

System.Data.SqlClient.SqlError: データベースはバージョン 15.00.2000 を実行中のサーバーにバックアップされました。このバージョンは、このサーバー (バージョン 14.00.3192 を実行) とは互換性がありません。バックアップをサポートしているサーバーでデータベースを復元するか、またはこのサーバーと互換性のあるバックアップを使用してください。 (Microsoft.SqlServer.SmoExtended)

どうもSQL Serverのバージョンが違うと復元できないようです。
バージョン違いのバックアップしたDBを復元をするには、同じバージョンのSQL Serverをインストールするしかないっぽい。

DBの復元手順

仮想環境を立ち上げて、SQL Serverをインストールする。
SSMSからDBを復元して、復元したDBを選択して右クリックからの[タスク]-[スクリプトの生成]を実行する。
"特定のデータベース オブジェクトを選択"からテーブルを選択する。
スクリプトは"ファイルに保存"を選択して"単一ファイル"に出力する。

SQL Server 2019からOPTIMIZE_FOR_SEQUENTIAL_KEYというコマンドが追加されており、このままではSQL Server 2017で使用できないので出力したスクリプトファイルからOPTIMIZE_FOR_SEQUENTIAL_KEYを削除した。

開発環境側で同じDB名を作成しておき、このスクリプトを実行してテーブルを作成する。
exp_table.bat, exp_table.vbsを用意し、仮想環境側でexp_table.batを実行する。
直下にexp_impフォルダが作成され、フォルダの中のrun_exp.batを実行する。
exp_imp\dataフォルダにエクスポートしたデータが出力される。
exp_impフォルダごと開発環境にコピーして、exp_impフォルダの中のrun_imp.batを実行する。

参考URL 【SQL Server】bcpを使用したインポートとエクスポート - 小物SEのメモ帳

exp_table.bat

CScript .\exp_table.vbs

exp_table.vbs

Dim FROM_DB_SERVER
Dim FROM_DB_NAME
Dim FROM_DB_USER
Dim FROM_DB_PASSWORD
Dim TO_DB_SERVER
Dim TO_DB_NAME
Dim TO_DB_USER
Dim TO_DB_PASSWORD
Dim strRootPath
'==========
'DB接続情報(バックアップ元)
FROM_DB_SERVER   = "PC-A\HOGE_SQL"
FROM_DB_NAME     = "HOGE_DB"
FROM_DB_USER     = "sa"
FROM_DB_PASSWORD = "P@ssw0rd"
'DB接続情報(復元先)
TO_DB_SERVER     = "PC-B\DEV_SQL"
TO_DB_NAME       = "HOGE_DB"
TO_DB_USER       = "sa"
TO_DB_PASSWORD   = "P@ssw0rd"
'==========

'データ出力先
strRootPath = GetFolderPath()
Call CreateFolder(strRootPath & "\exp_imp")
Call CreateFolder(strRootPath & "\exp_imp\data")

'batファイルを作成(run_exp.bat, run_imp.bat)
Call CreateBatchFile

WScript.Quit

Sub CreateBatchFile()

    Dim adoConn
    Dim adoComm
    Dim adoRS
    Dim objFso
    Dim expTs
    Dim impTs
    Dim strBuff

    Set adoConn = CreateObject("ADODB.Connection")

    Dim strConnect
    strConnect = "Provider=SQLOLEDB;" & _
                 "Data Source=" & FROM_DB_SERVER & ";" & _
                 "Initial Catalog=" & FROM_DB_NAME & ";" & _
                 "User ID=" & FROM_DB_USER & ";" & _
                 "Password=" & FROM_DB_PASSWORD
    adoConn.ConnectionString = strConnect
    adoConn.Open

    Set adoComm = CreateObject("ADODB.Command")
    adoComm.ActiveConnection = adoConn
    adoComm.CommandTimeout = 0
    adoComm.CommandType = 1

    Set objFso = CreateObject("Scripting.FileSystemObject")

    adoComm.CommandText = "SELECT name AS TABLE_NAME FROM sys.tables"

    Set adoRS = adoComm.Execute

    If adoRS.EOF = False Then

        Set expTs = objFso.CreateTextFile(strRootPath & "\exp_imp\" & "run_exp.bat")
        Set impTs = objFso.CreateTextFile(strRootPath & "\exp_imp\" & "run_imp.bat")

        Dim strTableName
        Do While Not adoRS.EOF
            strTableName = adoRS("TABLE_NAME").Value
            
            'run_exp.bat フォーマット
            strBuff = "bcp " & strTableName & " format nul -c -x -f .\data\" & strTableName & "_fmt.xml -t, -S " & FROM_DB_SERVER & " -U " & FROM_DB_USER & " -P " & FROM_DB_PASSWORD & " -d " & FROM_DB_NAME
            expTs.WriteLine (strBuff)
            'run_exp.bat データCSV
            strBuff = "bcp " & strTableName & " out .\data\" & strTableName & ".csv -S " & FROM_DB_SERVER & " -U " & FROM_DB_USER & " -P " & FROM_DB_PASSWORD & " -d " & FROM_DB_NAME & " -f .\data\" & strTableName & "_fmt.xml -t, -o .\data\" & strTableName & "_output.log"
            expTs.WriteLine (strBuff)
            
            'run_imp.bat データCSV
            strBuff = "bcp " & strTableName & " in .\data\" & strTableName & ".csv -S " & TO_DB_SERVER & " -U " & TO_DB_USER & " -P " & TO_DB_PASSWORD & " -d " & TO_DB_NAME & " -f .\data\" & strTableName & "_fmt.xml -t, -o .\data\" & strTableName & "_input.log"
            impTs.WriteLine (strBuff)
            
            adoRS.MoveNext
        Loop

        expTs.Close
        Set expTs = Nothing

        impTs.Close
        Set impTs = Nothing

        adoRS.Close
        Set adoRS = Nothing
    End If

    Set objFso = Nothing

    Set adoComm = Nothing

    adoConn.Close
    Set adoConn = Nothing

End Sub

Function GetFolderPath()

    Dim objFso

    Set objFso = CreateObject("Scripting.FileSystemObject")

    GetFolderPath = objFso.GetParentFolderName(WScript.ScriptFullName)

    Set objFso = Nothing

End Function

Sub CreateFolder(strFolder)

    Dim objFso

    Set objFso = CreateObject("Scripting.FileSystemObject")

    If objFso.FolderExists(strFolder) = False Then
        Call objFso.CreateFolder(strFolder)
    End If

    Set objFso = Nothing

End Sub

ログファイルを削除するためのスクリプト(VBS)

ログファイルを定期的に削除したい。
専用アプリを作成してまで削除するのもどうかと思ったのでスクリプト(VBScript)で実行することにした。

ログファイルの削除は、ファイル名に日付が入っているので、ファイル名の日付で削除する。

実行は、タスクスケジューラを使用する。
VBSのまま、タスクスケジューラに登録しても実行されないので、batファイルからVBSを実行するようにする。

DELETE_TASK.bat

CScript C:\HOGE_HOGE\TASK\DELETE_FILE.vbs

DELETE_FILE.vbs

Dim targetPath

'削除対象のフォルダ
targetPath = "C:\HOGE_HOGE\LOG\"

'3年前のファイルを削除する
Call DeleteFile(targetPath, 3)

WScript.Quit

'ログ用フォルダにhoge_yyyymmdd.logのファイルがある
Sub DeleteFile(folderPath, deleteYear)

    Dim fso
    Dim files
    Dim file
    Dim deleteDate
    Dim temp

    Set fso = CreateObject("Scripting.FileSystemObject")
    Set files = fso.GetFolder(folderPath).Files

    'n年前のファイルを削除する
    deleteDate = DateAdd("yyyy", -deleteYear, Date)

    'フォルダ内のファイルを列挙
    For Each file In files
        '拡張子がlogファイルのみを削除対象とする
        If Right(file.Name, 4) = ".log" Then
            '"hoge_yyyymmdd.log"を"yyyy-mm-dd"の書式に変換
            temp = Mid(file.Name, 6, 8)
            temp = Left(temp, 4) & "-" & Mid(temp, 5, 2) & "-" & Right(temp, 2)
            If IsDate(temp) = True Then
                If CDate(temp) < deleteDate Then
                    file.Delete(True)
                End If
            End If
        End If
    Next

    Set file = Nothing
    Set files = Nothing
    Set fso = Nothing

End Sub

'日付(yyyymm)フォルダが作成され、そのフォルダ内にログファイルがある
'日付フォルダごと削除する
Sub DeleteFile2(folderPath, deleteYear)

    Dim fso
    Dim folders
    Dim folder
    Dim deleteDate
    Dim temp

    Set fso = CreateObject("Scripting.FileSystemObject")
    Set folders = fso.GetFolder(folderPath).SubFolders

    'n年前のファイルを削除する
    deleteDate = DateAdd("yyyy", -deleteYear, Date)

    'フォルダ内のフォルダを列挙
    For Each folder In folders
        '"yyyymm"を"yyyy-mm-dd"の書式に変換
        temp = Left(folder.Name, 4) & "-" & Right(folder.Name, 2) & "-01"
        If IsDate(temp) = True Then
            If CDate(temp) < deleteDate Then
                folder.Delete(True)
            End If
        End If
    Next

    Set folder = Nothing
    Set folders = Nothing
    Set fso = Nothing

End Sub

波線を描画したい(VB.net)

波線を描画したくなった。
作成はVisual Basic 2017で行った。

DrawLineを使って描いても良かったんだけど、もう少し効率の良い方法はないかと探してみて色々試した結果、DrawCurveを使うことにした。

等間隔で中央、上、中央、下、中央…という感じでy位置を変化させてPointに格納しDrawCurveで曲線を描画させた。

横線のみです。

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    Dim canvas As New Bitmap(PictureBox1.Width, PictureBox1.Height)
    Dim g As Graphics = Graphics.FromImage(canvas)
    
    DrawWavyLine(g, Pens.Blue, 0, 200, 200)
    
    PictureBox1.Image = canvas
End Sub

'横の波線を描画
Public Sub DrawWavyLine(ByRef g As Graphics, ByRef pen As Pen, ByVal x1 As Single, ByVal y1 As Single, ByVal x2 As Single)
    '--------------------
    '波線の設定
    '--------------------
    '波線の曲線(0.0 = 直線 ~ 1.0 = 曲線)
    Dim tension As Single = 1.0F
    '波線の幅
    Dim waveWidth As Single = 5.0F
    '波線の高さ
    Dim waveHeight As Single = 5.0F
    '--------------------
    Dim points As New List(Of PointF)
    Dim x As Single = 0F
    Dim y As Single = 0F
    Dim f As Integer = 0

    While x < (x2 - x1)
        Select Case f
            Case 0, 2
                y = 0F
            Case 1
                y = -waveHeight
            Case 3
                y = waveHeight
        End Select
        f = If(f < 3, f + 1, 0)

        points.Add(New PointF(x, y1 + y))

        x += waveWidth / 2.0F
    End While

    g.DrawCurve(pen, points.ToArray(), tension)
End Sub

f:id:ssCARO:20200701103403p:plain

斜めの波線の途中未完成!!

  • 横線を引き、そこに等間隔で縦線を引く
  • その横線を傾けたときの角度を求める
  • 縦線の上、中央、下の位置に対して角度を適応した位置を求める
  • 中央、上、中央、下、中央…の位置をPointに格納してDrawCurveに投げると…なんか上手くいかない!
Public Sub DrawWavyLine2(ByRef g As Graphics, ByRef pen As Pen, ByVal x1 As Single, ByVal y1 As Single, ByVal x2 As Single, ByVal y2 As Single)
    '--------------------
    '波線の設定
    '--------------------
    '波線の曲線(0.0 = 直線 ~ 1.0 = 曲線)
    Dim tension As Single = 0.0F
    '波線の幅
    Dim waveWidth As Single = 10.0F
    '波線の高さ
    Dim waveHeight As Single = 10.0F
    '--------------------
    Dim points As New List(Of PointF)
    Dim x As Single = 0F
    Dim y As Single = 0F
    Dim f As Integer = 0

    Dim wx1 As Single = 0F
    Dim wy1 As Single = 0F
    Dim wx2 As Single = 0F
    Dim wy2 As Single = 0F
    Dim wx3 As Single = 0F
    Dim wy3 As Single = 0F

    '2点間の角度
    Dim radian As Double = Math.Atan2(y2 - y1, x2 - x1)
    Dim cos As Double = Math.Cos(radian)
    Dim sin As Double = Math.Sin(radian)

    While (x1 + x) < x2
        '角度から座標を求める
        wx1 = x * cos - -waveHeight * sin
        wy1 = x * sin + -waveHeight * cos

        wx3 = x * cos - 0F * sin
        wy3 = x * sin + 0F * cos

        wx2 = x * cos - waveHeight * sin
        wy2 = x * sin + waveHeight * cos

        Select Case f
            Case 0, 2
                y = wy3
            Case 1
                y = wy1
            Case 3
                y = wy2
        End Select
        f = If(f < 3, f + 1, 0)

        points.Add(New PointF(x1 + x, y1 + y))

        x += waveWidth / 2.0F
    End While

    g.DrawCurve(pen, points.ToArray(), tension)
End Sub

参考URL
開発メモ:円周上の点
2点間の距離と角度と座標の求め方

Windows 10 BitLockerを無効にする

Windows 10 のパソコンのバックアップをAcronis True Imageで取ろうとしたけど「BitLockerが有効のためバックアップできない」と言われた。

BitLockerを無効にするためにコントローラパネルから"BitLocker ドライブ暗号化"を開くと「Windows (C:) BitLocker はアクティブ化を待機中です」と表示されており、BitLockerを無効にするが選べなかった。

調べてみると[スタート]-[設定]-[更新とセキュリティ]を開く。
左側の一番下にある「デバイスの暗号化」を開き"オフにする"を押すと出来るみたいだけど[更新とセキュリティ]にあるはずの「デバイスの暗号化」が見つからない!

色々試してみると、サービスの「BitLocker Drive Encryption Service」を無効にしていると「デバイスの暗号化」が表示されないみたい。
このサービスを手動にして開始すると「デバイスの暗号化」が表示されて無事にBitLockerを無効にすることが出来た。

Webカメラのかわりにスマホ(iVCam)を使う

テレワークをすることになったけど、Webカメラが売ってない。
色々検索してみるとスマートフォンWebカメラがわりにすることが出来る情報を得たので早速ためしてみたときのメモ。

参考URL
Webカメラが品切れでもOK? スマホをWebカメラとして使える「iVCam」を試してみた
E2ESOFT iVCam

使用するスマートフォンは、HUAWEI P20 lite
パソコンは、Windows 10 Professional
Webカメラのアプリは、iVCam

インストールも簡単に終わり、スマートフォンWebカメラのかわりにできた。
パソコンにはマイクが付いており、スマホの方でも音声をひろっていたのでスマホ側の設定を変更し音声を無効にした。