🎉

pico-jxglib のシェルでファイルシステムを操作する話 (自動補完とヒストリ機能で入力楽々)

に公開

pico-jxglib は、ワンボードマイコン Raspberry Pi Pico の Pico SDK プログラミングをサポートするライブラリです。

https://zenn.dev/ypsitau/articles/2025-01-24-jxglib-intro

今回は、pico-jxglib のシェル でファイルシステムを操作する方法について解説します。自動補完機能やリダイレクト機能を使って、コマンドラインからファイルやディレクトリを簡単に操作できるようになります。

ファイルシステムとシェル

以下の記事で、ファイルシステムの基本的な使い方を紹介しました。

▶️ pico-jxglib で Pico ボードにファイルシステムを実装してフラッシュメモリをフル活用する話
▶️ pico-jxglib で Pico ボードに SD カードや USB ストレージを接続する話

ファイルシステムをプログラムに組み込むという観点からするとこれらの内容で十分なのですが、ファイルやディレクトリを操作するためにいちいちプログラムを書いてビルドして書き込むのは面倒です。そこで、pico-jxglib のシェルにファイルシステムを操作するためのコマンドを追加してより便利に扱えるようにしました。

基本劇なシェルの操作方法についてはこちらを参照ください。以下の機能もあわせて使うことで、ファイル操作が容易になります。

  • 自動補完機能
    Tab キーでコマンド名やファイル名を自動的に補完します。ヒストリ機能もあわせて使うことでコマンドの入力が非常に楽になります

  • リダイレクト機能
    コマンドの出力をファイルに保存することができます

組み込みシステムにシェルを組み込むというと、シェルを動かしている間はほかの処理が停止したり負荷がかかりそうに思えますが、pico-jxglib のシェルはバックグラウンドで動作して、本来の機能に影響を与えないように設計されています。組み込みや取り外しも簡単にできますから、必要なときだけシェルを有効にして使うこともできます。Pico ボードに USB キーボードと TFT LCD を接続し、それらを使ってシェルを操作することもできます。詳細は「pico-jxglib で Pico ボードにシェルを実装する話」 をご覧ください。

実際のプロジェクト

システム構成

本記事はシェルを使ったファイルシステムの操作方法を紹介するのが目的なので、Pico ボード上の Flash メモリ上にファイルシステムを作り、Pico ボード単体で動作するシステム構成にします。シェルの操作は UART または USB で接続したホスト PC のターミナルソフトから行います。

開発環境のセットアップ

Visual Studio Code や Git ツール、Pico SDK のセットアップが済んでいない方は「Pico SDK ことはじめ」 をご覧ください。

GitHub から pico-jxglib をクローンします。

git clone https://github.com/ypsitau/pico-jxglib.git
cd pico-jxglib
git submodule update --init

プロジェクトの作成

VSCode のコマンドパレットから >Raspberry Pi Pico: New Pico Project を実行し、以下の内容でプロジェクトを作成します。Pico SDK プロジェクト作成の詳細や、ビルド、ボードへの書き込み方法については「Pico SDK ことはじめ」 を参照ください。

  • Name ... プロジェクト名を入力します。今回は例として fs-shell を入力します
  • Board type ... ボード種別を選択します
  • Location ... プロジェクトディレクトリを作る一つ上のディレクトリを選択します
  • Stdio support .. ターミナルソフトに接続するポート (UART または USB) を選択します
  • Code generation options ... Generate C++ code にチェックをつけます

プロジェクトディレクトリと pico-jxglib のディレクトリ配置が以下のようになっていると想定します。

├── pico-jxglib/
└── fs-shell/
    ├── CMakeLists.txt
    ├── fs-shell.cpp
    └── ...

以下、このプロジェクトをもとに CMakeLists.txt やソースファイルを編集してプログラムを作成していきます。

シェルとファイルシステム操作コマンドの組込み

プログラムにシェルとファイルシステムコマンドを組み込むには、CMakeLists.txt の最後に以下の行を追加します。

CMakeLists.txt
target_link_libraries(fs-shell jxglib_LFS_Flash jxglib_FAT_Flash jxglib_Serial jxglib_ShellCmd_FS)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
jxglib_configure_FAT(fs-shell FF_VOLUMES 1)

ソースファイルを以下のように編集します。

fs-shell.cpp
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/Serial.h"
#include "jxglib/Shell.h"
#include "jxglib/LFS/Flash.h"
#include "jxglib/FAT/Flash.h"

using namespace jxglib;

int main()
{
    ::stdio_init_all();
    LFS::Flash driveA("A:",  0x1010'0000, 0x0004'0000); // Flash address and size 256kB
    LFS::Flash driveB("B:",  0x1014'0000, 0x0004'0000); // Flash address and size 256kB
    LFS::Flash driveC("*C:", 0x1018'0000, 0x0004'0000); // Flash address and size 256kB
    FAT::Flash driveD("D:",  0x101c'0000, 0x0004'0000); // Flash address and size 256kB
    Serial::Terminal terminal;
    Shell::AttachTerminal(terminal.Initialize());
    for (;;) {
        // :
        // any other jobs
        // :
        Tickable::Tick();
    }
}

このコードでは、Flash メモリ上に 3 つの LittleFS ファイルシステムと一つの FAT ファイルシステムを作成します。ドライブ名に * をつけたドライブはプライマリドライブとして扱われ、シェルのカレントドライブになります。

Shell::Terminal のインスタンスを生成して Shell::AttachTerminal() 関数でシェルに接続すると、シェルがメインループ中で動作するようになります。

シェルにファイルシステム操作コマンドを登録するために必要なのは CMakeLists.txttarget_link_libraries()jxglib_ShellCmd_FS ライブラリを追加することだけです。ソースファイルの編集は必要ありません。

シェルによるファイル操作

作成したプログラムを使って、シェルを使ったファイル操作の実例を紹介します。Tab キーで補完機能が働くので、コマンドやファイルを入力する際に適宜利用してみてください。

プログラムを実行すると、以下のプロンプトが表示されます。

C:?>

プロンプトには、カレントドライブとカレントディレクトリが表示されています。C: ドライブはまだフォーマットされていないので、カレントディレクトリは ? になっています。

ls-drive でドライブの一覧を表示できます。

C:?>ls-drive
 Drive  Format           Total
 A:     unmounted            0
 B:     unmounted            0
*C:     unmounted            0
 D:     unmounted            0

この時点ではどのドライブもフォーマットされていないので、unmounted になっています。C: ドライブには、それがプライマリドライブであることを示す * がついています。

format で、指定したドライブをフォーマットします。複数のドライブを一度にフォーマットすることができます。

C:?>format a: b: c: d:
drive a: formatted in LittleFS
drive b: formatted in LittleFS
drive c: formatted in LittleFS
drive d: formatted in FAT12

フォーマットが完了すると、プロンプトが以下のように変わります。

C:/>

ls-drive でドライブの状態を確認します。

C:/>ls-drive
 Drive  Format          Total
 A:     LittleFS       262144
 B:     LittleFS       262144
*C:     LittleFS       262144
 D:     FAT12          262144

これで、ドライブがフォーマットされて使用可能になりました。以下、このファイルシステムを使ってファイルやディレクトリを操作していきます。Pico ボードのフラッシュメモリ上に構築されたファイルシステムなので、どんな操作をしても大丈夫です。すべてをもとに戻したいときは format コマンドを実行してください。

ドライブ名のみを入力すると、カレントドライブを切り替えることができます。

C:/>a:
A:/>c:
C:/>

mkdir でディレクトリを作成します。複数のディレクトリを一度に作成することができます。

C:/>mkdir dir1 dir2 dir3
C:/>mkdir dir1/dir1-1 dir1/dir1-2
C:/>mkdir dir2/dir2-1 dir2/dir2-2

ls でディレクトリの一覧を表示します。

C:/>ls
d---- 2000-01-01 00:00:00        dir1/
d---- 2000-01-01 00:00:00        dir2/
d---- 2000-01-01 00:00:00        dir3/
C:/>ls dir1
d---- 2000-01-01 00:00:00        dir1-1/
d---- 2000-01-01 00:00:00        dir1-2/
C:/>ls dir2
d---- 2000-01-01 00:00:00        dir2-1/
d---- 2000-01-01 00:00:00        dir2-2/

タイムスタンプが 2000-01-01 00:00:00 になっているのは、Pico ボードに RTC (リアルタイムクロック) が接続されていないためです。RTC を接続する方法については「pico-jxglib で Pico ボードに RTC を接続してファイルシステムにタイムスタンプを記録する話」で解説します。

tree でディレクトリの階層構造をツリー形式で表示します。

C:/>tree
./
├── dir1/
│   ├── dir1-1/
│   └── dir1-2/
├── dir2/
│   ├── dir2-1/
│   └── dir2-2/
└── dir3/

cd でカレントディレクトリを移動します。

C:/>cd dir1
C:/dir1>

touch はすでに存在するファイルやディレクトリのタイムスタンプを更新するコマンドです。存在しないファイルを指定すると中身がないファイルを生成します。

C:/dir1>touch file1-1.txt file1-2.txt file1-3.txt
C:/dir1>ls
d---- 2000-01-01 00:00:00        dir1-1/
d---- 2000-01-01 00:00:00        dir1-2/
-a--- 2000-01-01 00:00:00      0 file1-1.txt
-a--- 2000-01-01 00:00:00      0 file1-2.txt
-a--- 2000-01-01 00:00:00      0 file1-3.txt

リダイレクト機能を使って、標準出力をファイルに保存することができます。リダイレクトで >> を使うと、ファイルの末尾に追記されます。以下の例ではメモリダンプを出力するコマンド d の結果をファイルに保存しています。

C:/dir1>cd /dir2
C:/dir2>d 0x10000000 > file2-1.txt
C:/dir2>d > file2-2.txt
C:/dir2>d >> file2-2.txt
C:/dir2>ls
d---- 2000-01-01 00:00:00        dir2-1/
d---- 2000-01-01 00:00:00        dir2-2/
-a--- 2000-01-01 00:00:00    232 file2-1.txt
-a--- 2000-01-01 00:00:00    464 file2-2.txt

cat でファイルの内容を表示します。

C:/dir2>cat file2-1.txt
10000000  00 B5 32 4B 21 20 58 60 98 68 02 21 88 43 98 60
10000010  D8 60 18 61 58 61 2E 4B 00 21 99 60 02 21 59 61
10000020  01 21 F0 22 99 50 2B 49 19 60 01 21 99 60 35 20
10000030  00 F0 44 F8 02 22 90 42 14 D0 06 21 19 66 00 F0
C:/dir2>cat file2-2.txt
10000040  34 F8 19 6E 01 21 19 66 00 20 18 66 1A 66 00 F0
10000050  2C F8 19 6E 19 6E 19 6E 05 20 00 F0 2F F8 01 21
10000060  08 42 F9 D1 00 21 99 60 1B 49 19 60 00 21 59 60
10000070  1A 49 1B 48 01 60 01 21 99 60 EB 21 19 66 A0 21
10000080  19 66 00 F0 12 F8 00 21 99 60 16 49 14 48 01 60
10000090  01 21 99 60 01 BC 00 28 00 D0 00 47 12 48 13 49
100000A0  08 60 03 C8 80 F3 08 88 08 47 03 B5 99 6A 04 20
100000B0  01 42 FB D0 01 20 01 42 F8 D1 03 BD 02 B5 18 66

cat に複数のファイルを指定すると内容を結合した結果を表示します (cat コマンドの名前の由来は concatenate) 。

C:/dir2>cat file2-1.txt file2-2.txt
10000000  00 B5 32 4B 21 20 58 60 98 68 02 21 88 43 98 60
10000010  D8 60 18 61 58 61 2E 4B 00 21 99 60 02 21 59 61
10000020  01 21 F0 22 99 50 2B 49 19 60 01 21 99 60 35 20
10000030  00 F0 44 F8 02 22 90 42 14 D0 06 21 19 66 00 F0
10000040  34 F8 19 6E 01 21 19 66 00 20 18 66 1A 66 00 F0
10000050  2C F8 19 6E 19 6E 19 6E 05 20 00 F0 2F F8 01 21
10000060  08 42 F9 D1 00 21 99 60 1B 49 19 60 00 21 59 60
10000070  1A 49 1B 48 01 60 01 21 99 60 EB 21 19 66 A0 21
10000080  19 66 00 F0 12 F8 00 21 99 60 16 49 14 48 01 60
10000090  01 21 99 60 01 BC 00 28 00 D0 00 47 12 48 13 49
100000A0  08 60 03 C8 80 F3 08 88 08 47 03 B5 99 6A 04 20
100000B0  01 42 FB D0 01 20 01 42 F8 D1 03 BD 02 B5 18 66

cat を引数なしで実行するとキーボードの入力内容を標準出力に出力します。リダイレクト機能を使って cat > filename のように実行することで、Pico ボード上で簡易的にテキストファイルを作成することができます。入力が終わったら Ctrl-C を押して終了します[1]

C:/dir2>cat > file2-3.txt
This is the first hand-written file.
^C
C:/dir2>cat > file2-4.txt
This is the second hand-written file.
^C
C:/dir2>ls
d---- 2000-01-01 00:00:00        dir2-1/
d---- 2000-01-01 00:00:00        dir2-2/
-a--- 2000-01-01 00:00:00    232 file2-1.txt
-a--- 2000-01-01 00:00:00    464 file2-2.txt
-a--- 2000-01-01 00:00:00     37 file2-3.txt
-a--- 2000-01-01 00:00:00     38 file2-4.txt

デフォルトでは ls は名前順にソートしてファイルやディレクトリの一覧を表示します。ls --sort=WORD オプション (WORDname, size, time のいずれか) で、日付やサイズでソートができます。ls -r オプションをつけると、逆順にソートされます。

C:/dir2>ls --sort=size
d---- 2000-01-01 00:00:00        dir2-1/
d---- 2000-01-01 00:00:00        dir2-2/
-a--- 2000-01-01 00:00:00     37 file2-3.txt
-a--- 2000-01-01 00:00:00     38 file2-4.txt
-a--- 2000-01-01 00:00:00    232 file2-1.txt
-a--- 2000-01-01 00:00:00    464 file2-2.txt
C:/dir2>ls --sort=size -r
d---- 2000-01-01 00:00:00        dir2-1/
d---- 2000-01-01 00:00:00        dir2-2/
-a--- 2000-01-01 00:00:00    464 file2-2.txt
-a--- 2000-01-01 00:00:00    232 file2-1.txt
-a--- 2000-01-01 00:00:00     38 file2-4.txt
-a--- 2000-01-01 00:00:00     37 file2-3.txt

cp でファイルをコピーします。複数のファイルをコピーすることができます。ワイルドカードが使えます。

C:/dir2>cd /
C:/>cp dir1/file1-1.txt dir1/file1-2.txt dir3
C:/>cp dir2/*.txt dir3
C:/>ls dir3
-a--- 2000-01-01 00:00:00      0 file1-1.txt
-a--- 2000-01-01 00:00:00      0 file1-2.txt
-a--- 2000-01-01 00:00:00    232 file2-1.txt
-a--- 2000-01-01 00:00:00    464 file2-2.txt
-a--- 2000-01-01 00:00:00     37 file2-3.txt
-a--- 2000-01-01 00:00:00     38 file2-4.txt

cp -r オプションを使うと、ディレクトリごとコピーできます。

C:/>cp -r dir1 dir2
C:/>tree dir2
dir2/
├── dir1/
│   ├── dir1-1/
│   ├── dir1-2/
│   ├── file1-1.txt
│   ├── file1-2.txt
│   └── file1-3.txt
├── dir2-1/
├── dir2-2/
├── file2-1.txt
├── file2-2.txt
├── file2-3.txt
└── file2-4.txt
C:/>cp -r dir1 dir4
C:/>ls
d---- 2000-01-01 00:00:00        dir1/
d---- 2000-01-01 00:00:00        dir2/
d---- 2000-01-01 00:00:00        dir3/
d---- 2000-01-01 00:00:00        dir4/

異なるドライブへのコピーもできます。

C:/>cp -r dir1 dir2 dir3 a:
C:/>ls a:
d---- 2000-01-01 00:00:00        dir1/
d---- 2000-01-01 00:00:00        dir2/
d---- 2000-01-01 00:00:00        dir3/
C:/>cp -r * d:
C:/>ls d:
d---- 2000-01-01 00:00:00        dir1/
d---- 2000-01-01 00:00:00        dir2/
d---- 2000-01-01 00:00:00        dir3/
d---- 2000-01-01 00:00:00        dir4/

mv でファイルやディレクトリの名前の変更、または移動をします。

C:/>mv dir1/file1-1.txt .
C:/>ls dir1
d---- 2000-01-01 00:00:00        dir1-1/
d---- 2000-01-01 00:00:00        dir1-2/
-a--- 2000-01-01 00:00:00      0 file1-2.txt
-a--- 2000-01-01 00:00:00      0 file1-3.txt
C:/>ls
d---- 2000-01-01 00:00:00        dir1/
d---- 2000-01-01 00:00:00        dir2/
d---- 2000-01-01 00:00:00        dir3/
d---- 2000-01-01 00:00:00        dir4/
-a--- 2000-01-01 00:00:00      0 file1-1.txt

rm でファイルやディレクトリを削除します。複数のファイルを指定でき、ワイルドカードも使用できます。rm -r オプションを使うと、ディレクトリごと削除できます。

C:/>rm dir1/file1-2.txt
C:/>ls dir1
d---- 2000-01-01 00:00:00        dir1-1/
d---- 2000-01-01 00:00:00        dir1-2/
-a--- 2000-01-01 00:00:00      0 file1-3.txt
C:/>rm dir2/*.txt
C:/>ls dir2
d---- 2000-01-01 00:00:00        dir1/
d---- 2000-01-01 00:00:00        dir2-1/
d---- 2000-01-01 00:00:00        dir2-2/
C:/>rm -r dir1 dir2
C:/>ls
d---- 2000-01-01 00:00:00        dir3/
d---- 2000-01-01 00:00:00        dir4/
-a--- 2000-01-01 00:00:00      0 file1-1.txt
脚注
  1. Linux の bash などでは Ctrl-D で入力終了するのが一般的ですが、pico-jxglib のシェルでは Ctrl-D を一文字消去のキーとして扱うため、入力終了は Ctrl-C で行います。 ↩︎

GitHubで編集を提案

Discussion