0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

古いシステム(xfs, ext4)をbtrfsにコピーしてシステムを入れ替える-1

0
Last updated at Posted at 2026-05-12
  • 注意:以下の記事を参考にされ、問題が生じた場合、責任は負えません。
  • 基本的には私の忘備録です。
  • 使用環境は manjaro-linuxです。
  • link先に了解なく引用している部分がありますので、問題があればご連絡ください。
  • 260605付近で間違い箇所を修正。最後のオマケを修正。

背景

SSDをxfsでformatして、長年利用してきたが、manjaroがbtrfsを採用しており、昔よりは安全性が上がったようだ。そこで、新しいSSDをbtrfsでformatしたのち、xfsのシステムをコピーして移行させた。その時の作業記録。(2026/05/12)

  • deviceについては、各自の環境に沿って読み替えてください。
  • SSDの容量は1TBで作業してます。これも各自の環境に合わせて修正ください。
  • install-USBを作成し、そのUSBから作業を行う前提です。

この方法を採用した理由

  • reinstall不要。
  • 現行環境をそのまま移行可能。
  • 外付けSSDで安全に起動テストできる。
  • Btrfs snapshot運用へ移行できる。
  • rollbackしやすい。
  • Timeshiftの使用可。

作業前のチェックポイント

コピー元: /dev/sdb  xfs環境 (内蔵SSD)
コピー先: /dev/sdc  btrfs環境 (外付けSSD)
  1. /dev/sdc が本当にコピー先か確認。
  2. /dev/sdc をformatしてよいか確認。
  3. /mnt/etc/fstab を新UUIDに変更。
  4. 古い /dev/sdb のUUIDを残さない。
  5. fallback用GRUBは --removable を使う。
  6. /boot/efi/EFI/BOOT/BOOTX64.EFI が作成されたか確認。
  7. 外付けSSDで起動確認後に、内蔵SSDと入れ替える。

作業一覧

  • 確認
lsblk -f
sudo fdisk -l
  • create partition: 1TB SSD.
sudo parted /dev/sdc --script mklabel gpt
sudo parted /dev/sdc --script mkpart ESP fat32 1MiB 1025MiB
sudo parted /dev/sdc --script set 1 esp on
sudo parted /dev/sdc --script mkpart root 1025MiB 251GiB
sudo parted /dev/sdc --script mkpart home 251GiB 100%
  • format: btrfs
sudo mkfs.vfat -F32 -n EFI_WD_1T_B /dev/sdc1
sudo mkfs.btrfs -f -L root_wd_1t_b /dev/sdc2
sudo mkfs.btrfs -f -L home_wd_1t_b /dev/sdc3
  • if you use kernelbelow 6.1, use following command.
    kenel below 6.1 can not handle block-group-tree.
sudo mkfs.btrfs -f -L root_wd_1t_b -O '^block-group-tree' /dev/sdc2
sudo mkfs.btrfs -f -L home_wd_1t_b -O '^block-group-tree' /dev/sdc3
  • subvolume root
sudo mount /dev/sdc2 /mnt
sudo btrfs subvolume create /mnt/@
sudo btrfs subvolume create /mnt/@snapshots
sudo btrfs subvolume create /mnt/@var_log
sudo btrfs subvolume create /mnt/@pkg
sudo umount /mnt
  • subvolume home
sudo mount /dev/sdc3 /mnt
sudo btrfs subvolume create /mnt/@home
sudo btrfs subvolume create /mnt/@home_snapshots
sudo umount /mnt
  • コピー先(外付けSSD: /dev/sdc)をmount
sudo mount -o subvol=@,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt
sudo mkdir -p /mnt/boot/efi /mnt/.snapshots /mnt/var/log /mnt/var/cache/pacman/pkg /mnt/home /mnt/home/.snapshots
sudo mount /dev/sdc1 /mnt/boot/efi
sudo mount -o subvol=@snapshots,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt/.snapshots
sudo mount -o subvol=@var_log,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt/var/log
sudo mount -o subvol=@pkg,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt/var/cache/pacman/pkg
sudo mount -o subvol=@home,compress=zstd:3,noatime,ssd,discard=async /dev/sdc3 /mnt/home
sudo mount -o subvol=@home_snapshots,compress=zstd:3,noatime,ssd,discard=async /dev/sdc3 /mnt/home/.snapshots
  • コピー元(内蔵SSD: /dev/sdb)をmount
sudo mkdir -p /oldroot
sudo mount /dev/sdb2 /oldroot

#sudo mkdir -p /oldroot/home
sudo mount /dev/sdb3 /oldroot/home

#sudo mkdir -p /oldroot/boot/efi
sudo mount /dev/sdb1 /oldroot/boot/efi
  • copy system ; rsync
sudo rsync -aAXHv --numeric-ids --info=progress2 \
  --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/.snapshots/*","/home/.snapshots/*","/swapfile"} \
  /oldroot/  /mnt/

  • コピー先SSD(/dev/sdc)のfstab編集
lsblk -f /dev/sdc
#sudo blkid /dev/sdc1 /dev/sdc2 /dev/sdc3

sudo vim /mnt/etc/fstab
  • chroot & install grub (/dev/sdc)
sudo manjaro-chroot /mnt
mkinitcpio -P

# fallback用GRUB: removable mode
grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck

update-grub
exit
  • unmount & reboot
sudo umount -R /mnt
sync
sudo reboot

==========================================================

1. 準備と確認事項

外付けSSD(dev/sdc)に内臓のSSD (/dev/sda)のシステムをコピーする。
外付けSSDから起動させ、問題がないことを確認後、外付けSSDを内蔵SSDと入れ替える。
install用のUSBを作成して、usbから起動。

コピー元: /dev/sdb  xfs環境
コピー先: /dev/sdc  btrfs環境

/dev/sdcの最終的なpartition構成

device      LABEL   mount point
--------------------------------
/dev/sdc1   EFI     /boot/efi
/dev/sdc2   root    /
/dev/sdc3   home    /home

format:

/      = btrfs
/home  = btrfs

GRUB:

fallback用として --removable を使用
bootloader-id は使わない.

Timeshift:

/ の週1 snapshot 用
  /home は基本含めない

/home:
  設定変更前だけ手動snapshot

2. 事前確認

install用のUSBを作成して、usbから起動させた後、確認。

lsblk -f
sudo fdisk -l

確認:
/dev/sdb = コピー元
/dev/sdc = コピー先・外付けSSD:消してよい方

重要:
/dev/sdc は全消去される。
/dev/sdb と /dev/sdc を絶対に取り違えないこと。

3. /dev/sdc にpartition作成

構成:1TB SSDを使用した場合。

device      LABEL   Volume
---------------------------
/dev/sdc1   EFI     1G
/dev/sdc2   root    250G
/dev/sdc3   home    残り全部

partitionの作成:

Attention!! delete all data of /dev/sdc
partitionの容量は各自で修正のこと.

sudo parted /dev/sdc --script mklabel gpt

sudo parted /dev/sdc --script mkpart ESP fat32 1MiB 1025MiB
sudo parted /dev/sdc --script set 1 esp on

sudo parted /dev/sdc --script mkpart root 1025MiB 251GiB
sudo parted /dev/sdc --script mkpart home 251GiB 100%

確認:

lsblk -f /dev/sdc

#sudo parted /dev/sdc print

4. format

partitionをbtrfsでformat.

sudo mkfs.vfat -F32 -n BOOT_WD_1T_B /dev/sdc1

sudo mkfs.btrfs -f -L root_wd_1t_b /dev/sdc2
sudo mkfs.btrfs -f -L home_wd_1t_b /dev/sdc3

確認:


lsblk -f /dev/sdc

5. btrfs subvolume作成

5-1. root側

sudo mount /dev/sdc2 /mnt

sudo btrfs subvolume create /mnt/@
sudo btrfs subvolume create /mnt/@snapshots
sudo btrfs subvolume create /mnt/@var_log
sudo btrfs subvolume create /mnt/@pkg

sudo umount /mnt

5-2. home側

sudo mount /dev/sdc3 /mnt

sudo btrfs subvolume create /mnt/@home
sudo btrfs subvolume create /mnt/@home_snapshots

sudo umount /mnt

5-3. 確認

/root

sudo mount /dev/sdc2 /mnt
sudo btrfs subvolume list /mnt
sudo umount /mnt

/home

sudo mount /dev/sdc3 /mnt
sudo btrfs subvolume list /mnt
sudo umount /mnt

6. コピー先(外付けSSD)を /mnt にmount

sudo mount -o subvol=@,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt

sudo mkdir -p /mnt/boot/efi
sudo mkdir -p /mnt/.snapshots
sudo mkdir -p /mnt/var/log
sudo mkdir -p /mnt/var/cache/pacman/pkg
sudo mkdir -p /mnt/home
sudo mkdir -p /mnt/home/.snapshots

sudo mount /dev/sdc1 /mnt/boot/efi

sudo mount -o subvol=@snapshots,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt/.snapshots
sudo mount -o subvol=@var_log,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt/var/log
sudo mount -o subvol=@pkg,compress=zstd:3,noatime,ssd,discard=async /dev/sdc2 /mnt/var/cache/pacman/pkg

sudo mount -o subvol=@home,compress=zstd:3,noatime,ssd,discard=async /dev/sdc3 /mnt/home

sudo mount -o subvol=@home_snapshots,compress=zstd:3,noatime,ssd,discard=async /dev/sdc3 /mnt/home/.snapshots

注意:

  • SSDやUSBケースによっては discard=async が不安定な場合がある。
  • 問題がある場合は discard=async を削除し、fstrim.timer を利用する。

確認:

lsblk -f

##もしくは以下。

findmnt /mnt
findmnt /mnt/home
findmnt /mnt/boot/efi
findmnt /mnt/.snapshots
findmnt /mnt/var/log
findmnt /mnt/var/cache/pacman/pkg
findmnt /mnt/home/.snapshots

7. コピー元(内蔵SSD)をmount

sudo mkdir -p /oldroot
sudo mount /dev/sdb2 /oldroot

#sudo mkdir -p /oldroot/home
sudo mount /dev/sdb3 /oldroot/home

#sudo mkdir -p /oldroot/boot/efi
sudo mount /dev/sdb1 /oldroot/boot/efi

8. rsyncで system + home をコピー

Manjaro Live USBから起動してコピーする。
稼働中systemからコピーすると、browser DB、cache、log、設定ファイルなどがコピー中に変化する可能性がある。

sudo rsync -aAXHv --numeric-ids --info=progress2 \
  --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/.snapshots/*","/home/.snapshots/*","/swapfile"} \
  /oldroot/  /mnt/

コピー後確認:

ls /mnt
ls /mnt/home
ls /mnt/etc

8. コピー先のfstab を作成・修正

UUID確認:

lsblk -f

# sudo blkid /dev/sdc1 /dev/sdc2 /dev/sdc3

編集:

sudo vim /mnt/etc/fstab


例:
UUID=EFI_UUID   /boot/efi               vfat   umask=0077  0 2


UUID=ROOT_UUID  /                       btrfs  subvol=@,compress=zstd:3,noatime,ssd,discard=async  0 0

UUID=ROOT_UUID  /.snapshots             btrfs  subvol=@snapshots,compress=zstd:3,noatime,ssd,discard=async  0 0

UUID=ROOT_UUID  /var/log                btrfs  subvol=@var_log,compress=zstd:3,noatime,ssd,discard=async  0 0

UUID=ROOT_UUID  /var/cache/pacman/pkg   btrfs  subvol=@pkg,compress=zstd:3,noatime,ssd,discard=async  0 0


UUID=HOME_UUID  /home                   btrfs  subvol=@home,compress=zstd:3,noatime,ssd,discard=async  0 0

#UUID=HOME_UUID  /home/.snapshots        btrfs  subvol=@home_snapshots,compress=zstd:3,noatime,ssd,discard=async  0 0

EFI_UUID  = /dev/sdc1 のUUID

ROOT_UUID = /dev/sdc2 のUUID
HOME_UUID = /dev/sdc3 のUUID

注意:
UUIDを間違えると起動しなくなるので、慎重に。
古い /dev/sdb のUUIDを残さない。

9. コピー先のSSDにchrootで入りgrubのinstallなどを実施

Manjaro Live USBの場合:

sudo manjaro-chroot /mnt

chroot内で確認:

lsblk -f

cat /etc/fstab

findmnt /
findmnt /home

mkinitcpio.conf確認:
古いxfs環境から移行した場合、HOOKS に btrfs が入っているか確認。

cat /etc/mkinitcpio.conf

initramfs更新:

mkinitcpio -P

fallback用GRUBを --removable で作成

今回は通常のNVRAM boot entryを作る目的ではなく、外付けSSD fallback用。
そのため --bootloader-id は使わず、--removable を使う。

grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck

update-grub

--removable:
EFI fallback path にGRUBを配置する。
通常は以下へ配置される。

利点:
UEFIのNVRAM entryに依存しにくい。
外付けSSDとして起動しやすい。
他の既存Manjaro boot entryを上書きしにくい。

課題:
複数OSを運用している場合、EFI/BOOT/BOOTX64.EFI が競合する可能性がある。

確認:

ls -lh /boot/efi/EFI/BOOT/

期待:
BOOTX64.EFI が存在する。

/boot/efi/EFI/BOOT/BOOTX64.EFI

chroot終了:

exit

10. umount 外付けSSD

sudo umount -R /mnt
sync

11. 外付けSSDから起動テスト

再起動。
BIOS/UEFI boot menuから、外付けSSDを選択。

起動後に確認:

lsblk -f

findmnt /
findmnt /home
findmnt /.snapshots
findmnt /home/.snapshots
findmnt /boot/efi

期待:

/                    -> /dev/sdc2 subvol=@
/home                -> /dev/sdc3 subvol=@home
/.snapshots          -> /dev/sdc2 subvol=@snapshots
/home/.snapshots     -> /dev/sdc3 subvol=@home_snapshots
/boot/efi            -> /dev/sdc1

btrfs確認:

sudo btrfs subvolume list /
sudo btrfs subvolume list /home
sudo btrfs filesystem usage /
sudo btrfs filesystem usage /home

12. 起動後のsystem確認

lsblk -f
df -h
cat /etc/fstab
uname -a
sudo pacman -Q linux

失敗service確認:

systemctl --failed

重要error確認:

journalctl -p 3 -xb

EFI fallback確認:

ls -lh /boot/efi/EFI/BOOT/

14. 内蔵SSDと入れ替える前の確認

外付けSSDとして正常起動したら確認:

読み書き確認
network確認
systemctl --failed確認
journalctl -p 3 -xb確認

確認コマンド例:

systemctl --failed
journalctl -p 3 -xb

findmnt /
findmnt /home
lsblk -f

15. 内蔵SSDと入れ替え後の注意

入れ替え後、デバイス名は変わる可能性あり。

外付け時: /dev/sdc
内蔵後: /dev/sda or /dev/sdb

ただし /etc/fstab をUUIDで書いていれば基本問題なし。

入れ替え後に確認:

lsblk -f

findmnt /
findmnt /home
findmnt /boot/efi
cat /etc/fstab

16. 最終構成イメージ

/dev/sdc1  EFI       vfat

/dev/sdc2  root      btrfs
  @
  @snapshots
  @var_log
  @pkg

/dev/sdc3  home      btrfs
  @home
  @home_snapshots

17. Timeshiftの設定

Timeshiftは rootだけ 対象。

/ を対象
/home は基本含めない

mode:
  BTRFS

運用

root:
Timeshift weekly 2〜4個
大きな更新前だけ手動snapshot

snapshot頻度:

  Monthly 0〜1
  Weekly 2〜4
  Daily 1
  Hourly 0
  Boot 0

home:
Timeshiftには含めない
設定変更前だけ手動snapshot
重要データは別SSDへrsync backup

sample setting

sudo vi /etc/timeshift/timeshift.json

{
  "backup_device_uuid" : "UUID",  # /root:  UUID
  "parent_device_uuid" : "",
  "do_first_run" : "false",
  "btrfs_mode" : "true",				# false >> true
  "include_btrfs_home" : "false",		# false : not  backup /home
  "stop_cron_emails" : "true",
  "schedule_monthly" : "true",		# false >> true
  "schedule_weekly" : "true",		# false >> true
  "schedule_daily" : "false",
  "schedule_hourly" : "false",
  "schedule_boot" : "false",
  "count_monthly" : "0",
  "count_weekly" : "2",
  "count_daily" : "2",
  "count_hourly" : "0",
  "count_boot" : "1",
  "snapshot_size" : "0",
  "snapshot_count" : "0",
  "exclude" : [
  ],
  "exclude-apps" : [
  ]
}


snapshot list up

sudo timeshift --list

sudo btrfs subvolume list /
sudo btrfs subvolume list /home

初回snapshot:

sudo timeshift --create --comments "initial btrfs system snapshot"

system更新前だけ手動追加:

sudo timeshift --create --comments "before pacman update"

## system update
#sudo pacman -Syu

restore system from snapshot. You should start system from install-USB.

## GUI
sudo timeshift --restore

## CUI
sudo timeshift --restore --snapshot '2026-05-11_10-30-00'

18. /home の手動snapshot

/homeはTimeshiftに含めず、設定変更前だけ手動snapshot。

sudo btrfs subvolume snapshot -r /home /home/.snapshots/home-before-dotfiles-260505

確認

sudo btrfs subvolume list /home

ID 256 gen 77 top level 5 path @home
ID 257 gen 70 top level 5 path @home_snapshots
ID 258 gen 70 top level 257 path @home_snapshots/home-before-dotfiles-260505

snapshotの使用量

sudo btrfs filesystem du -s /home/.snapshots/home-before-dotfiles-260605

     Total   Exclusive  Set shared  Filename
  73.08GiB     6.52MiB    51.82GiB  /home/.snapshots/home-before-dotfiles-260605
項目        意味
------------------------------------------------------------
Total       73.08GiB : snapshotから見えるファイル総量
Exclusive   6.52MiB  << このsnapshotだけが専有している容量
Set shared  51.82GiB : 他のsubvolume/snapshotと共有している容量

戻す時は、まずファイル単位で戻す。

cp -a /home/.snapshots/home-before-zsh-260505/someone/.zshrc /home/someone/.zshrc

.config の一部を戻す. 例:

cp -a /home/.snapshots/home-before-kde-260505/someone/.config/kwinrc /home/someone/.config/kwinrc

snapshot削除:

sudo btrfs subvolume delete /home/.snapshots/home-before-zsh-260505

保持目安:

/home snapshotは3〜5個程度。

古いものは削除。
大量に残さない。

snapshotの実用的な運用例

通常運用:
  snapshotは作業前だけ取る

保持:
  直近1〜3個だけ残す

削除:
  作業が安定したら古いsnapshotは削除

変更前に snapshotをとる。

sudo btrfs subvolume snapshot -r /home /home/.snapshots/home-before-dotfiles-260605

snapshotを確認。

sudo btrfs subvolume list /home

更新に問題がなければ、不要になった古いsnapshotを削除。

sudo btrfs subvolume delete /home/.snapshots/古いsnapshot名

容量確認。

sudo btrfs filesystem du -s /home/.snapshots/*
df -h /home

注意:snapshot削除直後に df -h の空き容量がすぐ大きく戻らないことがあります。
その場合はまず同期します。

sudo btrfs filesystem sync /home
df -h /home

du と df は見ているものが少し違うため、数字が完全には一致しません。

よくある失敗

  • コピー元(/dev/sdb)とコピー先(/dev/sdc)のdeviceを取り違える。(深刻:systemの消失)
  • fstab中のUUIDを間違える。(起動できない。)
  • EFI mount を忘れる。(起動できない。)
  • mkinitcpio -P を忘れる。(起動できない?)
  • fallback EFI が作成されていない。(起動できない)
  • rsync中に稼働systemが更新される。
  • snapshot を大量に残して容量を圧迫する。(運用時に注意)

Reference

2022年の少し古い記事。

RasberryPiでbtrfsを利用した例。よく理解せずに実行すると、危険かもしれない。

補足:
上記は少し古い記事のため、現状で利用できるか未確認。

オマケ

1. btrfsとext4,xfsの比較

項目                  Btrfs                         ext4                         XFS
------------------------------------------------------------------------------------------------
安定性                ○ 実用十分                   ◎ 枯れている              ◎ 大容量で強い
初心者向け            △ 概念が多い                 ◎ 簡単                    ○ 比較的簡単
snapshot             ◎ 標準機能                   × 標準なし                 × 標準なし
rollback             ◎ Timeshiftと相性良い        △ 別ツール依存             △ 別ツール依存
圧縮                 ◎ zstd圧縮が使いやすい        × 標準なし                 × 標準なし
容量節約             ◎ text/log/sourceで有効      × なし                     × なし
checksum            ◎ data/metadata検出可        △ metadata中心            △ metadata中心
scrub               ◎ 破損検出に強い              × なし                     × あり/限定的
send/receive        ◎ 差分backupに強い           × なし                      × なし
容量拡張             ◎ device add可能            △ LVM等が必要              △ LVM等が必要
縮小                 ○ 可能                      ◎ 可能                     × 原則不可
大容量file性能       ○ 良い                       ○ 良い                     ◎ 非常に良い
小file大量           ○〜△ 設定次第               ○ 安定                     ○ 安定
DB/VM image         △ CoWが邪魔な場合あり        ○ 安定                     ◎ 強い
HDD倉庫             △〜○ 用途次第                ◎ 無難                    ◎ 大容量倉庫向き
SSD system          ◎ とても相性良い             ◎ 無難                    ○ 良い
復旧容易性           △ 専門知識が要る             ◎ 情報多い                ○ 情報多い
運用ミス耐性         △ snapshot肥大化等に注意     ◎ 単純で強い               ○ 単純で強い

2. btrfsのsystemを別のdiskにcloning

同じ容量のDsikが必要です。
危険なので、以下をよく理解してから、自己責任でご利用ください。
万一、障害が発生しても、責任は負えません。

#!/usr/bin/env bash
#////////////////////////////////////////////////////
#
# v03:260608
#  copy system if target SSD has different size, but enough vol.
#   eg) copy 1TB system but root+ home =512GB  >> 512GB SSD
#  - copy /home/.snapshots by creating @home_snapshots on TARGET3.
#  - write fstab by UUID, not LABEL.
#  
# copy_btrfs_system_to_disk_rsync.sh
#
# usage: copy from /dev/nvme0n1 to /dev/sda
#   sudo ./copy_btrfs_system_to_disk_rsync_v06_3p_match_revised_v03.sh --source /dev/nvme0n1 --target /dev/sda
#
# ## 続行するには、手入力で次のように打つ必要があります。
# ## start format & copy
#   FORMAT /dev/sda
#
# Purpose:
#   Copy a Manjaro/Arch Btrfs system disk to another disk by rsync only.
#   This script prepares the target disk up to the point just before manjaro-chroot.
#
# Expected source layout:
#   SOURCEp1 = EFI /boot/efi
#   SOURCEp2 = Btrfs root partition with subvol=@
#   SOURCEp3 = Btrfs home partition with subvol=@home and optional @home_snapshots
#   SOURCEp4 = optional data partition, ignored
#
# Target layout created:
#   TARGET1 = EFI FAT32, LABEL=BOOT
#   TARGET2 = Btrfs root, LABEL=ROOT
#     @
#     @snapshots
#     @var_log
#     @pkg
#   TARGET3 = Btrfs home, LABEL=HOME
#     @home
#     @home_snapshots
#
# After this script:
#   Target root is mounted at:
#     /mnt/target
#   Target home is mounted at:
#     /mnt/target/home
#   Target home snapshots are mounted at:
#     /mnt/target/home/.snapshots
#   Target EFI is mounted at:
#     /mnt/target/boot/efi
#   Target extra subvolumes are mounted at:
#     /mnt/target/.snapshots
#     /mnt/target/var/log
#     /mnt/target/var/cache/pacman/pkg
#
# Then run manually:
#   sudo manjaro-chroot /mnt/target /bin/bash
#   mkinitcpio -P
#   grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck
#   update-grub
#   exit
#   sudo vi /mnt/target/etc/fstab
#   sudo umount -R /mnt/target
#   sync
#
# WARNING:
#   The TARGET disk will be completely erased.
#
# memo:
#   - v06 creates only first 3 partitions on TARGET.
#   - TARGET1/TARGET2/TARGET3 sizes are based on SOURCEp1/SOURCEp2/SOURCEp3.
#   - SOURCEp4, for example /data0, is NOT created and NOT copied.
#   - Any remaining target disk space is left unallocated.
#   - rsync only; btrfs send/receive is NOT used.
#   - Running from Manjaro Live USB is strongly recommended.
#   - Root snapshot history under /.snapshots is not copied.
#   - Home snapshot history under /home/.snapshots is copied into TARGET3:@home_snapshots.
#   - fstab is generated using UUID= entries.
#   - Previous versions are kept separately.
#
#/////////////////////////////////////////////////


set -euo pipefail

VERSION="v03"

SOURCE_DISK=""
TARGET_DISK=""
YES=0

SRC_ROOT_MNT="/mnt/src_root"
SRC_ROOT_TOP="/mnt/src_root_top"
SRC_HOME_MNT="/mnt/src_home"
SRC_HOME_TOP="/mnt/src_home_top"
SRC_HOME_SNAP_MNT="/mnt/src_home_snapshots"
SRC_EFI_MNT="/mnt/src_efi"

TARGET_ROOT="/mnt/target"
TARGET_TOP_ROOT="/mnt/tgt_root_top"
TARGET_TOP_HOME="/mnt/tgt_home_top"

usage() {
  cat <<'EOF'
Usage:
  sudo ./copy_btrfs_system_to_disk_rsync_v06_3p_match_revised_v03.sh --source /dev/nvme0n1 --target /dev/sda

memo:
  - TARGET disk is formatted completely.
  - Only first 3 partitions are created:
      TARGET1 = EFI,  size approx SOURCEp1, LABEL=BOOT
      TARGET2 = root, size approx SOURCEp2, LABEL=ROOT
      TARGET3 = home, size approx SOURCEp3, LABEL=HOME
  - SOURCEp4, for example /data0, is ignored.
  - Any remaining target disk space is left unallocated.
  - v03 creates @snapshots, @var_log, and @pkg on TARGET2.
  - v03 creates @home and @home_snapshots on TARGET3.
  - v03 copies /home/.snapshots from source @home_snapshots if it exists.
  - v03 writes fstab using UUID= entries, not LABEL=.
  - rsync only; btrfs send/receive is NOT used.
  - Root snapshot history under /.snapshots is not copied.

Dangerous, no final confirmation:
  sudo ./copy_btrfs_system_to_disk_rsync_v06_3p_match_revised_v03.sh --source /dev/nvme0n1 --target /dev/sda --yes

Confirmation:
  The script asks you to type exactly:
    FORMAT /dev/sda

Options:
  --source DEV     Source disk, e.g. /dev/nvme0n1
  --target DEV     Target disk, e.g. /dev/sda
  --yes            Skip final interactive confirmation
  -h, --help       Show this help

Example:
  sudo ./copy_btrfs_system_to_disk_rsync_v06_3p_match_revised_v03.sh --source /dev/nvme0n1 --target /dev/sda

option:  add LABEL to partition manually if needed

 sudo dosfslabel /dev/sda1 BOOT
 sudo btrfs filesystem label /dev/sda2 ROOT
 sudo btrfs filesystem label /dev/sda3 HOME

After script:
  sudo manjaro-chroot /mnt/target /bin/bash
  mkinitcpio -P
  grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck
  update-grub

  exit
  sudo vi /mnt/target/etc/fstab
  ## and modify UUID or LABEL name if needed

  reboot

EOF

  echo -e "\n-----------------------------------------------------"
  lsblk -f || true
}

log() {
  printf '\n\033[1;32m==>\033[0m %s\n' "$*"
}

warn() {
  printf '\n\033[1;33mWARN:\033[0m %s\n' "$*" >&2
}

die() {
  printf '\n\033[1;31mERROR:\033[0m %s\n' "$*" >&2
  exit 1
}

part_path() {
  local disk="$1"
  local num="$2"
  case "$disk" in
    *nvme*n*|*mmcblk*) printf '%sp%s' "$disk" "$num" ;;
    *)                 printf '%s%s' "$disk" "$num" ;;
  esac
}

require_root() {
  [[ "${EUID}" -eq 0 ]] || die "Run as root: sudo $0 ..."
}

require_cmds() {
  local missing=()
  for c in lsblk findmnt blkid blockdev parted partprobe wipefs mkfs.vfat mkfs.btrfs btrfs rsync awk grep sort; do
    command -v "$c" >/dev/null 2>&1 || missing+=("$c")
  done
  if ((${#missing[@]})); then
    die "Missing commands: ${missing[*]}"
  fi
}

parse_args() {
  if (($# == 0)); then
    usage
    exit 0
  fi

  while (($#)); do
    case "$1" in
      --source)
        SOURCE_DISK="${2:-}"
        shift 2
        ;;
      --target)
        TARGET_DISK="${2:-}"
        shift 2
        ;;
      --yes)
        YES=1
        shift
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        die "Unknown argument: $1"
        ;;
    esac
  done

  [[ -n "$SOURCE_DISK" ]] || die "--source is required"
  [[ -n "$TARGET_DISK" ]] || die "--target is required"
  [[ -b "$SOURCE_DISK" ]] || die "Source disk not found: $SOURCE_DISK"
  [[ -b "$TARGET_DISK" ]] || die "Target disk not found: $TARGET_DISK"
  [[ "$SOURCE_DISK" != "$TARGET_DISK" ]] || die "Source and target must be different"
}

ensure_not_mounted() {
  local dev="$1"

  if findmnt -rn -S "$dev" >/dev/null 2>&1; then
    die "$dev or its partition is mounted. Unmount it first."
  fi

  while read -r child; do
    [[ -n "$child" ]] || continue
    if findmnt -rn -S "$child" >/dev/null 2>&1; then
      die "$child is mounted. Unmount it first."
    fi
  done < <(lsblk -lnpo NAME "$dev" | tail -n +2)
}

cleanup_mounts() {
  umount -R "$TARGET_ROOT" 2>/dev/null || true
  umount -R "$TARGET_TOP_ROOT" 2>/dev/null || true
  umount -R "$TARGET_TOP_HOME" 2>/dev/null || true
  umount -R "$SRC_ROOT_MNT" 2>/dev/null || true
  umount -R "$SRC_ROOT_TOP" 2>/dev/null || true
  umount -R "$SRC_HOME_MNT" 2>/dev/null || true
  umount -R "$SRC_HOME_TOP" 2>/dev/null || true
  umount -R "$SRC_HOME_SNAP_MNT" 2>/dev/null || true
  umount -R "$SRC_EFI_MNT" 2>/dev/null || true
}

bytes_to_mib_ceil() {
  local bytes="$1"
  awk -v b="$bytes" 'BEGIN { printf "%d", int((b + 1048575) / 1048576) }'
}

show_current_state() {
  log "Current disk state"
  lsblk -f "$SOURCE_DISK" "$TARGET_DISK" || true

  echo
  echo "Source partitions:"
  lsblk -b -o NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS "$SOURCE_DISK"

  echo
  echo "Target partitions:"
  lsblk -b -o NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS "$TARGET_DISK"

  echo
  echo "Source usage:"
  df -hT / /home /boot/efi 2>/dev/null || true
}

confirm_plan() {
  local sp1 sp2 sp3 tp1 tp2 tp3
  sp1="$(part_path "$SOURCE_DISK" 1)"
  sp2="$(part_path "$SOURCE_DISK" 2)"
  sp3="$(part_path "$SOURCE_DISK" 3)"
  tp1="$(part_path "$TARGET_DISK" 1)"
  tp2="$(part_path "$TARGET_DISK" 2)"
  tp3="$(part_path "$TARGET_DISK" 3)"

  [[ -b "$sp1" ]] || die "Source EFI partition missing: $sp1"
  [[ -b "$sp2" ]] || die "Source root partition missing: $sp2"
  [[ -b "$sp3" ]] || die "Source home partition missing: $sp3"

  local p1_mib p2_mib p3_mib target_mib required_mib remaining_mib
  p1_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$sp1")")"
  p2_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$sp2")")"
  p3_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$sp3")")"
  target_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$TARGET_DISK")")"

  required_mib=$((1 + p1_mib + p2_mib + p3_mib + 4))
  remaining_mib=$((target_mib - required_mib))

#  (( target_mib > required_mib )) || die "Target disk is too small. Need more than ${required_mib}MiB."

  log "Planned operation"
  cat <<EOF
Version:
  $VERSION

Copy source:
  Disk:       $SOURCE_DISK
  EFI:        $sp1
  root:       $sp2 subvol=@
  home:       $sp3 subvol=@home
  ignored:    ${SOURCE_DISK}p4 or ${SOURCE_DISK}4 if present

Copy target:
  Disk:       $TARGET_DISK
  EFI:        $tp1, LABEL=BOOT
  root:       $tp2, LABEL=ROOT
  home:       $tp3, LABEL=HOME

New target partition plan:
  ${tp1}: FAT32 EFI, approx source p1 size = ${p1_mib}MiB
  ${tp2}: Btrfs root, approx source p2 size = ${p2_mib}MiB
  ${tp3}: Btrfs home, approx source p3 size = ${p3_mib}MiB

Remaining target disk space:
  approx ${remaining_mib}MiB left unallocated

New target Btrfs subvolumes:
  ${tp2}: @
  ${tp2}: @snapshots
  ${tp2}: @var_log
  ${tp2}: @pkg
  ${tp3}: @home
  ${tp3}: @home_snapshots

Mount after copy:
  ${tp2} subvol=@          -> /mnt/target
  ${tp2} subvol=@snapshots -> /mnt/target/.snapshots
  ${tp2} subvol=@var_log   -> /mnt/target/var/log
  ${tp2} subvol=@pkg       -> /mnt/target/var/cache/pacman/pkg
  ${tp3} subvol=@home           -> /mnt/target/home
  ${tp3} subvol=@home_snapshots -> /mnt/target/home/.snapshots
  ${tp1}                        -> /mnt/target/boot/efi

DANGER:
  ALL DATA ON $TARGET_DISK WILL BE ERASED.
EOF

  show_current_state

  if (( YES == 0 )); then
    echo
    echo "To continue, type exactly:"
    echo "  FORMAT $TARGET_DISK"
    read -r -p "> " ans
    [[ "$ans" == "FORMAT $TARGET_DISK" ]] || die "Confirmation failed. Aborted."
  else
    warn "--yes specified. Skipping interactive confirmation."
  fi
}

create_partitions() {
  local sp1 sp2 sp3 tp1 tp2 tp3
  sp1="$(part_path "$SOURCE_DISK" 1)"
  sp2="$(part_path "$SOURCE_DISK" 2)"
  sp3="$(part_path "$SOURCE_DISK" 3)"
  tp1="$(part_path "$TARGET_DISK" 1)"
  tp2="$(part_path "$TARGET_DISK" 2)"
  tp3="$(part_path "$TARGET_DISK" 3)"

  local p1_mib p2_mib p3_mib end1 end2 end3
  p1_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$sp1")")"
  p2_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$sp2")")"
  p3_mib="$(bytes_to_mib_ceil "$(blockdev --getsize64 "$sp3")")"

  end1=$((1 + p1_mib))
  end2=$((end1 + p2_mib))
  end3=$((end2 + p3_mib))

  log "Wiping and partitioning target disk: $TARGET_DISK"
  wipefs -a "$TARGET_DISK" || true
  parted "$TARGET_DISK" --script mklabel gpt
  parted "$TARGET_DISK" --script mkpart ESP fat32 1MiB "${end1}MiB"
  parted "$TARGET_DISK" --script set 1 esp on
  parted "$TARGET_DISK" --script mkpart root btrfs "${end1}MiB" "${end2}MiB"
  parted "$TARGET_DISK" --script mkpart home btrfs "${end2}MiB" "${end3}MiB"

  partprobe "$TARGET_DISK" || true
  sleep 2

  [[ -b "$tp1" && -b "$tp2" && -b "$tp3" ]] || die "Target partitions not found after partitioning."

  log "Formatting target partitions"
  mkfs.vfat -F32 -n BOOT "$tp1"
  mkfs.btrfs -f -L ROOT "$tp2"
  mkfs.btrfs -f -L HOME "$tp3"
}

create_target_subvolumes() {
  local tp2 tp3
  tp2="$(part_path "$TARGET_DISK" 2)"
  tp3="$(part_path "$TARGET_DISK" 3)"

  log "Creating target root subvolumes: @ @snapshots @var_log @pkg"
  mkdir -p "$TARGET_TOP_ROOT"
  mount "$tp2" "$TARGET_TOP_ROOT"
  btrfs subvolume create "$TARGET_TOP_ROOT/@"
  btrfs subvolume create "$TARGET_TOP_ROOT/@snapshots"
  btrfs subvolume create "$TARGET_TOP_ROOT/@var_log"
  btrfs subvolume create "$TARGET_TOP_ROOT/@pkg"
  umount "$TARGET_TOP_ROOT"

  log "Creating target home subvolumes: @home @home_snapshots"
  mkdir -p "$TARGET_TOP_HOME"
  mount "$tp3" "$TARGET_TOP_HOME"
  btrfs subvolume create "$TARGET_TOP_HOME/@home"
  btrfs subvolume create "$TARGET_TOP_HOME/@home_snapshots"
  umount "$TARGET_TOP_HOME"
}

mount_sources() {
  local sp2 sp3
  sp2="$(part_path "$SOURCE_DISK" 2)"
  sp3="$(part_path "$SOURCE_DISK" 3)"

  log "Mounting source root and home read-only"
  mkdir -p "$SRC_ROOT_MNT" "$SRC_HOME_MNT"

  mount -o ro,subvol=@ "$sp2" "$SRC_ROOT_MNT"
  mount -o ro,subvol=@home "$sp3" "$SRC_HOME_MNT"

  # If source has @var_log / @pkg, mount them so rsync copies their current contents.
  mkdir -p "$SRC_ROOT_MNT/var/log" "$SRC_ROOT_MNT/var/cache/pacman/pkg" "$SRC_ROOT_TOP"
  mount -o ro "$sp2" "$SRC_ROOT_TOP"
  if [[ -d "$SRC_ROOT_TOP/@var_log" ]]; then
    mount -o ro,subvol=@var_log "$sp2" "$SRC_ROOT_MNT/var/log"
  fi
  if [[ -d "$SRC_ROOT_TOP/@pkg" ]]; then
    mount -o ro,subvol=@pkg "$sp2" "$SRC_ROOT_MNT/var/cache/pacman/pkg"
  fi
  umount "$SRC_ROOT_TOP"

  # If source has a separate home snapshots subvolume, mount it separately.
  # It will be rsynced into TARGET /home/.snapshots later.
  mkdir -p "$SRC_HOME_TOP" "$SRC_HOME_SNAP_MNT"
  mount -o ro "$sp3" "$SRC_HOME_TOP"
  if [[ -d "$SRC_HOME_TOP/@home_snapshots" ]]; then
    mount -o ro,subvol=@home_snapshots "$sp3" "$SRC_HOME_SNAP_MNT"
  elif [[ -d "$SRC_HOME_TOP/@home/.snapshots" ]]; then
    warn "Source /home/.snapshots appears to be a normal directory inside @home. It will be copied separately by rsync."
  else
    warn "Source @home_snapshots was not found. /home/.snapshots may be empty on target unless it exists inside @home."
  fi
  umount "$SRC_HOME_TOP"

  findmnt "$SRC_ROOT_MNT" || true
  findmnt "$SRC_ROOT_MNT/var/log" || true
  findmnt "$SRC_ROOT_MNT/var/cache/pacman/pkg" || true
  findmnt "$SRC_HOME_MNT" || true
  findmnt "$SRC_HOME_SNAP_MNT" || true
}

mount_target_for_copy() {
  local tp1 tp2 tp3
  tp1="$(part_path "$TARGET_DISK" 1)"
  tp2="$(part_path "$TARGET_DISK" 2)"
  tp3="$(part_path "$TARGET_DISK" 3)"

  log "Mounting target for copy"
  mkdir -p "$TARGET_ROOT"
  mount -o noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=@ "$tp2" "$TARGET_ROOT"

  mkdir -p "$TARGET_ROOT/home" "$TARGET_ROOT/boot/efi" "$TARGET_ROOT/.snapshots" "$TARGET_ROOT/var/log" "$TARGET_ROOT/var/cache/pacman/pkg"
  mount -o noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=@snapshots "$tp2" "$TARGET_ROOT/.snapshots"
  mount -o noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=@var_log "$tp2" "$TARGET_ROOT/var/log"
  mount -o noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=@pkg "$tp2" "$TARGET_ROOT/var/cache/pacman/pkg"
  mount -o noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=@home "$tp3" "$TARGET_ROOT/home"
  mkdir -p "$TARGET_ROOT/home/.snapshots"
  mount -o noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvol=@home_snapshots "$tp3" "$TARGET_ROOT/home/.snapshots"
  mount "$tp1" "$TARGET_ROOT/boot/efi"

  findmnt "$TARGET_ROOT" || true
  findmnt "$TARGET_ROOT/.snapshots" || true
  findmnt "$TARGET_ROOT/var/log" || true
  findmnt "$TARGET_ROOT/var/cache/pacman/pkg" || true
  findmnt "$TARGET_ROOT/home" || true
  findmnt "$TARGET_ROOT/home/.snapshots" || true
  findmnt "$TARGET_ROOT/boot/efi" || true
}

copy_efi() {
  local sp1
  sp1="$(part_path "$SOURCE_DISK" 1)"

  log "Copying EFI partition by rsync"
  mkdir -p "$SRC_EFI_MNT"
  mount "$sp1" "$SRC_EFI_MNT"
  rsync -aHAXv --delete "$SRC_EFI_MNT"/ "$TARGET_ROOT/boot/efi"/
  umount "$SRC_EFI_MNT"
}

copy_root_home() {
  log "Copying root by rsync"
  rsync -aAXHv --numeric-ids --delete \
    --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/.snapshots/*","/home/*","/data0/*","/swapfile"} \
    "$SRC_ROOT_MNT"/ "$TARGET_ROOT"/

  log "Copying home by rsync"
  rsync -aAXHv --numeric-ids --delete \
    --exclude={".snapshots/*",".cache/*",".local/share/Trash/*"} \
    "$SRC_HOME_MNT"/ "$TARGET_ROOT/home"/

  if findmnt -rn "$SRC_HOME_SNAP_MNT" >/dev/null 2>&1; then
    log "Copying /home/.snapshots from source @home_snapshots by rsync"
    rsync -aAXHv --numeric-ids --delete \
      "$SRC_HOME_SNAP_MNT"/ "$TARGET_ROOT/home/.snapshots"/
  elif [[ -d "$SRC_HOME_MNT/.snapshots" ]]; then
    log "Copying /home/.snapshots normal directory by rsync"
    rsync -aAXHv --numeric-ids --delete \
      "$SRC_HOME_MNT/.snapshots"/ "$TARGET_ROOT/home/.snapshots"/
  else
    warn "No source /home/.snapshots content found to copy."
  fi
}

fix_fstab() {
  local tp1 tp2 tp3 boot_uuid root_uuid home_uuid
  tp1="$(part_path "$TARGET_DISK" 1)"
  tp2="$(part_path "$TARGET_DISK" 2)"
  tp3="$(part_path "$TARGET_DISK" 3)"

  boot_uuid="$(blkid -s UUID -o value "$tp1")"
  root_uuid="$(blkid -s UUID -o value "$tp2")"
  home_uuid="$(blkid -s UUID -o value "$tp3")"

  [[ -n "$boot_uuid" ]] || die "Could not get UUID for $tp1"
  [[ -n "$root_uuid" ]] || die "Could not get UUID for $tp2"
  [[ -n "$home_uuid" ]] || die "Could not get UUID for $tp3"

  log "Creating target fstab backup and replacing with UUID based fstab"
  cp -a "$TARGET_ROOT/etc/fstab" "$TARGET_ROOT/etc/fstab.bak.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true

  cat > "$TARGET_ROOT/etc/fstab" <<EOF
# /etc/fstab generated by copy_btrfs_system_to_disk_rsync_v06_3p_match_revised_v03.sh
# TARGET: first 3 partitions only
# UUIDs:
#   BOOT: $boot_uuid
#   ROOT: $root_uuid
#   HOME: $home_uuid

UUID=$boot_uuid  /boot/efi               vfat   umask=0077                                                    0 2

UUID=$root_uuid  /                       btrfs  subvol=@,compress=zstd:3,noatime,ssd,discard=async            0 0
UUID=$root_uuid  /.snapshots             btrfs  subvol=@snapshots,compress=zstd:3,noatime,ssd,discard=async   0 0
UUID=$root_uuid  /var/log                btrfs  subvol=@var_log,compress=zstd:3,noatime,ssd,discard=async     0 0
UUID=$root_uuid  /var/cache/pacman/pkg   btrfs  subvol=@pkg,compress=zstd:3,noatime,ssd,discard=async         0 0

UUID=$home_uuid  /home                   btrfs  subvol=@home,compress=zstd:3,noatime,ssd,discard=async        0 0
UUID=$home_uuid  /home/.snapshots        btrfs  subvol=@home_snapshots,compress=zstd:3,noatime,ssd,discard=async 0 0

tmpfs           /tmp                    tmpfs  defaults,noatime,mode=1777,size=8G                           0 0

# NOTE:
#   If discard=async is unstable on your SSD/USB enclosure,
#   remove discard=async and use:
#
#     sudo systemctl enable --now fstrim.timer

EOF

  log "New fstab"
  cat "$TARGET_ROOT/etc/fstab"
}

final_check() {
  log "Final mount check"
  findmnt "$TARGET_ROOT" || true
  findmnt "$TARGET_ROOT/.snapshots" || true
  findmnt "$TARGET_ROOT/var/log" || true
  findmnt "$TARGET_ROOT/var/cache/pacman/pkg" || true
  findmnt "$TARGET_ROOT/home" || true
  findmnt "$TARGET_ROOT/home/.snapshots" || true
  findmnt "$TARGET_ROOT/boot/efi" || true

  log "Target lsblk"
  lsblk -f "$TARGET_DISK"

  cat <<EOF

Next manual steps:

  sudo manjaro-chroot $TARGET_ROOT /bin/bash

Inside chroot:

  mkinitcpio -P
  grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck
  update-grub

If update-grub is not available:

  grub-mkconfig -o /boot/grub/grub.cfg

After exit:

  exit
  sudo vi $TARGET_ROOT/etc/fstab
  sudo umount -R $TARGET_ROOT

EOF
}

main() {
  require_root
  require_cmds
  parse_args "$@"

  trap 'warn "Interrupted or failed. Some mount points may remain mounted. Check with: findmnt | grep /mnt";' ERR

  cleanup_mounts
  ensure_not_mounted "$TARGET_DISK"
  confirm_plan

  create_partitions
  create_target_subvolumes
  mount_sources
  mount_target_for_copy
  copy_efi
  copy_root_home
  fix_fstab
  final_check

  log "Done. Target is ready for manjaro-chroot."
}

main "$@"

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?