覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Windows 10 - All Users のスタートアップフォルダを開く

Windows 7 だとAll Users のスタートアップフォルダの場所にExplorerなんかでも簡単に行きつけるんですが、Windows 10 だとすぐ迷子になってしまいます(行きつけません)。

というわけで、今後迷子にならないためのメモです。

場所は通常であれば以下パスになります

C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp


ふー、これさえメモっておけば一安心。

と、これだけでも良かったんですが、なんとExplorerで以下を入力してEnter押すだけでも行きつけるようです。全然知らなかった。
(それも、Windows 7でも利用可能!)

shell:Common Startup


実際やってみた感じは以下のようになりました

Explorer開いて、shell:Common Startupと入力してEnterキー押すと f:id:nini_y:20181106232548p:plain

ばっちり All Users のフォルダが開かれました。 f:id:nini_y:20181106232934p:plain


楽だ。これからは、これでやっていこう

Go言語 - 置換文字列を一部置換しなかったらどうなるの?

fmt.Sprintf などでフォーマット文字列を置換する場合に、置換対象のパラメータが複数あることもあります。
その際に、置換すべき値の数が合わないとどうなってしまうのかな?と思って一応試してみました。

http://127.0.0.1:%d/%s というフォーマット用の文字列があって、ポート番号とコンテキストパスにあたる部分の値を与えて、URLを完成させたい
という例で試してみます


正しく置換する

フォーマット上の変数の数と置換する値を合わせます


■サンプルコード

import "fmt"

func main() {
    fmtString := "http://127.0.0.1:%d/%s"
    result0 := fmt.Sprintf(fmtString, 8080, "app")
    fmt.Printf("0-A >> Format replace value. [%s] -> [%s]\n", fmtString, result0)
}


■実行結果

0-A >> Format replace value. [http://127.0.0.1:%d/%s] -> [http://127.0.0.1:8080/app]

ほしかった文字列が得られています。


パラメータに対して値が不足している

フォーマット上の変数の数に対して置換する値の数が少なかった場合にどうなるか試してみます。


■サンプルコード

import "fmt"

func main() {
    fmtString := "http://127.0.0.1:%d/%s"
    result1 := fmt.Sprintf(fmtString, 8080)
    fmt.Printf("1-A >> Format replace value. [%s] -> [%s]\n", fmtString, result1)
}


■実行結果

1-A >> Format replace value. [http://127.0.0.1:%d/%s] -> [http://127.0.0.1:8080/%!s(MISSING)]

置換すべき値が無かった場合、置換用の記述(今回の場合 %s)のまま残ったりしないかななんて期待しましたが、そんなアホなことはありませんでした。
不足してた部分が、 %!s(MISSING) という文字列になってしまっています。


パラメータに対して値が多い

フォーマット上の変数の数に対して置換する値の数が多かった場合にどうなるか試してみます。


■サンプルコード

import "fmt"

func main() {
    fmtString := "http://127.0.0.1:%d/%s"
    result2 := fmt.Sprintf(fmtString, 8080, "app", "/dummy")
    fmt.Printf("2-A >> Format replace value. [%s] -> [%s]\n", fmtString, result2)
}


■実行結果

2-A >> Format replace value. [http://127.0.0.1:%d/%s] -> [http://127.0.0.1:8080/app%!(EXTRA string=/dummy)]

多くて余ってしまう値(置換部分に当てはまらない値)のことは無視してくれるかなとか期待しましたが、そんなアホなことはありませんでした。
余計な値の分、 %!(EXTRA string=/dummy) という文字列が付与された結果になってしまっています。


まとめ

置換パラメータの数と置換する値の数が合わなかったりすると結果がおかしなことになるので、ちゃんと数を合わせましょう。

実際のところ、公式のドキュメントにもっと色々詳しく書いてあります。

bashのProcess Substitutionを利用したdiff

bashに Process Substitution(プロセス置換) という機能があるのを全然知りませんでした。

詳しい説明は別の方の記事を参照ください。。。

たとえば、この機能をdiffコマンドと組み合わせて使ったりすると効果的なようです。

diffは、入力対象としてファイルのみを対象としているため、通常のパイプで処理済みテキストを渡すことがでないのですが、
Process Substitutionを利用して、コマンドの処理結果同士を diff で比較することが可能です。

呼び出し方法は以下のようになります。

diff <(なんかのコマンド1) <(なんかのコマンド2)


例えば以下の2つのファイルがあったとして、重複行は1行にまとめた上で、データに差分があるかないかをチェックしたいとします。
33333 という行が sample1.txt にだけ存在するという差分があります)


■sample1.txt

00000
11111
11111
12345
12346
11111
22222
33333
44444
55555
66666
77777
88888
99999
00000
12345
12345
11111
99999
00000
11111
22222
33333
44444
55555
66666
77777
88888
99999
00000
12345
12345
12346
11111
23456


■sample2.txt

00000
11111
11111
12345
11111
22222
44444
55555
66666
77777
88888
99999
00000
12345
12345
11111
99999
00000
11111
22222
44444
77777
88888
99999
00000
12345
12346
11111
23456


通常であれば以下のような処理手順になると思います。

$sort sample1.txt | uniq > sample1.txt.tmp
$sort sample2.txt | uniq > sample2.txt.tmp
$diff sample1.txt.tmp sample2.txt.tmp
7d6
< 33333

一度、sortuniqコマンドを利用して中間ファイルを作り出して、その中間ファイル同士をdiffで比較します。


これを、Process Substitutionを利用してやると以下のようになります。

$diff <(sort sample1.txt | uniq) <(sort sample2.txt | uniq)
7d6
< 33333

なんということでしょう、中間ファイルが不要となっております!。


今後、Process Substitution が利用できる場面では、どんどん使っていきたいです。

例外のスタックトレースが出力されなくなる

Javaアプリケーションを実行していると、ずっと出力できていた例外(Exception)のスタックトレース(StackTrace)が出力されなくなる場合があります。
気づいてみたら、あれ?ログに例外のスタックトレースが出ていない! というような状況に出くわすことが稀にあったりします。

同じスタックトレースを何度も何度も出力していると、HotSpot VMの最適化が適用されて本現象が発生する様です。

HotSpot VMの実行時最適化の一環で、繰り返し出力される一部の組み込み例外では、
同じインスタンスを使い回すことでインスタンス生成コストを抑える処理が行われる様です。
使いまわされる例外のインスタンスは、他の場所ででthrowされた例外の情報が混入しないように、スタックトレースそのものが省略されるようです。

これを抑止するためのVMオプションが -XX:-OmitStackTraceInFastThrow です。

このオプションを指定すると、この最適化が行われなくなり、スタックトレースが出ないということは無くなります。

サンプルで試してみましょう。


基本的にひたすらNullPointerExceptionのスタックトレースをログ出力するサンプルになっています


(1) 数回NullPointerExceptionのスタックトレースを出力する

繰り返し3回だけNullPointerExceptionのスタックトレースをログ出力します

■実行用プログラム

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OmitExceptionSample {

    private static final Logger logger = LoggerFactory.getLogger(OmitExceptionSample.class);

    private static String nullString = null;

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            traceNulPointerException(i);
        }

    }

    private static void  traceNulPointerException(int index) {
        logger.info("call traceNulPointerException {}", index);
        try {
            int length = nullString.length();
        } catch (NullPointerException e) {
            logger.warn("Caught Exception.", e);
        }
    }
}


■実行結果

16:41:32.922 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 0
16:41:32.932 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:41:32.933 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 1
16:41:32.933 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:41:32.933 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 2
16:41:32.933 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)

スタックトレースが出力され続けています。


(2) 10万回NullPointerExceptionのスタックトレースを出力する

繰り返し10万回NullPointerExceptionのスタックトレースをログ出力します

■実行用プログラム

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OmitExceptionSample {

    private static final Logger logger = LoggerFactory.getLogger(OmitExceptionSample.class);

    private static String nullString = null;

    public static void main(String[] args) {
        for (int i = 0; i < 100000; i++) {
            traceNulPointerException(i);
        }

    }

    private static void  traceNulPointerException(int index) {
        logger.info("call traceNulPointerException {}", index);
        try {
            int length = nullString.length();
        } catch (NullPointerException e) {
            logger.warn("Caught Exception.", e);
        }
    }
}


■実行結果

16:43:14.822 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 0
16:43:14.822 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:43:14.823 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 1
16:43:14.823 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)

(中略)

16:43:15.753 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99996
16:43:15.753 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
16:43:15.753 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99997
16:43:15.753 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
16:43:15.753 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99998
16:43:15.753 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
16:43:15.753 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99999
16:43:15.753 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null

最初はスタックトレースが出力さていますが、後半出力されなくなっています。


(3) -XX:-OmitStackTraceInFastThrowを指定する。

-XX:-OmitStackTraceInFastThrowをVMオプションに指定して、実行してみます。10万回NullPointerExceptionのスタックトレースを出力します。

実行用のプログラムは、(2)と同じです

■実行結果

16:43:04.722 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 0
16:43:04.722 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:43:04.723 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 1
16:43:04.723 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)

(中略)

16:47:08.694 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99996
16:47:08.694 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:47:08.694 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99997
16:47:08.694 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:47:08.694 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99998
16:47:08.694 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)
16:47:08.694 [main] INFO net.yyuki.exception.OmitExceptionSample - call traceNulPointerException 99999
16:47:08.694 [main] WARN net.yyuki.exception.OmitExceptionSample - Caught Exception.
java.lang.NullPointerException: null
    at net.yyuki.exception.OmitExceptionSample.traceNulPointerException(OmitExceptionSample.java:22)
    at net.yyuki.exception.OmitExceptionSample.main(OmitExceptionSample.java:14)

最後までスタックトレースが出力さています。


まとめ

組み込み例外のスタックトレースを何度も出力しなければならないが、そのスタックトレースが出なくなるのを抑止したい場合は、
-XX:-OmitStackTraceInFastThrow の VMオプション を付与しましょう。

truncate(ftruncate) はサイズを切り詰める(減らす)だけじゃないよ

Linuxのシステムコールの truncate, ftruncate は、対象のファイルのサイズを切り詰めるものです。

書式は以下の通りです。

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
  • 引数
    • pathname / fd・・・サイズを切り詰めるファイルのパス / サイズを切り詰めるファイルのファイルディスクリプター
    • length ・・・切り詰めるサイズ(byte)
  • 戻り値
    • 0 ・・・成功
    • -1 ・・・エラー発生 (この場合 errno が適切にセットされる)

各関数で、ファイルのパスを指定するのか、ファイルディスクリプター(ファイル記述子)を指定するのかの違いはありますが、
基本的にファイサイズを切り詰めるという動作は同じです。

で、私がいつの間にか勘違いしてただけなのですが、
これらシステムコールは、ファイルのサイズを減らす動きをさせることができると同時に、元のファイルのサイズより大きな lengthを指定してファイルを伸張することも可能です。
伸張された場合、サイズが増えた部分を読み出すとヌルバイト (0x00) のデータが返されます。
(サイズが増えた部分には、すぐには物理ディスクは割り当てられず、対象ファイルはholeを含んだファイル(Sparse File)になるのが普通です)。

伸長される(ファイルサイズが増える)というパターンが、私の頭からは欠落していました。


ファイルサイズが増えるパターンも含めて念のためにtruncate, ftruncate の動作確認をしてみます

truncate の動作確認

sample01.txt というファイルのサイズを 10 byteにします。(システムコール呼び出しとは別で対象のファイルは先に用意しておきます)

■サンプルプログラム

truncate_sample.c

#include <unistd.h>
#include <sys/types.h>

#include <stdio.h>

int main() {
    if (truncate("sample01.txt", 10) < 0) {
        perror("truncate error!");
        return 1;
    }

    return 0;
}

以下でコンパイルします

gcc truncate_sample.c -o truncate_sample


そして以下で動作確認してみます

■ファイルサイズが減るパターン

$ echo 12345678901234 > sample01.txt
$
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  15 10 20 14:50 sample01.txt
$
$ hexdump sample01.txt 
0000000 31 32 33 34 35 36 37 38 39 30 31 32 33 34 0a   
000000f
$
$ ./truncate_sample 
$
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  10 10 20 14:51 sample01.txt
$ 
$ hexdump sample01.txt 
0000000 31 32 33 34 35 36 37 38 39 30                  
000000a

サンプルプログラム実行後にサイズが10バイトに減っていることがわかります


■ファイルサイズが増えるパターン

$ echo 1234 > sample01.txt
$
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  5 10 20 14:57 sample01.txt
$
$ hexdump sample01.txt 
0000000 31 32 33 34 0a                                 
0000005
$ ./truncate_sample 
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  10 10 21 14:57 sample01.txt
$
$ hexdump sample01.txt 
0000000 31 32 33 34 0a 00 00 00 00 00                  
000000a

サンプルプログラム実行後にサイズが10バイトに増えて、増えた部分がヌルバイト(0x00)のデータになっていることがわかります。


ftruncate の動作確認

sample02.txt というファイルのサイズを 20byte にします。(システムコール呼び出しとは別で対象のファイルは先に用意しておきます)

サンプルプログラム

#include <unistd.h>
#include <sys/types.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main() {

    int fd = open("sample02.txt", O_WRONLY);

    if (fd < 0) {
        perror("open error!");
        return 1;
    }

    if (ftruncate(fd, 20) < 0) {
        perror("ftruncate error!");
        close(fd);
        return 1;
    }

    close(fd);

    return 0;
}

以下でコンパイルします

 gcc ftruncate_sample.c -o ftruncate_sample


そして以下で動作確認してみます

■ファイルサイズが減るパターン

$ echo 1234567890123456789012345 > sample02.txt
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  26 10 20 15:06 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
0000010 37 38 39 30 31 32 33 34 35 0a                  
000001a
$
$ ./ftruncate_sample 
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  20 10 20 15:06 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
0000010 37 38 39 30                                    
0000014

サンプルプログラム実行後にサイズが20バイトに減っていることがわかります


■ファイルサイズが増えるパターン

$ echo 123456789 > sample02.txt
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  10 10 20 15:08 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 0a                  
000000a
$
$ ./ftruncate_sample 
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  20 10 20 15:08 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 0a 00 00 00 00 00 00
0000010 00 00 00 00                                    
0000014

サンプルプログラム実行後にサイズが20バイトに増えて、増えた部分がヌルバイト(0x00)のデータになっていることがわかります


まとめ

truncate, ftruncate はファイルサイズを減らすだけではなく、増やすこともできることがわかりました。
そして増えた場合は、ヌルバイトのデータが読み出されることがわかりました。

そうそう無いと思いますが、仮想ファイルシステムなんかを作る場合にもこの仕様は明確に把握しておく必要があります。



関連エントリ