部屋を掃除したら漫画が沢山出てきたので書く日記

漫画とか合唱とかUNIXとかLinuxとかについて書く日記です。

転職しました(その1)

2002年に入社して以来働いていた会社(某システムインテグレータ)を2020年4月末で退職し、5月から別の会社で働く事になりました。つまり、転職をしました。
転職は人生の中で滅多にない事なので、以下の通り経緯や感想を書いておきます。
転職活動は以下の通り、3回行いました。内定を得ることが出来たのは第三回においてです。

第一回 2015年11月~2016年2月

第二回 2018年7月

第三回 2019年9月 ~ 2020年2月

まずは第一回についてです。

第一回 はじめての転職活動(2015/11 ~ 2016/2)

2015年11月頃、仕事で以下のようにいろいろな事が重なりました。

  1. 退職者の残したプロジェクトが炎上し、その対応で疲弊
  2. 炎上プロジェクトの対応をする中で同僚との人間関係が悪化
  3. その他色々、会社の将来に悲観的になる

など

そんな中、私のネット上で発信している情報(ブログ、twitterFacebookなど)を見て転職エージェント(仮にA社とします)がメールでコンタクトを取ってきました。上記のようないろいろな事情を抱えていた私は、話だけでも聞いてみよう、情報収集だ!と考え、A社担当者と会う事にしました。都内の喫茶店で担当者と会い、アンケート形式で色々聞かれました。その上で、

  • まずは職務経歴書を作ってほしい
  • 職務経歴書記載のキャリア、志望に合う企業を紹介するので、話を聞きに行ってほしい

と伝えられました。
これを受け、以下のプロセスにて職務経歴書を作成しました。

  1. 入社してから続けていた週報(その週にやった事、翌週にやる予定を書いて関係者に送るメール)を辿り、関わったプロジェクトを全てリストアップする
  2. 会社名、PJ名など社外秘情報を伏せ、各プロジェクトで扱った技術分野(OS、サーバ、ミドルウェア、プログラム言語など)、担当した役割(PJ管理、プログラム開発、システム設計、サーバ構築など)をプロジェクトごとに記載する

作成した職務経歴書をA社担当者に渡して1~2週間ぐらい経った後、某IT企業(仮にG社とします)のインフラエンジニアの求人を紹介され、カジュアル面談を受ける事になりました。散髪に行き、買ったばかりのスーツを着て、一番良いYシャツとネクタイをつけて意気揚々とG社のオフィスに行ったことを覚えています。

が、結果は不合格でした。

カジュアル面談終了後、A社担当者と再び会った際に不合格である旨を伝えられショックを受けました。
「まずは話を聞いてみるだけで大丈夫ですから」というA社担当者のコメントを信じて、カジュアル面談がどういうものかを意識せずに本当に「話を聞いてみる」スタンスで行ったため、合格、不合格という判定が行われる認識が無かったのです。すでに選考は始まっていたという事になります。

A社担当者によると「意欲が感じられない、スキルセットが古いので当社には合わない、と言われた」との事でした。「スキルセットが古い」はSIerに勤めている人が転職活動をするにあたりもっとも心配している事ではないでしょうか。あーやっぱりそうなのかーダメなんだなー転職市場で商品価値無いんだなーと落ち込みましたが、今まで積み重ねてきた事に対する評価なので仕方のない事です*1

このやりとりが発生した時点でA社に不信感(『最初の話と違うじゃないか』と思った)を抱いてしまいました。また、担当者もそれまでは「今のご年齢(当時35歳)でも転職に遅すぎることはありませんのでじっくり探していきましょう」とか言っていたのですが、「その年齢じゃ選り好みしてられないからもっと真剣に考えないと駄目です」と論調が変わってきました。こちらを焦らせて転職活動に本腰を入れるよう促したかったのかもしれないですが、当時はそこまで考えていなかった事と、A社ともう一緒に活動したくない気持ちになっていたので、活動打ち切りを先方に伝えました。

こうしてはじめての転職活動は終わりました。

反省点
  • G社のカジュアル面談は「とりあえず情報収集」という訳には行かず、ある程度志望しているとみなされた模様です。しかし第三回目の転職活動で転職エージェント経由ではなく直接申し込んだいくつかのカジュアル面談においては、合否判断をされる事はなく「話だけでも聞いていってください。その上で受けたかったら採用選考に申し込んでくださいね」というスタンスの会社ばかりでした。昔は違ったのか、その会社はそういうスタンスだったのか、間に入ったA社がニュアンスを変えて伝えて来たのか、あるいは当時の私が先方から見て余りにも駄目だったのか、今となってはわかりません・・・。
  • A社から渡されたWordのテンプレートで作った職務経歴書を今読み返すと、長すぎだと思いました。2002年から2016年の14年間の経歴を全部書いたので、A4でたぶん10ページくらいはあったと思います(A社担当者からはこれでOKと言われましたが・・・)。2019年の第三回目の活動では、全ての経歴の中から「これはアピールできる」とか「これは話のネタにできる」と思えるものをピックアップして、A4で2ページ程度でまとめました*2。何度か面接に臨んだ結果、職務経歴書を面接当日に面接官が初めて読んでるらしいケースがいくつかあったので、長過ぎても読んでもらえないな、と実感しました。
  • 職務経歴書を作るために入社してから現在までの経歴を再点検する事になりましたが、これは社会人になってからの様々な思い出を辿る事でもあるのでなんとも懐かしい気持ちになりました。また、どういう事が得意だと思っているのか、どういう事をやりたいのかを整理するきっかけになりました。必ずしも転職しなくても一度作ってみた方が良いと思いました。
  • 活動をするにあたりA社から現在の年収を聞かれたのですが、自分の年収がいくらかわかっておらず、たぶんこれくらいかな?という金額を回答したところ、「そんなに低いんですか?御社の同年代の方だともう少し高い気がするのですが・・・」と言われてしまいました。後になって直近1月~12月の給与明細の記載額(天引き前の金額)の合計と、夏・冬の賞与支給額(天引き前の金額)を合算してみたところ、実際の額より100万円ほど低い額を伝えてしまっていました。現在の年収は転職活動での提示報酬の目安になる事が多い(現職の年収と同額を出すとか、それよりも高い額だ、など)ので、まずは正確に把握した方が良いです。あと、今の年収と、転職活動をして知る求人の報酬額相場を比較して、相場と比べて高いのか低いのかを知るだけでも価値がある事だと思います。「今の年収は相場より高いんだな」と思うだけで転職を踏みとどまる人もいる事でしょう。*3
  • A社に限らず転職エージェントは入社させる事が利益につながるビジネスのようなので、よく広告で出てくる「相手のキャリアを親身に考えて相談してくれるパートナー」ではなく「こちらの望む応募先を紹介してくれる業者」と考えるべきです。もちろんそのような転職エージェントも存在すると思うのですが、残念ながら私は巡り合う事はありませんでした。良い表現なのかは判りませんが不動産会社の営業担当者と同じような業種であると認識して接するべきではないかと思いました。
まとめ
  • 職務経歴書を作る事はとても有意義なので、転職する予定が無くてもおすすめ
  • 週報のような日々の業務報告はまめに記録しておくべき
  • カジュアル面談に臨むにあたってのスタンスは事前に確認しておいた方が良い
  • 自分の年収は把握しておいた方が良い。職務経歴とセットで、転職市場での自分の価値を図る指標となる
  • 転職エージェントは転職活動のパートナーではなく、こちらの必要に応じて活用する業者と考えるべき

続きます
では。

*1:と、後になってからは気にしなくなりました

*2:ちなみにビズリーチのテンプレートが大変作りやすかったのでそれに準じて作りました

*3:ちなみに、これをきっかけに給料明細の内容を毎月グラフ化するプログラムを作成して年収を管理する事にしました

新型コロナウイルス感染症流行中の合唱活動の記録(2020年2月~4月8日)

タタミちゃんもTwitterで言っている事ですし、2020年2月から現在までの、新型コロナウイルスに関連して自分の身の回りで起きた事、とりわけ、アマチュア合唱団の活動にまつわる事を中心に書いておこうと思います。



所属している合唱団について

混声合唱団 R

  • 自身が団長を務めている
  • 3/1に合唱コンクール(東京春のコーラスコンテスト)に出場予定
  • 7月に常任指揮者が関わっている某自治体の合唱祭にて招待演奏をする予定
  • 9月に演奏会を開催予定

男声合唱団 F

  • 団の運営メンバーを務めている
  • 3月に川崎市内の老人ホームに訪問演奏をする予定
  • 5月に演奏会を開催予定

2月~4月8日までの出来事

2020年2月中旬

新型コロナウイルスが中国で感染が拡大、クルーズ船「ダイヤモンド・プリンセス号」でも感染者が出ている事がニュースになっていた。
Rの次の本番である春のコーラスコンテストの開催はどうなるのか?という事が少し気になっていたが、主催者である東京都合唱連盟は開催する旨アナウンスしていたので、予定通り参加するつもりでいた。
Fは3月の老人ホーム訪問、および5月の演奏会に向けて練習をしていた。

2月下旬

新型コロナウイルスの感染拡大が国内でも少しずつ増えてきている状態になり、人が集まるコンクールに行く事にリスクがあるのではないか?という意見がRの団内から出始める。
この時点では、病人、高齢者は重篤になる危険性があるものの、それ以外の人はそこまで気にする必要はないと自分は考えていたが、常任指揮者が指揮をする他団体は出場辞退を決めた事、常任指揮者ご本人も懸念している事、複数の合唱団員も懸念している状況を踏まえ、Rはコンクール辞退とすることを2/28に判断。
当日の練習会場キャンセルや団員、公募して参加する予定だったメンバーに周知する等の対応に追われる。
この時点でも自分としては「実際にはそんなに危険はないが関係者の懸念を汲んでの念のための処置」という認識だった。
Fの方も、老人ホーム入居者の方の健康を考慮して訪問中止の判断を2/28に実施。

2/27には安倍首相が公立の小中高を休校する発表を行い、3月からの子供の生活をどうしたらいいのか困惑していた。
ただし、4月には落ち着いているだろうという認識だった。

3月

各種本番が無くなってしまったものの、RもFも以下の方針にて練習は継続する事とした。

  • 参加は強制しない。来れる人だけで開催。
  • なるべく換気をしながら練習

その上で練習は開催したものの、学校関係者、医療関係者、高齢者等の団員が自主的に欠席。少ない人数だがとにかく練習は行っていた。
Fについては5月の演奏会が開催できるのか?という懸念点がだんだん大きくなってきた。練習への参加者が段々減り、演奏会参加を辞退する人も現れてきていた。これを受け3/20に関係者で協議し、4月上旬時点でどうするか判断しようという事を話し合った。
2月末から3月中旬まで2団体の対応を行い、個人的にもいろいろあったのでこの時期はくたびれていた・・・。

3月25日ごろ、岐阜県の合唱団にて新型コロナウイルス感染者が判明、しかも練習によって感染した可能性が高いと報道される。
更に同日夜に東京都知事から週末に不要不急の外出を自粛するよう要請する声明が出される。
このころ、東京オリンピックの延期決定もされたはず。
これを受け、Rは3月末の練習と4月のすべての練習を休止する事を決定。
同じくFについても4月の練習を休止する事を決定。
また、3/20の打ち合わせで話した前提である、4月上旬の時点でどうするか検討するという事態では無くなってしまったという事で5/24の演奏会は無期限延期と決定。

3月下旬には、4~7月の合唱イベントの中止・延期が多数発表された。
Rが出演予定だった7月の某市合唱祭も中止。
Fが出演予定だった6月の神奈川県合唱祭も中止。

ヨーロッパやアメリカでの感染拡大、死亡者の増大が大きく報道され、志村けんが感染した報道の後すぐに亡くなった事も大きな衝撃を与え、「これはかなりまずいのでは」と思うようになった。
さらに岐阜県の合唱団で新型コロナウィルスに感染した方が亡くなったという報道が出て、今後のアマチュア合唱団への逆風を予感させた。

4月

練習休止となりメールでのやり取りは少し落ち着く。自分もやる事が減り気持ちがとても楽になった。
Zoomを使った打ち合わせ、練習を行う団体がちらほらと登場、PCに明るくない常任指揮者も関係者と会議したとの事。
活用を検討し始める。
4/8 政府が緊急事態宣言を出す。合唱活動はいつから再開できるのか、目途が立たない。

では。

Youtubeへ音声ファイルをアップロードする方法 2020年春

マチュア合唱団活動をしていると、以下のような状況でYoutubeに音声ファイルをアップロードする機会があります。

  1. 自分たちが演奏した録音を公開するためにアップロードする*1
  2. 練習を録音した音源を合唱団内で共有するためにアップロードする

最近やる機会が多くなり、手順を整理しましたので紹介します。

録音データをパソコンに取り込む

私は2008年に購入したオリンパスLS-10を使用しています。

bata64.hatenablog.jp

公開用の音源はWAV(音質重視)、練習の録音はmp3(録音時間が長くなるのでファイルサイズ重視)で録音していますが、これをWindows10パソコンに取り込ます。
USBケーブルで接続するとUSBマスストレージとして認識する(USBメモリみたいに使える)ので、コピーアンドペーストでパソコン内の作業フォルダに格納します。

必要に応じて録音データを加工する

公開用の音源の場合は、公開したい演奏前後の余分な個所を削除します。あと、音量レベルが小さい場合は大きく加工します。
なお、私は上記LS-10を購入した時に付属していたソフト(DigiOnSound5 Express)を使用しています。

PowerPointで画像を作成する

Youtubeには音声ファイルだけをアップロードすることはできません。動画としてアップロードする必要があります。
という事で、静止画像と音声データによって、「静止画像が表示されて音声が流れる」動画ファイルを作ります。
画像の作り方は色々あると思いますが、私はMicrosoft PowerPointを使って以下の通り作成しています。なお、PowerPoint 2016で作成しているものとします。

  1. スライドを新規作成
  2. 動画に表示すべき必要情報を入力(録音の日付とか場所とか曲名とか)
  3. 必要に応じて写真(その日の練習風景とか合唱団のロゴとか)を挿入
  4. [ファイル] -> [エクスポート] -> [ファイルの種類の変更] と遷移、「JPEGファイル交換形式~」をクリックした後「名前を付けて保存」をクリック。表示されるポップアップでは「このスライドのみ」を選択。

これで以下のようなJPEGファイルが生成されます。なお、内容はサンプルです。

f:id:bata64:20200103123048j:plain

ffmpegで動画ファイル(flv形式)を作成

上記の手順で作成された音声ファイルと画像ファイルから、Youtubeにアップロードできる動画ファイルである「flv形式」のファイルを作成します。
作成にあたっては、私はコマンドライン形式のフリーソフトffmpeg」を使っています。

www.ffmpeg.org

Windows10版もありますしLinuxディストリビューションのパッケージとしても提供されている、フリーの動画や音声加工ソフトのデファクトスタンダードと言えるでしょう。
インストールや使い方の詳細は省きます。
私はWindows10上でLinuxを動作させることのできる機能「WSL」でインストールしたUbuntu 18上で以下のコマンドで実行しています。

ffmpeg -loop 1 -i [入力画像ファイル名] -i [入力音声ファイル名] -vcodec flv -acodec copy -r 1 -shortest [出力FLVファイル名]

例) test.jpgとtest.mp3からtest.flvという動画ファイルを作る

ffmpeg -loop 1 -i /home/bata64/test.jpg -i /home/bata64/test.mp3 -vcodec flv -acodec copy -r 1 -shortest /home/bata64/test.flv

動画ファイル作成をシェルスクリプト化する

上述の通りYoutubeへアップロードする機会が割とあり、その度にffmpegの実行オプションなんだっけなーと調べなおすのが無駄なのでコマンドをテキストファイルに控えておき都度コピペして実行をしていましたが、
更に省力化すべく以下のようなbashシェルスクリプト(create_flv.sh) を作りました。

#!/bin/bash

if [ $# -ne 3 ]; then
  echo "USAGE:`basename $0` [INPUT_IMAGE] [INPUT_AUDIO] [OUTPUT_FLV]" 1>&2
  exit 1
fi

ffmpeg -loop 1 -i $1 -i $2 -vcodec flv -acodec copy -r 1 -shortest $3

これを以下のように実行します。ファイル名を指定するだけで済みます。

./create_flv.sh ./test.jpg ./test.mp3 ./test.flv

ファイルが沢山ある場合は以下のような感じで並べて実行すればいいです。

./create_flv.sh ./test1.jpg ./test1.mp3 ./test1.flv
./create_flv.sh ./test2.jpg ./test2.mp3 ./test2.flv
./create_flv.sh ./test3.jpg ./test3.mp3 ./test3.flv
./create_flv.sh ./test4.jpg ./test4.mp3 ./test4.flv
~省略~

Youtubeにアップロード

2020年1月時点での公開の仕方は以下の通りです。

  1. Googleにログイン済みのGoogle Chromeを開き、ホーム画面から「Youtube」をクリック
  2. アップロードしたいアカウントである事を確認(複数のGoogleアカウントを使い分けている人は特に注意)し、「動画または投稿を作成」 -> 「動画をアップロード」をクリック
  3. 「動画をアップロード」の画面に作成したFLV形式ファイルをマウスでドラッグ

あとは所定の情報を記入します。注意点としては公開設定を、練習録音は「限定公開」(URL知っている人だけアクセス可能)にするところかと思います。

では。

*1: 著作権は考慮する必要あり。くわしくはこちら参照

タスクスケジューラをコマンドラインから管理する(schtasks)

Windowsには、指定の日時になったら指定の処理を自動で行うための仕組みとして「タスクスケジューラ」という機能があります。
これを使うと、例えば「毎日17:30になったらパソコンをシャットダウンする」みたいな事が出来ます。
「指定時間になったらシャットダウンする」というタスクを元に、この機能をコマンドラインから使うことについて説明します。

前提条件

OS: Windows10 バージョン 1903

がインストールされたパソコンで実行するものとします。

指定時間になったらシャットダウンするタスクを作成

まず、必要なタスクを作成します。これもコマンドラインからできるようですが最初だけなのでGUIから作ることにします。

  • Windowsキーを押下してスタートメニューを表示
  • キーボードから「タスクスケジューラ」と入力し、表示される「タスクスケジューラ」をクリックしてタスクスケジューラを起動

タスクスケジューラ
タスクスケジューラが起動した状態

  • 画面右ペインの「タスクの作成」をクリック、表示される「タスクの作成」画面にて以下の通り入力。
「全般」タブ
名前:シャットダウン
説明:指定された時間にOSシャットダウン実行
  • 「トリガー」タブ -> 「新規」ボタンを押下、「新しいトリガー」画面を開いて以下の通り入力。
開始:実行したい日付と時刻を入力
  • 「OK」を押下して「新しいトリガー」画面を閉じる。
  • 「操作」タブ -> 「新規」ボタンを押下、「新しい操作」画面を開いて以下の通り入力。
プログラム/スクリプト:C:\Windows\System32\shutdown.exe
引数の追加:-s -t 60 -f
  • 「OK」を押下して「新しい操作」画面を閉じる。
  • 「OK」を押下して「タスクの作成」画面を閉じる。
  • 「タスクスケジューラ」画面中央ペインに「シャットダウン」という名前のタスクが表示されている事を確認し、「タスクスケジューラ」画面を閉じる(右上×をクリック、またはファイル -> 終了)

これで、タスクを作ることができました。指定した時間まで待ち、PCがシャットダウンされる事を確認しましょう。

実行日時を変更する

基本的には「タスクスケジューラ」を起動して該当タスクをダブルクリック、「トリガー」を編集して時間を変更すればよいです。
また、定期的にするのでよければ「毎日〇〇時に実行」という指定もできます。

が、私の場合はやりたい日や時間がまちまちで、その都度「タスクスケジューラ」を起動して画面に従い入力、という手続きがだんだん面倒になってきたのでした。

という事で、コマンドから実行することにします。

schtasksコマンドから設定変更する

  • Windowsキーを押下してスタートメニューを表示
  • キーボードから「cmd」と入力し、表示される「コマンド プロンプト」を右クリックして「管理者として実行」をクリック
  • 現在の設定を確認
>schtasks /query /TN "シャットダウン" /v /FO list

以下のように表示される。

フォルダー\
ホスト名:                                       bata64PC
タスク名:                                       \シャットダウン
次回の実行時刻:                                 N/A
状態:                                           準備完了
ログオン モード:                                対話型のみ
前回の実行時刻:                                 1999/11/30 0:00:00
前回の結果:                                     267011
作成者:                                         bata64
実行するタスク:                                 C:\Windows\System32\shutdown.exe -s -t 60 -f
開始:                                           N/A
コメント:                                       N/A
スケジュールされたタスクの状態:                 有効
アイドル時間:                                   無効
電源管理:                                       バッテリ モードで停止, バッテリで開始しない
ユーザーとして実行:                             bata64
再度スケジュールされない場合はタスクを削除する: 無効
タスクを停止するまでの時間:                     72:00:00
スケジュール:                                   スケジュール データをこの形式で使用することはできません。
スケジュールの種類:                             一度だけ
開始時刻:                                       9:12:08
開始日:                                         2019/11/13
終了日:                                         N/A
日:                                             N/A
月:                                             N/A
繰り返し: 間隔:                                 無効
繰り返し: 終了時刻:                             無効
繰り返し: 期間:                                 無効
繰り返し: 実行中の場合は停止:                   無効

実行したい日時を指定する(例:2019年11月14日 11:50とする場合)

>schtasks /change /TN "シャットダウン" /SD "2019/11/14" /ST "11:50" /RU "bata64"

以下のようにユーザのパスワードを聞かれるので入力してEnterを押下。

bata64 の実行者パスワードを入力してください:

正しいパスワードを入力すると以下の通り表示される。

成功: スケジュール タスク "シャットダウン" のパラメーターは変更されました。

再度確認。「開始時刻」と「開始日」が変わっている事がわかる。

>schtasks /query /TN "シャットダウン" /v /FO list

フォルダー\
ホスト名:                                       bata64PC
タスク名:                                       \シャットダウン
次回の実行時刻:                                 N/A
状態:                                           準備完了
ログオン モード:                                対話型のみ
前回の実行時刻:                                 1999/11/30 0:00:00
前回の結果:                                     267011
作成者:                                         bata64
実行するタスク:                                 C:\Windows\System32\shutdown.exe -s -t 60 -f
開始:                                           N/A
コメント:                                       N/A
スケジュールされたタスクの状態:                 有効
アイドル時間:                                   無効
電源管理:                                       バッテリ モードで停止, バッテリで開始しない
ユーザーとして実行:                             bata64
再度スケジュールされない場合はタスクを削除する: 無効
タスクを停止するまでの時間:                     72:00:00
スケジュール:                                   スケジュール データをこの形式で使用することはできません。
スケジュールの種類:                             一度だけ
開始時刻:                                       11:50:00
開始日:                                         2019/11/14
終了日:                                         N/A
日:                                             N/A
月:                                             N/A
繰り返し: 間隔:                                 無効
繰り返し: 終了時刻:                             無効
繰り返し: 期間:                                 無効
繰り返し: 実行中の場合は停止:                   無効

バッチファイルにする

都度都度schtasks コマンドで指定されたオプションを書くのも面倒なので、更に簡略化します。

以下のような記載内容の「shutdown.bat」というファイル名のテキストファイルを作成、適当なフォルダに保存します。

schtasks /change /TN "シャットダウン" /SD %1 /ST %2 /RU "bata64"
schtasks /query /TN "シャットダウン" /v /FO list

コマンドプロンプトを管理者として実行、このバッチファイル格納ディレクトリに移動し、

>.\shutdown.bat 2019/11/15 11:10

と実行すれば実行日時を変更できます。変更したいときにはこれからはこのバッチを実行すれば良くなります。便利ですね。

では。

Windows10のロック画面の画像を保存する

Windows10でログインする前のロック画面では様々な画像が日替わりで表示されますが(Windows スポットライトという機能だそうです)、ある日表示されたハイイロアザラシの赤ちゃんの画像がかわいくて子供たちにも好評だったので、保存したくなったので方法を調べてみました。


windowsfaq.net


こちらに記載の通りなのですが、ちょっと自分なりにアレンジしてやってみました。
Windows Subsystem for Linux (WSL)がインストールされているWindows10パソコンである、という前提です。

%LOCALAPPDATA%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets

エクスプローラでアクセスし、格納されているファイルをとりあえず一時的な作業フォルダにコピー。

作業フォルダを開いたエクスプローラでShiftを押しながら右クリックして表示されるメニューから「Linuxシェルをここに開く」を選択。
WSLのウィンドウが表示されるので、

ls -1 | perl -lne 'printf "mv %s %s.jpg \n",$_,$_'

を実行。各ファイルの末尾に拡張子「.jpg」が付与されるようにリネームされるコマンドが表示されることを確認したら、実際に実行します。

ls -1 | perl -lne 'printf "mv %s %s.jpg \n",$_,$_' | bash -v

エクスプローラに戻り、探していた画像をプレビューから探して保存しておきます。

なお、複数のPCで試してみましたが、表示されているときにやらないとAssetsフォルダ以下には存在しないようです・・・。
画像検索で探した方が見つかるかも

では。

Ubuntu19.10へのアップグレード後にDBD::mysqlでエラー発生

マイUbuntuサーバをUbuntu 19.10にアップグレードした後、毎日の体重をグラフ化したりするのに使用しているHRForecastが正常動作しておらず、エラーを出している事に気づきました。

起きた事

hrforecast.pl --config ./config.pl

を実行しているコンソールに出力されていたのは以下のようなメッセージでした。

install_driver(mysql) failed: Can't load '/opt/hrforecast-income/extlib/lib/perl5/x86_64-linux/auto/DBD/mysql/mysql.so' for module DBD::mysql: libmysqlclient.so.20: 共有オブジェクトファイルを開けません: そのようなファイルやディレクトリはありません at /home/bata64/.plenv/versions/5.28.1/lib/perl5/5.28.1/x86_64-linux/DynaLoader.pm line 193.
 at (eval 158) line 3.
Compilation failed in require at (eval 158) line 3.
Perhaps a required shared library or dll isn't installed where expected
 at /opt/hrforecast-income/extlib/lib/perl5/Scope/Container/DBI.pm line 69.
 at /opt/hrforecast-income/lib/HRForecast/Data.pm line 33.
 at /home/bata64/.plenv/versions/5.28.1/lib/perl5/5.28.1/Carp.pm line 291
	Carp::croak('install_driver(mysql) failed: Can\'t load \'/opt/hrforecast-income/extlib/lib/perl5/x86_64-linux/auto/DBD/mysql/mysql.so\' for module DBD::mysql: libmysqlclient.so.20: M-eM-^EM-1M-fM-^\M-^IM-cM-^BM-*M-cM-^CM-^VM-cM-^BM-8M-cM-^BM-'M-cM-^BM-/M-cM-^CM-^HM-cM-^CM-^UM-cM-^BM-!M-cM-^BM-$M-cM-^CM-+M-cM-^BM-^RM-iM-^VM-^KM-cM-^AM-^QM-cM-^AM->M-cM-^AM-^[M-cM-^BM-^S: M-cM-^AM-^]M-cM-^AM-.M-cM-^BM-^HM-cM-^AM-^FM-cM-^AM-*M-cM-^CM-^UM-cM-^BM-!M-cM-^BM-$M-cM-^CM-+M-cM-^BM-^DM-cM-^CM-^GM-cM-^BM-#M-cM-^CM-,M-cM-^BM-/M-cM-^CM-^HM-cM-^CM-*M-cM-^AM-/M-cM-^AM-^BM-cM-^BM-^JM-cM-^AM->M-cM-^AM-^[M-cM-^BM-^S at /home/bata64/.plenv/versions/5.28.1/lib/perl5/5.28.1/x86_64-linux/DynaLoader.pm line 193.^J^@ at (eval 158) line 3.^JCompilation failed in require at (eval 158) line 3.^JPerhaps a required shared library or dll isn\'t installed where expected^J at /opt/hrforecast-income/extlib/lib/perl5/Scope/Container/DBI.pm line 69.^J') called at /opt/hrforecast-income/extlib/lib/perl5/Scope/Container/DBI.pm line 81
	Scope::Container::DBI::connect('Scope::Container::DBI', 'dbi:mysql:hrforecast;hostname=127.0.0.1', 'bata64', 'XXXX') called at /opt/hrforecast-income/lib/HRForecast/Data.pm line 33
	HRForecast::Data::dbh('HRForecast::Data=HASH(0x5589a8cd0748)') called at /opt/hrforecast-income/lib/HRForecast/Data.pm line 234
	HRForecast::Data::get_complex('HRForecast::Data=HASH(0x5589a8cd0748)', 'money', 'Complex', 'income') called at /opt/hrforecast-income/lib/HRForecast/Web.pm line 110
	HRForecast::Web::__ANON__('HRForecast::Web=HASH(0x5589a84b1848)', 'Kossy::Connection=HASH(0x5589a8aef700)') called at /opt/hrforecast-income/lib/HRForecast/Web.pm line 122
	HRForecast::Web::__ANON__('HRForecast::Web=HASH(0x5589a84b1848)', 'Kossy::Connection=HASH(0x5589a8aef700)') called at /opt/hrforecast-income/extlib/lib/perl5/Kossy.pm line 179
	Kossy::try {...}  at /home/bata64/.plenv/versions/5.28.1/lib/perl5/site_perl/5.28.1/Try/Tiny.pm line 100
	eval {...} at /home/bata64/.plenv/versions/5.28.1/lib/perl5/site_perl/5.28.1/Try/Tiny.pm line 93
	Try::Tiny::try('CODE(0x5589a8aeeff8)', 'Try::Tiny::Catch=REF(0x5589a8aeee60)') called at /opt/hrforecast-income/extlib/lib/perl5/Kossy.pm line 185
	Kossy::__ANON__('HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Middleware/Scope/Container.pm line 14
	Plack::Middleware::Scope::Container::call('Plack::Middleware::Scope::Container=HASH(0x5589a8ac9ef0)', 'HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Component.pm line 50
	Plack::Component::__ANON__('HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Middleware/Static.pm line 18
	Plack::Middleware::Static::call('Plack::Middleware::Static=HASH(0x5589a8aca0e8)', 'HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Component.pm line 50
	Plack::Middleware::StackTrace::try {...}  at /home/bata64/.plenv/versions/5.28.1/lib/perl5/site_perl/5.28.1/Try/Tiny.pm line 100
	eval {...} at /home/bata64/.plenv/versions/5.28.1/lib/perl5/site_perl/5.28.1/Try/Tiny.pm line 93
	Plack::Middleware::StackTrace::call('Plack::Middleware::StackTrace=HASH(0x5589a8aca178)', 'HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Component.pm line 50
	Plack::Component::__ANON__('HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Middleware/Lint.pm line 24
	Plack::Middleware::Lint::call('Plack::Middleware::Lint=HASH(0x5589a8aca2b0)', 'HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Component.pm line 50
	Plack::Component::__ANON__('HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Util.pm line 145
	eval {...} at /opt/hrforecast-income/extlib/lib/perl5/Plack/Util.pm line 145
	Plack::Util::run_app('CODE(0x5589a8aca268)', 'HASH(0x5589a8cc7388)') called at /opt/hrforecast-income/extlib/lib/perl5/Starlet/Server.pm line 366
	Starlet::Server::handle_connection('Plack::Handler::Starlet=HASH(0x5589a8a95c90)', 'HASH(0x5589a8cc7388)', 'IO::Socket::INET=GLOB(0x5589a8cc6ef0)', 'CODE(0x5589a8aca268)', '', '', '') called at /opt/hrforecast-income/extlib/lib/perl5/Starlet/Server.pm line 190
	Starlet::Server::accept_loop('Plack::Handler::Starlet=HASH(0x5589a8a95c90)', 'CODE(0x5589a8aca268)', 100) called at /opt/hrforecast-income/extlib/lib/perl5/Plack/Handler/Starlet.pm line 80
	Plack::Handler::Starlet::run('Plack::Handler::Starlet=HASH(0x5589a8a95c90)', 'CODE(0x5589a8aca268)') called at hrforecast.pl line 77
install_driver(mysql) failed: Can't load '/opt/hrforecast-income/extlib/lib/perl5/x86_64-linux/auto/DBD/mysql/mysql.so' for module DBD::mysql: libmysqlclient.so.20: 共有オブジェクトファイルを開けません: そのようなファイルやディレクトリはありません at /home/bata64/.plenv/versions/5.28.1/lib/perl5/5.28.1/x86_64-linux/DynaLoader.pm line 193.

メッセージを精査すると「DBD::mysqlがlibmysqlclient.so.20を見つけられていない」という事が原因だとわかるのですが、発見した当初はCPANモジュールを入れ直したりplenv経由でPerlバージョンを5.30.0にしてみたりと遠回りをしてしまいました(いずれも改善せず)。

で、「libmysqlclient.so.20」が無いらしいという事がわかりましたので確認したところ、

$ ldd /opt/hrforecast-income/extlib/lib/perl5/x86_64-linux/auto/DBD/mysql/mysql.so
	linux-vdso.so.1 (0x00007ffdbc7e0000)
	libmysqlclient.so.20 => not found
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f033e2c3000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f033e0d2000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f033e31d000)

という事で、確かにlibmysqlclient.so.20が見つからない模様。

更に

$ cd /usr/lib
$ ls -l ./x86_64-linux-gnu/libmysqlclient.so*
lrwxrwxrwx 1 root root      20 10月  9 22:03 ./x86_64-linux-gnu/libmysqlclient.so -> libmysqlclient.so.21
lrwxrwxrwx 1 root root      25 10月  9 22:03 ./x86_64-linux-gnu/libmysqlclient.so.21 -> libmysqlclient.so.21.1.17
-rw-r--r-- 1 root root 6776968 10月  9 22:03 ./x86_64-linux-gnu/libmysqlclient.so.21.1.17

という事で「libmysqlclient.so.21」はインストールされているが「libmysqlclient.so.20」は無いようです。

とりあえず「libmysqlclient.so.20」というシンボリックリンクを作ればいいかと思い

$ sudo ln -s libmysqlclient.so.21.1.17 libmysqlclient.so.20

してからhrforecastを起動してみましたが事象は改善せず。

ここで同様事象が報告されていないかGoogle検索してみたところ、

qiita.com

を発見。やっぱりまずはシンボリックリンクを作ってみるよね、と思いつつ、

launchpad.net

というようにlibmysqlclient20のパッケージファイル(.deb)が公開されていたのでこれをダウンロードして

$ sudo dpkg -i ./libmysqlclient20_5.7.26-1_amd64.deb

でインストールしてからhrforecastを再度起動したところ正常に起動しました。

なお、参照した記事ではやっているapt installはいらんのでは(libmysqlclient20というlibmysqlclient21とは別のパッケージを入れたので)と思うので、とりあえずこのまま運用してみます。

考察

  • アプリケーション稼働サーバでむやみにOSアップグレードするのは軽率(ただし反省はしていない)
  • DBD::mysqlが将来アップデートされたら「libmysqlclient.so.21」を使うようになる?そうなったらこの事象は起きなくなるのではないか


では。

LinuxでSCSIデバイス(sdデバイスとsgデバイス)の対応を調べる

LinuxSCSIバイスを示す、sdデバイス(/dev/sdX)とsgデバイス(/dev/sgX)の対応を調べる必要があったので手順をメモします。

お前は何を言っているんだ

www.ibm.com

に、

Linux での SCSIバイスには多くの場合、ユーザーがそのデバイスを認識しやすいような名前が付けられます。例えば最初の SCSI CD-ROMは /dev/scd0 です。SCSI ディスクには /dev/sda、/dev/sdb、/dev/sdc のようなラベルが付けられます。デバイスの初期化が終わると、LinuxSCSI ディスク・ドライバー・インターフェース (sd) が SCSI READ と SCSI WRITE コマンドのみを送信します。

これらの SCSIバイスは汎用的な名前すなわちインターフェース (/dev/sg0、/dev/sg1、/dev/sga、/dev/sgb など) を持っている場合があります。これらの汎用ドライバー・インターフェースを利用すると、SCSIバイスに直接 SCSI コマンドを送信することができ、通常 SCSI ディスク上に作成されてディレクトリー配下にマウントされるファイルシステムを使う必要がなくなります。

と記されている、

  • 「/dev/sda、/dev/sdb、/dev/sdc」のような表記のものをsdデバイス
  • 「/dev/sg0、/dev/sg1」のような表記のものをsgデバイス

と仮に呼ぶ事にします。

で、以下のような事情があります。

  • sdデバイスは一対一でsgデバイスに対応している。(例:/dev/sdbは/dev/sg3と対応)
  • SCSIリザーブなどのオペレーションをする時にはsgデバイスを指定する必要がある
  • sdデバイスの一覧と、sgデバイスの一覧は探しやすい(/dev以下をlsとかすればOK)
  • しかしどのsdデバイスがsgデバイスと対応しているかはわかりにくい

という事で、sdデバイスとsgデバイスの対応を調べる必要がありました。

前提条件

Ubuntu 19.10
CentOS 6

で確認しました。

方法1.sg_mapコマンドで確認する

$ sudo sg_map -i

以下のように表示される。

/dev/sg0  /dev/sda  ATA       TOSHIBA DT01ACA3  ABB0
/dev/sg1  /dev/sdb  ATA       TOSHIBA DT01ACA3  ABB0
/dev/sg2  /dev/sdc  ATA       WDC WD15EARS-00Z  0A80
/dev/sg3  /dev/sdd  ATA       WDC WD15EARS-00Z  0A80

これによって

sg0がsda
sg1がsdb
:

と対応していることがわかります。

この手順を使う条件

sg3-utilsがインストールされている事と、スーパユーザ権限で実行する必要あります。

Ubuntuの場合は

sudo apt install sg3-utils

CentOSの場合は

sudo yum install sg3_utils.x86_64

でインストールできます。

方法2./proc以下のファイルで確認

$ cat /proc/scsi/sg/device_strs

以下のように表示されます。

ATA             TOSHIBA DT01ACA3        ABB0
ATA             TOSHIBA DT01ACA3        ABB0
ATA             WDC WD15EARS-00Z        0A80
ATA             WDC WD15EARS-00Z        0A80

上から順に、1行目がsg0、2行目がsg1・・・となる。
あとはデバイス情報から何となくsdデバイスが何かを推測する。

この手順を使う条件

/proc以下のファイルの読み取り権限のあるユーザで実行する必要があります。

何らかの都合(お客様システムのサーバである、など)でsg3-utilsがインストールできない場合はこちらの方法でがんばって調べましょう。


では。