命名って難しい

変数、関数、クラスなどなど実装より命名に毎回悩むタイプの人間による技術についてのメモ。

Powershellで複数のCSVファイルを特定の列で1つのファイルにマージ

概要

以下2つのCSVを共通の列名"HeaderA"でまとめたCSVにしたい!

A.csv

HeaderA,HeaderB
ValueA,ValueB

B.csv

HeaderA,HeaderC
ValueA,ValueC

以下Powershellスクリプトで実現。

get-childItem "*.csv" | foreach {
    Import-Csv -Path $_ | Select HeaderA | Export-Csv -Append -NoTypeInformation -Path AB.csv
}

備考/感想

  • Selectに存在しないヘッダーを選んでも大丈夫(無いものは空文字になる)
  • 結果ファイルの全部のデータがダブルクォーテーションで囲まれて出力される。注意。
  • もちろんヘッダー指定は複数でもOK("Select HeaderA, HeaderB"という感じ)

社内ホームページにRiot.jsを導入してみた質の極めて低いメモ

前提

環境

  • 社内のホームページはホームページビルダーなどで作った静的なページ。
  • ホームページサーバーはWinServerでIISの標準的な機能でホストしている。

実装者

  • web系開発経験なし。全然ノウハウなし。
  • ウェブサービスのフロントエンドとバックエンドがどうデータをやり取りしているのかも分からないレベル。

契機

社内のホームページにある社内連絡のtable要素を非エンジニアがhtmlを直接編集しており、記述に難儀していた。 直接編集する作業をやめたい、という要望があった。

導入

全体像

  • 社内連絡の内容を記載し、マクロによってjson出力するExcelマクロ有効ブックを作る…①
  • 非エンジニアに①を使用させ、出力を行う…②
  • ホームページで②を読み込ませ、それを元にRiot.jsで特定のタグをマウントする。

簡単ですね。実際はVBAが実装面倒すぎて辛かったんですけど。

実装

jsonファイル

トップの"renraku"から、"date","text","url"の3つのデータを配列で持たせています。

{  
   "renraku":[  
      {  
         "date":"2016/11/30",
         "text":"おしらせ~",
         "url":"http://google.com"
      },
      {  
         "date":"2016/11/29",
         "text":"ファイルへのリンクお知らせ",
         "url":"files/お知らせ.xlsx"
      }
   ]
}

社内連絡のページHTML

カスタムタグではなくID指定で試してみた。

<!doctype html>
<html lang="ja">

<head>
  <title>社内連絡</title>
</head>

<body>
  <div id="riot-renraku-table"></div>
  <script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
  <script type="riot/tag" src="tag/renraku-table.tag"></script>
  <script type="text/javascript" src="js/riot_compiler.min.js"></script>
  <script type="text/javascript">
    $.ajax({
      url: 'json/renraku.json',
      dataType: 'json',
      async: true,
      cache: false,
      success: function(res) {
        riot.mount('#riot-renraku-table', 'renraku-table', res.renraku);
      }
    });
  </script>
</body>

</html>

カスタムタグ

if文を使って、URLがあればa要素、なければdivという表示をしています。

<renraku-table>
  <table>
    <thead>
      <tr>
        <th>日付</th>
        <th>お知らせ</th>
      </tr>
    </thead>
    <tbody>
      <tr each={opts}>
        <td>{date}</td>
        <td>
            <a if={url!==""} href="{url}" target="_blank">{text}</a>
            <div if={url===""}>{text}</div>
        </td>
      </tr>
    </tbody>
  </table>
</renraku-table>

感想

なんか書くのが面倒になってしまったので感想でまとめて終わらそうと思うんですけど、
「なんかよくわからないが動いた」の一言につきます。

とりあえず初心者の手習いだと思っていただければ。

VBScriptで古い形式のExcelを新しい形式に変換する。

いちいち開いて名前を付けて保存するの面倒だよね、という事からつくりました。
変換したいファイル達をドラッグ&ドロップするだけで同ディレクトリに保存されます。

コード

' 定数
Const XlFileFormat_xlOpenXMLWorkbook = 51 ' .xlsx : Excel ブック

' ドラッグドロップで渡されたファイルを文字列で取り込む。メッセージ用。
set args = WScript.Arguments
fileList = ""
for each arg in args
  fileList = fileList & vbNewLine & arg
next

' 引数のチェック。対象ファイル以外が混ざっている場合終了。
set fobj = CreateObject("Scripting.FileSystemObject")
for each arg in args
    ext = fobj.GetextensionName(arg)
    if ext <> "xls" then
        msgbox "xlsファイル以外が指定されました。終了します。" & vbNewLine & fileList
        WScript.Quit
    end if
next

' 引数で貰った各Excelファイルを最新の形式で保存する。
set oXlsApp = CreateObject("Excel.Application")
for each path in args
    oXlsApp.Application.Visible = true
    set book = oXlsApp.Application.Workbooks.Open(path)
    book.SaveAs Replace(path, ".xls", ".xlsx"), XlFileFormat_xlOpenXMLWorkbook
    book.Close
next
oXlsApp.Quit
set oXlsApp = nothing

msgbox "変換完了しました。"

Powershell版つくりました(2017/02/03更新)

notshown.hatenablog.jp

Windows Server 2008 R2 の PowerShellをアップデートした。

方法

下記URLを参照し、.Net FrameworkWindows Windows Management Framework 4.0をインストールするだけ。

Step by Step Upgrading the Powershell Version 4 on 2008 R2 - TechNet Articles - United States (English) - TechNet Wiki

気をつけないといけないのはWindows Updateが適用されていないと接続されないこと。

ローカルサーバーだからアップデート切っていたのを失念していて時間を食ってしまった。

Powershellでタスクスケジューラのタスクをまとめてエクスポートするスクリプト

サーバーに多数タスクが登録されているのですが、 何かあった時のためにタスクをエクスポートし、バックアップしようと思い作成。

前提

以下の環境を前提とします。

  • Scheduled Tasks Cmdletsが使える

ソースコード

$taskBasePath = "\MyTask\*"
$taskSaveDir = "C:\MyTaskBackup"

Get-ScheduledTask -TaskPath $taskBasePath  | foreach {
    $taskDir = Join-Path $taskSaveDir  $_.TaskPath
    if( -not (Test-Path -Path $taskDir)) {
        mkdir -Path $taskDir
    }
    $path = (Join-Path $taskDir "$($_.TaskName).xml")
    Export-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath | Out-File $path # -WhatIf
}

解説

使う時書き換える変数については以下表を参照。

変数 用途 備考
taskBasePath タスクスケジューラの特定のパス。 このフォルダにまとめて定義しておけばよいという運用をしてます。
taskSaveDir 保存先のパス。 ここをSVNやGITのリポジトリに指定して定期的に自動コミット/プッシュしたりしたい。

動作としては 指定のパスのタスクを、格納しているフォルダの中に、そのタスク名で保存する という感じです。

無いフォルダは勝手に作ります。

既存のタスクを削除したりはしないので何度でも実行可能。

参考

Scheduled Tasks Cmdlets in Windows PowerShell

以上!

チートシート的メモ

概要

1,2行のちょっとしたコードをまとめる。
ちょいちょい更新していく。

PowerShell

現在のスクリプトディレクトリを取得する。

Split-Path $MyInvocation.MyCommand.Path
# pushd %~dp0と同じ処理
Push-Location -Path (Split-Path $MyInvocation.MyCommand.Path)

バッチ(コマンドプロンプト)

BCPで[-w]オプションつけて出力するとforで読めない

単純な文字列型[-c]にしよう。

javascript

getElementsByTagNameの結果をforEachする

Array.prototype.slice.callを使って配列に変換する必要がある。

Array.prototype.slice.call(document.getElementsByTagName("table")).forEach...

BCPコマンドで同一構造のサーバーのテーブルを比較する。

作ったのでメモ。

経緯

DBサーバーを入れ替える時、全データが入替えられているか確認する作業があったのですが、
「SSMSでSelectして目視比較して・・・」みたいな話が持ち上がり初めていたので必死で書きました。

どう考えても目視はやばい。

構造

以下の2ファイルを同一ディレクトリに用意して使います。

  • このバッチ
  • DB名とテーブル名を持つCSV

以下のように処理してDBのデータを比較します。

  • 新旧サーバーの対象のテーブルに対してBCPでエクスポート
  • エクスポートの結果をfcコマンドで比較。fcのログを出す。
    • 同一なら先頭にok
    • 相違なら先頭にngがつく

バッチのソース

@echo off
setlocal

pushd %~dp0

rem DBとテーブルのリストファイル名
set targetList=

rem ユーザーのアカウント
set OldSvrUser=
set NewSvrUser=
set OldSvrPass=
set NewSvrPass=

rem 比較サーバーインスタンス
set OldServer=
set NewServer=

rem 出力先フォルダ
powershell -Command "$(Get-Date).ToString('yyyyMMdd_HHmmss')" > %temp%\ret
set /p ymd=< %temp%\ret
set BaseFolder=%UserProfile%\Desktop\SvrDataComp\%ymd%\
set OldFolder=%BaseFolder%Old\
set NewFolder=%BaseFolder%New\

mkdir %BaseFolder%
mkdir %OldFolder%
mkdir %NewFolder%

rem 指定のリストからループして出力
for /f "tokens=1,2* delims=," %%i in (%targetList%) do (  
    call :CompareOldNewServer %%i %%j
)

popd
endlocal
exit

rem --------------------------------------------------------------------------------------
rem エクスポート処理
rem --------------------------------------------------------------------------------------
:ExportTable

rem 引数
set instance=%1
set db=%2
set table=%3
set exportPath=%4
set usr=%5
set pass=%6

TITLE Exporting [Server:%instance% DB:%db% Table:%table%]
bcp "SELECT * FROM %db%.dbo.%table%" queryout %exportPath% -S %instance% -U%usr% -P%pass% -w -k -o %exportPath%.log

exit /b

rem --------------------------------------------------------------------------------------
rem 新旧サーバーのテーブルのエクスポートと比較
rem --------------------------------------------------------------------------------------
:CompareOldNewServer

rem 引数
set db=%1
set table=%2

set  fileName=%db%_%table%.txt
set  oldExport=%OldFolder%%fileName%
set  newExport=%NewFolder%%fileName%


call :ExportTable %OldServer% %db% %table% %oldExport% %OldSvrUser% %OldSvrPass%
call :ExportTable %NewServer% %db% %table% %newExport% %NewSvrUser% %NewSvrPass%

set  compFile=%BaseFolder%ng_%db%_%table%.txt
fc  /a /c /n /u %oldExport% %newExport% > %compFile% 

if "%errorlevel%"=="0"  move %compFile% %BaseFolder%ok_%db%_%table%.txt
exit /b