メモの日々


2023年02月02日(木) [長年日記]

[shell][unix] Bashでリダイレクト時のディスクリプタ番号自動生成

Bashのリダイレクトについては以前にメモした。

このときメモしていなかったが、件のメモで「[n]」と書いている部分は「{varname}」のように書くことができる。こう書くと、ファイルディスクリプタの番号が自動的に生成され、その番号が変数varnameに設定される。

$ echo hello {a}>/tmp/oreore
hello
$ echo $a
10

Bashのmanから引用しておく。

ファイル・ディスクリプター番号で始まるリダイレクトでは、 代わりに {varname} という形式の単語で始めることもできます。 この場合、>&- と <&- 以外のリダイレクト演算子では、 シェルは 10 より大きいファイル・ディスクリプターを割り当て、 varname に代入します。 {varname} で始まる >&- や <&- の場合には、varname の値は クローズするファイル・ディスクリプターを示します。


2023年02月03日(金) [長年日記]

[shell][unix] nohupは必要

以前、Bashがデフォルトではログアウト時にSIGHUPシグナルをジョブへ送らないことをメモした。

だから、Bash使用時にnohupコマンドを使う必要はないと思っていたが、そうではなかった。nohupは必要だ。

単にログアウトした時にはSIGHUPは送られないのだけれど、ログインした状態で端末ソフト(自分の環境だとWindows Terminal)を終了するとSIGHUPは送られるようで、このときバックグラウンドプロセスは終了してしまう。

nohupは指定したコマンドがSIGHUPを無視するようにしてくれるので、使っているシェルの挙動とは無関係にSIGHUPによるプロセス終了を避けられる。ただし、SIGHUP以外のシグナルを受けてプロセスが終了してしまうことはあることに注意。

次のページが参考になるのでメモ。

自分でも試しておく。

huponexitオフでログアウトした場合

Bashのデフォルトの設定だと、ログアウトしてもバックグランドプロセスは終了しない。

% bash --login
$ shopt huponexit
huponexit       off
$ sleep 200 &
[1] 9224
$ nohup sleep 100 &> /dev/null &
[2] 9225
$ exit
logout
% ps u -C sleep
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
kenichi   9224  0.0  0.0   5364   560 pts/16   S    18:18   0:00 sleep 200
kenichi   9225  0.0  0.0   5364   560 pts/16   S    18:19   0:00 sleep 100

huponexitオンでログアウトした場合

huponexitを有効にすると、nohupを使っていないバックグランドプロセスは終了する。

% bash --login
$ shopt -s huponexit
$ shopt huponexit
huponexit       on
$ sleep 200 &
[1] 9233
$ nohup sleep 100 &> /dev/null &
[2] 9234
$ exit
logout
% ps u -C sleep
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
kenichi   9234  0.0  0.0   5364   560 pts/16   S    18:21   0:00 sleep 100

huponexitオフで端末ソフトを終了した場合

huponexitが無効であっても、ログイン状態で端末ソフトを終了するとnohupを使っていないバックグランドプロセスは終了する。

% bash --login
$ shopt huponexit
huponexit       off
$ sleep 200 &
[1] 9241
$ nohup sleep 100 &> /dev/null &
[2] 9242
$
(この状態で端末ソフトを終了する)
% ps u -C sleep
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
kenichi   9242  0.0  0.0   5364   496 ?        S    18:23   0:00 sleep 100

2023年02月04日(土) [長年日記]

[shell] Bashで配列の各要素の先頭に文字列を追加する

Bashで配列の各要素の先頭に文字列を追加する例をメモ。

$ a=(a b c d e)
$ echo "${a[@]/#//home/}"
/home/a /home/b /home/c /home/d /home/e

Bashのパラメータ展開の次の機能を使っている。

${parameter/pattern/string}

パターンの置換。pattern が展開され、パス名展開の場合と同じようなパターンを作ります。parameter の展開が行われ、その値のうち pattern に最長一致する部分が string に置換されます。pattern が / で始まる場合には、pattern にマッチした部分は全て string に置換されます。そうでない場合には、最初にマッチした部分だけが置換されます。pattern が # で始まる場合には、パターンは parameter を展開した値の先頭にマッチしなければなりません。pattern が % で始まる場合には、パターンは parameter を展開した値の末尾にマッチしなければなりません。string が空の場合には pattern にマッチした部分は削除されます。またこの場合には、pattern の後に続く / は省略可能です。parameter が @ または * である場合、置換操作は全ての位置パラメータに順番に適用され、展開結果はリストとして得られます。parameter が @ または * が添字になっている配列変数である場合、置換操作は配列の全ての要素に順番に適用され、展開結果はリストとして得られます。


2023年02月06日(月) [長年日記]

[hard] ES-CLV9B用の替刃

4年前に買ったシェーバーの替刃についてメモ。

  • 内刃の品番:ES9170
  • 外刃の品番:ES9177
  • 外刃の品番:ES9179 ES9177の改良版
  • セットの品番:ES9036 ES9170とES9177のセット
  • セットの品番:ES9038 ES9170とES9179のセット

現状、Amazonだとセットよりも内刃と外刃を別々に買った方が安いので、ES9170 と ES9177 を買った。合わせて約5500円。ES9179の方がよかったのだけれど、入荷日が未定だったのでやめた。

次のページが参考になった。


2023年02月14日(火) [長年日記]

[dev] ROS2でノードに対するテストを書く

ROS2にlaunch_pytestというものがあることを知った。これを使えばノードを立ち上げて外部からその振る舞いをテストすることができる。

Writing a simple publisher and subscriber (Python)にあるMinimalPublisherをテストするコードをメモ。

まず、テスト対象ノードと通信するテスト用ノードを作成する。topicトピックを購読して、受信したメッセージをキューに保存するだけ。

from queue import Queue

from rclpy.node import Node
from std_msgs.msg import String

class DummyNode(Node):
    def __init__(self):
        super().__init__("dummy_node")
        self.topic_queue = Queue()
        self.topic_subscription = self.create_subscription(
            String, "topic", self._receive_topic, 10
        )

    def _receive_topic(self, msg):
        self.topic_queue.put(msg)

テスト本体は次の通り。

import queue

import launch
import launch_pytest
import launch_ros
import pytest
import rclpy

from concurrent.futures import ThreadPoolExecutor
from dummy_node import DummyNode

@launch_pytest.fixture
def talker_description():
    return launch.LaunchDescription(
        [
            launch_ros.actions.Node(
                executable="talker",
                package="py_pubsub",
                output="screen",
            ),
        ]
    )

@pytest.fixture(scope="session")
def thread_pool():
    pool = ThreadPoolExecutor()
    yield pool
    pool.shutdown()

@pytest.fixture
def dummy_node(thread_pool):
    rclpy.init()
    node = DummyNode()
    thread_pool.submit(rclpy.spin, node)
    yield node
    node.destroy_node()
    rclpy.shutdown()

@pytest.mark.launch(fixture=talker_description)
def test_talker(dummy_node):
    for i in range(10):
        try:
            msg = dummy_node.topic_queue.get(timeout=2.0 / (i + 1))
            assert msg.data == f"Hello World: {i}", f"index={i}"
        except(queue.Empty):
            pytest.fail(f"no message. index={i}")
  • @launch_pytest.fixture を指定して、launch.LaunchDescriptionの配列を返す関数を定義する。ここにテスト対象ノードの情報を記述することで、テスト時にそのノードが立ち上がる。配列なので複数のノードを立ち上げ可能。
  • dummy_nodeというフィクスチャで先に定義したDummyNodeノードを生成して立ち上げている。ThreadPoolExecutorを使って別スレッドで動くようにしている。
  • テストは @pytest.mark.launch で修飾し、先に定義したlaunch.LaunchDescription配列を指定する。メッセージを受信する間隔がだんだん短くなるようにしているので、ループの5回目くらいでテストが失敗する。

2023年02月21日(火) [長年日記]

[c++] OpenMPの処理結果をスレッド番号順に並べる

先日書いたOpenMPの例は、作られるvectorの要素の順序が毎回変わってしまう。毎回同じ結果を得るにはどうしたらいいか。

の回答を見てなるほどと思った。スレッド数分だけループして、#pragma omp orderedで順番に処理させている。

先日の例に適用してみる。「schedule(static,1)」は指定してもしなくても同じだと思うので指定していない。

#include <iostream>
#include <vector>

#include <omp.h>

std::vector<int> make_vector(int size)
{
  std::vector<int> result;

  #pragma omp parallel
  {
    std::vector<int> local;

    #pragma omp for nowait
    for (int i = 0; i < size; ++i) {
      if (i % 2 == 0) local.push_back(i);
    }

    #pragma omp for ordered nowait
    for (int i = 0; i < omp_get_num_threads(); ++i) {
      #pragma omp ordered
      result.insert(result.end(), local.begin(), local.end());
    }
  }

  return result;
}

int main()
{
  const auto max_threads = omp_get_max_threads();
  std::cout << "max_threads = " << max_threads << std::endl;

  const auto v = make_vector(max_threads * 10);

  std::cout << "v.size = " << v.size() << std::endl;
  for (auto e : v) {
    std::cout << e << ", ";
  }
  std::cout << std::endl;
}
max_threads = 6
v.size = 30
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,