３ 各スクリプトの詳細＆引用元・参考先

**[１ Minecraft bedrock server の建て方（Backup by Git）](https://qiita.com/QiitaYkuyo/items/625ed2b6025c93211514)**
**[２ スクリプト一覧](https://qiita.com/QiitaYkuyo/items/cdbb95c79746f85453a0)**
**３ 各スクリプトの詳細＆引用元・参考先（今ここ）**

###### ＜ ユーザー・グループ作成 ＞
```bash:bash
sudo adduser --system \
             --home $HOME_DIR \
             --shell $Login_Shell \
             --group \
             $New_USER
```
    ・--system：プログラム実行用アカウントとして構成する
    ・--home  ：新規ユーザー用の作業ディレクトリを生成する
    ・--shell ：ログインしたときに使うシェルを指定する
    ・--group ：システム ユーザーと同じ名前と ID を持つグループを作成する
               （--systemと組み合わせない場合、ユーザーではなくグループを作成する）
**引用元・参考先**
Ubuntu Manuals [adduser, addgroup]
　・https://manpages.ubuntu.com/manpages/trusty/man8/adduser.8.html
Run a private Minecraft server with Tailscale
　・https://tailscale.com/kb/1137/minecraft/
```bash:bash
# 新規ユーザーのID、所属グループを表示
id $New_USER
# 特定のグループの所属メンバーを確認
getent group $New_GROUP
```
    ・uid=[user id]     ：ユーザーのID（ユーザー名）を表す
    ・gid=[group id]    ：メイングループGID（メイングループ名）を表す
    ・groups=[group id] ：サブグループ名一覧（＋GID）の表示

    ・user:x:gid:group  グループの所属メンバーを確認
**引用元・参考先**
ユーザの情報を確認するidコマンドの説明【Linuxコマンド集】
　・https://eng-entrance.com/linux-command-id
Linux グループ一覧の確認と/etc/group ファイル
　・https://kazmax.zpp.jp/linux_beginner/etc_group.html
Linuxで、グループ内のユーザを確認するコマンド
　・https://www-creators.com/archives/417

###### ＜ home ディレクトリに、マインクラフトを展開 ＞
```bash:bash
# 作成したユーザーにログイン
sudo -i -u Minecraft

# サーバーのアーカイブをダウンロード、展開
DOWNLOAD_URL=$(curl -H "Accept-Encoding: identity" -H "Accept-Language: en" -s -L -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; BEDROCK-UPDATER)" https://minecraft.net/en-us/download/server/bedrock/ |  grep -o 'https://minecraft.azureedge.net/bin-linux/[^"]*')
sudo wget $DOWNLOAD_URL -O /home/mcserver/minecraft_bedrock/bedrock-server.zip
```
**スクリプト元**
Setting up a Minecraft Bedrock Server on Ubuntu
　・https://pimylifeup.com/ubuntu-minecraft-bedrock-server/#installing-the-minecraft-bedrock-server-on-ubuntu

###### ＜ 設定ファイル・ワールドデータのバックアップ先を作成 ＞
```bash:bash
sudo rm -r "/opt/MC_Manage"
sudo mkdir -p /opt/MC_Manage/{Properties,World_Backup}
sudo chown -R $New_USER:$New_GROUP "/opt/MC_Manage/"
sudo chmod -R 750 "/opt/MC_Manage/"
# 確認
sudo ls -laF "/opt/MC_Manage/"
```
    ・rm -r    ：フォルダごと削除
    ・mkdir -p ：親ディレクトリごと作成
    ・chown -R ：$New_USERを所有者とし、$New_GROUPを所有グループとする
    ・chmod -R ：所有者に完全なアクセス権(=750)を付与
**引用元・参考先**
Linuxコマンド【 chown 】ファイルの所有者やグループを変更する
　・https://webkaru.net/linux/chown-command/
【Linuxコマンド】chmodでパーミッションを設定する方法
　・https://www.sejuku.net/blog/50161

###### ＜ サービス化、起動・停止スクリプト ＞
```bash:minecraft.service
ExecStart=+/usr/bin/setpriv --reuid=$(/usr/bin/id -u minecraft) --regid=$(/usr/bin/id -g minecraft) --init-groups /opt/MC_Manage/start.sh
ExecStartPost=+/usr/bin/bash /opt/MC_Manage/backup.sh
ExecStop=+/usr/bin/setpriv --reuid=$(/usr/bin/id -u minecraft) --regid=$(/usr/bin/id -g minecraft) --init-groups /usr/bin/bash -c "/usr/bin/env XDG_RUNTIME_DIR=/run/user/$(/usr/bin/id -u minecraft) /usr/bin/systemctl --user stop nonstop_save.timer; exit 0"
ExecStop=+/usr/bin/setpriv --reuid=$(/usr/bin/id -u minecraft) --regid=$(/usr/bin/id -g minecraft) --init-groups /opt/MC_Manage/stop.sh
```
    ・Prefix："+"
    　　実行パスの先頭に "+"が付いている場合、そのプロセスは完全な特権で実行されます。
    　　このモードでは、User=, Group=, CapabilityBoundingSet= や様々なファイルシステムの名前空間オプション (PrivateDevices=, PrivateTmp= など) で設定した特権制限は、起動したコマンドラインには適用されません (ただし、他の ExecStart=, ExecStop=, ... 行には影響があります)。
    　　しかし、DevicePolicy= のような、コントロールグループ全体に適用されるオプションはバイパスされないことに注意してください。
    ・setpriv --reuid "uid" --regid "gid" "--clear-groups/--groups/--keep-groups/--init-groups" --reset-env
        ･--reuid
            "uid" はユーザー名でも構いません。
        ･--regid
            "gid" はグループ名でも構いません。
        ･--clear-groups/--groups/--keep-groups/--init-groups
            グループを指定した場合には、いずれかが必要です。
        ･--reset-env
            各変数：HOME, SHELL, USER, LOGNAME, PATH=/usr/local/bin:/bin:/usr/bin を uid で指定したユーザーのものに初期化します。
**スクリプト元**
Run a private Minecraft server with Tailscale
　・https://tailscale.com/kb/1137/minecraft/
**引用元・参考先**
Systemd入門(4) - serviceタイプUnitの設定ファイル
　・https://enakai00.hatenablog.com/entry/20130917/1379374797
systemd.serviceのmanページ
　・https://www.freedesktop.org/software/systemd/man/systemd.service.html
systemd serviceから呼ぶシェルではsudoではなくsetprivを使う
　・https://rheb.hatenablog.com/entry/setpriv
setpriv(1) — Linux manual page
　・https://man7.org/linux/man-pages/man1/setpriv.1.html

```bash:start.sh
/usr/bin/tmux new-session -s minecraft -d
/usr/bin/tmux pipe-pane -t minecraft "cat >$Mine_DIR/Command.log"
/usr/bin/tmux send -t minecraft "LD_LIBRARY_PATH=$Mine_DIR ./bedrock_server | tee $Mine_DIR/Result.log" ENTER
```
    ・tmux new-session -s minecraft -d
        バックグラウンドで、minecraft と名前を付けたターミナルを起動します
    ・tmux pipe-pane -t minecraft "cat >$Mine_DIR/Command.log"
        ターミナルに表示されたものを丸ごとログファイルに出力
    ・tmux send -t minecraft "LD_LIBRARY_PATH=$Mine_DIR ./bedrock_server | tee $Mine_DIR/Result.log" ENTER
        Minecraft Server の実行結果をターミナルとログファイルに出力
**スクリプト元**
Run a private Minecraft server with Tailscale
　・https://tailscale.com/kb/1137/minecraft/
**引用元・参考先**
tmux でログ保存
　・https://seaoak.cocolog-nifty.com/read/2014/08/tmux-f687.html

```bash:backup.sh
/usr/bin/env XDG_RUNTIME_DIR=/run/user/$(/usr/bin/id -u minecraft) \
  /usr/bin/systemd-run \
    --on-active=${BK_INTERVAL} \
    --on-unit-active=${BK_INTERVAL} \
    --user \
    --collect \
    --unit=nonstop_save.service \
      /bin/bash -c "~~~~~~"
```
    ・--on-active
        タイマーが有効にされてからユニットを初めて実行するまでの時間
    ・--on-unit-active
        ユニットが最後に実行された時から次に実行されるまでの間隔
    ・--user
        root ではなく各ユーザー権限で実行する
    ・--collect
        正常に終了したか失敗したかにかかわらず、ユニットをアンロードします
    ・--unit
        既存のユニットが存在し、かつこの後にコマンドを入力しない場合は、指定したユニットが実行されます
        既存のユニットが存在せず、この後にコマンドを入力した場合は、指定したユニットの名前で入力したコマンドがサービス化されます
**引用元・参考先**
ユーザー権限のsystemdにFailed to connect to busで繋がらない時の対処方法
　・https://blog.n-z.jp/blog/2020-06-02-systemd-user-bus.html
systemd-run
　・https://www.freedesktop.org/software/systemd/man/systemd-run.html
systemd-run で定期的に実行するタスクをさっと登録する
　・https://qiita.com/ngyuki/items/a0404322e59f77cd512c

```bash:stop.sh
/usr/bin/tmux send -t minecraft stop ENTER
/usr/bin/tmux kill-session -t minecraft
```
    ・tmux send -t minecraft stop ENTER
        Minecraft Server に stop を送信します
    ・tmux kill-session -t minecraft
        tmux で起動した Minecraft 用のターミナルを終了します
**スクリプト元**
Run a private Minecraft server with Tailscale
　・https://tailscale.com/kb/1137/minecraft/

```bash:bash
sudo loginctl enable-linger minecraft
```
    ・ログインしていないユーザーの長時間実行サービスを実行可能にする
**引用元・参考先**
ユーザー権限のsystemdにFailed to connect to busで繋がらない時の対処方法
　・https://blog.n-z.jp/blog/2020-06-02-systemd-user-bus.html
loginctl
　・https://www.freedesktop.org/software/systemd/man/loginctl.html

```bash:bash
function MC () {
  unset Arguments
  Arguments="${*//\"/\\\"}"
  Arguments="${Arguments:-exec bash}"
  sudo setpriv --reuid=$(/usr/bin/id -u minecraft) --regid=$(/usr/bin/id -g minecraft) --init-groups --reset-env bash --login -O expand_aliases -c "cd ~;IFS=' ' ${Arguments//\\\"/\"}"
}
```
    ・引数がない場合は ユーザー：minecraft でターミナルを起動する
    ・引数に含まれるエスケープされた二重引用符をさらにエスケープし、実行時にエスケープを外す
    ・bash --login -O expand_aliases
        ログインシェルとして起動し、/etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile を読み込む。終了時に ~/.bash_logout を読み込む。
        エイリアスの展開を行う
**引用元・参考先**
BASH
　・https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html

###### ＜ バージョン管理の環境を用意 ＞
```bash:bash
git init "~~~~~~"

cat << __EOF__ > "~~~~~~"
[user]
     name = Minecraft Server
    email = MinecraftServer@mydomain.com
__EOF__

git -C "~~~~~~" config --local gc.reflogExpire "never"
git -C "~~~~~~" config --local gc.reflogExpireUnreachable "3.days"
git -C "~~~~~~" config --local gc.pruneExpire "3.days.ago"

git -C "~~~~~~" commit --allow-empty --allow-empty-message -m ''
git -C "~~~~~~" commit --amend --allow-empty --allow-empty-message --no-edit

git -C "~~~~~~" reflog delete HEAD@{1} $(git -C "~~~~~~" branch --show-current)@{1}
```
    ・git init
       ：git で管理できるようにディレクトリを初期化
    ・cat << __EOF__ > "~~~/.git/config"
        ⌇　⌇　⌇
    　__EOF__ 
       ：git commit に必要な情報を設定
    ・git commit --allow-empty --allow-empty-message -m ''
       ：空コミット
    ・git commit --amend --allow-empty --allow-empty-message --no-edit
       ：上書き空コミット
    ・git reflog delete HEAD@{1} $(git branch --show-current)@{1}
       ：初回の空コミットを消す
    ＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿
    バックアップファイルの保存期限を設定
    ・git config --local gc.reflogExpire "never"
       ：最新のバックアップは無期保存
    ・git config --local gc.reflogExpireUnreachable "3.days"
       ：最新でなくなった後の保存期限
    ・git config --local gc.pruneExpire "3.days.ago"
       ：最新でなくなった後の保存期限
**引用元・参考先**
Git gc
　・https://www.atlassian.com/git/tutorials/git-gc

###### ＜ サーバーを止めないで保存するスクリプト ＞
```bash:
function mc_save () {
  if [ "$(tmux ls -F '#{session_name}' -f '#{==:#{session_name},minecraft}' 2>/dev/null)" != "minecraft" ]; then
    git --git-dir="$Worlds_Bak_DIR/.git" \
        --work-tree="$Worlds_DIR" \
        add --intent-to-add -- :/
    git --git-dir="$Worlds_Bak_DIR/.git" \
        --work-tree="$Worlds_DIR" \
        diff --quiet --exit-code
    [ "$?" = "1" ] && backup_worlds 'Backup of stopped servers'
    # 新規ファイルか更新されたファイルがあった場合は、$?=1 を示す
    return 0
  fi

  /usr/bin/tmux send -t minecraft "save resume" ENTER
  /usr/bin/tmux send -t minecraft "save hold" ENTER

  EXITCODE=1 ; TRY_COUNT=0
  while [ "$EXITCODE" -ne "0" ]; do
   ((TRY_COUNT++))
    /usr/bin/tmux send -t minecraft "save query" ENTER
    sleep 1
    grep "Data saved. Files are now ready to be copied." <(tail -30 "$Mine_DIR/Result.log") > /dev/null 2>&1
    EXITCODE=$?
    if [ "$TRY_COUNT" -eq "20" ]; then break; fi
  done
}
```
    ・tmux ls -F '#{session_name}' -f '#{==:#{session_name},minecraft}'
        tmux に拠る minecraft のセッションが存在するか確認
    ・git add --intent-to-add
        blob オブジェクトは作成せず、インデックスに記入のみ行う
    ・diff --quiet --exit-code
        新規ファイルか更新されたファイルがあった場合は、$?=1 を返す
    ・[ "$?" = "1" ] && 
        $?=1 を返したときのみ後続のコマンドを実行する
    ・while ～～～ done
       Minecraft Server が "Data saved. Files are now ready to be copied." を返すまで "save query" を送信し続ける。最大20回まで。
**スクリプト元**
mikenye/docker-minecraft_bedrock_server
　・https://github.com/mikenye/docker-minecraft_bedrock_server/blob/main/rootfs/usr/local/bin/run_backup
**引用元・参考先**
Working with formats
　・https://github.com/tmux/tmux/wiki/Formats
tmuxチートシート
　・https://qiita.com/nmrmsys/items/03f97f5eabec18a3a18b
Git での新規ファイル作成を含んだファイル変更有無の判定方法
　・https://reboooot.net/post/how-to-check-changes-with-git/
diffコマンドの exit code って、差分がなければ0、差分が有れば1、失敗したら2なんだね
　・https://kinoppyd.dev/blog/diff-command-returns-1-but-success/

###### ＜ Git を使ってワールドデータ・設定ファイルのバックアップ ＞
```bash:backup_props.bash
  git --git-dir="$Props_Bak_DIR/.git" \
      --work-tree="$Mine_DIR" \
      add -- permissions.json server.properties whitelist.json
  git -C "$Props_Bak_DIR" commit --amend --date=now --allow-empty-message -m ''"$@"
  git -C "$Props_Bak_DIR" --no-pager log --max-count=${1:-5} --reflog --format='%C(auto)%+h [%ad] %Cgreen%s' --name-only; echo ''
}
```



```bash:backup_worlds.bash
function backup_worlds () {
  SET_PATH="GIT_DIR=\"$Worlds_Bak_DIR/.git\" GIT_WORK_TREE=\"$Worlds_DIR\""
  while read READ; do 
    Args="$Args \"$READ\""
  done < <(eval $SET_PATH git ls-files --others --exclude-standard :/)
  eval $SET_PATH git add -- $Args >/dev/null 2>&1
  eval $SET_PATH git add --update
  eval $SET_PATH git update-index --remove --stdin < <(eval $SET_PATH git ls-files :/)
  git -C "$Worlds_Bak_DIR" commit --amend --date=now --allow-empty-message -m ''"$@"
}
function show_worlds_bak () {
  SKIP=0
  while read LINE; do
    git -C "$Worlds_Bak_DIR" log --max-count=1 --skip=$SKIP --reflog --format='%C(auto)%h [%ad] %Cgreen%s'
    git -C "$Worlds_Bak_DIR" ls-tree -d --name-only $LINE
    echo ''
   ((SKIP++))
  done < <(git -C "$Worlds_Bak_DIR" log --max-count=${1:-5} --reflog --format='%C(auto)%h')
}
```
    ・git add --update
       ：リポジトリにある（追跡されている）更新されたすべてのファイルをステージに上げる
    ・git commit --date=now
       ：今の日時を付けて情報を記録
    ・git log --max-count=n/-n --reflog --format='%C(auto)%h [%ad] %Cgreen%s'
       ：コミットの履歴を表示する
       -n       ：直近 n 件を表示（＝ --max-count=n ）
       --reflog ：git reflog で表示される操作の内、すべての git commit を列挙する
       --format
          %C(auto)：次のみ自動で色付け
            green ：以降すべて緑で表示
          %h      ：オブジェクトを一意に指定するプレフィックスを表示
          [%ad]   ：[日付] で表示
          %s      ：コミット時のメッセージを表示
       --name-only
                ：変更されたファイルの名前のみを表示
    ・git ls-tree -d --name-only <commit id>
       ：ツリーオブジェクトの内容を一覧表示する
       -d       ：サブディレクトリは表示せず、ツリーエントリのみ表示
       --name-only
                ：ディレクトリ/ファイル名のみを 1 行に 1 つずつリスト
**引用元・参考先**
Git - Documentation
　・https://git-scm.com/doc
　・https://git-scm.com/docs/git-log
　・https://git-scm.com/docs/git-ls-tree
GITのls-treeで内部ファイル一覧の取得方法とそれらが文字化けする時の解決方法
　・https://blog.myntinc.com/2020/04/gitls-tree.html

###### ＜ 復元用スクリプト ＞
```bash:# 既存のワールドを上書き
    read Commit_ID < \
    <(git --git-dir="$Worlds_Bak_DIR/.git" \
          --work-tree="$Worlds_DIR" \
          for-each-ref \
            --sort=-committerdate \
            --count=1 \
            --format="%(objectname:short)" \
            refs/heads)
  backup_worlds
  git --git-dir="$Worlds_Bak_DIR/.git" \
      --work-tree="$Worlds_DIR" \
      restore --no-overlay --worktree --source=$Commit_ID -- "$2"
```
```bash:# 別のワールドとして復元
  mkdir -p "$Another_DIR"
  git -C "$Worlds_Bak_DIR" archive --format=tar $1 -- "$2" | tar Cx "$Another_DIR" --strip-components 1
```
    ・read Commit_ID < <(~~~~~~)
        ：コマンド置換をして、その標準出力を変数に代入する
    ・for-each-ref \
        --sort=-committerdate --count=1 \
        --format="%(objectname:short)" refs/heads
        ：refs/heads 以下にある各参照の内、最も最近コミットされたもののハッシュ値を取得する
    ・backup_worlds
        ：バックアップ処理内で worlds 内のすべてのファイルをインデックスに記入する（追跡状態にする）
    ・git restore --no-overlay --worktree --source="commit id" -- world
        = git restore -WS -s "commit id" -- file
        ："backup_worlds" ですべてのファイルを追跡し、"git restore ~~~~~~ -- world" で指定したワールドを過去の状態に復元する
    ・mkdir -p "new world"
    ・git archive (--format=tar) world -- "new world" | tar Cx "new world" --strip-components 1
        ：新しいワールドフォルダーを作成し、バックアップしたワールドデータをそこに復元する
        --format=tar         ：既定
        --strip-components 1 ：ディレクトリ構造の第一階層を無視する
        [C/--directory] DIR  ：指定したディレクトリに展開する
        [x/--extract] ~.tar  ：指定した tar ファイルを展開する
                             　 | を介して tar ファイル自体も受け取れる
**引用元・参考先**
Git - Documentation
　・https://git-scm.com/doc
In BASH How Can One Recursively Copy Only Content Version-Controlled by Git?
　・https://stackoverflow.com/questions/44143371/in-bash-how-can-one-recursively-copy-only-content-version-controlled-by-git/44144020#44144020

###### ＜ バックアップの削除用スクリプト ＞
```bash:git_delete.bash
  while read COUNT_HEAD; do 
    git -C "$Working_DIR" reflog delete "HEAD@{$SKIP}"
  done < <(git -C "$Working_DIR" --no-pager log --walk-reflogs --skip=$SKIP --format='%gd' HEAD)

  while read COUNT_MAIN; do 
    git -C "$Working_DIR" reflog delete "$(git -C "$Working_DIR" branch --show-current)@{$SKIP}"
  done < <(git -C "$Working_DIR" --no-pager log --walk-reflogs --skip=$SKIP --format='%gd' refs/heads/$(git -C "$Working_DIR" branch --show-current))

  git -C "$Working_DIR" gc --quiet
```
    ・while ~~~~~~ done
        ：参照ログを、最新から "$SKIPに代入した数-1" まで残して、あとを消す
        ：これを HEAD と refs/heads/~~~ 2つの参照ログで行う
    ・git gc --quiet
        ：上記で残らなかった参照ログ及び, gc.reflogExpire, gc.reflogExpireUnreachable, gc.pruneExpire を元にして blob オブジェクトの削除を行う
        ：ただし、参照ログに表示されているコミット+1までコミット本体が残る
        --quiet
        ：ターミナル出力を抑制する
**引用元・参考先**
Git - Documentation
　・https://git-scm.com/doc
　・https://git-scm.com/docs/git-reflog