CMDとBashと変数展開と・・・

備忘録。

CUIではほとんど WSL1/ubuntu を使っています。WSL2の方が実行パフォーマンスはいいんでしょうけど・・・。

WSL1メインとはいいつつ、コマンドプロンプト(cmd.exe)を全く使わない・・・ということはない。たとえばタスクスケジューラに仕事をしてもらいたい処理は バッチファイル(CMDファイル)に書いて渡す方が何かとトラブルは少なくなりますし。
bitlockerで暗号化しているVHDファイルのマウント処理とかをWindowsのスタートアップスクリプトに登録したりとか、ログオン/ログアウトスクリプトに、後始末するスクリプトとか・・・Windowsのサービスを制御したりとか、コンピュータ名とIPアドレスの対応を調べたりとか、IPのルーティングを変えたりとか、やっぱりcmdファイル(バッチファイル)じゃないと不便なこともあります。

Powershellもありますが・・・ps1ファイルの実行がデフォルトでブロックされているので他所のPCで手軽に動かせない・・・とか、なんかイマイチです。

bashでのシェルスクリプトもウェブ開発では必須なので、いろんな処理の自動化スクリプトをちょくちょく書きます。
・・・で、bashとcmdのスクリプトをいったりきたり、色々書く時いつも躓くのは、変数展開の文法・・・要は書き方をよく忘れてしますこと。頭は悪い上、加齢でどんどん記憶力が落ちていく・・・。
あれ、bashのシェルスクリプトのこういう書き方って、バッチファイルではどうやるんだっけ???ということが度々あるので、カンタンな対応・比較表があれば便利だなと思い、メモついでに書いておく。

最低限こんだけ覚えてればなんとかなる・・・かもしれない。

  bash cmd
1行目 #!/bin/bash @echo off
変数代入 hoge=”This is a sample” SET hoge=This is a sample
変数参照 echo $hoge or echo ${hoge} echo %hoge%
入力 echo -n “please input: ”
read hoge
echo $hoge
SET /P hoge=please input:
echo %hoge%
文字列置換 hoge=”this is my appple pen”
echo ${hoge//this/that}
SET hoge=this is my appple pen
echo %hoge:this=that%
部分文字列 hoge=”this is my appple pen”
echo ${hoge:8:2}
echo ${hoge:8}
SET hoge=this is my appple pen
echo %hoge:~8,2%
echo %hoge:~8%
パス分解
パス
ベース名
拡張子
ファイル名
echo $0
echo ${0%/*}
filename=${0##*/}; echo ${filename%.*}
echo ${0##*.}
echo ${0##*/}
echo %0
echo %~dp0
echo %~n0
echo %~x0
echo %~nx0
日付時刻
乱数(簡易)
echo $(date)
echo $RANDOM
echo %DATE% %TIME%
echo %RANDOM%
IF-ELSE文 if [ expression ] ; then
 …
else
 …
fi
if expression (
 command1
 command2
  ….
) else (
 command3
 command4
 …
)
ループ(while) while [ expression ]
do
 …
done
loop:

if expresssion goto loop
※gotoで代替
実行結果の変数への格納 VAR=$(command arg1 arg2)
VAR=`command arg1 arg2`
FOR /F “usebackq delims=” %%A IN (`command arg1 arg2`) DO (
SET VAR=%%A
)
CSVでのX列目取得 COLX=`echo $str | cut -d, -f X -s` FOR /F ” delims=, tokens=X” %%A IN (“%STR%”) DO SET COLX=%%A

間違いは随時修正中。思いついたら随時追加中。

ロック画面のスポットライト画像をコピーしてデスクトップにも流用させたい

ロック画面は何にしてますか?僕は設定でWindowsスポットライトにしてます。
これ、たぶんBingから定期的にダウンロードしてロック画面にスライドショー的に表示していると思うんだけど、なぜかログイン後のデスクトップの背景の設定でWindowsスポットライトの選択肢がないんですよねぇ。

Microsoft Storeで壁紙系のアプリを探せばたぶんあると思いますが、たぶんに要らん機能がくっついてきたり個人情報が抜かれたりと面倒なのでやっぱり自分でなんとかするのが基本です。そうです、信じられるのは唯一自分なのです(笑)

Windowsスポットライトで使用される画像ファイルは、
%LOCALAPPDATA%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets
に保存されているようです。拡張子がないので判別はできませんが、概ね400KB以上のファイルサイズのファイルが該当する画像で、任意のフォルダにコピーして拡張子.JPGでリネームすれば使いまわせます。ライセンスとか分からんが、どこぞに配布するわけではないので大丈夫でしょう。

定期的にピクチャフォルダにコピーすればいいんですが・・・いちいちフォルダ開いてファイルを選別してコピーしてリネームして・・・うわぁぁぁあーーーーーーん、超絶メンドクセーーーーーーー。
さらに、バックグラウンドでダウンロードされる画像ファイルには縦長と横長がごちゃまぜになっている。縦長の画像は不要なので、これも選別項目です。さらにメンドクサイ。

UNIX系のOSなら find コマンドやらimagemagickを駆使してシェルスクリプト化すればいいんですが・・・。
PowerShellを使えばできそうですけど、んーーーー、C#で組んだ方が早ぇよ。。。ということで、組んでみる。

ファイルサイズを判別するのは簡単で、JPEGファイルの高さと幅を取得するのがメンドクサそうですが・・・単純にSOFマーカーから取得することで手抜きします。完璧にしようとすると僕の知識では無理です。

github

JavaScript Map オレオレ拡張

今、かつてないほどJavaScriptと関わっている。
※JavaScriptという名称は正式にはOracleの登録商標で一般的には ECMA Script って事になるんだろうけど、めんどくさいので JavaScript と明記する。あらかじめご了承のほど。

僕のJavaScriptの文法、その他の知識は正直2010年(ES5)ぐらいで止まってる。というのも今持っている知識だけで要求されるほとんどのケースが実現可能だからだ。
もちろんパフォーマンス的な、効率的な・・・というのは棚に上げまくっているんだけど。その当時も ES6 も意識していましたが、まだブラウザでの実装状況が混沌としててInternetExplorerがまだまだ全盛時代だったので、どうしてもすべてのケースで動くように ES5 を強制していた。

今の JavaScript って5~6年前によく見たコーディングスタイルとは全然違いますよね。
ブラウザでホスティングされているJavaScriptで普通にラムダ式が使えるし、変数宣言で var ではなく let を使うことでブロックスコープを意識したコーディングができる。
まぁ、今頃こんなこと書いても、今更感が半端ないんですけどね。

去年から現在進行形で携わっている仕事では、徐々に(少しずつ) ES6 を意識したコーディングを心がけるようになってきました。ほんとに少しずつですけどね💦

余談:(余談ばっかりだが・・・)——————
理由はマイクロソフトが正式に Internet Explorer 及び edge-HTMLをベースにしたEdgeブラウザのサポートを止める、もしくは新規開発しない、事を宣言したから。
残念なことだけど、ITリテラシーの低い人たちからすれば、未だにInternet Explorer がインターネット業界(笑)の標準だと思っている人が結構多い。
マイクロソフトがレガシーブラウザと決別宣言してくれたおかげで、ChromeやFireFox,Chromium版Edgeを強制することが可能になったことが非常に大きい。
———————-

一番意識しているのは(オブジェクト初期化子{}でインスタンスを作る)オブジェクトを連想配列的に使用するのを止めてMap や Set を使うことにしたこと。
MapやSetもかなり以前から実装されているけど、やっぱり InternetExplorer11 では限定的な実装なので使うのも躊躇してた。

Map,Set は非常に便利ですよね。オブジェクトを使用した連想配列では、キー(プロパティー)に文字列ぐらいしか使えない。Mapだとキーに何でも入る。
MDNでの説明だと、頻繁に削除・挿入を繰り返すケースでパフォーマンスが上がるんだそうで。

たいがいの場合、キーには文字列を使うのが大半の用途だと思います。DOM要素やオブジェクトもキーにできると思うけど、正直僕には使いどころが分からない。まぁ、思いつくのは コールバック(関数)なんかをためておく用途にするぐらい。それでも、そういう時は Map よりSetを使うし。。。

でも特にブラウザ上でDOMとか扱っていると、やっぱりプレーンなオブジェクトの方がコーディングが楽な時もある。ので、Map.prototype空間にオレオレMap拡張メソッドを追加して使うことになってくる。

※2020/04/02 ちょっと追記した。

Map.prototype.toPlainObject

たぶん誰もが書いてる・・・と思う、文字通り、文字列をキーにしたMapをオブジェクトに変換する。
最新のブラウザとかだと、Object.fromEntries とかいう Object.entriesの逆動作を行う関数が実装されているので↓は不要なのかも。

Map.prototype.toPlainObject = function()
{
  var rv = {};
  this.forEach(function(v,k) { 
    if(typeof k === 'string')
      rv[k] = v;
  });
  return rv;
};
// もしくはもっと簡単に・・・
Map.prototype.toPlainObject = function()
{
  return Object.fromEntries(this);
};

Map.from

オブジェクトからMapへの変換は・・・↓でいいのかな? Object.entries は比較的新しめのブラウザしか対応してなくない?

Map.from = function(o)
{
  return new Map(Object.entries(o));
};

Map.prototype.stringify

JSON.stringify のもろパクリですね。toString をオーバーライドすればいいんでしょうけど、そこまでするのは止めましょう。
主に localStorage/SessionStorage へ格納するときに使う。

Map.prototype.stringify = function()
{
  return JSON.stringify(this.toPlainObject());
};

Map.parse

これも JSON.parse のパクリ。JSONのパクリというか、文字列を直接parseするのではなく、一旦オブジェクトにJSON.parseで変換して Map に変換します💦

Map.parse = function(str)
{
  return new Map(Object.entries(JSON.parse(str)));
};

Map.prototype.numIncr/Map.prototype.numDecr

オブジェクトだと、普通にobj.counter++ とか、obj.counter += 10 とかできますよね。。。。
Mapだと一旦 getで取得してsetで更新しないといけません。JavaScriptは演算子を定義もしくはオーバーロードできませんよねぇ。。。。
演算子のオーバーロードは混乱の元になるので極力やっちゃいけない、ってC++の時言われてたな・・・。今もそうなのかなぁ。。。

Map.prototype.numIncr = function(key,delta)
{
  if(typeof delta !== 'number' || delta == 0)
    delta = 1;

  var value = this.get(key);
  if(typeof value === 'number')
  {
    value += delta;
    this.set(key,value);
  } 
  return this;
};
Map.prototype.numDecr = function(key,delta)
{
  if(typeof delta !== 'number' || delta == 0)
    delta = 1;

  return this.numIncr(key,-1 * delta);
};

なんか、Mapの良さ(キーはなんでもOK)を殺してしまうようなものばかりだな。。。キーをstringに限定するようなものあればな~・・・とは思いますが、型が強制されない言語だと難しい。

ざっとよく使うものを列挙しました。自分がコピペできるように💦
ここまで書いてて、気づいたのは・・・var使いすぎ問題! ついつい癖で var って書いてしまうんす。
ラムダ式もイマイチ好きになれない記法だったりします。 C#だと当たり前のよう書くんですけどね。。。好き嫌いというより単に癖なのかも。

CentOS 8(7以降)のネットワーク周りの設定について

■2020年10月15日 リンク切れ修正およびリンク追加


CenOS6でネットワーク周りの設定を行うには、当然のように /etc/sysconfig/network-scripts/* をいじっていましたが、CentOS7以降はNetwork Managerのコマンドを利用することが推奨されているようです。
いろいろググったり、Redhatのサイトで公開されているドキュメントを読み、なんとなくでコマンドを操作をしていたものをある程度理解できるようになってきました。

その備忘録として未来の自分のために残しておくエントリです。

ネットワーク周りの設定は究極的にいえば、素人的な使い方をしている限り、nmcli コマンドだけで十分かな、と思います。ホスト名もIPアドレスの設定も全部nmcliコマンドを叩けば殆どのケースでOKです。
あ、firewall関連は別ですけどね。firewall関連も firewall-cmd に集約され、バックエンドがiptablesであろうがnftablesだろうが設定方法自体(インターフェイス)は統一されている。
今後こういうフロントエンドとバックエンドを別々にしてフロントエンドは固定し、バックエンドをどんどん改良していく、というのが今風なんだろうな、と思います。

それはさておき。

大前提として、NetworkManagerをデフォルトで有効になっている環境です。そもそもNetworkManagerをsystemctlでenableにしていない環境は想定していません。
あしからず。ご了承のほど。

NetworkManagerの説明はRedHatのドキュメントを読め進めれば他にググって怪しげな情報に振り回される必要はありません。現に僕は思いっきり振り回された側の人間です💦

CentOSは RedHat Enterprise Linux のクローンなので大部分のドキュメントはCentOSにも当てはまるはずです。

第4章 NETWORKMANAGER によるネットワーク管理の開始

第8章 NMCLI の使用

最初ネットで情報を探していろいろ試してみましたが、書いていることがバラバラで全く要領がつかめず、頭の中が???状態でコマンドだけコピペしてきました。
その中で一番わからなくなったのは、nmcli コマンドでよく出てくる、インターフェイス名、デバイス名と接続名の関係が一体どいうことなのか全く理解できていませんでした。
ほとんどの記事でインターフェイス名と接続名を一緒にしているので全く???だったんです。

が、上記RedHatサイトの説明を読むと明快に説明されておりました。やっぱり公式のドキュメントをまず読むべきでした。

そして、nmcliコマンドで指定する device と connection の関係をまず理解することがはじめの第一歩です。
deviceは文字通りデバイス、ネットワークインターフェイスを指す。そしてconnectionは、このdeviceに適用するプロファイルのようなもの、と僕は理解した。
そして特定のdeviceに適切に設定されたプロファイルを適用することで通信ができるようになります。

基本的には一つのネットワークインターフェイス(通常はNICとかWiFiとか)に対して、複数のプロファイルを作り、その時々の環境によってプロファイルを切り替えてネットワークインターフェイスに適用する、という形になっているようです。

このプロファイルの名前こそ、接続名(connection.id)であり、con-nameというのは connection.id の別名であり、正式には connection.idというプロパティ、そしてconnection.idというプロパティ名を使うのが推奨されている、らしいです。

(追記:接続名とかいうとワケワカンネーし僕は混乱するので、接続名(con-name/connection.id)のことをプロファイル名と呼ぶようにしています。このエントリでも接続名の事をプロファイル名と書くようにしました。あしからず。)

ググると、LAN I/Fの設定のコマンドは以下のようになっていることが多いです。

# nmcli c add type ethernet con-name eth0 ifname eth0 

これは非常に分かりにくい、と思います。まず eth0 が2回も出てきてますが、この二つは全く別物です。分かっている人は分かるけど、NetworkManager自体よく知らない人が見てもこの2つのeth0が別物である、なんて知りようがありませんし、大抵の記事ではこの辺の説明がサラッと流されていることが多いです。(eth0のところはens01とかNICの命名規則によります)

あえてこれを正しく書き直すと、

# nmcli connection add connection.type ethernet connection.id eth0 connection.interface-name eth0

ここでこの二つのeth0が何を示すのか? を説明されている記事がほとんどなく、ググって検索下位の方に出てくる記事を辿るとやっと出てくる程度です。こんなの分かってて当然なんでしょうね・・・。
connection.type(別名 type)は、ethernetとかwifiとかvpnとかadslとかの文字通りの意味。ethernetやwifiもまた別名なんですけど、これは上記ドキュメントを参照。
connection.interface-name(別名 ifname)は、下記コマンドで確認できるDEVICE名のこと。

# nmcli device
DEVICE  TYPE      STATE     CONNECTION
eth0    ethernet  接続済み  xxxxxxx
lo      loopback  管理無し  --

ここで CONNECTIONを xxxxxx で伏字にしたのはワケがあります。大抵の記事ではこのCONNECTION名を DEVICE名と同じものにしていることが多いです。
コレ、たぶんOSインストール時に自動設定された情報なんでしょうね。おそらく。だけど、これが結局僕が理解する上で邪魔だった。なまじDEVICE名とCONNECTION名が同じになっているのでワケワカンネー状態に陥った原因だと思う。xxxxxxxは自分で任意の名前をつけるべきところです。
CONNECTION名は、上で何度も言ってきた プロファイル名と同じです。NetworkMangerではプロファイルを複数用意できる。
理由は、環境が変わる毎に設定ファイルをいじるのではなく、環境ごとに複数のプロファイルを用意しておき、その都度DEVICE(インターフェイス)に適切なプロファイルを適用することでラクしよう、ということだと思います。バックアップファイルをパカパカ作るより効率的ですし。
(まぁ、最終的には /etc/sysconfig/network-scripts/ 以下のファイル、その他が書き換わるんですけどね。。。精通しているパワーユーザーにとっては昔ながらのファイル修正とかの方がいいのかも?)

これが一番効率的だなと思うのは、無線LAN(wifi)を利用するときだと思います。無線接続時、接続ポイントによってSSIDとかパスワードとか暗号化方法とか、そういった無線LANの接続ポイント毎にプロファイルを用意しておくと、あとは nmcli コマンドで切り替えるだけで済むようになりますよねぇ。

そしてそのプロファイルは、connection.id(別名con-name)で指定するプロパティ、これは自分で勝手に名前をつけるべきもの。デフォルトでDEVICE名が使われています。分かっている人は別にいいんですけどね。。。僕は分かんなかったです。。。。

僕は HyperVでCentOS8を稼働していますが、この接続(プロファイル)の一覧を表示すると・・・

# nmcli connection
NAME      UUID                                  TYPE      DEVICE
external  fdfa3402-1a88-4e49-8877-ee44480fd405  ethernet  eth0
internal  c541d202-e756-4680-af82-fe9343de6b8c  ethernet  --

と、二つのプロファイルを用意しておいて、ケースに応じて切り替えて使ってます。
上記では externalが eth0 に適用されています。

internalプロファイルをeth0に適用するには、

# nmcli connection up internal connection.interface-name eth0
もしくは省略形を使うと・・・
# nmcli c up internal ifname eth0

と、するだけで再起動してもinternalが適用されます。いちいち down する必要もない。ただし、一つのデバイスに適用できるのは当然だけど一つのプロファイルだけ。
複数のデバイスがある場合で、プロファイルをスワップしたいときも nmcli connection up コマンドを二つ叩けば済みますしね。

nmcliで殆どのネットワーク設定が可能なので、複数のプロファイルを作ってDEVICEに適用する、というのは、以前のような/etc/sysconfig/network-scripts/*の各ファイルをいちいち編集するより非常に効率的な方法だと思います。

たとえば、違うdnsを切り替えたいとき、dnsの設定だけ違う別プロファイルを作っておき、nmcli connection up コマンドで切り替える。
たとえば、インターフェイスのゾーンを切り替えたいときもfirewall-cmdでもいいですけど、nmcliで切り替えたほうがカンタンなような気もします。

まぁ、素人考えですけどね。

プロファイルの各プロパティの値を変更するには・・・(めんどくさいので省略表記を利用します connection ⇒ c , modify ⇒ mod 等)

# nmcli c mod プロファイル名 プロパティー名 値

というように修正していく。具体的なプロパティー名は、show サブコマンドで確認できる。たとえば僕のテスト環境だと・・・

# nmcli c show external
connection.id:                          external
connection.uuid:                        fdfa3402-1a88-4e49-8877-ee44480fd405
connection.stable-id:                   --
connection.type:                        802-3-ethernet
connection.interface-name:              eth0
connection.autoconnect:                 はい
connection.autoconnect-priority:        0
connection.autoconnect-retries:         -1 (default)
connection.multi-connect:               0 (default)
connection.auth-retries:                -1
connection.timestamp:                   1584863103
connection.read-only:                   いいえ
connection.permissions:                 --
connection.zone:                        trusted
connection.master:                      --
connection.slave-type:                  --
connection.autoconnect-slaves:          -1 (default)
connection.secondaries:                 --
connection.gateway-ping-timeout:        0
connection.metered:                     不明
connection.lldp:                        default
connection.mdns:                        -1 (default)
connection.llmnr:                       -1 (default)
connection.wait-device-timeout:         -1
802-3-ethernet.port:                    --
802-3-ethernet.speed:                   0
802-3-ethernet.duplex:                  --
802-3-ethernet.auto-negotiate:          いいえ
802-3-ethernet.mac-address:             --
802-3-ethernet.cloned-mac-address:      --
802-3-ethernet.generate-mac-address-mask:--
802-3-ethernet.mac-address-blacklist:   --
802-3-ethernet.mtu:                     自動
802-3-ethernet.s390-subchannels:        --
802-3-ethernet.s390-nettype:            --
802-3-ethernet.s390-options:            --
802-3-ethernet.wake-on-lan:             default
802-3-ethernet.wake-on-lan-password:    --
ipv4.method:                            manual
ipv4.dns:                               8.8.8.8,8.8.4.4
ipv4.dns-search:                        --
ipv4.dns-options:                       --
ipv4.dns-priority:                      0
ipv4.addresses:                         10.0.0.153/24
ipv4.gateway:                           10.0.0.1
ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)
ipv4.routing-rules:                     --
ipv4.ignore-auto-routes:                いいえ
ipv4.ignore-auto-dns:                   いいえ
ipv4.dhcp-client-id:                    --
ipv4.dhcp-timeout:                      0 (default)
ipv4.dhcp-send-hostname:                はい
ipv4.dhcp-hostname:                     --
ipv4.dhcp-fqdn:                         --
ipv4.never-default:                     いいえ
ipv4.may-fail:                          いいえ
ipv4.dad-timeout:                       -1 (default)
ipv6.method:                            ignore
ipv6.dns:                               --
ipv6.dns-search:                        --
ipv6.dns-options:                       --
ipv6.dns-priority:                      0
ipv6.addresses:                         --
ipv6.gateway:                           --
ipv6.routes:                            --
ipv6.route-metric:                      -1
ipv6.route-table:                       0 (unspec)
ipv6.routing-rules:                     --
ipv6.ignore-auto-routes:                いいえ
ipv6.ignore-auto-dns:                   いいえ
ipv6.never-default:                     いいえ
ipv6.may-fail:                          はい
ipv6.ip6-privacy:                       -1 (unknown)
ipv6.addr-gen-mode:                     stable-privacy
ipv6.dhcp-duid:                         --
ipv6.dhcp-send-hostname:                はい
ipv6.dhcp-hostname:                     --
ipv6.token:                             --
proxy.method:                           none
proxy.browser-only:                     いいえ
proxy.pac-url:                          --
proxy.pac-script:                       --
GENERAL.NAME:                           external
GENERAL.UUID:                           fdfa3402-1a88-4e49-8877-ee44480fd405
GENERAL.DEVICES:                        eth0
GENERAL.STATE:                          アクティベート済み
GENERAL.DEFAULT:                        はい
GENERAL.DEFAULT6:                       いいえ
GENERAL.SPEC-OBJECT:                    --
GENERAL.VPN:                            いいえ
GENERAL.DBUS-PATH:                      /org/freedesktop/NetworkManager/ActiveConnection/2
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/Settings/1
GENERAL.ZONE:                           trusted
GENERAL.MASTER-PATH:                    --
IP4.ADDRESS[1]:                         10.0.0.153/24
IP4.GATEWAY:                            10.0.0.1
IP4.ROUTE[1]:                           dst = 10.0.0.0/24, nh = 0.0.0.0, mt = 100
IP4.ROUTE[2]:                           dst = 0.0.0.0/0, nh = 10.0.0.1, mt = 100
IP4.DNS[1]:                             8.8.8.8
IP4.DNS[2]:                             8.8.4.4

ってな感じになります。

externalプロファイルのdnsを変更するには・・・

# nmcli c mod external ipv4.dns 1.1.1.1
# nmcli c up external
接続が正常にアクティベートされました (D-Bus アクティブパス: /org/freedesktop/NetworkManager/ActiveConnection/3)
# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 1.1.1.1

upサブコマンドでexternal を up すると、即座に/etc/resolveが更新されていることが確認できます。
ほとんどの場合 プロパティーを変更してup すると更新されるみたい。

dnsを一時的に変更したい、というようなときは上記のようにプロファイルを修正すればいいけど、それにともなって gatewayも変更したい、ゾーンも変更したい・・・とかいう場合はプロファイルを修正するのではなく、新しいプロファイルを別につくってそれをdeviceに適用した方が断然効率的です。
その場合、既存のプロファイルをコピーするとラクかな、と思います。上記の例でexternal をコピーしてexternal2 としてそのプロファイルを修正していきます。

# nmcli c clone external external2
# nmcli c mod external2 connection.zone public ipv4.dns "8.8.8.8 8.8.4.4"
# nmcli c up external2
# nmcli d
DEVICE  TYPE      STATE     CONNECTION
eth0    ethernet  接続済み  external2
lo      loopback  管理無し  --

仮想マシンを違うPCから持ってきてインポートしたとき、大概ネットワーク関連を再設定するはめになるんですが、その都度ネットで調べて・・・という風にしてたんですが、NetworkManagerの仕組みをおおよそ理解することで、仮想マシンの引っ越しが躓かずにできるようになった。

編集可能な簡易グリッドビュー画面をイチから作ってみる

直接関係はありませんが、これ の続きです。

ここにきて、カンタンなグリッドを表示する仕事があって、それならセルの内容を書き換えるだけの簡易的なセル編集機能もあればなぁ、と家に帰ってから唐突に思いつき、テレビを見ながら数時間ぐらいで突貫工事で作った。

仕様的には・・・
(1)Excelファイル(もしくはUTF-8なCSVファイル)をブラウザに読込み、
(2)簡単なグリッドビュー表示を行い、
(3)セルをクリックしたら編集し、
(4)リターンキー押下で編集確定、
(5)エスケープキーで編集取消
という感じ。

これならHTMLとCSS、JavaScriptで百数十行ぐらいでいけそうです。
実際業務で使うには、変更内容を追跡し、サーバーへ送信してデータベースに反映して・・・というような事が考えられますが・・・そこまでやっとれん!w

まずは、見てくれ、画面デザインですが、あくまでサンプル的なものなので、ほとんど素のHTMLです。
CSSのGridなんて初めて書いてみました。普段はBootstrapのレイアウトグリッドを利用して画面を作っているんですが・・・CSSのグリッドって、なんか難解ですね・・・ドキュメントを一回読んだだけではオッサンには理解できません(笑) エクセルの読込には、みんな大好き SheetJS ライブラリを利用しました。

ただ、セルを結合しまくったりしてるような複雑なエクセルファイルとか読み込むとたぶん滅茶苦茶になります。そういう複雑なエクセルファイルを持っていないので試していないんだけど・・・

デモ画面はこちら

CSS自体はそんなに凝ったことしてないので、読めば大体やろうとしていることが分かるでしょう。

続いて、肝心のJavaScript ですが、ほとんど各種イベントの処理ぐらいで、これといって特筆するべき事はありません。
ところどころ jQuery 使って見苦しいのですが・・・jQueryだと明らかにタイプ量が減るので僕は多用しています。世間の流れとしてはjQueryは排除されつつあるのですが・・・やっぱりjQueryに慣れきってしまうと document.querySelector なんてタイプするのがすんげぇ面倒。jQueryだと $ の一文字ですよ?イベント登録も on の二文字ですよ? addEventListener とかタイプするのがアホらしくなりますけどね、オッサンは。

いつも利用させてもらってる疑似テストデータを利用してエクセルデータを流し込んでやると、こんな感じです。「こんな感じです」って言われてもね。。。

いちおう、エクセルファイルから簡易グリッドビューにデータを流し込み最低限の編集はできるようになりました。
・・・が、グリッドのHTML的な構造は見直す必要がありますよねぇ。

<div class="row body">
  <span class="cell"></span> ....延々とセルが続く
</div>

一つの要素(div.row.body)にすべてのセル(span.cell)を全部詰め込んで無理くりCSSでグリッド表示させてるだけなんで、<table>要素みたいに、行で分かれていないので、任意の行を選択したいときは、いちいち計算しないといけません。

ま、ちょっとだけグリッド表示したい、って時に使う用、と割り切る。