bashでゼロパディングする手法2つ

連番を頭につけたファイル群を取り扱う時、並び順を考えるとゼロパディングしておきたくなる。

例えばソートしたときに、

1
10
11
~略~
2
20
21
~略~

みたいな事されると、理想の並び順ではない。理想は勿論、

1
2
~略~
10
11
~略~

というような状況。最大桁数をカバー出来る桁数でゼロパディングしておけばこういう事故も起こらないはず…

という訳でゼロパディングする手法2つ。

話を簡単にするために8~12の5つを作ってみる。

 

その1

$ for ((i=8; i<13; i++));do
str=00$i;
str=${str:(-3)};
echo $str;
done
008
009
010
011
012

2行目では008~0012までを単純に作る。後にこれを${str:(-3)}で、末尾から3文字だけ取り出す事によってゼロパディングをする。

 

その2

$ for ((i=8; i<13; i++));do
str=`printf "%03d" ${i}`;
echo $str;
done
008
009
010
011
012

C言語のprintfでゼロパディングするときと同じ感覚。

 

 

その1の方がすっきりと言えばすっきりだけど、2行にまたがるのが微妙な気持ち(1行にまとめる方法は勿論ありそうだけど)

その2なら新たな記法を覚えなくて済むし簡単に1行にまとまるから良い…けれどbashで、という気持ちのくせになぁ、みたいな…そもそもforの中身がCスタイルなんですけど。

マルチプロセスとマルチスレッドの区別

C++でマルチスレッドにプログラムを書いた事はあったので、並列動作の基本はOKだぜ!と思っていた。

ネットで拾ったコードを特に意識せずいじっていたらどうしてもうまくいかなくて、途中で気づいた。これマルチスレッドじゃなくてマルチプロセスだ。

 

というわけで区別しましょう。

 

まずはマルチプロセス

まずはマルチプロセス。fork()関数で子プロセスを作るんだけど、これ、状態を全部コピーした上で子プロセスが動く。例えば以下のようなコード。

#include <iostream>
#include <unistd.h>
using namespace std;
int i;

int main(){
 i=0;
 cout<<"process fork test"<<endl;
 pid_t pid;
 if((pid = fork()) == 0){
 cout<<"this is child"<<endl;
 sleep(10);
 cout<<"child dead"<<endl;
 cout<<"i:"<<i<<endl;
 }else{
 sleep(5);
 cout<<"this is parent"<<endl;
 i++;
 }
 return 0;
}

fork()は、プロセスをフォークしてくれる関数だが、この戻り値をうまく使う。戻り値は、当人が(?)子プロセスの場合には0、親プロセスの場合には子プロセスのPIDを与える。

これを実行すると、

[tea@teaarch]$ ./a.out
process fork test
this is child
this is parent
[tea@teaarch]$ child dead
i:0

のようになる。

実行の順番は、

1. i=0に初期化
2. フォーク
3. 5秒経過
4. 親プロセスが自己紹介してi++;して終了
5. 5秒経過
6. 子プロセスが自己紹介してiの中身を出力して終了

単純に考えると、6.では1を出力しそうなもんだが、別プロセスであり、(マルチスレッドの時のように)共有していないので、インクリメントされる前のiを子プロセスは持っており、インクリメントされないまま出力を迎える。

それから、親プロセスが終了した時点で、制御が帰ってくるみたい。(親プロセスが終了しても子プロセスは生き残る)

 

それからマルチスレッド

こちらはプロセスの中で処理スレッドを増やすものなので、メモリ空間は共有。例えば以下みたいなコード。

#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;

int i;

void sub_thread(){
 cout<<"[s]this is sub thread"<<endl;
 sleep(3);
 cout<<"[s]i:"<<i<<endl;
 sleep(3);
 cout<<"[s]i:"<<i<<endl;
}

int main(){
 i=0;
 cout<<"multi thread test"<<endl;
 thread t(sub_thread);
 t.detach();
 cout<<"[m]sleep..."<<endl;
 sleep(5);
 cout<<"[m]increment"<<endl;
 i++;
 cout<<"[m]sleep..."<<endl;
 sleep(5);
 return 0;
}

コンパイル時には忘れずライブラリくっつけておく。g++ ./thread.cpp -lpthreadみたいな感じ。実行すると、

[tea@teaarch]$ ./a.out
multi thread test
[m]sleep…
[s]this is sub thread
[s]i:0
[m]increment
[m]sleep…
[s]i:1

動作は、

1. i=0に初期化
2. サブのスレッドt作成(中身はsubthread()が動作する)
3. detach()でスレッドtの管理を放棄(スレッドtが終了するまで待つのは.join())
4. 3秒経過
5. サブスレッドがiの中身表示(0)
6. 2秒経過
7. メインスレッドがiをインクリメント
8. 1秒経過
9. サブスレッドがiの中身表示(1)
10. 4秒経過
11. メインスレッドが最後のスリープをして終了

これ、最後のスリープをしないと、メインスレッドが5秒経過後インクリメントした直後にプロセスが終了する。サブスレッドは6秒たたないと2回めの表示をしないので、つまり、1回目の表示しか実行されない。

 

まとめ

マルチプロセス:fork()とかでうまくやる。メモリの状況はコピーされ、共有されない。親プロセスが終了しても子プロセスは生き残る。親子プロセスで何かやりとりしたければプロセス間通信の手を考える必要がある?(名前付きパイプとか?)

マルチスレッド:threadでうまくやる。メモリの状況は共有される。メインスレッドが終了すると派生した方のスレッドも終了する。スレッド間のやり取り自体は難しくないが、タイミングの制御やロックなど考えないと死ぬ(よくあるマルチスレッドの注意点)

Ubuntu14.04から16.04に上げたら起動しなくなった

途中で変な状況になっているのを、考えるのが面倒になって強引に進めただけなんですけど。起動しなくなりました。

Kernel Panic – not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

 

rootのファイルシステムが見えませんぜと言われている気がしたので、grubの設定が合わなくなったのかなと思って、grub-installとかやればいいのかな?と思ったはいいものの、マザーボードが古いのかなんなのか(2008年頃のものなので10年近く前か)USBキーボードをgrubメニューの間(後からわかったけど、カーネルが起きてくるまで?)認識してくれない。過去のカーネルで起動して作業しようと思ったけど、これじゃダメだ。Live環境が必要…

手元にあったUSBメモリに入っていたのはArchだったんだけど、強引にいけばイケるんじゃねっと思って強引に作業したら、grub shellに入るようになりました(状況悪化)

 

しょうがないので部屋のどこかにあるPS/2接続のキーボードをどうにか見つけて引っ張り出してきて、接続。grub shellから過去バージョンのカーネルで起動して、それからboot – Kernel Panic – not syncing: VFS: Unable to mount root fs on unknown-block(0,0) – Ask Ubuntuで「update-initramfsしろよな、それからupdate-grub2しろよな!」って書いてあったのでそれを実行。

 

grub> set pager=1
grub> ls
(hd0) (hd0,msdos1) (hd0,msdos5)
grub> set root=(hd0,1)
grub> linux /boot/vmlinux-3.13.0-92-generic root=/dev/sda1
grub> initrd /boot/initrd.img-3.13.0-92-generic
grub> boot

ここまでで新しいカーネル4.4.0-47じゃなくて、正常に動作していた過去のカーネル3.13.0-92で起動できる。

頭のset pager=1で常にmoreみたいな状況にする。ちなみに、lsの結果のmsdos5がswap、msdos1がrootパーティション(/bootもこの中)。

最近/bootを分ける構成ばかりだったので、つまり/dev/sda1が/、/dev/sda5が/bootだと勘違いしていた。容量もそれぞれ600GBくらいと4GBくらい。4番目の操作で間違った入力、つまりroot=/dev/sda5していると、当然起動には失敗する。非常に簡単な対話式の操作画面に入った。これに気付くまで割りと困ってた。

なにはともあれ正しく過去バージョンのカーネルで起動したらログインして、terminalから以下のような感じ

$ sudo update-initramfs -u -k 4.4.0-47-generic
$ sudo update-grub2

カーネルバージョンが不明の場合には、dpkg –list | grep linux-image-とかで調べましょう。

あとは再起動してやれば完了。

Cinnamonクラッシュ対応しようとしたらArchLinuxごと起動しなくなった

発端はCinnamonがクラッシュしたところ。Ctrl+Alt+F2でtty2に移動して$ cinnamonすると、libreadline.so.6が無いよ、と。

取り敢えず再起動しても状況は変わらない。そもそもCinnamonが起動しない。

インストールされているのはreadlineの7らへんだったので、cinnamonがまだ対応出来てないのかなと思って、じゃあreadlineを6系統にダウングレードすれば良いのでは、という強引な発想をした。

pacman -Rdd readlineしてローカルのパッケージキャッシュからインストール…

したら起動しなくなりました。てへぺろ。

 

GUIが、とかじゃなくて、そもそもログイン画面まで届かない(CUIなログイン画面にも届かない)。

しょうがないので、元に戻すことに。

 

Live環境からarch-chrootして、そこでpacman -S readlineしちゃえば良いのでは?

と思っていたのもつかの間、arch-chrootがlibreadline.so.7を要求していて出来ない。ただのchrootだとpacmanとか出来ない(/etc/mtabが無いよ、とか言ってくる)ので、arch-chrootがいいんだけど。

arch-chrootはシェルスクリプトなんですよ、というのをどこかで聞いた記憶があったので、じゃあそれの動作を一つ一つやればいいんちゃう?と思ったあたりで、archwikiを見に行くと、ズバリな記述を見つけたので、それを参考にchroot。chrootを使う – Change Root – ArchWiki

その後pacman -S readlineしたけれど、GPGME error: invalid crypto engineと怒られて出来なかった。–forceつけても変わらないので、もうめんどくさいので/etc/pacman.confでSigLevel=Neverにしてreadlineだけアップグレード、pacman.confもとに戻して、chroot抜けて再起動!

# mount /dev/sdb2 /mnt
# mount /dev/sdb1 /mnt/boot
# cd /mnt
# mount -t proc proc proc/
# mount --rbind /sys sys/
# mount --rbind /dev dev/
# mount --rbind /run run/
# cp /etc/resolv.conf etc/resolv.conf
# chroot /mnt
# vi /etc/pacman.conf
# pacman -S readline
# vi /etc/pacman.conf

そしたら起動するようになった

 

この起動はゴールではなく、最初の状況に戻ってきたってことだ。

 

変わらずCinnamonはlibreadline.so.6を要求してクラッシュし続けるので、tty2に移動して、libreadline.soで検索、ダメ元だけど、そこにlibreadline.so.6という名前でシンボリックリンクを張って再起動してみた。

ら、Cinnamon普通に起動しました。これでやっと普通に使える。

GPTなディスクをMBRにする

MBRからGPTはなんだか気づいたらなってることもおおいし(?)別にわりとどうでもいいんだけど、その逆は気づいたらなってるってことも無いのでメモ。

一発メモっておけば記憶にも残るでしょうということで。

parted /dev/sdb -s mklabel msdos

単純な感じだった。

参考:GPTをmbrに変換 – Qiita

ddするときに進捗状況をを確認する

pkill使えとかいうのが多いんだけど、killとかいう言葉が怖いし、pv使えばかんたんにいけるっぽいというお話。

パイプを使ってcatをpvに通してそれをddに通す。

ddでもpvコマンドを使えば進捗を簡単に見れる

ddでもpvコマンドを使えば進捗を簡単に見れる

 

cat→pv→ddという流れさえ覚えておけば簡単に使えるね。

ちなみにpvのインストールは普通にpacmanで良い

$ sudo pacman -S pv
$ cat ./img | pv |sudo dd of=/dev/mmcblk1

みたいな感じだね

既存のArchLinuxからUSBメモリにArchLinuxをインストールする

USBメモリから起動するArchLinux、という環境を作る。通常のHDDやSSDにインストールしてそこからブートする感覚で、ストレージデバイスとしてUSBメモリを使う。

普通にArchLinuxをインストールする時、isoイメージから起動ディスクを作成して、そこからブートしてインストールするが、今回は既存の環境から必要なデータをコピーするようにした。

ちなみにUEFI+GPT環境。(今回のターゲットはSurface Pro 4だったりする。Secure bootは気にしない方向で…)

 

なにはともあれlsblkしてUSBメモリが/dev/sdbである事を確認。

ついでにarch-install-scriptsをインストールしておく。

 

最初の山(最初っていうか山はこれしかない気もする)、パーティショニング。

購入したばかりのUSBメモリ(16GB)、MBRだったのでGPTに変換して、パーティションを切る。作業はともかく、こんな状態であれば良さそう。

# gdisk /dev/sdb
GPT fdisk (gdisk) version 1.0.1

Partition table scan:
MBR: protective
BSD: not present
APM: not present
GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): p
Disk /dev/sdb: 30497664 sectors, 14.5 GiB
〜略〜

Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI System
2 1050624 30497630 14.0 GiB 8300 Linux filesystem

Command (? for help):

要は、

・/dev/sdb1がEFI System(/bootになる)
・/dev/sdb2がLinux filesystem(/になる)

という事。

ターゲットになる環境はRAMが8GBあるし、メインストレージがUSBメモリになるのでswapは無しの方向で。

 

パーティショニングが完了したらファイルシステムのフォーマットとマウント。

普通にやれば良いが、

ジャーナルを無効にしたext4が良い

あるので、そうする。

# mkfs.vfat -F32 /dev/sdb1
# mkfs.ext4 -O "^has_journal" /dev/sdb2
# mount /dev/sdb2 /mnt
# mkdir /mnt/boot
# mount /dev/sdb1 /mnt/boot

 

次にベースシステムのインストール。

主要パッケージについては既存環境からコピーしてくるので、そのための準備をする。

手軽に取り扱える簡単なhttpdであるところのdarkhttpdを使う。

$ sudo pacman -S darkhttpd
$ sudo darkhttpd /var/cache/pacman/pkg

そして/etc/pacman.d/mirrorlistにServer http://localhost/とでも先頭に書いておく。

この状態でpacstrapすれば、/var/cache/pacman/pkgに存在するパッケージはコピーされ、そうでないものは404が返され、(次のリポジトリを探す事になるので)自動的にダウンロードされる。

# pacstrap /mnt base base-devel

 

fstabを作成する。

# genfstab -U -p /mnt >> /mnt/etc/fstab

/mnt/etc/fstabに書き込む許可が無いよ、と言われたので、genfstabの出力をクリップボードに放り込んで、# gedite /mnt/etc/fstabして強引に放り込んでおいた。

このfstabに、/dev/sdb1等ではなく、きちんとパーティションのUUIDで指定されているかどうか、必ず確認しておく。

 

chrootしてロケール設定その他諸々てきとうに…

# arch-chroot /mnt /bin/bash
# vi /etc/locale.gen
# localge-gen
# echo LANG=en_US.UTF-8 > /etc/locale.conf
# export LANG=en_US.UTF-8
# ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
# echo HOSTNAME > /etc/hostname
# passwd

 

最後にブートローダーのセットアップ。

grubを使うのだけど、grub-install時に–removableオプションを付けておけと書いてあったのでそうしてある。

# pacman -S grub dosfstools efibootmgr
# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub --removable --recheck --debug
# grub-mkconfig -o /boot/grub/grub.cfg

/boot/efi/boot/bootx64.efiが存在しているか確認しておく。無ければ以下の感じで。

# mkdir /boot/efi/boot
# cp /boot/efi/arch_grub/grubx64.efi /boot/efi/boot/bootx64.efi

 

初期RAMディスクの作成をする前にゴニョゴニョしておけとUSB キーに Arch Linux をインストール – ArchWikiに書いてあったのでしておく。タイミングはよくわかってない(最後にやった)。

# vi /etc/mkinitcpio.conf
# mkinitcpio -p linux

/etc/mkinitcpio.confの中でHOOKSのudev直後にblockと追加しておく。

 

以上で基本のインストールは完了。その後適宜環境を構築していこう。(今回はここでおしまい)

指紋認証使わない事にした

指紋認証を使うで、ノートPCで指紋認証を使うようにしてみたはいいけど、指紋認証ってめんどい。

指を指紋リーダに持っていく→優しくかつ力強く撫でる→たまに失敗する

ゴミか…。

という訳で使わない事にした。

/etc/pam.d以下の編集を元に戻して、sudo pacman -Rs fingerprint-gui でさようなら。

再起動して無事処理完了という事ですね。

 

指紋認証って、なんかかったるいんだよね。i{Phone, Pad}の指紋認証も、ホームボタン押して、ひと呼吸おいてからロック解除される。このひと呼吸がかったるい。普通にロック解除操作した方が速いのだ。

ノートPCで使ってた状態もそんなような感じでかったるかったので、結局キーボードでちゃんと操作した方が速いと。

生体を使った認証技術、他にも虹彩とか声紋とか諸々あるみたいだけど、どれもキーボード操作には速度的には負けるんじゃないかなとか思うんだよね(使ったことないけど)

生体認証技術の目的はきっとそこじゃなくて、個人に固有とか、覚える必要無しとか、そういうとこなんだろうけどね。

 

おしまい。

ArchLinuxのリポジトリミラーをローカルに立てる

Ubuntuでならやったことあるんだけど、そういえばArchLinuxではやったことないな~とふと思ったのでやってみた。

基本はLocal Mirror – ArchWikiの通りにしたんだけど、再三「マジ?やめとけよ、な?無駄だろ?pacman cacheとかにしなよ?な?」と言われまくったので、sync.shの内容は省略する。

ちなみにいつもどおり、ArchLinuxと見せかけてAntergosでの実験である()

 

自分のところにリポジトリミラーを作っておけば、ローカルでの通信になるので非常に高速に処理が可能になる。リモートのミラーサーバからなら、大抵の場合ボトルネックになるのはインターネット回線(というよりミラーサーバ側の通信帯域制限)だけど、ローカルからだとストレージの速度になるんかな。LANはギガビットイーサだし。

 

/dev/sdc1を/var/mirrorとしてmount、その中にLinuxというディレクトリを作成し、その中にミラーリングするようにした。実際ArchLinuxのミラーサーバには不満全然なくていいんだけど、Antergosのほうにこそ不満があるため、こちらもミラーリングする。

ftp.tsukuba.wide.ad.jpから、core, extra, community, iso, multilib、それからmirror.us.leaseweb.netからantergosをrsyncでもらってくる。rsyncするサーバは新しいミラーのガイドライン|antergosウィキで見つけたものを使った。

/var/mirror/Linux/$repoではなく、/var/mirror/Linux/{archlinux, antergos}/$repoという構成にしておかないと、rsyncでmultilibが削除されてしまうので横着せず、それぞれ処理を行う。

最終的なフォルダツリーはこんな感じ。

$ cd /var/mirror/Linux
$ tree -d
.
├── antergos
│ ├── antergos
│ │ ├── i686
│ │ └── x86_64
│ ├── antergos-staging
│ │ ├── i686
│ │ └── x86_64
│ ├── iso
│ │ ├── release
│ │ └── testing
│ │   └── uefi
│ │      └── antergos-staging
│ │         ├── i686
│ │         └── x86_64
│ └── project
│    └── trace
└── archlinux
   ├── community
   │ └── os
   │   └── x86_64
   │     └── local
   ├── core
   │ └── os
   │   ├── i686
   │   └── x86_64
   ├── extra
   │ └── os
   │   ├── i686
   │   └── x86_64
   ├── iso
   │ ├── 2016.06.01
   │ │ └── arch
   │ │       ├── boot
   │ │       │ └── x86_64
   │ │       └── x86_64
   │ ├── 2016.07.01
   │ │ └── arch
   │ │   ├── boot
   │ │   │ └── x86_64
   │ │   └── x86_64
   │ ├── 2016.08.01
   │ │ └── arch
   │ │    ├── boot
   │ │    │ └── x86_64
   │ │    └── x86_64
   │ ├── archboot
   │ │ ├── 2016.05
   │ │ │ └── boot
   │ │ ├── history
   │ │ └── latest
   │ │    └── boot
   │ └── latest
   │    └── arch
   │       ├── boot
   │       │ └── x86_64
   │       └── x86_64
   ├── multilib
   │ └── os
   │ └── x86_64
   └── project
      └── trace
61 directories

後は好きなHTTPサーバを入れて、

・DocumentRootを/var/mirror/Linuxにする

・インデックス表示されるようにするといいかも

・ポート番号変えても良い

mirrorlistはそれぞれこんな感じで。

# ArchLinux
Server = http://localhost:1123/archlinux/$repo/os/$arch

# Antergos
Server = http://localhost:1123/antergos/$repo/$arch

ISOが手元にあるので、毎度インストールメディア作ろうと思って拾ってくる待ち時間を過ごす必要が無くなったし、新規にインストールするのも時間かからずいけるな!って気持ち。

新規にインストールする機会がそう沢山あるのかどうかは置いといて感がすごい。

ArchLinuxのmirrorlistを自動で選択してもらう

Reflectorを使うだけ。

$ sudo pacman -S reflector
$ sudo reflector --verbose --country Japan -l 200 -p http --sort rate --save /etc/pacman.d/mirrorlist

日本にいないとか実は国外サーバのほうが早い地域なんだよねとかの事情があれば(?)country指定はやめよう。

-l 200で200個調べる感じになるけど、countryでJapan指定したあたりでもうftp.jaist.ac.jpとftp.tsukuba.wide.ad.jpしか残ってない状態なんだなぁ…