Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all 2885 articles
Browse latest View live

curlで取得したウェブのソースコードに色を付けたい

$
0
0

iPadでコーディングをしているとブラウザからソースを確認できないため不便です。
ソースを見る専用のアプリもあるようだけど、アプリを入れずにどうにかならないかと模索しました。

対象はiPadからリモートでLinuxに入って作業するような人です。
※iPadのプログラミング環境構築の話はこちら
https://qiita.com/shuuuta/items/7a3f3801e0209f9ad5ac

コマンドの流れ

curlで取得してsource-highlightで色つけてlessで見ます。

source-highlightをインストール

source-highlightが必要なのでまずはインストール。aptなら以下で。

$ sudo apt install source-highlight

使ってみる

コマンドはこんな感じ。

curl -s https://qiita.com|source-highlight --failsafe-f esc --line-number-s html|less -R

オプションはこんな感じになっています。

  • source-highlight
    • -f esc: エスケープシーケンスを使って色を表現。
    • -s html: ハイライトの種類をhtmlに指定。
  • less -R: カラーエスケープシーケンスを読み込む。

関数にしておくときっと便利

~/.bashrcにこんな感じで書いておくと都度長々書かなくてすみますね。

.bashrc
function websource {
  curl -s$1|source-highlight --failsafe-f esc --line-number-s html|less -R}
websource https://www.yahoo.co.jp

D2D41134-A51C-4731-9E31-CB078E14D286.jpeg

ちゃんと色分けされて取得できました。

ソース表示と開発者ツールをお願いします

ブラウザのソース表示とウェブインスペクタがiPad OSで実装されたらいいなぁと思っていましたが、残念ながらきませんでしたね。
実装される気配もないですが、この2つさえ使えればあとはフロントエンド制作もなんとかなりそうなんだけどなぁ。
appleさんよろしくお願いします。


RHEL5,6,7,8で変更された主要まとめ

$
0
0

RHEL5,6,7,8で変更された主要まとめ

RHELのバージョン混在環境で、困惑するのでまとめてみました。
まあ、今更RHEL5はないでしょうが一応、記載しておきます。
また、調査しても分からない箇所もありましたので、追々追加していきます。

パッケージRHEL 5RHEL 6RHEL 7RHEL 8
カーネル2.6.182.6.323.10.04.18
GrubGrub 0.97Grub 0.97Grub2 v2.00
GUIGnome 2.16Gnome 2.28Gnome 3.8Gnome 3.28
起動シーケンスSVR4 initUpstartsystemdsystemd
LogingSyslogRsyslogjournalctl plus Rsyslogjournalctl plus Rsyslog
User PasswordMD5SHA-512SHA-512SHA-512
パッケージ管理yum
rpm
yum
rpm
yum
rpm
dnf
yum
rpm
時刻同期ntpntpntp
chrony
chrony
ディスプレイサーバXorgXorgXorgWayland
パケットフィルタリングiptablesiptables
firewalld
iptables
firewalld
nftable

RHEL8の主なバージョン
OpenSSL 1.1.1、TLS 1.3,GCC 8.2、OpenJDK 11/8などがサポートされる。PythonはPython 3.6がデフォルト、Node.jsのサポートや、PHP 7.2、Ruby 2.5、、Perl 5.26などが提供される。データベースはMariaDB 10.3、MySQL 8.0、PostgreSQL 10/9.6、redis 5、Apache HTTP 2.4、nginx 1.14がデフォルト。

サービス系コマンド

RHEL 7,8では、サービス起動デーモンとして、SysVinit/Upstartに代わり、systemdが導入されました。これにより、サービス系コマンドが大幅に変更されています。RHEL8でもsystemdが採用されていることからRHEL7とは差異はありません。

サービス逐次起動系

処理内容RHEL 5,6RHEL 7,8
状態の表示(サービス単位)/sbin/service ※1 status/usr/bin/systemctl status
状態の表示(全サービス)/sbin/service --status-all/usr/bin/systemctl list-units ※2--type service
起動/sbin/service start/usr/bin/systemctl start
終了/sbin/service stop/usr/bin/systemctl stop
強制終了kill -9 PID kill -s 9
再起動/sbin/service <service_name? restart/usr/bin/systemctl restart
設定反映/sbin/service reload/usr/bin/systemctl reload

※1:/etc/init.d/service_nameでも同様
※2:list-unitsは省略可

サービス自動起動系

処理内容RHEL 5,6RHEL 7,8
(全サービス)定義の表示ls /etc/init.d//usr/bin/systemctl list-unit-files --type
(サービス単位の)定義の登録/sbin/chkconfig --add /usr/bin/systemctl daemon-reload
※3
自動起動の確認/sbin/chkconfig --list /usr/bin/systemctl is-enabled
自動起動の有効化/sbin/chkconfig on/usr/bin/systemctl enable
自動起動の無効化/sbin/chkconfig off/usr/bin/systemctl disable

※3:Unitファイルを再読み込みすることにより登録される

systemdでは、「Unit」という単位でさまざまな処理を行います。従来のサービスに相当する処理は「.service」という拡張子がつくファイルとして管理されており、サービス関連の処理を行うときは、この名前を指定します。Unitには、ほかにもマウントポイントの処理を行う「.mount」、デバイスの処理を行う「.device」、Unitをグループ化する「.target」などがあります。

また、自動起動の登録を行ったUnitは、「/usr/lib/systemd/system/unit_name」内の[Install]セクションのWantedByオプションで指定されているUnitのディレクトリ(例:/etc/systemd/system/multi-user.target.wants/)にシンボリックリンクが張られることにより、自動起動が有効となります。

RHEL 6と同じコマンドを利用したい方は……

RHEL 7,8のserviceコマンドやchkconfigコマンドは、systemctlコマンドにリダイレクトするシェルスクリプトとなっているのでそのまま利用できます。

  • サービスの一時停止
$ sudo service crond stop
Redirecting to /bin/systemctl stop crond.service
  • サービスの自動起動の無効化
$ sudo chkconfig crond off
Note: Forwarding request to 'systemctl disable crond.service'. rm '/etc/systemd/system/multi-user.target.wants/crond.service' 

ただし、chkconfig --listについては、出力結果にも注意書きがあるとおり、一部のサービスしか表示されないため注意が必要です。

$ chkconfig --list
Note: This output shows SysV services only and does not include native systemd services. SysV configuration data might be overridden by native systemd configuration. 

ランレベル

RHEL 7,8では、従来のランレベルに相当する起動モードも「.target」という拡張子がつくUnitで管理されるようになっています。これまでのランレベルとの対応は、下記のとおりです。なお、targetファイルを自作して、独自の起動モードを利用することも可能です。

処理内容RHEL 5,6RHEL 7,8
システム停止0poweroff.target
シングルユーザモード1rescue.target
マルチユーザモード3multi-user.target
グラフィカルモード5graphical.target
再起動6reboot.target
緊急モードN/Amergency.target

通常の起動の際は、いずれかのtargetへのシンボリックリンク(通常はmulti-user.targetかgraphical.target)となっているdefault.targetが参照されます。

ランレベル系コマンドの一覧

処理内容RHEL 5,6RHEL 7,8
起動モードの変更telinit
init
systemctl isolate
起動モードの認runlevelsystemctl list-units --type target
Default起動モードの変更/etc/inittabの編集sudo systemctl set-default
Default起動モードの確認/etc/inittabの参照systemctl get-default
  • rescue.target(従来のシングルユーザモード相当)に変更
$ sudo systemctl isolate rescue.target
  • デフォルトの起動モードをマルチユーザモードへ変更
$ sudo systemctl set-default multi-user.target
$ rm '/etc/systemd/system/default.target'
$ ln -s '/usr/lib/systemd/system/multi-user.target' '/etc/systemd/system/default.target' 
  • RHEL 6と同じコマンドを利用したい……

現在と1つ前のランレベルを表示するrunlevelコマンド、ランレベルを一時的に変更するtelinit(init)コマンドも利用できます。ただし、/etc/inittabは、ファイルそのものはあるもののコメントだけとなっているため、標準起動モードの変更については、systemctl set-default の実行が必要なようです。

ログ確認系コマンド

systemdによって起動されたUnitのログは、/var/log/journal以下にバイナリ形式で保存されるようになっています。そのため、ログの確認にはjournalctlコマンドを利用します。

ログ確認系コマンドの一覧は下記のとおりです。

処理内容RHEL 5,6RHEL 7,8
(サービス単位の)ログ確認cat /var/log/journalctl -u
ログの更新監視tail -f /var/log/journalctl -f -u
起動ログの確認dmesgsystemctl journalctl --dmesg

journalctlによる表示には、デフォルトでページャ(less)で表示されます。パイプ処理などでページャを利用したくない場合は、--no-pagerオプションを付加します。また、--sinceオプションで相対時間(例:30 min ago)、絶対時間(2015-04-30)以降のログ出力、--outputオプションでJSONなどの各種形式で出力、-bオプションでN世代前のブートログ表示など、数多くの機能を備えています。
ただし、デフォルト設定のままの場合、/run/systemd/journal以下にログが書き込まれ、再起動すると消えてしまうので注意が必要です。再起動後もログを残すには、/var/log/journalを作成して、journaldの再起動(systemctl restart systemd-journald)を行う必要があります。

ネットワーク系コマンド

RHEL 7,8では、ifconfigやnetstatといった馴染みのあるコマンドが含まれている、net-toolsパーッケージがインストールされなくなりました。

ネットワーク系コマンドの一覧は下記のとおりです。なお、ipコマンドのオプションは、一意な文字列まで入力すれば認識してくれるので、短縮表記例も併記しておきます。

処理内容RHEL 5,6RHEL 7,8(フル表記)RHEL 7,8(短縮表記例)
全アクティブインターフェイスの情報表示/sbin/ifconfig/sbin/ip address show
or /sbin/ip link show
/sbin/ip a
or /sbin/ip
特定インターフェイスの情報表示/sbin/ifconfig eth0/sbin/ip address show
dev enp0s3
/sbin/ip a s
dev enp0s3
ルーティングテーブルの表示/sbin/route/sbin/ip route/sbin/ip r
デフォルトゲートウェイの追加/sbin/route add default gw xxx.xxx.xxx.xxxip route add default via xxx.xxx.xxx.xxx/sbin/ip r a default via xxx.xxx.xxx.xxx
デフォルトゲートウェイの削除/sbin/route del default gw xxx.xxx.xxx.xxxip route del default via xxx.xxx.xxx.xxx/sbin/ip r d default via xxx.xxx.xxx.xxx
TCPの接続状態の表示/bin/netstat -tanss -tan左に同じ
インターフェイスごとの統計情報/bin/netstat -iip -s linkip -s
ARPテーブルの表示/sbin/arp -n/sbin/ip neighbour/sbin/ip n

MAC Address /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/udev/rules.d/70-persistent-net.rules

RHEL 6と同じコマンドを利用したい方は……
net-toolsパッケージをインストールすれば、RHEL 6で使用していたネットワーク系コマンドはすべて利用可能となります。

NetworkManager系コマンド

NetworkManagerは、RHEL 4から搭載されているネットワークユーティリティで、RHEL 7で新しく追加されたわけではありません。しかし、RHEL 7より、ネットワーク設定にはこのユーティリティを使用することが推奨されるようになったので、今後は利用頻度が高くなると思います。

NetworkManagerには、X上で動作するGUIツールのほか、ターミナル上でGUIで設定できるnmtuiコマンド、CUIで設定するnmcliコマンドがあります。ここでは、最も利用しやすいとnmcliコマンドを用いたNetworkManager系コマンドを下記に紹介します。

なお、デバイスを示す「デバイス名」と、接続に名前をつけた「接続名」は異なることがあるので注意してください。

処理内容nmcliコマンド(フル表記)nmcliコマンド(短縮表記例)
デバイス一覧の表示/usr/bin/nmcli device/usr/bin/nmcli d
デバイス情報の表示/usr/bin/nmcli device
show device_name
/usr/bin/nmcli d sh device_name
接続の作成/usr/bin/nmcli connection add type type_name ifname if_name con-name connection_name/usr/bin/nmcli c a type type_name ifname if_name con-name connection_name
接続の削除/usr/bin/nmcli connection delete connection_name/usr/bin/nmcli c de connection_name
接続一覧の表示/usr/bin/nmcli connection/usr/bin/nmcli c
接続情報の表示(すべて)/usr/bin/nmcli connection show connection_name/usr/bin/nmcli c s connection_name
接続の変更/usr/bin/nmcli connection modify ..../usr/bin/nmcli c m ...
接続の開始/usr/bin/nmcli connection up connection_name/usr/bin/nmcli c u connection_name
接続の停止/usr/bin/nmcli connection down connection_name/usr/bin/nmcli c d connection_name
接続情報の表示(特定フィールド)/usr/bin/nmcli --fields field_name connection show connection_name/usr/bin/nmcli -f field_name c s connection_name

RHEL6でのNetworkManager

デフォルトで NetworkManager サービスが起動するため /etc/sysconfig/network-scripts/ifcfg-XXX の書式が従来と変わっています。

従来の書式で設定ファイルを書く場合は、設定ファイルに下記の1行を追加します。
NM_CONTROLLED=no

もしくは NetworkManager サービスを停止します。

# chkconfig NetworkManager off
# service NetworkManager stop

RHEL6でのethX と MAC Address の紐付け

RHEL5では/etc/sysconfig/network-scripts/ifcfg-XXXにHWADDRとして記載していたが
RHEL6では、/etc/udev/rules.d/70-persistent-net.rules で指定します。

# cat /etc/udev/rules.d/70-persistent-net.rules
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.

# PCI device 0x14e4:0x164c (bnx2) (custom name provided by external tool)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:14:5e:fa:98:a2", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

# PCI device 0x14e4:0x164c (bnx2) (custom name provided by external tool)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:14:5e:fa:98:a0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

が、相変わらず、/etc/sysconfig/network-scripts/ifcfg-ethX にも HWADDR 指定があるので、両方を合わせないといけない。ifcfg-ethX の HWADDR は削除した方が幸せになれるかも知れません。

ロードモジュール

RHEL6では、/etc/modprobe.conf が無くなりました。目的別に対応を説明します。

(1) initrdに投入する内蔵ディスク用のデバイス・ドライバの指定(alias scsi_hostadapterXX)
RHEL5 までは、mkinitrd コマンドは /etc/modporbe.conf をみて、そのサーバを起動するために必要なドライバを含む initrd を作成しました。initrd に含まれる初期化スクリプト init では、下記の例のように必要なドライバを直接 insmod しています。

echo "Loading ehci-hcd.ko module"
insmod /lib/ehci-hcd.ko
echo "Loading ohci-hcd.ko module"
insmod /lib/ohci-hcd.ko
...

RHEL6 では、initrd の作成が dracut に変わって、サーバ個別の initrd ではなく、汎用的な initrd が作成されるようになったので、/etc/modprobe.conf による指定は不要になりました。dracut が作成する initrd では、udev によって、サーバに接続されたデバイスに応じたドライバを自動判別してロードします。

具体的には、initrd に含まれる etc/udev/rules.d/80-drivers.rules のルールから modprobe が呼び出されます。

# cat 80-drivers.rules
# do not edit this file, it will be overwritten on update

ACTION!="add", GOTO="drivers_end"

DRIVER!="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe -b $env{MODALIAS}"
SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -b tifm_sd"
SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN+="/sbin/modprobe -b tifm_ms"
SUBSYSTEM=="memstick", RUN+="/sbin/modprobe -b --all ms_block mspro_block"
SUBSYSTEM=="i2o", RUN+="/sbin/modprobe -b i2o_block"
SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST!="[module/sg]", RUN+="/sbin/modprobe -b sg"
SUBSYSTEM=="module", KERNEL=="parport_pc", RUN+="/sbin/modprobe -b ppdev"

LABEL="drivers_end"

そうは言っても特定のドライバを特定の順序で読み込ませたい場合は、カーネルオプション rdloaddriver にドライバを読み込む順に記載します。具体的には、init スクリプトから下記のスクリプトが実行されて、指定のドライバがロードされます。

# cat cmdline/01parse-kernel.sh
#!/bin/sh

for p in $(getargs rdloaddriver=); do
        modprobe $p
done

dracut が標準で initrd に含めないドライバを使用する際は、dracut の -d オプションで強制的に initrd に入れておきます。dracut については、レッドハットニュースレター:vol.53 - dracut もどうぞ。
http://www.jp.redhat.com/magazine/jp/201102/rhel.html

2) NIC用のデバイス・ドライバの指定(alias ethXX)

サーバ起動後の追加のドライバのロードは、すべて udev で処理されるようになっています。具体的には、rc.sysinit から start_udev したタイミングで、/lib/udev/rules.d/80-drivers.rules に従って自動的に modprobe されます。(dracut 内部の動作と基本的には同じです。)

# cat /lib/udev/rules.d/80-drivers.rules
# do not edit this file, it will be overwritten on update

ACTION!="add", GOTO="drivers_end"

DRIVER!="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe -b $env{MODALIAS}"
SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -b tifm_sd"
SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN+="/sbin/modprobe -b tifm_ms"
SUBSYSTEM=="memstick", RUN+="/sbin/modprobe -b --all ms_block mspro_block"
SUBSYSTEM=="i2o", RUN+="/sbin/modprobe -b i2o_block"
SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST!="[module/sg]", RUN+="/sbin/modprobe -b sg"
SUBSYSTEM=="module", KERNEL=="parport_pc", RUN+="/sbin/modprobe -b ppdev"

LABEL="drivers_end"

ちなみに、udev が必要なドライバを判別する方法は次のとおりです。まず、PCI デバイスなどは、/sys に登録されるとカードに固有の modalias 情報が登録されます。

# cat /sys/devices/pci0000:00/0000:00:06.0/0000:03:00.0/0000:04:00.0/modalias
pci:v000014E4d0000164Csv00001014sd00000342bc02sc00i00

一方、各ドライバは、サポートするデバイスの modalias 情報を持ちます。下記の alias: 部分です。

# modinfo bnx2
filename:       /lib/modules/2.6.32-71.el6.x86_64/kernel/drivers/net/bnx2.ko
firmware:       bnx2/bnx2-rv2p-09ax-5.0.0.j10.fw
firmware:       bnx2/bnx2-rv2p-09-5.0.0.j10.fw
firmware:       bnx2/bnx2-mips-09-5.0.0.j15.fw
firmware:       bnx2/bnx2-rv2p-06-5.0.0.j3.fw
firmware:       bnx2/bnx2-mips-06-5.0.0.j6.fw
version:        2.0.8-j15
license:        GPL
description:    Broadcom NetXtreme II BCM5706/5708/5709/5716 Driver
author:         Michael Chan <mchan@broadcom.com>
srcversion:     8CDA41B3E0DF70A112FBA80
alias:          pci:v000014E4d0000163Csv*sd*bc*sc*i*
alias:          pci:v000014E4d0000163Bsv*sd*bc*sc*i*
alias:          pci:v000014E4d0000163Asv*sd*bc*sc*i*
alias:          pci:v000014E4d00001639sv*sd*bc*sc*i*
alias:          pci:v000014E4d000016ACsv*sd*bc*sc*i*
alias:          pci:v000014E4d000016AAsv*sd*bc*sc*i*
alias:          pci:v000014E4d000016AAsv0000103Csd00003102bc*sc*i*
alias:          pci:v000014E4d0000164Csv*sd*bc*sc*i*
alias:          pci:v000014E4d0000164Asv*sd*bc*sc*i*
alias:          pci:v000014E4d0000164Asv0000103Csd00003106bc*sc*i*
alias:          pci:v000014E4d0000164Asv0000103Csd00003101bc*sc*i*
depends:
vermagic:       2.6.32-71.el6.x86_64 SMP mod_unload modversions
parm:           disable_msi:Disable Message Signaled Interrupt (MSI) (int)

これらの情報は、depmod がまとめて /lib/modules/$(uname -r)/modules.alias に記録しており、「modporbe 」を実行すると modules.alias から必要なドライバを判別して自動的にロードします。/lib/udev/rules.d/80-drivers.rules では、Kernel が udev に通知した modalias を使って modprobe しています。udev に勝手にロードしてほしくないドライバは、/etc/modprobe.d/blacklist.conf 内に blacklist オプションで指定しておきます。

ホスト名操作系コマンド

RHEL 7,8では、ホスト名もsystemdが管理しているため、永続的なホスト名の変更方法も変更されました(hostnameコマンドを利用した一時的なホスト名の変更はRHEL 6と同じです)。

ホスト名操作系コマンドの一覧は下記のとおりです。

処理内容RHEL 5,6RHEL 7,8
ホスト名の確認hostnamehostname or hostnamectl
ホスト名の変更(永続的)/etc/sysconfig/networkを編集hostnamectl set-hostname new_host_name
,/etc/hostnameを編集

なお、「nmcli global hostname new_host_hame」を実行して、NetworkManager経由でも変更することができます。

ファイアウォール系コマンド

RHEL 7では、ファイアウォールとしてfirewalldが導入されました。これは、内部的にはiptablesを利用しているものの、「ゾーン」と呼ばれるiptablesとはまったく異なる機能を提供しているため、コマンド一覧は割愛します(iptabelesサービスとの併用もできません)。

RHEL 8では、ファイアウォールとしてnftablesが新たにiptableと置き換えで実装されたパケットフィルターです。

デフォルトでは nftablesは RHEL 7 での iptables と同様に、firewalld のバックエンドとして動作するので、既に firewalld に移行している場合はあまり気にならないかもしれません。

nftables を利用するメリット

  • フィルターの処理が、線形処理からテーブル探索に(処理速度の高速化)

  • IPv4とIPv6をひとつのコマンドで管理可能に(ip6tablesのような、IPv6専用コマンドは使いません)

  • ネットワーク層以外のプロトコルも管理可能に(entablesarptablesは、nftablesに統合されました)

  • 一貫性があり、簡潔な文法(iptables の –dportのようなプロトコル独自の拡張はありません)

モジュール組み込み

aliasの記載もRHEL6,7では変更されている。

RHEL 5RHEL 6RHEL 7,8
/etc/modprobe.conf/etc/modprobe.d/
※bondingのaliasはnet-alias.conf
/etc/modprobe.d/
※bondingのaliasはnet-alias.conf

Storage

FS Type,Size

ReleaseDefault FS暗号化 FSMaximum
Volume sizeFile size
RHEL5ext3LUKS4-32TiB16GiB-2TiB
RHEL6ext4LUKS1EiB16TiB
RHEL7xfsLUKS16EiB8EiB
RHEL8xfsLUKS216EiB8EiB

利用可能なFS

Release利用可能なFS
RHEL5ext3,ext4,GFS,GFS2,XFS
RHEL6ext3,ext4,GFS2,XFS
RHEL7ext3,ext4,GFS2,XFS,Btrfs
RHEL8XFS,ext3,ext4、NFS、SMB、GFS2、Stratis

Diskパーティション系コマンド

ブート方式パーティション管理
BIOSMBR形式
UEFIGPT形式
処理内容MBR形式GPT形式
サポートするファームウェアBIOS,UEFIUEFI
基本パーティション最大4最大128
拡張パーティション1無し
拡張パーティション内の論理パーティション制限無し無し
ツールfdisk、partedparted,gdisk,gparted
ブートローダの制限ハードディスクの2.2TB内9.4ZB(ゼタバイト)
Redhat Linux サポート状況RHEL 5 x86
RHEL 5 x86_64
RHEL 6 x86
RHEL 6 x86_64
RHEL 6 x86_64
RHEL 7 x86_64
RHEL 8 x86_64

(*)各ディスクの先頭に保存されているMBR(マスター・ブート・レコード)はパーティション・テーブルのデータ長が32bitです。よって2の32乗セクタ×512bytes=約2.2Tbytesとなります。

BIOSとUEFIではパーティション管理形式も異なります。BIOSではMBR形式のみです。一方UEFIではMBR形式とGPT形式がサポートされています。
RHEL5,6ではpartedが OS 標準のRPMとして含まれている。
RHEL7 では gdisk が OS 標準のRPMとして含まれている。

2TBを越えるディスク領域を使用する際の注意事項
パーティション作成にはpartedユーティリティを使用し、GPT(GUIDパーティションテーブル)を使用します。fdiskでは2TBを越えるディスクスペースは扱えません。
Anacondaインストーラーは、GPTをサポートしていない為、2TBを越えるディスクスペースは扱えません。
/boot, / パーティションは 2TB未満のディスク領域にインストールする必要があります。

■■■ ファイルシステム系コマンド ■■■
RHEL 7ではファイルシステムもext4からxfsに変更されています。
・EXT4 と XFS コマンドの参照表

処理内容ext3ext4XFSBtrfs
FS作成mkfs.ext3mkfs.ext4mkfs.xfsmkfs.btrfs
FSをマウントするmountmountmountmount
FSのサイズを変更するresize2fsresize2fsxfs_growfs
[※1]
btrfs filesystem resize
FSの準備を整えるe2fsck
fsck.ext3
e2fsck
fsck.ext4
xfs_repairbtrfsck
FSのラベルを変更するe2labele2labelxfs_admin -Lbtrfs property
ディスク領域およびファイル使用量を報告quota,quotaon, repquotaquota,quotaon, repquotaxfs_quotabtrfs quota
FSをデバッグするdebugfsdebugfsxfs_db
FSの重要なメタデータをファイルに保存するe2imagee2imagexfs_metadumpbtrfs-image
FSの情報表示dumpe2fsdumpe2fsxfs_info
FSのメタデータの変更tune2fstune2fsxfs_adminbtrfs property
FSのバックアップdumpdumpxfsdumpbtrfs image
FSのリストアrestorerestorexfsrestorebtrfs image
ファイルのディスク上のブロック一覧表示filefragfilefragxfs_bmap
FSのデフラグe4defrage4defragxfs_fsbtrfs filesystem defragment
FSの一時停止fsfreezefsfreezexfs_freeze,fsfreeze

※1:XFSファイルシステムのサイズは縮小できません。サイズを拡大する場合にのみコマンドを使用します。

XFSの設定のポイント

ほとんどのシステムでは特別なチューニングを行うことなくデフォルトの状態でXFSを利用できますが、要件によっては設定次第で性能が向上する可能性があります。例えば、RAIDアレイ上にXFSを構築する場合、ストライプユニットをRAIDアレイのストライプユニットと一致させることにより、性能向上が期待できます。ストレージデバイスがOSに自身のストライプユニットを通知している場合は、特にユーザーが設定する必要はありません。手動でストライプユニット、ストライプ幅を設定する場合、以下のようにしてファイルシステムを作成します。

# mkfs.xfs -d su=64k,sw=4 /dev/sda1

suはファイルシステムのストライプユニット、swはストライプ幅を意味します。swにはストライピングを構成するハードディスクの数(パリティディスクは含まない)を設定します。

上記の設定をした場合、ジャーナル領域へのログ書き込みのストライプユニットも同様の値に設定されます(上記の例では64kB)。もし同期書き込みが多いワークロードでXFSを使用する場合、ログ書き込みのストライプユニットが大きいと性能低下の原因となることがあります。そのような場合、以下のようにログ書き込みのストライプユニットを最小値(1ブロックサイズ:通常は4kB)に設定すると性能が改善するかもしれません。

# mkfs.xfs -d su=64k,sw=4 -l su=4k /dev/sda1

また、ログ書き込みのオーバヘッドを改善する対処策としては、ログが書き出されるジャーナル領域を、データ用の領域として使用するデバイスとは別に用意するという方法も考えられます。
その場合、以下のようにしてファイルシステムを作成します。

# mkfs.xfs -d su=64k,sw=4 -l logdev=/dev/sdb1 /dev/sda1

上記コマンドでは、データ用の領域を/dev/sda1に、ジャーナル領域を/dev/sdb1に作成しています。

最後に

今回調べてみて、RHEL 7,8での変更点の多さを改めて痛感しました。もはや、これまでのRHELとは別のOSになったような雰囲気すらあります。ただし、RHELにかぎらず、多くのディストリビューションがsystemdへの対応を表明していますので、使いこなせるようになっておきたいところです。そのためにも、今回は詳しくは触れられなかった、Unitの仕組みの理解が不可欠と思いました。

= bashの置換${var/a/b}と配列

$
0
0

基礎の復習

bashの変数展開の一つに、値を置換するものがあります

# ${var/a/b}# varに最初に含まれるaをbに置換$ var="foo"$ echo${var/o/_}
f_o

又、含まれる全ての対象を置換するには${var//a/b}を使います

$ var="foo"$ echo${var//o/_}
f__

本題: 配列に適用した場合

${var/a/b}を配列に適用した場合、各値に置換を適用した結果になります

# ${var/a/b}のパターン$ var=(1 2 3 4 11 12 13)$ echo{var[@]/1/_}
_ 2 3 4 _1 _2 _3

# ${var//a/b}のパターン$ var=(1 2 3 4 11 12 13)$ echo{var[@]/1/_}
_ 2 3 4 __ _2 _3

シラナカッタ

[2019-11-26 15:07]

Git Hooksでgit cloneだけをフックする

$
0
0

やりたいこと

git clone実行後だけリポジトリディレクトリに対して特定の処理がしたい。

TLDR

  • trap DEBUGgit cloneを叩いたことを検知して、コマンド実行前にフラグを立てる。
  • post-checkoutスクリプト内でフラグをチェックして分岐させる。
  • 今回の記事の本筋ではないが、注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要あり。

背景

前の記事で、gitアカウントを自動で切り替えるためにgit clone前に処理をする必要があったので、cdgitのラッパー関数を作り、エイリアスで元のcdgitを上書きして、cdをフックにgitコマンドが使用する変数を切り替えるようにしました。しかし、他のライブラリを利用してcd以外でディレクトリ移動するケースを考えるとやはり汎用性に欠け、取り急ぎとはいえ筋が良くなかったので、潔くgit cloneを直接フックすることにしました。

意外にもgit cloneだけをフックする方法に関する記事を見かけなかったので書きました。記事がないということは全く必要とされていないのか、それとも記事にならないほどもっと楽な方法があるのか、知ってる人が書いてないだけなのか。

ちなみに、GitHooksとは、というお話は以下を参照。
git hook はじめの一歩
git hookでできること

問題

Git Hooksでgit cloneをフックしたいとき、post-checkoutを使う必要がありますが、これだとgit checkoutのときにも実行されます。

解決策

環境

macOS

sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.5
BuildVersion:   18F132

Git

git --version
git version 2.23.0

Bash

bash --version
GNU bash, version 5.0.11(1)-release(x86_64-apple-darwin18.6.0)

git cloneの実行を検知する

Gist: .bash_hooksとして置いておきました。使い方はGist冒頭に書いてあります。このGistには、ディレクトリ移動の検知や、コマンド実行前だけでなくコマンド実行後に実行される処理もまとめて書いているのですが、今回の記事に関わるポイントは以下の3点です。

  • 各コマンドの実行前に__hooks__before_each_commandが実行されるようにする
trap'__hooks__before_each_command' DEBUG
  • __hooks__before_each_command内でBASH_COMMANDで直前のコマンド名(=叩かれたコマンド名)を取得
  • フックがアクティベートされていたら(この上記Gistの最後でアクティベートしてます)__hooks__set_git_clone_flagに直前のコマンド名を渡して実行
function __hooks__before_each_command(){local last_command=($BASH_COMMAND)if[-z"$__HOOKS__ACTIVATED"];then
        return
    fi# before each command
    __hooks__set_git_clone_flag "${last_command[@]}";if["$PREVPWD"!="$PWD"];then# on changing directoriesexport PREVPWD="$PWD"fi

    if[-z"$__HOOKS__EXECUTING_LINE"];then
        return
    fi

    unset __HOOKS__EXECUTING_LINE
    # before each first command of a comamnd line}
  • 直前に叩かれたコマンド名がgit cloneだった場合、__hooks__set_git_clone_flag内で__HOOKS__ON_GIT_CLONEフラグを立てる
function __hooks__set_git_clone_flag(){if['git'="$1"]&&['clone'="$2"];then
        echo"Setting __HOOKS__ON_GIT_CLONE=true in $filename"export __HOOKS__ON_GIT_CLONE=true
    fi}

post-checkout内でフラグをチェックして処理する

Gist: post-checkoutとして置いておきました。.gitvariablesというファイルを置いてgitアカウントの情報をロードするようにして、そのファイル自体は~/.gitignoreに登録してトラックしないようにしてあります。

  • ここでフラグをチェックして、フラグが立ってなかったらexitしてます。
if[-z"$__HOOKS__ON_GIT_CLONE"];then
    exit
fi
  • 処理の最後にフラグを倒します。
unset __HOOKS__ON_GIT_CLONE

GitHooks中でユーザの入力を待ちたいときの注意点

オフトピックですが注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要があります。ちょっとハマりました。

read-rp'Type anything you like:'< /dev/tty

まとめ

コードが散在することと、暗示的にフックすることでハマったりしないように、多用には注意が必要ではありますが、cdとgit上書きより汎用的かつ拡張しやすくなった感はあります。

ちなみに、Bashのコマンドの実行前後をフックする方法の解説はChuan Ji: DEBUG trap and PROMPT_COMMAND in Bashの記事がわかりやすいです。

参考

アタシがプログラミングするときの環境はこんなカンジヨ!!

$
0
0

対象

「最近Web開発を始めたワヨ」なアンタ, もしくは 「お気に入りの設定を既にしているけど, 面白そうなのがあれば取り入れよっかな〜」とか言ってるアンタ達には是非見てって欲しいワ.

前提

アタシはRailsが大好きなの. 休日はよくRailsでWebアプリ作ったりしてるワ.
思いついたアイデアをパパッと形にできるところも好きな理由の一つヨ.
だから特別なIDEとかそれに伴ったプラグインとかの紹介はここではしないワ.(個人でネイティヴアプリを作ったりもしてるケドそれは別途記事にするかもしれないワヨ)

エディタ

Vim

アンタ達は普段何でコード書いてるかしら?
アタシは Vimを使っているワ.
なんてったって Vimはキーボード操作で完結できちゃうのヨ.
Vimの詳しい説明はここでは割愛するワ. 興味があったら調べてみてネ.
ただデフォルトの Vimだとちょっと寂しすぎるから, アタシは色んなプラグインをいれてるの.
プラグインマネージャーは vim-plugを使ってるワ.
導入については この記事がオススメヨ.
dotfilesGitHubにあげてるワヨ.

コンソール

次は コンソール ヨ.
アタシはターミナルアプリは iTerm2, シェルは bashを使ってるワ. Catalinaからはログインシェルが zshになるみたいネ.

bash

git

アタシは普段 gitはCUIで操作してるからその辺は少しカスタマイズしてるワ.
ちょこっと紹介するワネ.
💥インストールには Homebrewを使うから先にインストールしておいてネ💥

git-completion

gitコマンドを自動補完してくれるワ.

git-prompt

pwdの脇に作業中のブランチを表示してくれるワ.

tig

git logをGUIっぽく表示してくれるワ.

git-detroit

実はこれアタシが作ったの.
プロジェクトが大きくなってくるとローカルに実装済の作業ブランチが結構溜まってくるわよネ?(アタシだけ?)
git-detroitなら簡単に整理できるワ.
インストールは簡単ヨ.

$ brew tap dj-hirrot/git-detroit
$ brew install git-detroit

これを実行すると git-detroitがインストールされるワ.
整理したいプロジェクトまで移動して,

$ git-detroit
=> do you remove feature/xxxxx_xxxxx ? (y/n): y
Deleted branch feature/xxxxx_xxxxx (was xxxxxx).

で整理できるようになるワ.
詳しくは https://github.com/dj-hirrot/git-detroitをみて欲しいワ. (READMEの英訳PR待ってます!)

最後に

こんなカンジの環境で開発してるワ.
使えそうなのがあったら参考にして欲しいワ.
他にもオススメがあったら是非コメントしてネ.

シェルスクリプトのちょっと実用的な数行の関数(数値と日時)

$
0
0

シェルスクリプトでたまに必要になる、ちょっとした処理を関数にしました。

たった数行の処理をわざわざ関数にする目的の一つは、スクリプトの可読性を向上するためです。例えばdate +'%F %T'nowでは後者の方が意図を理解しやすくなります。

もう一つの目的は、たまにしか使わないコマンドのオプションや文法などのナレッジを関数のコードにまとめておくことで、自分が忘れた時に見直しやすくするためです。

数値

整数(10進数)の判定

引数を 10進数の整数と判定できたら真(終了ステータス 0)を返す関数です。

# $1  整数かを判定する文字列function is_integer(){[[${1}==""]]&&return 1
  printf"%d"${1}>/dev/null 2>&1
}

printf は"%d"で整数に変換できない文字が与えられたら偽を返すのですが、空文字列の場合に真を返してしまいます。しかしこの関数では引数が空文字列の場合に偽を返したいので、先に空文字列の判定をしています。

テスト

for value in'-100''0''9999999999''1.1''-0.2''3e5''A''zz'''do
  if is_integer "${value}";then
    echo"'${value}': OK."else
    echo"'${value}' is NOT integer."fi
done

結果は以下のようになりました。

  • 正/負の整数と 0 で真を返す
  • 正/負の小数で偽を返す
  • eを使った指数表現で偽を返す
  • 16進数として評価可能な英文字で偽を返す
  • 文字列/空文字列で偽を返す
'-100': OK.
'0': OK.
'9999999999': OK.
'1.1' is NOT integer.
'-0.2' is NOT integer.
'3e5' is NOT integer.
'A' is NOT integer.
'zz' is NOT integer.
'' is NOT integer.

補足

空文字列の判定を以下のように書けば bash 以外のシェルでも使えるようになりますが、一般的なプログラム言語と同様の比較演算子==による可読性を優先しました。

[-z${1}]&&return 1

実数の判定

引数を実数と判定できたら真を返す関数です。上の"%d""%f"に変えただけです。

# $1  実数かを判定する文字列function is_numeric(){[[${1}==""]]&&return 1
  printf"%f"${1}>/dev/null 2>&1
}

テスト

for value in'-100''0''9999999999''1.1''-0.2''3e5''A''zz'''do
  if is_numeric "${value}";then
    echo"'${value}': OK."else
    echo"'${value}' is NOT numeric."fi
done

結果は以下のようになりました。

  • 正/負の整数と 0 で真を返す
  • 正/負の小数で真を返す
  • eを使った指数表現で真を返す
  • 16進数として評価可能な英文字で偽を返す
  • 文字列/空文字列で偽を返す
'-100': OK.
'0': OK.
'9999999999': OK.
'1.1': OK.
'-0.2': OK.
'3e5': OK.
'A' is NOT numeric.
'zz' is NOT numeric.
'' is NOT numeric.

日時

どれも一般的なプログラム言語の経験者が関数の名前を見ただけで、機能と引数を何となく連想できるようにしたつもりです。

現在日時

# $1  書式指定文字列(省略可)function now(){date +"${1:-%F %T}"}

今日の日付

# $1  書式指定文字列(省略可)function today(){date +"${1:-%F}"}

日時の書式設定

# $1  日時を表す文字列# $2  書式指定文字列(省略可)function format_datetime(){date-d"${1}" +"${2:-%F %T}"}

日時の加算/減算

# $1  日時を表す文字列# $2  正/負の整数(未来/過去)# $3  $2の単位 {year|month|day|hour|minute|second}# $4  書式指定文字列(省略可)function add_datetime(){if((${2}< 0 ));then
    local date_opt="$((${2}*-1))${3}s ago"else
    local date_opt="${2}${3}s"fi

  date-d"${1}${date_opt}" +"${4:-%F %T}"}

テスト

add_datetime '1999-12-31 23:59:59' 1 second
add_datetime '1999-12-31 23:59:59'-1 second
add_datetime '1999-12-31 23:59:59' 180 day '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59'-180 day '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59' 10 hour '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59'-10 hour '%Y/%m/%d %H:%M:%S'
add_datetime '1999-12-31 23:59:59' 20 year '%Y年%m月%d日 %H時%M分%S秒'
add_datetime '1999-12-31 23:59:59'-20 year '%Y年%m月%d日 %H時%M分%S秒'

以下のように意図した結果になりました。

2000-01-01 00:00:00
1999-12-31 23:59:58
2000/06/28 23:59:59
1999/07/04 23:59:59
2000/01/01 09:59:59
1999/12/31 13:59:59
2019年12月31日 23時59分59秒
1979年12月31日 23時59分59秒

bashでシングルコーテーションをネストさせる

$
0
0

経緯

シェルスクリプトを作成する際にどうしてもシングルコーテーションをネストさせる必要がありましたが、ちょっぴり悩んだのでいろいろと試しました(だいたいawkコマンドのせい)。

解答

bash
[root@server]# echo 'Hello, '\''NEW'\'' world!'
Hello, 'NEW' world!

解説

シングルコーテーションをネストすることは、bashの仕様上そもそもできません(タイトル詐欺では?)。
そこで、それっぽくするためにはネストさせる予定だったシングルコーテーションをシングルコーテーションの外に出し、外に出したシングルコーテーションをエスケープする必要があります。
上の例だと

'Hello, '
\'   ←ネストさせたかったシングルコーテーション
'NEW'
\'   ←ネストさせたかったシングルコーテーション
' world!'

を並べているだけです。

これができると下記のようなことができます。

bash
[root@server]# STATUS=OLD

[root@server]# echo 'Hello, '\''$STATUS'\'' world!'
Hello, '$STATUS' world!
#変数がシングルコーテーションで保護されている状態

[root@server]# eval echo 'Hello, '\''$STATUS'\'' world!'
Hello, $STATUS world!
#変数がシングルコーテーションで保護されているため二重展開では展開されない

[root@server]# eval eval echo 'Hello, '\''$STATUS'\'' world!'
Hello, OLD world!
#三重展開によってようやく変数が展開される

通常業務では使う機会ないと思いますが、変数でコマンドを生成する時によく使います。

やったこと

色々試して失敗したものを備忘として下記にメモ書き

bash
[root@server]# echo 'Hello, "NEW" world!'
Hello, "NEW" world!
#ダブコであればいたって簡単
bash
[root@server]# echo 'Hello, 'NEW' world!'
Hello, NEW world!
#同じように入れたんじゃもちろん表示されない
bash
[root@server]# echo 'Hello, \'NEW\' world!'
-bash: !': event not found
#シングルコーテーション内でエスケープすると正しく認識されない
bash
[root@server]# echo 'Hello, '''NEW''' world!'
Hello, NEW world!
#VBっぽく書いてもダメ
bash
[root@server]# echo 'Hello, '"'"'NEW'"'"' world!'
Hello, 'NEW' world!
#ダブコをうまいこと使えば一応できる
bash
[root@server]# eval echo "'"'Hello, '"'"\\\'"'"'NEW'"'"\\\'"'"' world!'"'"
Hello, 'NEW' world!
#おまけ

シェル芸の片鱗を感じる

バッチ処理の実行ログを日毎にローテーションする

$
0
0

実行ログを出力するシェルスクリプトというのは大抵以下のような形をしています。

# 変数定義Log=/var/log/batch.log

# メイン処理echo 処理開始 >>$Log
...

ちゃんと作るのであればログ出力などの一般的な処理は分割されていることが普通だと思いますが、ここでは単体のスクリプトの中にログ出力処理が実装されている場合を考えます。

さて、上記のままでは、バッチ処理が実行されるたびに延々と同じファイルにログを追記し続けることになり、ファイルはどんどん肥大化していきます。ログが肥大化すると管理しづらくなるので、あるタイミングでログをbatch.log_20191130のように別名に変更(ローテーション)したくなります。ローテーションする条件は色々ありますが、今回は日付が変わったタイミングとしました。

ここで問題となったのは、バッチ処理が日付をまたぐ可能性があるという点でした。一連の実行ログは同じファイルに出力されていると見返すときに都合が良いですが、日付が変わった瞬間にローテーションしてしまうと実行ログが別々のファイルに分割されてしまいます。逆に、日付が変わってから十分に時間が経ってからローテーションすると、当日実行されたバッチ処理のログが前日分のファイルに混ざることなります。

この問題を解決するためには、「バッチ処理の開始日付」と「実行ログファイルの作成日付」を比較し、両者が同じであればログファイルに追記し、異なっていればローテーションを実行してから新しいログファイルを作成するのが良いと考えました。

バッチ処理の開始日付を得るには dateの実行結果を利用しますが、後で比較がしやすいようにフォーマットを明示的に指定します。%Fを指定するとISO形式(YYYY-MM-DD)の日付を得ることができます。

比較しやすい日付を得る
$TODAY=$(date +%F)# YYYY-MM-DD

一方で、実行ログファイルの日付は ls -lで取得できます。これも比較しやすいようにフォーマットを指定します。--time-style=long-isoでISO形式(YYYY-MM-DD)の日付を得ることができます。ただし、lsが既定で出力する日付は更新日付なので、作成日付を得るために -cを指定します。

比較しやすいファイル作成日付を得る
$FILE_CREATION_TIME=$(ls-lc--time-style=long-iso $Log | cut-d' '-f6)# YYYY-MM-DD

一般的(?)に日時の比較は面倒ですが、今回は日付が同一かどうかなので簡単です。

if[$FILE_CREATION_TIME!=$TODAY];then# もしログファイルの作成日付が実行日付と異なるならローテーションmv$Log$Log_$FILE_CREATION_TIMEfi

この数行を、バッチ処理の前に書いておくことで、実行ログのローテーションが上手くできると思います。
もし、$Log_$FILE_CREATION_TIMEが既に存在する場合は、ローテーションに失敗しつづけるので気をつける必要があります。

以上


シェルスクリプトTIPS

$
0
0

シェルスクリプトのTIPS

最近はPyrhonやRubyなど他のスクリプト言語も流行っていますが、Unixエンジニアと言えばシェルスクリプト。この記事は基本的なシェルスクリプトがかける人向けのTIPSです。他人が書いたシェルスクリプト読んでいて、「なんだこれ?」と思った時の参考にして下さい。

◆安全なSHEBANGについて(/bin/shの実体)

昨今、多くのUnixOSの/bin/shはBashとなっているが、実はすべてのUnix/Linuxがそうなっているとは限らない。AshだったりZshだったりDashだったりの可能性もある。なので、スクリプトの一行目に書く安全なシェバンは、シェルを明示しておく。

#!/bin/bash

もしくはすべてのUnixシェルで実行できるよう古典的で安全な(PORTABLEな)シェルスクリプトを記述しておくと良い。ただし、実行速度が低下したり、配列が使えない、EXPRコマンドを用いたインクリメントがトリッキーなどの制限がある。

◆DEBUGモード

どこでエラーが起きているのかわからないが正常終了しない場合、Xオプションを付けてDEBUGモードで実行する。シェルプログラミングの場合、これとPRINTFデバッグで大方は解決する。

# /bin/bash -x myscript.sh
◆インクルード

ドットの後にパラメータや自作の関数などを定義したファイルを指定すれば、実行時に読み込まれる。パラメータ専用ファイルなどを作成し、プロジェクトごとに編集すれば直接スクリプトを編集するより安全にカスタマイズ・デプロイが可能となる。

#!/bin/bash

. /usr/dev/param
. /user/dev/functions

#(以下省略)
◆ワンライナー1 セミコロンで複数行を1行に収める
for name in foo bar foo2
do
  echo ${name}
done

セミコロンを用いて一行で書くと次のように表せる

for name in foo bar foo2; do echo ${name}; done

ワンライナーの良い点として可読性は低下するがコマンドラインで気軽に投入が可能で、高度なコマンドを運用マニュアルに組み込むことができる。

◆ワンライナー2 異なる記述で同じ処理をさせる
if [ -e /var/tmp/test ]
then
   rm /var/tmp/test
fi

前のコマンドが正常終了(終了コードはTrue)した場合、&& 以降の処理を実行する。
頻出の記述法なので知っておくと〇

[ -e /var/tmp/test ] && rm /var/tmp/file 

よく知られた話だが if [ ] then ~ の [ は、実体は /bin/test のエイリアス(別名)である /bin/[ であり、次のように記述することができる

/bin/test -e /var/tmp/test && rm /var/tmp/file

&&と逆で、直前のコマンドが異常終了(終了コードがTrueでない)した場合、|| 以降の処理を実行する。
[ の後の ! はNOTを意味し、ファイルが存在しない場合を表す。

# testファイルが存在しない場合、作成しなさい
if [ ! -e /var/tmp/test ]
then
   touch /var/tmp/test
fi

下のように書くことが可能。

[ -e /var/tmp/test ] || touch /var/tmp/test
◆&&と||を利用したワンライナーのハマりポイント

よくやる間違いで下記のコードは意味が異なるので注意する。
test_Aが成立している場合、コマンドBを実行し、そうでない場合はコマンドCを実行する

If [ test_A ]
then
  command_B
else
  command_C
fi
[ test_A ] && command_B || command_C

上記ではtest_Aが成立している場合、コマンドBを実行する、そうでない場合は、コマンドCを実行する。ところが、test_Aが成立しておりコマンドBの実行した後、コマンドBの実行結果が失敗だった場合はコマンドCが実行されてしまう (ココがハマリポイント)

◆逆ワンライナー 1行で書ける処理を複数行に分割する

スクリプトの可読性を高める場合に利用される。

cat /var/tmp/test.log | grep -v ^# | sed -^#/d | egrep 'foo1|foo4|foo5' | awk '{print $1,$3,$7,$11}' | sort -e | sed -e 's/foo/bar/g' > /var/tmp/tmp.log.txt
cat /var/tmp/test.log              | \
    grep -v ^#                     | \
    sed -e '^$/d'                  | \
    egrep 'foo1|foo4|foo5'         | \
    awk '{print $1,$3,$7,$11}'     | \ 
    sort -u                        | \
    sed -e 's/foo/bar/g'             \
    > /var/tmp/tmp.log.txt
◆[ と [[ の違い

シェルスクリプトでは正規表現と組み合わせて[[がパターンマッチングで利用されます。
クォートしていない場合、パターンマッチングの結果、fooはfo[ou](fooもしくはfouのどちらか)に該当するためTrueが返されます。
ただし、[[でもクォートしている場合、文字列解釈の結果は変わらないためFalseが返されます。

# 文字列はアンマッチ
(bash)# [ qiita == 'qiit[aiueo]' ]; echo $?
1

# 文字列はマッチ
(bash)# [[ qiita == qiit[aiueo] ]]; echo $?
0

# 文字列はアンマッチ
(bash)# [[ qiita == 'qiit[aiueo]' ]]; echo $?
1

その他に大きな違いとしては、文字列のパターンマッチングに利用される他、[[の場合、文字列に数式が書かれている場合は文字列と解釈せず、計算を行ってからテストが実行される。こちらも覚えておくと良い。

(bash)# char='10 + 10'
(bash)# [ ${char} -eq 20 ]; echo $?
-bash: [: 10+10 integer expression expected

(bash)# [[ ${char} -eq 20 ]]; echo $?
0
◆インクリメント・デクリメント

外部コマンド expr を用いて計算。exprのプロセスをforkするコストが高いためスクリプトの実行速度はやや遅くなるが、どのUnixシェルでも動作するため安全でポータブル性が高い書き方となる。

cnt=`expr ${cnt} + 1`
cnt=`expr ${cnt} - 1`

BASHのビルドイン機能。以下の3つの書き方だと実行結果は高速だが、他のシェルで動かない場合もあるため、BASHの脆弱性が見つかった場合などに他のシェルに切り替えて実行するなどの対策は打てなくなるというデメリットがある。

((cnt++))
let cnt++
let ++cnt
※デクリメントは--にしてください 
◆ユーザによる入力を受け付ける

ITやCASEを利用してユーザによる値の入力や選択を記述できる。入力文字列を表示させたくない場合はSオプションを利用する。

(bash)# read str
aiueo <ENTER>
(bash)# echo ${str}
aiueo
◆分岐先で何もしたくない時はコロン

if elseやcaseにいろいろ分岐を書いていたけれど、いくつかの処理をとりやめたい場合でも、後に再度利用しそうだったり、条件式やパラメータなどが非常に重要で消したくなかったりする場合、その分岐構造そのものは残しておきたかったりする場合があります。そういう場合は処理部をコメントアウトし替わりに : を入れておきます。

if [ -e /usr/dev/functions ] 
then
      . /usr/dev/functions
else
      # (停止したい処理はコメントアウト)
      :
fi
◆yesコマンド

文字列yと終了コード0を超高速で延々と返す謎のコマンドyes。

(bash)# yes
y
y
y
y

使う場面が思い浮かばないと思いきや、無限ループさせたい場合に使ったりします。

#!/bin/bash

. /usr/dev/functions
. /usr/dev/param

while [ yes ]
do
   sleep ${interval}
   chk=$(ps -ef | grep -c ${srvs})
   if [ ${chk} -lt 2 ] 
   then
      # function alert_to
      aleat_to all_engineer@foobar.co.jp
      # funtion reboot_services
      reboot_services ${srvs}
   fi
done

◆スクリプトの簡単なDaemon化

常駐化させてデーモン化(サービス化)させたい場合なんかにはnohupを使います。以下のスクリプトですと、process_chk.shはログアウト後も常駐プロセスとなり働き続けてくれます。

(bash)# nohup /bin/bash process_chk.sh &
◆スクリプトの実行結果を画面でも見たいし、同時にファイルにも保存したい
(bash)# ./chk.sh | tee -a /var/tmp/result.log
OK
OK
NG
◆精密な計算を行いたい

exprコマンドは整数しか扱えないため、小数点何桁といった計算を行うにはbcコマンドを利用する。デフォルトでは小数点以下は表示しないので、scaleで桁数を指定する。

#!/bin/bash
result=$(echo "scale=1; 101354 * 1.08" | bc)
echo ${result}
◆ランダムな数値を得る

BASHのシェル変数を利用するパターン

(bash)# echo ${RANDOM}
8732

BASHの機能を利用せず安全でポータブルな方法を検討した結果、時間の文字列でハッシュをとれば良いんじゃないかというアイデアが上手くいった

(bash)# date | md5sum | tr -dc "0123456789"
87329348
◆ランダムな文字列を得る

先ほどの手法を再利用して、ランダムな文字列を得る。これらを組み合わせれば任意のパスワードなどを生成することができる。

(bash)# date | md5sum | tr -dc "abcdefghijklmnopqrstuvwxyz"
ndrtnrvlkjr
◆typesetとdeclare

どちらも同じ変数を宣言する命令。typedefはKSHなど他のシェルでも利用が可能で古典的かつポータブル。declaireの方が新しい。

オプション内容
a配列を宣言
i数値を宣言
r読み出し専用を宣言
p宣言した変数の値を表示

他にもあれば適当に更新の予定。

Linux コマンド rsync リモートマシンとのやり取り

$
0
0

目的

  • 業務内でコマンドrsyncを使用したので忘れないようにまとめる

コマンドの例

  • 下記にrsyncを用いてローカルマシンからリモートマシンにファイルをrsyncするコマンドを記載する。

    #カレントディレクトのフォルダAの中身をリモートのフォルダBにrsyncしたいとき
    $ rsync -av-e ssh ./フォルダA/ リモートユーザ名@IPアドレス フォルダパス/フォルダB
    #カレントディレクトリのフォルダAをリモートのフォルダB直下にrsyncしたいとき
    $ rsync -av e ssh ./フォルダA リモート先ユーザ名@IPアドレス フォルダパス/フォルダ名B
    

より具体的な例(ローカルの指定フォルダの中身を同期)

  • リモートマシンはIP: XXX.XXX.XXX.XXX ユーザ名: miriwo_remoteとする。
  • 下記にローカルマシンのフォルダ~/testの中身をリモートマシンのフォルダパス~/直下のフォルダtest_rsyncにrsyncするときのコマンドを記載する。

    $ rsync -av-e ssh ~/test/ miriwo_remote@XXX.XXX.XXX.XXX ~/test_rsync
    

より具体的な例(ローカルの指定フォルダを同期)

  • リモートマシンはIP: XXX.XXX.XXX.XXX ユーザ名: miriwo_remoteとする。
  • 下記にローカルマシンのフォルダ~/testをリモートマシンのフォルダパス~/直下のフォルダtest_masterにrsyncするときのコマンドを記載する。

    $ rsync -av-e ssh ~/test miriwo_remote@XXX.XXX.XXX.XXX ~/test_master
    

引数のコマンドを retry するシェルスクリプト

gitのカスタムコマンドで自動バージョン更新&タグ付けする

$
0
0

開発しているコードをリリースするときにバージョンを更新するのは地味に面倒です。

  • semverに則って新バージョンを決める or カスタムで決める
  • package.json 等の管理情報のバージョンを書き換えて commit する
  • 最終的に git tagでバージョンのタグ付けを行う

リリースのたびにこのような定型的な作業を行うのは大変なのと、人力でやることでミスを誘発する可能性も高くなるので、これらの一連の作業を自動で行うカスタムコマンド git-releaseを作って安全・安心なタグ付けをしましょう。

できること

Usage: git release [-p] [<newversion> | major | minor | patch]

git release NEWVERSIONで、指定したバージョンのタグを付与します。
package.json or build.sbt がある場合には、ファイル内のバージョン情報を上書きして commit します。

package.json or build.sbt で semver を利用している場合、以下のような自動 semver 更新が利用可能です。

git release patchで 1.1.1 -> 1.1.2 のように patch 部分がインクリメントされます。
git release minorで 1.1.1 -> 1.2.0 のように minor 部分がインクリメントされます。
git release majorで 1.1.1 -> 2.0.0 のように major 部分がインクリメントされます。

git release -p patchのように -pをつけると、origin に自動で push されます。

動作条件

  • git-flow(gitflow-avh等)を導入済みであること
    • Mac であれば brew install git-flow-avhで OK
  • git リポジトリが git flow initされていること

リリース時のタグ付けを git flow releaseで行っているだけなので、その他のブランチ運用を行っている場合は適宜個々人で改修してください。

設置方法

以下の bash script をchmod 755した上でパスの通っているところに設置するだけです。

/usr/local/bin等に設置しても良いですが、~/bin等 $HOME 以下に閉じたほうが良さげ。

git-release
#!/bin/bashset-e

usage(){echo"Usage: git release [-p] [<newversion> | major | minor | patch]">&2
  exit 1
}

getVersion(){local file=$1case$filein
    package.json)echo$(grep'"version":'$file | cut-d'"'-f4);;
    build.sbt)echo$(grep'version\s*:='$file | cut-d'"'-f2);;esac}

setVersion(){local file=$1local version=$2case$filein
    package.json)sed-i''-e'/"version"/s/"[^"]*",$/"'$version'",/'$file;;
    build.sbt)sed-i''-e'/^version :=/s/"[^"]*"$/"'$version'"/'$file;;esac}

incSemver(){local version=$1local type=$2local semver=($(echo$version | tr'.'' '))if[${#semver[@]}-ne 3 ];then
    echo"$version is not semver.">&2
    exit
  fi

  case$typein
    major)
      semver[0]=$(expr${semver[0]} + 1)
      semver[1]=0
      semver[2]=0
      ;;
    minor)
      semver[1]=$(expr${semver[1]} + 1)
      semver[2]=0
      ;;
    patch)
      semver[2]=$(expr${semver[2]} + 1);;esacecho$(echo${semver[@]} | tr' ''.')}while getopts hp OPT
do
  case$OPTin
    p)USEPUSH=1
      ;;
    h)
      usage
      ;;*)
      usage
      ;;esacdone
shift$((OPTIND -1))if[$# -eq 0 ];then
  echo"VERSION required.">&2
  usage
fi

VERSION=$1for VERFILE in package.json build.sbt "";do[-f$VERFILE]&&break
done

case$VERSIONin
  major|minor|patch)if[-z$VERFILE];then
      echo"package.json or build.sbt not found.">&2
      exit 1
    fi
    VERSION=$(incSemver $(getVersion $VERFILE)$VERSION);;esac

git flow release start $VERSION> /dev/null

if[-n"$VERFILE"];then
  setVersion $VERFILE$VERSION
  git add $VERFILE
  git ci -m"bump version to $VERSION"> /dev/null
fi

GIT_MERGE_AUTOEDIT=no git flow release finish -m release $VERSION> /dev/null

if[-n"$USEPUSH"];then
  git push origin $VERSION
  git push origin master
  git push origin develop
fi

echo"$VERSION released!"

最後に

類似のものは他にもありますが、こういった類のものは行いたい処理が環境によって異なるのと、1スクリプト=導入がシンプル&カスタマイズ性が高いので、この程度であれば自作のほうが使い勝手が良いと思います。

github 上には git-semverのようなツールもありますので、より高度なことを行いたい場合には検討してみても良いでしょう。

bashデバッガに助けられたので、bashデバッガを紹介します。

$
0
0

概要

  • 一回しかやらないダブルチェック作業が面倒くさくてシェルスクリプトにしてみたら色々動かなくて動作を追うのが大変だった。 (無能)
  • vscodeのbashデバッガを使えば視覚的にチェックできて最高に楽が出来た

結論

vscodeのプラグイン、Bash Debugを使おう
Bash Debug

何がいいか?

  • お手軽
  • 動作を追うためにecho を仕込まなくてよい。
  • sh -xの長ったらしい表示をみなくてもよい。
  • ちょっとずつスクリプトを実行できるので、その時の断面で状態が確認できる。(これは他でもできたかも?)

実際の画面

変数のウォッチ+ブレークポイント

ブレークポイントで動作が停止。
その断面での変数の値が、ウォッチ式のペインから確認できます。

Screenshot from 2019-11-05 10-54-50.png

さらに継続(「|>」ボタンを押す)することによって、次のループの値も確認できます。
Screenshot from 2019-11-05 10-54-59.png

おわりに

むっちゃ助かったわ。
これ作った人のコーヒー代を出したい。

【Git】自分が今いるブランチを確認するコマンド

サブディレクトリも含め、ls -lの結果を数件ずつ取りたい

$
0
0

サブディレクトリも含めて再帰的にファイルを取得するのはls -Rfind -maxdepthで指定すればいけますが、ディレクトリの一部だけ(直近のタイムスタンプだけ確認したい)ということがありました。
何か良いオプション無いかと見てみたけど、無さそうだったのでxargsと組み合わせてみます。

find .-type d -maxdepth 5 | xargs -I{} sh -c'echo {} ================== ; ls -ltr {} | head -n 3 '

maxdepthやlsのオプションは大体適当です。
状況に応じて変えていきます。

結果
.==================
total 0
drwxr-xr-x  4 user  staff  128 Dec  1 22:55 test
drwxr-xr-x  2 user  staff   64 Dec  1 22:55 moge
./moge ==================
./test ==================
total 0
drwxr-xr-x  3 user  staff  96 Dec  1 22:53 test2
-rw-r--r--  1 user  staff   0 Dec  1 22:53 hoge1
./test/test2 ==================
total 0
-rw-r--r--  1 user  staff  0 Dec  1 22:53 hogeFile
./moga ==================
total 0
drwxr-xr-x  6 user  staff  192 Dec  1 22:56 aaa
./moga/aaa ==================
total 0
drwxr-xr-x  4 user  staff  128 Dec  1 22:55 bbb
-rw-r--r--  1 user  staff    0 Dec  1 22:56 file1
./moga/aaa/bbb ==================
total 0
-rw-r--r--  1 user  staff  0 Dec  1 22:55 aiueo
-rw-r--r--  1 user  staff  0 Dec  1 22:55 kakiku

直接の処理には関係ないですが、無いとパッと見た感じ見づらかったので、見出し代わりにechoを入れています。
このあたりは出力の関係上要らないという場合もありそうなので、お好みで。


Git Hooksでgit cloneだけをフックする

$
0
0

やりたいこと

言わば、post-cloneフックです。git clone実行後だけリポジトリディレクトリに対して特定の処理がしたい。その際、clone先ディレクトリに応じて処理を分岐させたい。つまり、共通テンプレートを設定しておくだけではうまくいかない。

結論

  • trap DEBUGgit cloneを叩いたことを検知して、コマンド実行前にフラグを立てる。
  • post-checkoutスクリプト内でフラグが立っているときだけ処理を行い、処理後フラグを倒す。
  • 今回の記事の本筋ではないが、注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要あり。

背景

前の記事で、gitアカウントを自動で切り替えるためにgit clone前に処理をする必要があったので、cdgitのラッパー関数を作り、エイリアスで元のcdgitを上書きして、cdをフックにgitコマンドが使用する変数を切り替えるようにしました。しかし、他のライブラリを利用してcd以外でディレクトリ移動するケースを考えるとやはり汎用性に欠け、取り急ぎとはいえ筋が良くなかったので、潔くgit cloneを直接フックすることにしました。

意外にもgit cloneだけをフックする方法に関する記事を見かけなかったので書きました。記事がないということは全く必要とされていないのか、それとも記事にならないほどもっと楽な方法があるのか、知ってる人が書いてないだけなのか。

ちなみに、GitHooksとは、というお話は以下を参照。
git hook はじめの一歩
git hookでできること

問題

Git Hooksでgit cloneをフックしたいとき、post-checkoutを使う必要がありますが、これだとgit checkoutのときにも実行されます。

また、clone先ディレクトリに応じて処理を分岐させるので、共通テンプレートを設定するだけでは実現できません。

解決策

環境

macOS

sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.5
BuildVersion:   18F132

Git

git --version
git version 2.23.0

Bash

bash --version
GNU bash, version 5.0.11(1)-release(x86_64-apple-darwin18.6.0)

git cloneの実行を検知する

Gist: .bash_hooksとして置いておきました。使い方はGist冒頭に書いてあります。このGistには、ディレクトリ移動の検知や、コマンド実行前だけでなくコマンド実行後に実行される処理もまとめて書いているのですが、今回の記事に関わるポイントは以下の3点です。

  • 各コマンドの実行前に__hooks__before_each_commandが実行されるようにする
trap'__hooks__before_each_command' DEBUG
  • __hooks__before_each_command内でBASH_COMMANDで直前のコマンド名(=叩かれたコマンド名)を取得
  • フックがアクティベートされていたら(この上記Gistの最後でアクティベートしてます)__hooks__set_git_clone_flagに直前のコマンド名を渡して実行
function __hooks__before_each_command(){local last_command=($BASH_COMMAND)if[-z"$__HOOKS__ACTIVATED"];then
        return
    fi# before each command
    __hooks__set_git_clone_flag "${last_command[@]}";if[-z"$__HOOKS__EXECUTING_LINE"];then
        return
    fi

    unset __HOOKS__EXECUTING_LINE
    # before each first command of a comamnd line}
  • 直前に叩かれたコマンド名がgit cloneだった場合、__hooks__set_git_clone_flag内で__HOOKS__ON_GIT_CLONEフラグを立てる
function __hooks__set_git_clone_flag(){if['git'="$1"]&&['clone'="$2"];then
        echo"Setting __HOOKS__ON_GIT_CLONE=true in $filename"export __HOOKS__ON_GIT_CLONE=true
    fi}

post-checkout内でフラグをチェックして処理する

Gist: post-checkoutとして置いておきました。.gitvariablesというファイルを置いてgitアカウントの情報をロードするようにして、そのファイル自体は~/.gitignoreに登録してトラックしないようにしてあります。

  • ここでフラグをチェックして、フラグが立ってなかったらexitしてます。
if[-z"$__HOOKS__ON_GIT_CLONE"];then
    exit
fi
  • 処理の最後にフラグを倒します。
unset __HOOKS__ON_GIT_CLONE

GitHooks中でユーザの入力を待ちたいときの注意点

オフトピックですが注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要があります。ちょっとハマりました。

read-rp'Type anything you like:'< /dev/tty

まとめ

コードが散在することと、暗示的にフックすることでハマったりしないように、多用には注意が必要ではありますが、cdとgit上書きより汎用的かつ拡張しやすくなった感はあります。

ちなみに、Bashのコマンドの実行前後をフックする方法の解説はChuan Ji: DEBUG trap and PROMPT_COMMAND in Bashの記事がわかりやすいです。

参考

bashで配列の中身をランダムに抽出する。

$
0
0

やりたいこと

bashで配列の中身をランダムに抽出したい!
これってトリビアのタネになりませんか?

やり方

bashでは${#ARRAY[*]}で配列のサイズが取得できる。

random_array.sh
#!/bin/bashreadonly ARRAY=("foo""bar""baz""qux""quux""corge")echo${#ARRAY[*]}# 6echo${#ARRAY[@]}# 6 @でも取得できるらしい。

次にランダムな値ですが、bashではRANDOMという謎の変数が用意されているらしい。
0〜32767までが範囲になっている。

random_array.sh
#!/bin/bashecho$RANDOM# 14067echo$RANDOM# 31125 値がランダムに取れている。echo$(($RANDOM%10))# 3     0〜9までの10パターンが取得できる。

これを組み合わせて使えば、配列からランダムな値が取得できる!

random_array.sh
#!/bin/bashreadonly ARRAY=("foo""bar""baz""qux""quux""corge")echo${ARRAY[$(($RANDOM%${#ARRAY[*]}))]}# foo

電光掲示板シェル芸 その1

$
0
0

電光掲示板シェル芸とは?

営業中.png
電光掲示板シェル芸は、こういう電光掲示板風の文字列や画像を生成するシェル芸です。
シェル芸botでの実行例↓


電光掲示板シェル芸を、コマンドを実行しながら理解していきましょう。
前提として、imagemagickとtextimgがインストールされていること。
imagemagickだけでもできなくはないですが、textimgを使うと楽です。

シンプルな電光掲示板シェル芸

「あ」一文字を、黒地に赤の電光掲示板で表示してみましょう。

$ textimg あ|convert - -trim-compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍎🌑/'🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🍎🍎🍎🍎🍎🍎🌑🍎🌑🌑🍎🌑🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑
🌑🌑🌑🌑🌑🌑🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🍎🍎🍎🍎🌑🌑🌑🌑🌑
🌑🌑🌑🌑🍎🍎🍎🍎🍎🌑🌑🍎🍎🍎🍎🌑🌑🌑
🌑🌑🌑🍎🍎🍎🍎🌑🌑🌑🌑🍎🍎🌑🍎🍎🌑🌑
🌑🌑🍎🍎🌑🌑🍎🍎🌑🌑🌑🍎🌑🌑🌑🍎🍎🌑
🌑🍎🍎🌑🌑🌑🍎🍎🌑🌑🍎🍎🌑🌑🌑🍎🍎🌑
🌑🍎🍎🌑🌑🌑🍎🌑🌑🍎🍎🌑🌑🌑🌑🍎🍎🌑
🌑🍎🌑🌑🌑🌑🍎🍎🌑🍎🍎🌑🌑🌑🌑🌑🍎🌑
🌑🍎🍎🌑🌑🌑🍎🍎🍎🍎🌑🌑🌑🌑🌑🍎🍎🌑
🌑🍎🍎🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑🌑🍎🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑

動作原理を説明していきます。
まず textimg で「あ」の画像を作ります。

$ textimg あ -o/images/0.png

0.png

画像を imagemagick(convert) に渡して、周縁の余白をトリムして、無圧縮pbmフォーマットで出力します。

$ textimg あ|convert - -trim-compress none pbm:-
P1
18 19
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 
1 1 1 0 0 0 0 0 0 1 0 1 1 0 1 0 1 1 
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 
1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 
1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 
1 1 1 0 0 0 0 1 1 1 1 0 0 1 0 0 1 1 
1 1 0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 1 
1 0 0 1 1 1 0 0 1 1 0 0 1 1 1 0 0 1 
1 0 0 1 1 1 0 1 1 0 0 1 1 1 1 0 0 1 
1 0 1 1 1 1 0 0 1 0 0 1 1 1 1 1 0 1 
1 0 0 1 1 1 0 0 0 0 1 1 1 1 1 0 0 1 
1 0 0 1 1 1 0 0 0 1 1 1 1 1 0 0 1 1 
1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 1 1 1 
1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 

1行目の P1 は無圧縮PBMであることを示しています。
2行目の 18 19 は、画像サイズが 18x19 であることを示してます。
あとは各ピクセルの色情報で、1が黒、0が白です。
色情報部分だけを抜き出して少し整形します。

$ textimg あ|convert - -trim-compress none pbm:-|tail -n +3|tr -d' '
111111111111111111
111111001111111111
111111001111111111
111000000101101011
110000000000000011
111111011111111111
111111001111111111
111111000000011111
111100000110000111
111000011110010011
110011001110111001
100111001100111001
100111011001111001
101111001001111101
100111000011111001
100111000111110011
110000001111000111
111111111110001111
111111111111111111

tailコマンドで最初の2行を除去、trコマンドでスペースを除去しました。
1と0を色表示用の文字で置換します。

$ textimg あ|convert - -trim-compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍎🌑/'🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🍎🍎🍎🍎🍎🍎🌑🍎🌑🌑🍎🌑🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑
🌑🌑🌑🌑🌑🌑🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🍎🍎🍎🍎🌑🌑🌑🌑🌑
🌑🌑🌑🌑🍎🍎🍎🍎🍎🌑🌑🍎🍎🍎🍎🌑🌑🌑
🌑🌑🌑🍎🍎🍎🍎🌑🌑🌑🌑🍎🍎🌑🍎🍎🌑🌑
🌑🌑🍎🍎🌑🌑🍎🍎🌑🌑🌑🍎🌑🌑🌑🍎🍎🌑
🌑🍎🍎🌑🌑🌑🍎🍎🌑🌑🍎🍎🌑🌑🌑🍎🍎🌑
🌑🍎🍎🌑🌑🌑🍎🌑🌑🍎🍎🌑🌑🌑🌑🍎🍎🌑
🌑🍎🌑🌑🌑🌑🍎🍎🌑🍎🍎🌑🌑🌑🌑🌑🍎🌑
🌑🍎🍎🌑🌑🌑🍎🍎🍎🍎🌑🌑🌑🌑🌑🍎🍎🌑
🌑🍎🍎🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑🌑🍎🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑

これで文字列としては完成です。
環境によっては文字幅が違って崩れているかもしれません。
twitterのシェル芸botでは文字数制限で途中で切れてしまいます。
これらの問題は、もう一度textimgに入れて画像にすることで回避できます。

$ textimg あ|convert - -trim-compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍎🌑/'|textimg -s

t.png

色の変更

上のサンプルでは背景色に🌑(新月)、文字色に🍎(りんご)を使用していますが、別の絵文字を使うことで色を変更することができます。

$ textimg あお|convert - -trim-compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍈⚪/'|textimg -s

t.png

$ textimg -z-F40💩|convert - -trim-compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/💩🌕/'|textimg -s-F10

t.png

thresholdで線をきれいに

線がガタガタしているのが気になるなら、pbm出力の前にthresholdをかけるときれいになる場合があります。

$ textimg あ|convert - -trim-threshold 50% -compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍎🌑/'|textimg -s

t.png

-threshold 50% とするとこのようになりました。
値を変えると、線の太さを変えることができます。
10%

$ textimg あ|convert - -trim-threshold 10% -compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍎🌑/'|textimg -s

t.png

70%

$ textimg あ|convert - -trim-threshold 70% -compress none pbm:-|tail -n +3|tr -d' '|sed 'y/01/🍎🌑/'|textimg -s

t.png

dtimgコマンド

dtimg は以上の操作を一発でやってくれるコマンドです。
https://github.com/jiro4989/scripts/blob/master/bin/dtimg

$ dtimg あ -b🌑 -g🍎 -R-F 20 -t 20
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑🌑🌑
🌑🌑🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑🌑
🌑🌑🌑🍎🍎🍎🍎🍎🍎🌑🌑🌑🍎🍎🍎🍎🍎🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🌑🌑🍎🍎🍎🌑🍎🍎🍎🌑🌑
🌑🌑🍎🍎🍎🌑🍎🍎🍎🌑🌑🍎🍎🍎🌑🌑🍎🍎🌑🌑
🌑🌑🍎🍎🌑🌑🍎🍎🍎🌑🍎🍎🍎🌑🌑🌑🍎🍎🍎🌑
🌑🍎🍎🍎🌑🌑🍎🍎🍎🍎🍎🍎🌑🌑🌑🌑🍎🍎🌑🌑
🌑🌑🍎🍎🌑🌑🍎🍎🍎🍎🍎🍎🌑🌑🌑🍎🍎🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🍎🍎🌑🌑🌑🍎🍎🍎🍎🌑🌑
🌑🌑🍎🍎🍎🍎🍎🍎🍎🌑🌑🌑🍎🍎🍎🍎🍎🌑🌑🌑
🌑🌑🌑🍎🍎🍎🍎🌑🌑🌑🌑🍎🍎🍎🍎🍎🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🍎🍎🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑
🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑🌑

シェル芸botにも入っているので活用しましょう。
次回、駅の電光掲示板再現に続きます。

日本の祝日を判定するシェルスクリプトを書いた件

$
0
0

業務アプリケーション寄りのプログラマであれば一度は書いたであろう営業日判定のプログラム。
今回はBashで書いてみた。わりと汎用的に使えると思うので共有する。

開発動機

休日は実行しなくても良い夜間バッチがあった(休日に実行しても影響は無い)。
土日は実行しないようにcronでスケジュールしていたが、リソースを節約したいので、できれば祝日も実行させたくない。

日本の祝日情報をどこから貰うか

Google Calendar APIを利用しようと思ったが、調べてみると、内閣府ホームページで祝日情報を公開しているようなので、それを使う。

取得方法
curl -sL https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv | iconv -f cp932

URLの中に shukujitsu(ヘボン式)とsyukujitsu(訓令式)が混在しているが気にしないことにする。
将来URLが変わるかもしれないので、Lオプションでリダイレクトを有効にしておく。
iconvで指定する文字コードは sjis より cp932 の方が無難である。

実行結果
国民の祝日・休日月日,国民の祝日・休日名称
1955/1/1,元日
1955/1/15,成人の日
1955/3/21,春分の日
1955/4/29,天皇誕生日
     :
2020/7/24,スポーツの日
2020/8/10,山の日
2020/9/21,敬老の日
2020/9/22,秋分の日
2020/11/3,文化の日
2020/11/23,勤労感謝の日

日付の桁数も揃っていないが気にしないことにする。(2020/1/12020/01/01にならないのかという意味)
毎年2月になると官報に翌年の暦要項が掲載されるので、2021年のデータは2020年2月頃に追加されるのだろうか?
なお、このCSVファイルは、そのままExcelのWORKDAY関数に使えるので覚えておくと良い。

ちなみに・・・「スポーツの日」とは「体育の日」の名称が変わったものだが、2020年だけはオリンピック開会式の7月24日に移動するらしい。

シェルスクリプト

祝日情報を、内閣府のサーバに毎回問い合わせるのも忍びないので、ローカルにキャッシュする。
キャッシュが古くなれば更新される。

確実に祝日と言える場合のみ、終了ステータスに 0 がセットされる。

check_holiday.sh
#!/bin/bash################################################################   本邦休日判定スクリプト#   @author  MindWood#   @param   チェック日付を yyyymmdd で指定。省略すると今日を仮定#   @return  0       ... 確実に祝日#            1       ... おそらく平日#            上記以外 ... エラー#   @usage   check_holiday.sh || ”平日に必ず実行させるジョブ”################################################################ 引数チェックif[$# -eq 0 ];then
    CHECK_DATE=$(date +%s)elif[$# -eq 1 ];then
    CHECK_DATE=$(date +%s --date$1)||exit 254
else
    echo'Invalid argument'exit 255
fi

CACHE_PATH=/tmp                          # 内閣府提供の祝日ファイルをキャッシュするディレクトリHOLIDAY_FILE=$CACHE_PATH/holiday.csv     # 祝日登録ファイル名LIMIT=$(date +%s --date'3 months ago')# 3ヶ月以上前は古い祝日登録ファイルとする# 祝日登録ファイルが無い、もしくは祝日登録ファイルの更新日が古くなった場合、再取得するif[!-f$HOLIDAY_FILE]||[$LIMIT-gt$(date +%s -r$HOLIDAY_FILE)];then
    curl -sL https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv | iconv -f cp932 >$HOLIDAY_FILE||exit 250
fi# 祝日として登録されていれば 0 を返却して終了grep ^$(date-d @$CHECK_DATE +%Y/%-m/%-d)$HOLIDAY_FILE> /dev/null 2>&1 &&exit 0

# 土日なら 0 を返却して終了DAYOFWEEK=$(date-d @$CHECK_DATE +%u)[$DAYOFWEEK-eq 6 ]||[$DAYOFWEEK-eq 7 ]&&exit 0

# 年末年始(12月31日~1月3日)なら 0 を返却して終了MMDD=$(date-d @$CHECK_DATE +%m%d)[$MMDD-eq 1231 ]||[$MMDD-le 0103 ]&&exit 0

# 上記いずれでもなければ平日として終了exit 1

年末年始を休みとするかは会社によって異なるので適宜修正して欲しい。
2019年12月26日から2020年1月16日までの平日を表示させた結果が次の通りだ。

for((DATE=20191226;$DATE< 20200116;DATE=$(date-d"$DATE 1 day" +%Y%m%d)));do
    ./check_holiday.sh $DATE||echo"$DATEは恐らく平日です"done
20191226 は恐らく平日です
20191227 は恐らく平日です
20191230 は恐らく平日です
20200106 は恐らく平日です
20200107 は恐らく平日です
20200108 は恐らく平日です
20200109 は恐らく平日です
20200110 は恐らく平日です
20200114 は恐らく平日です
20200115 は恐らく平日です

平日なのに祝日と誤判定することは無いが、祝日なのに平日と誤判定することは有り得るため、「恐らく」と自信なげに書いている。
例えば、内閣府のサーバに接続できなければエラー終了となって ||の後ろが実行されてしまうが、平日に必ず実行させるジョブがある場合は、安全で都合が良い。

cron定義例

業務開始前に実行させるジョブが、次のように定義されていたとする。

crontab
00 7 * * * python3 /usr/local/bin/app.py

この場合、次のように追記するだけで、休日は実行しないようにできる。

crontab
00 7 * * * /usr/local/bin/check_holiday.sh || python3 /usr/local/bin/app.py

さいごに

日本の行政機関が提供する Web APIに祝日情報も加えていただきたいと思う。

【Git】SourceTree嫌いがよく使うgitコマンド

$
0
0

【Git】SourceTree嫌いがよく使うgitコマンド

この記事は「ちゅらっぷす Advent Calendar 2019」の3日目です。
https://qiita.com/advent-calendar/2019/churapps

SourceTree嫌い

えー唐突ですが私、SourceTreeが嫌いでして。
嫌いな理由はいくつかありますが、周りと同調できないダメな人間です。
そもそもGUIに苦手意識があって予告なくUIの変更などがあるともうキレそう。というかキレてます(苦笑)

基本的なコマンドばかりですが、SourceTreeを利用し続けていたら知らなかったかもしれないオプションも紹介しますので是非お付き合いください。

clone

## base branchを参照、フォルダ名はリポジトリ名依存$ git clone リポジトリ名

## ブランチを指定、指定のフォルダ名でcloneする$ git clone リポジトリ名 -bブランチ名(省略時はbase_branch)フォルダ名

ブランチ、フォルダ名指定オプション付きでgit cloneします。

diff

## ファイル差分$ git diff

「どのファイルにどんな変更加えたっけ?」って時に使ってました。
git addする直前とかですかね。
現在ではVSCodeのプラグインで差分や履歴が一目瞭然なのであまり使う機会はなくなりました。

status

## 変更点の確認$ git status

変更、削除、新規ファイルなのか見る際に利用します。

remote

## リポジトリのプッシュ先などを確認$ git remote -v

複数案件を並行で進めてる時が多いので、「これ本当に〇〇案件のリポジトリでPush先もおかしくないよな?」と不安になった際につかいます。

branch

## 現在のブランチ$ git branch

## remote含む全てのブランチ$ git branch -a

checkout後に確認のために利用することが多いですかね

checkout

## ブランチの切り替え$ git checkout ブランチ名

## ブランチを作成しつつ切り替え$ git checkout -bブランチ名

Checkoutの際に-bオプションで、新規でローカルブランチを作成しつつCheckoutできます。

commit

## 通常のコミット(add済み)$ git commit -m"動かないけどマージしたい"## addも一緒にするcommit$ git commit -a-m"なぜか動くからリリースしようぜ"

git addが面倒なので僕はだいたい-aオプション付きでCommitします。

push

## 通常$ git push

## 新規ローカルブランチ$ git push --set-upstream origin ブランチ名

新規ローカルブランチでgit pushすると--set-upstreamオプションつけろなどと怒られますね。
ちなみに後者の方でpushすると、プルリク用のURLが発行されるのでリンクにアクセスするだけでプルリク作成画面へ飛べます。便利。

GUIを使わない理由

多分ですけど、「コマンドで仕事してる俺カッケェェェ」って思ってるんだと思います笑
でも正直、コマンドベースで動きをある程度覚えてからGUIツールを利用した方が理解度高まるんじゃないかなと思ってます。

上記で紹介したコマンドはどれも基本的なものですが、オプションがややこしいので何か気になる点などございましたらご指摘ください。

Viewing all 2885 articles
Browse latest View live