Ubuntu 22.04/24.04/26.04 一括構築スクリプト
#!/bin/bash
# ============================================================
# setup.sh : Ubuntu 22.04 / 24.04 / 26.04(Lubuntu 含む)環境構築
#
# 【実行方法】
# 通常の利用者(sudo 可能なアカウント)自身で実行する。root では実行しない。
# ./setup.sh 通常実行(未完了ブロックから再開)
# ./setup.sh --remaster 配布イメージ作成前の整理(B90)も実行する
# ./setup.sh --reset 進捗を破棄し,最初のブロックから実行する
# ./setup.sh --check 導入結果の確認(B95)だけを実行する(状態を変更しない)
#
# 【自己完結】
# 外部スクリプトや配布サーバ上の設定ファイルは取得しない。利用者設定は
# すべて本スクリプト内に記述する(B70 のヒアドキュメント)。組織固有の
# 内容がある場合は当該箇所を直接書き換えること。
#
# 【対応する記事】
# 導入するアプリの一覧は「Ubuntu 22.04/24.04/26.04 開発・研究環境構築
# ガイド」と一致させてある。相違は,アカウント連携・ライセンス同意・配布
# URL の不安定さのため自動化しないものだけであり,いずれも確認事項に記録
# する。該当するのは次の 7 件である。
# Unreal Engine / Intel oneAPI MKL / MakeHuman /
# Steam・Lutris・Heroic Games Launcher /
# SQLiteStudio / SQL Workbench/J / BlueJ・Greenfoot
#
# 【ブロック分割の意味】
# 本スクリプトは役割ごとに以下のブロックへ分かれる。後のブロックは前の
# ブロックの成果に依存する。
# B10 システム基盤 ミラー変更・全体更新・基本ツール・時刻・ロケール
# → 以降すべての前提
# B20 リポジトリ登録 サードパーティ APT リポジトリ → B30 の前提
# B30 アプリ導入(apt) → B40/B60/B70 の前提
# B40 アプリ導入(ソース・外部配布物) → B70 の前提(pyenv)
# B50 不要パッケージ削除
# B60 システム設定・不要サービス停止
# B70 ユーザ設定
# B80 不要ファイル削除・後処理
# B90 リマスター準備(--remaster 指定時のみ)
# B95 導入結果の確認(ブロックではない。状態を変更しない)
# ロケールは B10 で確定させる。B30 の check-language-support が現在の
# ロケール設定をもとに必要パッケージを判定するためである。
#
# 【エラーの扱い】
# 普通のエラー : 記録して続行。末尾で一覧表示し,対処は人間が判断する。
# 深刻なエラー : 後続ブロックの失敗が確実な場合のみ。当該ブロックの末尾で
# 停止し,そのブロックは未完了のまま残す。原因を人手で除去
# して本スクリプトを再実行すれば,当該ブロックの先頭から
# 再開する。エラーによって処理内容を変えることはしない。
#
# 【リポジトリの提供状況】
# サードパーティ APT リポジトリは,登録前に当該コード名向けの Release を
# 取得できるかどうかだけを確認する。取得できない場合は登録せず,関連する
# パッケージも導入せず,確認事項として記録する。これは環境条件による分岐
# であり,エラーによる分岐ではない。
#
# 【APT ロックへの対処】
# 実行前に自動更新系ユニットを停止し,全 apt-get 呼び出しに
# DPkg::Lock::Timeout を付けてロックの解放を待つ。ロックファイルの削除や
# apt プロセスの強制終了は dpkg データベースを破壊するため行わない。
# 停止した自動更新ユニットは全ブロック完了後に再開する。深刻なエラーで
# 停止した場合は停止したままとし,再実行時に改めて停止処理を行う。
# ============================================================
set -u
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
STATE_DIR="/var/lib/setup-sh"
STATE_FILE="${STATE_DIR}/completed-blocks"
DO_REMASTER=0
DO_RESET=0
DO_CHECK=0
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
export NEEDRESTART_MODE=a
export NEEDRESTART_SUSPEND=1
APT_OPTS=(
-y
-o Acquire::Retries=3
-o DPkg::Lock::Timeout=1800
-o Dpkg::Options::=--force-confdef
-o Dpkg::Options::=--force-confold
)
FAILED_TASKS=() # 普通のエラー(全ブロック通算)
MANUAL_NOTES=() # 人手での確認事項
FATAL_TASKS=() # 深刻なエラー(現ブロック分)
CHECK_NG=() # B95 で NG となった項目
CURRENT_BLOCK=""
KEEPALIVE_PID=""
OS_ID=""; OS_VERSION_ID=""; OS_CODENAME=""; IS_LUBUNTU=0; HAS_NVIDIA=0
TARGET_USER=""; TARGET_HOME=""
# Python 環境(B40 で構築し B95 で確認する)
PY_MAIN_VERSION="3.13.15" # 主環境
PY_MP_VERSION="3.12.13" # MediaPipe 用(新しい Python 向けホイールが未提供のため)
VENV_MAIN="${HOME}/venvs/myproject_env"
VENV_MP="${HOME}/venvs/mediapipe_env"
# ------------------------------------------------------------
# 記録と表示
# ------------------------------------------------------------
info() { echo "[INFO ] $*"; }
step() { echo "------------------------------------------------------------"; echo "[STEP ] $*"; }
record_failure() { FAILED_TASKS+=("${CURRENT_BLOCK} / $1"); echo "[FAIL ] $1"; }
record_manual() { MANUAL_NOTES+=("$1"); echo "[NOTE ] $1"; }
record_fatal() { FATAL_TASKS+=("$1"); echo "[FATAL] $1"; }
report() {
echo
echo "============================================================"
echo "実行結果レポート"
echo "============================================================"
echo "OS : ${OS_ID} ${OS_VERSION_ID} (${OS_CODENAME}) Lubuntu=${IS_LUBUNTU} NVIDIA=${HAS_NVIDIA}"
echo "対象ユーザ : ${TARGET_USER} (${TARGET_HOME})"
if [ -f "${STATE_FILE}" ]; then
echo "完了ブロック: $(tr '\n' ' ' < "${STATE_FILE}")"
fi
echo
if [ "${#FAILED_TASKS[@]}" -eq 0 ]; then
echo "-- 普通のエラー: なし"
else
echo "-- 普通のエラー(${#FAILED_TASKS[@]} 件): 処理は続行済み。内容を確認し人手で対応すること"
for t in "${FAILED_TASKS[@]}"; do echo " - ${t}"; done
fi
echo
if [ "${#CHECK_NG[@]}" -gt 0 ]; then
echo "-- 導入確認(B95)で NG(${#CHECK_NG[@]} 件): 該当アプリの導入手順を確認すること"
for t in "${CHECK_NG[@]}"; do echo " - ${t}"; done
echo
fi
if [ "${#MANUAL_NOTES[@]}" -gt 0 ]; then
echo "-- 人手での確認事項"
for t in "${MANUAL_NOTES[@]}"; do echo " - ${t}"; done
echo
fi
echo "============================================================"
}
# 前提条件が満たされない場合(ブロック開始前)
abort() {
echo "[FATAL] $1"
report
echo "原因を除去してから ${SCRIPT_PATH} を再実行すること。"
exit 1
}
# ------------------------------------------------------------
# ブロック管理(進捗の記録と再開)
# ------------------------------------------------------------
block_completed() { [ -f "${STATE_FILE}" ] && grep -qx "$1" "${STATE_FILE}"; }
block_mark() { echo "$1" | sudo tee -a "${STATE_FILE}" >/dev/null; }
run_block() {
local id="$1" title="$2" func="$3"
if block_completed "${id}"; then
echo "[SKIP ] ${id} ${title} : 完了済みのため実行しない"
return 0
fi
echo
echo "============================================================"
echo "[BLOCK] ${id} ${title}"
echo "============================================================"
CURRENT_BLOCK="${id}"
FATAL_TASKS=()
"${func}"
if [ "${#FATAL_TASKS[@]}" -gt 0 ]; then
echo
echo "============================================================"
echo "[STOP ] ブロック ${id} (${title}) で深刻なエラーが発生した"
echo "============================================================"
for t in "${FATAL_TASKS[@]}"; do echo " ! ${t}"; done
echo
echo "後続ブロックの失敗が確実なため,ここで停止する。"
echo "APT の自動更新は停止したままである。"
report
echo "対処後に ${SCRIPT_PATH} を再実行すると,ブロック ${id} の先頭から再開する。"
exit 1
fi
block_mark "${id}"
echo "[DONE ] ${id} ${title}"
}
# ------------------------------------------------------------
# 共通処理
# ------------------------------------------------------------
unit_exists() { systemctl list-unit-files --no-legend "$1" 2>/dev/null | grep -q .; }
# 既存ファイルを一度だけ .DIST として退避する(何度実行してもよい)
backup_once() {
[ -e "$1" ] || return 0
[ -e "$1.DIST" ] && return 0
sudo cp -a "$1" "$1.DIST"
}
apt_prepare() {
local u
for u in apt-daily.timer apt-daily-upgrade.timer \
apt-daily.service apt-daily-upgrade.service \
unattended-upgrades.service packagekit.service; do
unit_exists "$u" || continue
sudo systemctl stop "$u" || return 1
done
return 0
}
apt_restore() {
local u
for u in apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service; do
unit_exists "$u" || continue
sudo systemctl start "$u" || record_failure "自動更新ユニットの再開: $u"
done
}
apt_update() { sudo -E apt-get "${APT_OPTS[@]}" update; }
apt_upgrade() { sudo -E apt-get "${APT_OPTS[@]}" dist-upgrade; }
apt_fix() { sudo -E apt-get "${APT_OPTS[@]}" -f install; }
apt_autoremove() { sudo -E apt-get "${APT_OPTS[@]}" --purge autoremove; }
apt_autoclean() { sudo -E apt-get "${APT_OPTS[@]}" autoclean; }
apt_clean() { sudo -E apt-get "${APT_OPTS[@]}" clean; }
# 1 パッケージずつ導入し,失敗は当該パッケージのみ記録する
apt_install() {
local p
for p in "$@"; do
sudo -E apt-get "${APT_OPTS[@]}" install "$p" || record_failure "導入: $p"
done
}
# ローカルの deb を依存解決付きで導入する
apt_install_deb() { sudo -E apt-get "${APT_OPTS[@]}" install "$1"; }
# 導入済みのものだけ削除する(何度実行してもよい)
apt_purge() {
local p
for p in "$@"; do
dpkg-query -W -f='${Status}' "$p" 2>/dev/null | grep -q "ok installed" || continue
sudo -E apt-get "${APT_OPTS[@]}" --purge remove "$p" || record_failure "削除: $p"
done
}
# パターンに一致する導入済みパッケージを列挙して削除する(apt に * を渡さない)
apt_purge_glob() {
local pat pkgs
for pat in "$@"; do
pkgs="$(dpkg-query -W -f='${Package} ${Status}\n' "${pat}" 2>/dev/null \
| awk '$NF=="installed"{print $1}')"
[ -n "${pkgs}" ] || continue
apt_purge ${pkgs}
done
}
pkg_installed() { dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -q "ok installed"; }
snap_install() {
local p
command -v snap >/dev/null 2>&1 || { info "snapd が無いため snap パッケージは導入しない: $*"; return 0; }
for p in "$@"; do
snap list "$p" >/dev/null 2>&1 && continue
sudo snap install "$p" || record_failure "snap 導入: $p"
done
}
service_disable() {
local u
for u in "$@"; do
unit_exists "$u" || continue
sudo systemctl disable --now "$u" || record_failure "サービス停止: $u"
done
}
fetch() { curl -fsSL --retry 3 --retry-delay 2 -o "$2" "$1"; }
add_keyring() { # add_keyring <URL> <出力先>
local url="$1" dst="$2" tmp rc
tmp="$(mktemp)"
fetch "${url}" "${tmp}" || { rm -f "${tmp}"; return 1; }
sudo install -d -m 0755 /etc/apt/keyrings
sudo gpg --batch --yes --dearmor -o "${dst}" "${tmp}"; rc=$?
rm -f "${tmp}"
sudo chmod 0644 "${dst}"
return ${rc}
}
add_source() { echo "$2" | sudo tee "/etc/apt/sources.list.d/$1" >/dev/null; }
# 当該コード名向けの配布が存在するかだけを見る(環境条件の確認)
repo_available() { curl -fsS --max-time 20 --retry 2 --retry-delay 2 -o /dev/null "$1"; }
# add_repo <名前> <鍵URL> <鍵出力先> <Release URL> <ソース行> <ファイル名>
add_repo() {
local name="$1" key_url="$2" key_dst="$3" rel_url="$4" srcline="$5" file="$6"
if ! repo_available "${rel_url}"; then
sudo rm -f "/etc/apt/sources.list.d/${file}"
record_manual "${name}: ${OS_CODENAME} 向けリポジトリが提供されていない(${rel_url} を取得できない)。登録せず,関連パッケージも導入しない。公式の提供状況を確認すること。"
return 1
fi
add_keyring "${key_url}" "${key_dst}" || { record_failure "${name} 公開鍵の取得"; return 1; }
add_source "${file}" "${srcline}"
return 0
}
repo_registered() { [ -f "/etc/apt/sources.list.d/$1" ]; }
# Python 仮想環境
venv_create() { # venv_create <パス> <pyenv のバージョン>
local dst="$1" ver="$2"
[ -x "${dst}/bin/python" ] && return 0
[ -x "${PYENV_ROOT}/versions/${ver}/bin/python" ] || return 1
mkdir -p "$(dirname "${dst}")"
"${PYENV_ROOT}/versions/${ver}/bin/python" -m venv "${dst}"
}
venv_pip_install() { # venv_pip_install <venv> <パッケージ...>
local venv="$1"; shift
local p
[ -x "${venv}/bin/pip" ] || { record_failure "仮想環境が無い: ${venv}"; return 1; }
for p in "$@"; do
"${venv}/bin/pip" install --upgrade "$p" || record_failure "pip 導入(${venv##*/}): $p"
done
}
# ------------------------------------------------------------
# 環境の判定(--check でも用いる)
# ------------------------------------------------------------
detect_env() {
. /etc/os-release
OS_ID="${ID}"
OS_VERSION_ID="${VERSION_ID}"
OS_CODENAME="${VERSION_CODENAME:-${UBUNTU_CODENAME:-}}"
TARGET_USER="$(id -un)"
TARGET_HOME="${HOME}"
if [ -x /usr/bin/lxqt-session ] || pkg_installed lubuntu-desktop; then
IS_LUBUNTU=1
fi
if lspci 2>/dev/null | grep -qi 'nvidia'; then
HAS_NVIDIA=1
fi
info "OS: ${OS_ID} ${OS_VERSION_ID} (${OS_CODENAME}) Lubuntu=${IS_LUBUNTU} NVIDIA=${HAS_NVIDIA}"
info "対象ユーザ: ${TARGET_USER} (${TARGET_HOME})"
}
# ------------------------------------------------------------
# 事前確認(毎回実行する。ブロックではない)
# ------------------------------------------------------------
preflight() {
[ "$(id -u)" -ne 0 ] || abort "root で実行されている。ユーザ設定が root に適用されるため,対象の利用者アカウントで実行すること。"
detect_env
# 22.04=jammy / 24.04=noble / 26.04=resolute の三種のみを対象とする
case "${OS_VERSION_ID}" in
22.04|24.04|26.04) ;;
*) abort "対象外の OS (${OS_ID} ${OS_VERSION_ID})。本スクリプトは Ubuntu 22.04 / 24.04 / 26.04 専用である。" ;;
esac
case "${OS_CODENAME}" in
jammy|noble|resolute) ;;
*) abort "コード名 ${OS_CODENAME} は対象外である。" ;;
esac
sudo -v || abort "sudo が利用できない。"
# 長時間のビルド中に sudo の認証が失効しないよう維持する
( while true; do sudo -n true; sleep 60; done ) >/dev/null 2>&1 &
KEEPALIVE_PID=$!
trap 'kill "${KEEPALIVE_PID}" 2>/dev/null' EXIT
sudo install -d -m 0755 "${STATE_DIR}" || abort "状態ディレクトリ ${STATE_DIR} を作成できない。"
if [ "${DO_RESET}" -eq 1 ]; then
sudo rm -f "${STATE_FILE}"
info "進捗を破棄した。最初のブロックから実行する。"
fi
sudo touch "${STATE_FILE}"
apt_prepare || abort "APT 自動更新ユニットを停止できない。ロックが解放されないため続行しない。"
info "APT 自動更新ユニットを停止した(全ブロック完了後に再開する)。"
}
# ============================================================
# B10 システム基盤
# ============================================================
block_10_base() {
step "リポジトリを日本ミラーへ変更"
# 24.04 以降は deb822 形式の ubuntu.sources を用いる。両形式に対応する。
# security.ubuntu.com は同期遅延によるセキュリティ更新の遅れを避けるため変更しない。
local f
for f in /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do
[ -f "$f" ] || continue
backup_once "$f"
sudo sed -i -E 's|//([a-z]{2}\.)?archive\.ubuntu\.com|//jp.archive.ubuntu.com|g' "$f" \
|| record_failure "ミラー変更: $f"
done
step "システム全体の更新"
apt_update || record_fatal "apt-get update に失敗(以降のすべての導入が失敗する)"
apt_upgrade || record_fatal "apt-get dist-upgrade に失敗(基盤が更新されていない)"
apt_fix || record_fatal "apt-get -f install に失敗(依存関係が破損している)"
step "基本ツールの導入"
apt_install \
ca-certificates curl wget gnupg dirmngr lsb-release software-properties-common \
locales tzdata net-tools pciutils \
build-essential git subversion emacs \
p7zip-full unzip zip xz-utils rsync jq tree htop lshw sysstat \
dpkg-dev gdebi-core gnome-disk-utility usb-creator-gtk cifs-utils sshfs \
nodejs npm poppler-utils html2text trash-cli deja-dup duplicity
step "タイムゾーンと NTP 時刻同期"
sudo timedatectl set-timezone Asia/Tokyo || record_failure "timedatectl set-timezone Asia/Tokyo"
sudo timedatectl set-ntp true || record_failure "timedatectl set-ntp true"
if unit_exists systemd-timesyncd.service; then
sudo install -d -m 0755 /etc/systemd/timesyncd.conf.d
sudo tee /etc/systemd/timesyncd.conf.d/50-jp.conf >/dev/null <<'EOF'
[Time]
NTP=ntp.nict.jp
FallbackNTP=ntp.ubuntu.com
EOF
sudo chmod 644 /etc/systemd/timesyncd.conf.d/50-jp.conf
sudo systemctl restart systemd-timesyncd.service || record_failure "systemd-timesyncd の再起動"
fi
timedatectl status
step "ロケール(日本語 UTF-8)"
# B30 の check-language-support が現在のロケールをもとに判定するため,ここで確定させる。
if [ -f /etc/locale.gen ]; then
backup_once /etc/locale.gen
sudo sed -i -E 's/^#[[:space:]]*(ja_JP\.UTF-8[[:space:]]+UTF-8)/\1/' /etc/locale.gen \
|| record_failure "/etc/locale.gen の書換"
fi
sudo locale-gen || record_failure "locale-gen"
# LC_ALL は恒久設定しない(コマンド単位の一時切替を妨げるため)
sudo update-locale LANG=ja_JP.UTF-8 LANGUAGE="ja:en" || record_failure "update-locale"
record_manual "ロケールの変更は再ログインまたは再起動で反映される。LC_ALL は恒久設定していない。"
step "不要パッケージと旧カーネルの整理"
# 旧カーネルは autoremove の保持規則(稼働中と最新を残す)に従って削除する。
apt_autoremove || record_failure "autoremove"
apt_autoclean || record_failure "autoclean"
info "稼働中のカーネル: $(uname -r)"
info "導入済みカーネル:"
dpkg-query -W -f=' ${Package}\n' 'linux-image-[0-9]*' 2>/dev/null
}
# ============================================================
# B20 リポジトリ登録
# ============================================================
block_20_repositories() {
step "Google Chrome"
add_repo "Google Chrome" \
"https://dl.google.com/linux/linux_signing_key.pub" \
"/etc/apt/keyrings/google-chrome.gpg" \
"https://dl.google.com/linux/chrome/deb/dists/stable/Release" \
"deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] https://dl.google.com/linux/chrome/deb/ stable main" \
"google-chrome.list"
step "CRAN (R 4 系)"
# jammy / noble / resolute のいずれも <codename>-cran40 で提供される。
add_repo "CRAN" \
"https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc" \
"/etc/apt/keyrings/cran.gpg" \
"https://cloud.r-project.org/bin/linux/ubuntu/${OS_CODENAME}-cran40/Release" \
"deb [signed-by=/etc/apt/keyrings/cran.gpg] https://cloud.r-project.org/bin/linux/ubuntu ${OS_CODENAME}-cran40/" \
"cran.list"
step "Visual Studio Code"
add_repo "Visual Studio Code" \
"https://packages.microsoft.com/keys/microsoft.asc" \
"/etc/apt/keyrings/microsoft.gpg" \
"https://packages.microsoft.com/repos/code/dists/stable/Release" \
"deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/code stable main" \
"vscode.list"
step "Sublime Text"
add_repo "Sublime Text" \
"https://download.sublimetext.com/sublimehq-pub.gpg" \
"/etc/apt/keyrings/sublimehq.gpg" \
"https://download.sublimetext.com/apt/stable/Release" \
"deb [signed-by=/etc/apt/keyrings/sublimehq.gpg] https://download.sublimetext.com/ apt/stable/" \
"sublime-text.list"
step "DBeaver Community Edition"
add_repo "DBeaver CE" \
"https://dbeaver.io/debs/dbeaver.gpg.key" \
"/etc/apt/keyrings/dbeaver.gpg" \
"https://dbeaver.io/debs/dbeaver-ce/Release" \
"deb [signed-by=/etc/apt/keyrings/dbeaver.gpg] https://dbeaver.io/debs/dbeaver-ce /" \
"dbeaver.list"
step "pgAdmin 4"
add_repo "pgAdmin 4" \
"https://www.pgadmin.org/static/packages_pgadmin_org.pub" \
"/etc/apt/keyrings/pgadmin.gpg" \
"https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/${OS_CODENAME}/dists/pgadmin4/Release" \
"deb [signed-by=/etc/apt/keyrings/pgadmin.gpg] https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/${OS_CODENAME} pgadmin4 main" \
"pgadmin4.list"
step "MongoDB"
add_repo "MongoDB 8.0" \
"https://www.mongodb.org/static/pgp/server-8.0.asc" \
"/etc/apt/keyrings/mongodb-server-8.0.gpg" \
"https://repo.mongodb.org/apt/ubuntu/dists/${OS_CODENAME}/mongodb-org/8.0/Release" \
"deb [arch=amd64,arm64 signed-by=/etc/apt/keyrings/mongodb-server-8.0.gpg] https://repo.mongodb.org/apt/ubuntu ${OS_CODENAME}/mongodb-org/8.0 multiverse" \
"mongodb-org-8.0.list"
step "NVIDIA CUDA"
if [ "${HAS_NVIDIA}" -eq 1 ] && [ "$(uname -m)" = "x86_64" ]; then
local cuda_distro="ubuntu${OS_VERSION_ID//./}" cuda_deb="/tmp/cuda-keyring.deb"
if fetch "https://developer.download.nvidia.com/compute/cuda/repos/${cuda_distro}/x86_64/cuda-keyring_1.1-1_all.deb" "${cuda_deb}"; then
apt_install_deb "${cuda_deb}" || record_failure "cuda-keyring の導入"
rm -f "${cuda_deb}"
else
record_failure "cuda-keyring の取得(${cuda_distro} 向けリポジトリの提供状況とファイル名を NVIDIA CUDA Downloads で確認すること)"
fi
else
info "NVIDIA GPU(x86_64)を検出しないため CUDA リポジトリは登録しない"
fi
step "リポジトリ情報の更新"
apt_update || record_fatal "apt-get update に失敗(登録したリポジトリ定義と公開鍵を確認すること。B30 の導入がすべて失敗する)"
}
# ============================================================
# B30 アプリ導入(apt)
# ============================================================
block_30_apps_apt() {
step "パッケージ情報の更新"
apt_update || record_fatal "apt-get update に失敗(以降の導入がすべて失敗する)"
step "コンパイラ・ビルド基盤"
apt_install gcc g++ gfortran cmake make pkg-config ninja-build ccache \
autoconf automake libtool bison flex swig scons llvm \
libc6-dev libncurses-dev libreadline-dev libffi-dev libssl-dev \
zlib1g-dev libbz2-dev liblzma-dev libyaml-dev libpcre2-dev \
libglib2.0-dev libxml2 libxml2-dev libxslt1-dev libxmlsec1-dev \
libusb-1.0-0-dev libpcap-dev tk-dev mpi-default-dev
step "Python(システム版)"
apt_install python3 python3-dev python3-pip python3-venv python3-setuptools python3-tk
step "Java"
apt_install default-jdk default-jre
step "データベース(PostgreSQL)"
apt_install postgresql postgresql-client postgresql-contrib libpq-dev
local pgver=""
command -v psql >/dev/null 2>&1 && pgver="$(psql --version | awk '{print $3}' | cut -d. -f1)"
if [ -n "${pgver}" ]; then
apt_install "postgresql-server-dev-${pgver}" \
postgis "postgresql-${pgver}-postgis-3" "postgresql-plpython3-${pgver}" \
python3-psycopg2 libpostgresql-jdbc-java
record_manual "PostgreSQL は peer 認証で 'sudo -u postgres psql' から利用する。空間拡張は 'CREATE EXTENSION postgis;',PL/Python は 'CREATE EXTENSION plpython3u;' で有効化する。"
else
record_failure "PostgreSQL のメジャーバージョンを判定できず,拡張パッケージを導入しない"
fi
repo_registered pgadmin4.list && apt_install pgadmin4-desktop
step "データベース(SQLite・Redis・MongoDB)"
apt_install sqlite3 libsqlite3-dev sqlitebrowser redis-server redis-tools
if repo_registered mongodb-org-8.0.list; then
apt_install mongodb-org
else
info "MongoDB のリポジトリが登録されていないため導入しない(B20 の確認事項を参照)"
fi
repo_registered dbeaver.list && apt_install dbeaver-ce
record_manual "SQLite の GUI は apt で管理できる sqlitebrowser を導入した。SQLiteStudio は公式サイトの書庫のみの配布であり自動導入しない。SQL Workbench/J も zip 配布のため自動導入しない。いずれもガイドの手順で必要時に導入すること。"
step "科学技術計算"
apt_install liblapack-dev libopenblas-dev libgsl-dev libfftw3-dev \
libglpk-dev glpk-utils libsuitesparse-dev libarpack2-dev libqhull-dev \
libeigen3-dev gnuplot graphviz libgraphviz-dev
record_manual "Intel oneAPI MKL は容量が大きく専用リポジトリを要するため本スクリプトでは導入しない。必要な場合はガイド記載の Intel 公式手順でリポジトリを追加し 'sudo apt-get install intel-oneapi-mkl-devel' を実行すること。"
step "R"
apt_install r-base r-base-dev r-recommended r-mathlib littler libcurl4-openssl-dev
step "マルチメディア・画像処理"
apt_install ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
libavdevice-dev \
libx264-dev libxvidcore-dev libjpeg-dev libpng-dev libtiff-dev liblcms2-dev \
libfreetype6-dev libopenexr-dev libva-dev libv4l-dev v4l-utils libjack-jackd2-dev \
vlc audacity obs-studio openshot-qt kdenlive guvcview \
gimp libbabl-dev libgegl-dev inkscape pinta blender meshlab \
imagemagick libmagickcore-dev libmagickwand-dev graphicsmagick librsvg2-bin
apt_install libopencv-dev python3-opencv libgtk-3-dev libtbb-dev libpcl-dev
step "空間データ処理(GDAL / PostGIS / SpatiaLite / QGIS / GRASS)"
apt_install libgeos-dev libgeos++-dev libproj-dev proj-bin proj-data \
libspatialite-dev spatialite-bin libsqlite3-mod-spatialite \
libgdal-dev gdal-bin python3-gdal libfreexl-dev libgeotiff-dev libhpdf-dev libreadosm-dev
apt_install qgis qgis-plugin-grass grass
step "ネットワーク・診断・リモート接続"
apt_install nmap tcpdump wireshark clamav clamav-daemon clamtk lynis \
netcat-openbsd intel-gpu-tools openssh-server filezilla \
remmina remmina-plugin-rdp remmina-plugin-vnc xrdp
if pkg_installed xrdp; then
sudo adduser xrdp ssl-cert >/dev/null 2>&1 || record_failure "xrdp を ssl-cert グループへ追加"
record_manual "xrdp は導入するが ufw が 3389/tcp を遮断しているため既定では接続できない。利用する場合のみ 'sudo ufw allow 3389/tcp' を実行すること。"
fi
record_manual "Wireshark を一般利用者で使う場合は 'sudo dpkg-reconfigure wireshark-common' で非 root 捕捉を許可し,'sudo usermod -aG wireshark ${TARGET_USER}' を実行して再ログインすること。ClamAV の定義更新は 'sudo freshclam' である。"
step "ユーティリティ"
apt_install plocate catfish inotify-tools flameshot gparted synaptic
sudo updatedb || record_failure "updatedb"
case "${OS_CODENAME}" in
resolute) apt_install hardinfo2 ;;
*) apt_install hardinfo ;;
esac
if [ "${IS_LUBUNTU}" -eq 0 ]; then
apt_install gnome-tweaks
case "${OS_CODENAME}" in
jammy) apt_install gedit ;;
*) apt_install gnome-text-editor ;;
esac
fi
# 26.04 では「ソフトウェアとアップデート」が既定で導入されない
case "${OS_CODENAME}" in
resolute) apt_install software-properties-gtk ;;
esac
step "エディタ・統合開発環境"
apt_install geany geany-plugins
repo_registered vscode.list && apt_install code
repo_registered sublime-text.list && apt_install sublime-text
record_manual "教育用 Java 環境の BlueJ と Greenfoot は,配布 deb のファイル名がバージョンごとに変わるため自動導入しない。必要時に各公式サイトから deb を取得し 'sudo apt install ./<ファイル名>.deb' で導入すること。"
step "ゲーム"
apt_install 0ad supertuxkart wesnoth gnome-mahjongg extremetuxracer \
aisleriot warzone2100 hedgewars frozen-bubble
record_manual "Steam / Lutris / Heroic Games Launcher はアカウント登録・ライセンス同意・追加アーキテクチャ(i386)の有効化を伴うため自動導入しない。手順はガイドのゲーム章に記載してある。"
step "グラフィックドライバ(NVIDIA)"
if [ "${HAS_NVIDIA}" -eq 1 ]; then
apt_install ubuntu-drivers-common
sudo ubuntu-drivers install || record_failure "ubuntu-drivers install"
record_manual "NVIDIA ドライバは導入後の再起動で有効になる。セキュアブート有効時は再起動時の MOK Manager で登録を完了させること。反映後 'nvidia-smi' で確認する。"
else
info "NVIDIA GPU を検出しないため専用ドライバは導入しない"
fi
step "NVIDIA CUDA Toolkit / cuDNN"
if [ "${HAS_NVIDIA}" -eq 1 ] && pkg_installed cuda-keyring; then
local cuda_pkg cuda_major
cuda_pkg="$(apt-cache search --names-only '^cuda-toolkit-[0-9]+-[0-9]+$' 2>/dev/null \
| awk '{print $1}' | sort -V | tail -1)"
if [ -n "${cuda_pkg}" ]; then
apt_install "${cuda_pkg}"
cuda_major="$(echo "${cuda_pkg}" | sed -E 's/^cuda-toolkit-([0-9]+)-[0-9]+$/\1/')"
apt_install "cudnn9-cuda-${cuda_major}"
record_manual "CUDA Toolkit は ${cuda_pkg},cuDNN は cudnn9-cuda-${cuda_major} を導入した。PyTorch・TensorFlow のホイールは CUDA ランタイムを同梱するため,この Toolkit は主に nvcc を用いる開発向けである。"
else
record_failure "cuda-toolkit パッケージが見つからない(CUDA リポジトリの登録を確認すること)"
fi
else
info "NVIDIA GPU または CUDA リポジトリが無いため CUDA Toolkit は導入しない"
fi
step "日本語環境"
apt_install language-pack-ja language-pack-gnome-ja manpages-ja \
language-selector-common \
fonts-noto-cjk fonts-noto-cjk-extra fonts-ipafont fonts-vlgothic fonts-takao \
nkf mecab libmecab-dev mecab-ipadic-utf8 python3-mecab
# 現在のロケール(B10 で設定済み)に対して不足する言語サポートを補う
if command -v check-language-support >/dev/null 2>&1; then
local lang_pkgs
lang_pkgs="$(check-language-support)"
if [ -n "${lang_pkgs}" ]; then
apt_install ${lang_pkgs}
else
info "check-language-support: 追加すべきパッケージはない"
fi
else
record_failure "check-language-support が見つからない(language-selector-common の導入を確認すること)"
fi
if [ "${IS_LUBUNTU}" -eq 1 ]; then
apt_install fcitx5 fcitx5-mozc mozc-utils-gui
else
apt_install ibus ibus-mozc mozc-utils-gui
fi
record_manual "Emacs は B70 の別名で X の入力メソッドを無効化している(XMODIFIERS=@im=none)。Emacs 内で Mozc を使う場合は emacs-mozc を導入し,.bashrc の当該別名を削除すること。"
step "デスクトップ関連"
repo_registered google-chrome.list && apt_install google-chrome-stable
apt_install papirus-icon-theme xdg-user-dirs-gtk
if [ "${IS_LUBUNTU}" -eq 1 ]; then
apt_install pcmanfm-qt lxqt-config lxqt-themes qterminal featherpad
fi
}
# ============================================================
# B40 アプリ導入(ソース・外部配布物)
# ============================================================
block_40_apps_source() {
step "pyenv"
export PYENV_ROOT="${HOME}/.pyenv"
export PATH="${PYENV_ROOT}/bin:${PATH}"
if [ -d "${PYENV_ROOT}/.git" ]; then
git -C "${PYENV_ROOT}" pull --ff-only || record_failure "pyenv の更新"
else
git clone https://github.com/pyenv/pyenv.git "${PYENV_ROOT}" \
|| record_fatal "pyenv の取得に失敗(B70 が前提とする Python 環境を構築できない)"
fi
if [ ! -d "${PYENV_ROOT}/plugins/pyenv-virtualenv" ]; then
git clone https://github.com/pyenv/pyenv-virtualenv.git \
"${PYENV_ROOT}/plugins/pyenv-virtualenv" || record_failure "pyenv-virtualenv の取得"
fi
step "Python 本体のビルド"
CONFIGURE_OPTS="--enable-shared" pyenv install -s "${PY_MAIN_VERSION}" \
|| record_fatal "Python ${PY_MAIN_VERSION} のビルドに失敗(B70 が前提とする Python 環境を構築できない)"
CONFIGURE_OPTS="--enable-shared" pyenv install -s "${PY_MP_VERSION}" \
|| record_failure "Python ${PY_MP_VERSION} のビルド(MediaPipe 用)"
pyenv global "${PY_MAIN_VERSION}" || record_failure "pyenv global ${PY_MAIN_VERSION}"
eval "$(pyenv init -)"
step "仮想環境の作成"
# システム Python は PEP 668 により pip 導入を拒否するため,用途別の仮想環境を用いる。
venv_create "${VENV_MAIN}" "${PY_MAIN_VERSION}" \
|| record_fatal "主仮想環境 ${VENV_MAIN} を作成できない(以降の Python パッケージ導入がすべて失敗する)"
venv_create "${VENV_MP}" "${PY_MP_VERSION}" \
|| record_failure "MediaPipe 用仮想環境 ${VENV_MP} を作成できない"
step "Python パッケージ(主環境)"
venv_pip_install "${VENV_MAIN}" pip setuptools wheel
venv_pip_install "${VENV_MAIN}" \
numpy scipy matplotlib pandas ipython jupyterlab notebook ipykernel spyder \
pillow scikit-image scikit-learn statsmodels seaborn sympy h5py plotly \
requests lxml beautifulsoup4 openpyxl xlsxwriter numexpr bottleneck sphinx \
opencv-python opencv-contrib-python dlib psycopg2-binary \
torch torchvision torchaudio insightface
# 推論基盤は GPU の有無で導入物が変わる(環境条件による分岐)
if [ "${HAS_NVIDIA}" -eq 1 ]; then
venv_pip_install "${VENV_MAIN}" onnxruntime-gpu "tensorflow[and-cuda]"
else
venv_pip_install "${VENV_MAIN}" onnxruntime tensorflow
fi
# GDAL バインディングはシステムの GDAL に版を合わせる
if command -v gdal-config >/dev/null 2>&1; then
venv_pip_install "${VENV_MAIN}" "GDAL==$(gdal-config --version)" \
fiona geopandas shapely pyproj rtree folium
else
record_failure "gdal-config が無く GDAL の Python バインディングを導入しない"
fi
"${VENV_MAIN}/bin/python" -m ipykernel install --user \
--name=myproject_env --display-name="Python (myproject_env)" \
|| record_failure "ipykernel の登録(myproject_env)"
record_manual "PyTorch は PyPI 既定のホイールを導入した。特定の CUDA 版に合わせる場合は pytorch.org の Get Started で示される --index-url 付きコマンドで入れ直すこと。onnxruntime-gpu も既定ビルドが対象とする CUDA 版が変わるため,公式のインストールページを確認すること。"
step "InsightFace の学習済みモデル"
if [ -x "${VENV_MAIN}/bin/python" ]; then
"${VENV_MAIN}/bin/python" - <<'EOF' || record_failure "InsightFace モデル(buffalo_l)の取得"
from insightface.app import FaceAnalysis
app = FaceAnalysis(name="buffalo_l", providers=["CPUExecutionProvider"])
app.prepare(ctx_id=-1, det_size=(640, 640))
EOF
fi
record_manual "InsightFace の学習済みモデル(buffalo_l 等)は非商用の研究目的に限定される。商用利用では自前で学習したモデルか別途ライセンスを得たモデルを ~/.insightface/models/ に配置すること。"
step "MediaPipe(専用仮想環境)"
if [ -x "${VENV_MP}/bin/pip" ]; then
venv_pip_install "${VENV_MP}" pip setuptools wheel
venv_pip_install "${VENV_MP}" mediapipe opencv-python numpy
mkdir -p "${HOME}/mediapipe_models"
if [ ! -f "${HOME}/mediapipe_models/face_landmarker.task" ]; then
fetch "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task" \
"${HOME}/mediapipe_models/face_landmarker.task" \
|| record_failure "MediaPipe Face Landmarker モデルの取得"
fi
record_manual "MediaPipe は Python ${PY_MP_VERSION} の仮想環境 ${VENV_MP} に導入した(新しい Python 向けホイールが未提供のため)。他タスクのモデルは MediaPipe Solutions の各ドキュメントから取得すること。"
fi
step "dlib (C++)"
local dlib_src="/usr/local/src/dlib"
sudo install -d -m 0755 /usr/local/src
if [ -d "${dlib_src}/.git" ]; then
sudo git -C "${dlib_src}" pull --ff-only || record_failure "dlib の更新"
else
sudo git clone --depth 1 https://github.com/davisking/dlib.git "${dlib_src}" \
|| record_failure "dlib の取得"
fi
if [ -d "${dlib_src}" ]; then
(
sudo cmake -S "${dlib_src}" -B "${dlib_src}/build" \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \
&& sudo cmake --build "${dlib_src}/build" -j"$(nproc)" \
&& sudo cmake --install "${dlib_src}/build"
) || record_failure "dlib のビルド"
sudo ldconfig
fi
step "Eclipse IDE"
local eclipse_rel="2026-06" eclipse_dir="/usr/local/eclipse" eclipse_tar="/tmp/eclipse-jee.tar.gz"
if [ ! -x "${eclipse_dir}/eclipse" ]; then
if fetch "https://download.eclipse.org/technology/epp/downloads/release/${eclipse_rel}/R/eclipse-jee-${eclipse_rel}-R-linux-gtk-x86_64.tar.gz" \
"${eclipse_tar}"; then
sudo rm -rf "${eclipse_dir}"
(cd /usr/local && sudo tar -xzf "${eclipse_tar}") || record_failure "Eclipse の展開"
sudo ln -sf "${eclipse_dir}/eclipse" /usr/local/bin/eclipse
rm -f "${eclipse_tar}"
else
record_failure "Eclipse の取得(公開中のリリース名を eclipse.org で確認すること)"
fi
fi
step "RStudio Desktop"
local rstudio_ver="2026.07.1-147" rstudio_deb="/tmp/rstudio.deb"
if ! command -v rstudio >/dev/null 2>&1; then
if fetch "https://download1.rstudio.org/electron/jammy/amd64/rstudio-${rstudio_ver}-amd64.deb" \
"${rstudio_deb}"; then
apt_install_deb "${rstudio_deb}" || record_failure "RStudio の導入"
rm -f "${rstudio_deb}"
else
record_failure "RStudio の取得(posit.co で現行版のファイル名を確認すること)"
fi
fi
record_manual "RStudio は jammy 版が配布され,公式対応は Ubuntu 22 / 24 である。26.04 では動作確認を行うこと。"
step "diagrams.net Desktop"
local drawio_deb="/tmp/drawio.deb" drawio_url=""
if ! command -v drawio >/dev/null 2>&1; then
drawio_url="$(curl -fsSL --retry 3 https://api.github.com/repos/jgraph/drawio-desktop/releases/latest 2>/dev/null \
| jq -r '.assets[].browser_download_url | select(test("amd64\\.deb$"))' | head -1)"
if [ -n "${drawio_url}" ] && fetch "${drawio_url}" "${drawio_deb}"; then
apt_install_deb "${drawio_deb}" || record_failure "diagrams.net Desktop の導入"
rm -f "${drawio_deb}"
else
record_failure "diagrams.net Desktop の取得"
fi
fi
step "Google Earth Pro"
local ge_deb="/tmp/google-earth-pro.deb"
if ! command -v google-earth-pro >/dev/null 2>&1; then
if fetch "https://dl.google.com/dl/earth/client/current/google-earth-pro-stable_current_amd64.deb" "${ge_deb}"; then
apt_install_deb "${ge_deb}" || record_failure "Google Earth Pro の導入"
rm -f "${ge_deb}"
record_manual "Google Earth Pro の deb は導入時に Google 独自の APT リポジトリを /etc/apt/sources.list.d/ に登録する。以降の更新は apt で行われる。"
else
record_failure "Google Earth Pro の取得(google.com/earth で配布状況を確認すること)"
fi
fi
step "snap パッケージ"
snap_install firefox chromium cloudcompare
step "自動導入しないアプリの案内"
record_manual "Unreal Engine は Epic Games / GitHub アカウントの連携とライセンス同意が必要で,取得とビルドに数十 GB と長時間を要するため本スクリプトでは導入しない。手順はガイドのゲーム章に記載してある。"
record_manual "MakeHuman は公式 PPA が 24.04(noble) までの提供であり 26.04 向けが無いため導入しない。必要時はガイドの手順(PPA またはソース実行,Blender アドオン MPFB)に従うこと。"
}
# ============================================================
# B50 不要パッケージ削除
# ============================================================
block_50_remove_packages() {
step "利用しないゲーム類"
# B30 で導入するゲームは対象外とする。方針を変える場合は当該行を編集すること。
apt_purge gnome-sudoku gnome-mines gnome-taquin \
gnome-tetravex gnome-nibbles gnome-robots gnome-chess gnome-klotski \
quadrapassel four-in-a-row hitori lightsoff swell-foop iagno \
five-or-more tali supertux2 openttd \
gcompris tuxpaint tuxmath tuxtype \
blinken gbrainy kanagram khangman ktuberling
record_manual "ゲームは 0ad / supertuxkart / wesnoth / gnome-mahjongg / extremetuxracer / aisleriot / warzone2100 / hedgewars / frozen-bubble を残し,それ以外は削除する。方針を変える場合は B30 の導入行と B50 の削除行を編集すること。"
step "プリインストールアプリ(オフィス・メール)"
apt_purge_glob 'libreoffice*' 'thunderbird*'
record_manual "LibreOffice と Thunderbird は削除する方針である。必要な環境では B50 の当該行を削除してから実行すること。"
step "常駐サーバ類"
apt_purge apache2 apache2-utils apache2-bin apache2-data \
postfix munin munin-common munin-node
step "その他の不要パッケージ"
apt_purge wine winetricks \
virtualbox-guest-utils virtualbox-guest-x11 \
ubiquity ubiquity-frontend-gtk ubiquity-frontend-debconf ubiquity-ubuntu-artwork \
ubiquity-slideshow-ubuntu ubiquity-slideshow-lubuntu
record_manual "Wine は削除する方針である。Steam(Proton) や Lutris で Windows 用ゲームを動かす場合は Wine 系パッケージが必要になるため,B50 の当該行を削除してから実行すること。"
step "整理"
apt_autoremove || record_failure "autoremove"
apt_autoclean || record_failure "autoclean"
apt_fix || record_failure "apt-get -f install"
}
# ============================================================
# B60 システム設定・不要サービス停止
# ============================================================
block_60_system() {
step "不要サービスの停止と無効化"
service_disable apport.service whoopsie.service kerneloops.service
step "クラッシュレポートの無効化"
if [ -f /etc/default/apport ]; then
backup_once /etc/default/apport
sudo sed -i 's/^enabled=1/enabled=0/' /etc/default/apport \
|| record_failure "/etc/default/apport の書換"
fi
step "SSH の root ログイン禁止"
if [ -f /etc/ssh/sshd_config ]; then
sudo install -d -m 0755 /etc/ssh/sshd_config.d
printf 'PermitRootLogin no\n' | sudo tee /etc/ssh/sshd_config.d/50-hardening.conf >/dev/null
# 24.04 以降は socket 起動方式のため socket 側も対象とする。
# 停止中でも失敗しないよう try-reload-or-restart を用いる。
local u
for u in ssh.socket ssh.service; do
unit_exists "$u" || continue
sudo systemctl try-reload-or-restart "$u" || record_failure "SSH の再読込: $u"
done
record_manual "SSH は 22/tcp のまま運用する。ポートを変更する場合は sshd_config の Port を変更し,'systemctl daemon-reload; systemctl restart ssh.socket ssh' を実行し,ufw で新ポートを許可して 22 の許可を削除する。反映は 'ss -tlnp' で確認する。"
record_manual "SSH 経由で設定を変更した場合は,現在のセッションを維持したまま別端末で接続を確認すること。"
fi
step "ufw ファイアウォール"
apt_install ufw
sudo ufw default deny incoming || record_failure "ufw default deny incoming"
sudo ufw default allow outgoing || record_failure "ufw default allow outgoing"
sudo ufw allow 22/tcp || record_failure "ufw allow 22/tcp"
sudo ufw --force enable || record_failure "ufw の有効化"
sudo ufw status verbose
step "キーボード(Caps Lock を Ctrl 化)"
if [ -f /etc/default/keyboard ]; then
backup_once /etc/default/keyboard
if grep -q 'ctrl:nocaps' /etc/default/keyboard; then
info "XKBOPTIONS は設定済みである"
else
local cur new
cur="$( . /etc/default/keyboard; echo "${XKBOPTIONS:-}" )"
new="${cur:+${cur},}ctrl:nocaps"
if grep -q '^XKBOPTIONS=' /etc/default/keyboard; then
sudo sed -i -E "s|^XKBOPTIONS=.*|XKBOPTIONS=\"${new}\"|" /etc/default/keyboard \
|| record_failure "/etc/default/keyboard の書換"
else
echo "XKBOPTIONS=\"${new}\"" | sudo tee -a /etc/default/keyboard >/dev/null \
|| record_failure "/etc/default/keyboard への追記"
fi
fi
record_manual "Caps Lock の Ctrl 化(ctrl:nocaps)は再ログインまたは再起動で反映される。無効化したい場合は /etc/default/keyboard の XKBOPTIONS を caps:none に書き換える。"
fi
step "Java の既定実装と JAVA_HOME"
# default-jdk が指す版は 26.04 が OpenJDK 25,24.04 が 21,22.04 が 11 である。
local d java_home=""
for d in /usr/lib/jvm/java-25-openjdk-amd64 \
/usr/lib/jvm/java-21-openjdk-amd64 \
/usr/lib/jvm/java-17-openjdk-amd64 \
/usr/lib/jvm/java-11-openjdk-amd64 \
/usr/lib/jvm/default-java; do
[ -d "$d" ] && { java_home="$d"; break; }
done
if [ -n "${java_home}" ]; then
sudo tee /etc/profile.d/java.sh >/dev/null <<EOF
export JAVA_HOME=${java_home}
export PATH=\${JAVA_HOME}/bin:\${PATH}
EOF
sudo chmod 644 /etc/profile.d/java.sh
sudo update-alternatives --auto java || record_failure "update-alternatives --auto java"
record_manual "JAVA_HOME は次回ログインから有効になる。複数の JDK を使い分ける場合は 'sudo update-alternatives --config java' を用いる。"
else
record_failure "JVM が見つからず JAVA_HOME を設定できない"
fi
step "CUDA の環境変数"
if [ -d /usr/local/cuda ]; then
sudo tee /etc/profile.d/cuda.sh >/dev/null <<'EOF'
export CUDA_HOME=/usr/local/cuda
export PATH=${CUDA_HOME}/bin:${PATH}
export LD_LIBRARY_PATH=${CUDA_HOME}/lib64:${LD_LIBRARY_PATH:-}
EOF
sudo chmod 644 /etc/profile.d/cuda.sh
record_manual "CUDA_HOME / PATH / LD_LIBRARY_PATH は次回ログインから有効になる。'nvcc --version' で確認する。"
else
info "/usr/local/cuda が無いため CUDA の環境変数は設定しない"
fi
step "R の Java 連携再設定"
sudo R CMD javareconf || record_failure "R CMD javareconf"
step "X11 セッションでの画面ブランキング抑止"
if [ -d /etc/X11/Xsession.d ]; then
sudo tee /etc/X11/Xsession.d/98x11-noblank >/dev/null <<'EOF'
xset s off
xset s noblank
xset -dpms
EOF
sudo chmod 644 /etc/X11/Xsession.d/98x11-noblank
fi
record_manual "Wayland セッションでは xset が効かない。B70 の gsettings 設定で抑止する。セッション種別は 'echo \$XDG_SESSION_TYPE' で確認できる。"
step "LightDM のゲストアカウント無効化"
if [ -d /etc/lightdm ]; then
sudo install -d -m 0755 /etc/lightdm/lightdm.conf.d
printf '[Seat:*]\nallow-guest=false\n' \
| sudo tee /etc/lightdm/lightdm.conf.d/50-no-guest.conf >/dev/null \
|| record_failure "LightDM 設定の書込"
fi
}
# ============================================================
# B70 ユーザ設定
# 設定ファイルは外部から取得せず,本ブロック内に埋め込む。
# ============================================================
gset() { # gset <schema> <key> <value> スキーマが無い環境(LXQt 等)は対象外
gsettings list-schemas 2>/dev/null | grep -qx "$1" || { info "スキーマ非該当のため設定しない: $1"; return 0; }
gsettings set "$1" "$2" "$3" || record_failure "gsettings set $1 $2"
}
xfq() { # xfq <プロパティ> <型> <値> XFCE が無い環境は対象外
command -v xfconf-query >/dev/null 2>&1 || return 0
xfconf-query -c xfce4-power-manager -p "$1" -s "$3" 2>/dev/null \
|| xfconf-query -c xfce4-power-manager -p "$1" -n -t "$2" -s "$3" \
|| record_failure "xfconf-query $1"
}
block_70_user() {
step "デスクトップ設定(gsettings)"
gset org.gnome.desktop.session idle-delay "uint32 0"
gset org.gnome.desktop.screensaver lock-enabled false
gset org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type "'nothing'"
gset org.gnome.nautilus.preferences always-use-location-entry true
gset org.gnome.shell.extensions.ding show-home true
gset org.gnome.shell.extensions.ding show-trash true
if [ "${IS_LUBUNTU}" -eq 1 ]; then
record_manual "Lubuntu(LXQt) の日本語入力は fcitx5 設定ツールで Mozc を追加すること。"
else
gset org.gnome.desktop.input-sources sources "[('xkb', 'jp'), ('ibus', 'mozc-jp')]"
record_manual "Wayland で変換候補の表示位置がずれる場合は fcitx5-mozc へ切り替え,「地域と言語」で IM システムを Fcitx 5 にすること。"
fi
step "電源管理(XFCE 環境のみ)"
xfq /xfce4-power-manager/dpms-enabled bool false
xfq /xfce4-power-manager/inactivity-sleep-mode-on-ac uint 0
step "ユーザディレクトリ名の英語化"
LANG=C LC_ALL=C xdg-user-dirs-gtk-update --force \
|| record_failure "xdg-user-dirs-gtk-update"
record_manual "旧名称の日本語ディレクトリが空でない場合,中身は自動では移動されない。人手で移動してから旧ディレクトリを削除すること。"
step "Emacs の設定"
backup_once "${HOME}/.emacs"
cat > "${HOME}/.emacs" <<'EOF'
;; 日本語環境
(set-language-environment "Japanese")
(prefer-coding-system 'utf-8)
;; 表示
(setq inhibit-startup-screen t)
(tool-bar-mode -1)
(column-number-mode 1)
(show-paren-mode 1)
;; 編集
(setq make-backup-files nil)
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
(global-set-key (kbd "C-h") 'delete-backward-char)
EOF
step "X リソースの設定"
backup_once "${HOME}/.Xresources"
cat > "${HOME}/.Xresources" <<'EOF'
XTerm*faceName: DejaVu Sans Mono
XTerm*faceSize: 12
XTerm*saveLines: 10000
XTerm*locale: true
XTerm*metaSendsEscape: true
EOF
step ".bashrc の管理ブロック更新"
local bashrc="${HOME}/.bashrc"
local begin="# >>> setup.sh >>>" end="# <<< setup.sh <<<"
touch "${bashrc}"
backup_once "${bashrc}"
sed -i "/${begin}/,/${end}/d" "${bashrc}"
cat >> "${bashrc}" <<EOF
${begin}
alias emacs='XMODIFIERS=@im=none emacs'
alias venv-main='source \$HOME/venvs/myproject_env/bin/activate'
alias venv-mp='source \$HOME/venvs/mediapipe_env/bin/activate'
case \$TERM in
linux) LANG=C ;;
esac
export PYENV_ROOT="\$HOME/.pyenv"
[ -d "\$PYENV_ROOT/bin" ] && export PATH="\$PYENV_ROOT/bin:\$PATH"
command -v pyenv >/dev/null 2>&1 && eval "\$(pyenv init -)"
command -v pyenv >/dev/null 2>&1 && eval "\$(pyenv virtualenv-init -)"
${end}
EOF
record_manual "Python の作業は仮想環境で行う。主環境は 'source ~/venvs/myproject_env/bin/activate'(別名 venv-main),MediaPipe は 'source ~/venvs/mediapipe_env/bin/activate'(別名 venv-mp)である。"
record_manual "設定ファイルは本スクリプトに埋め込んである。組織固有の内容がある場合は B70 の該当ヒアドキュメントを書き換えること。既存ファイルは .DIST として退避してある。"
record_manual "入力ソースとユーザディレクトリ名の変更は再ログイン後に反映される。"
}
# ============================================================
# B80 不要ファイル削除・後処理
# ============================================================
block_80_cleanup() {
step "クラッシュ情報と古いログの削除"
sudo rm -f /var/crash/*
sudo rm -f /var/log/Xorg.0.log.old
sudo journalctl --vacuum-time=7d || record_failure "journalctl --vacuum-time"
step "利用者のキャッシュと一時ファイルの削除"
local t
for t in .thumbnails .cache/thumbnails .xsession-errors .xsession-errors.old; do
rm -rf "${HOME:?}/${t}"
done
step "APT キャッシュの整理"
apt_autoremove || record_failure "autoremove"
apt_clean || record_failure "apt-get clean"
step "依存関係の最終確認"
apt_fix || record_failure "apt-get -f install"
step "ディスク使用量"
df -h /
record_manual "不具合の調査は journalctl で行う(-b 今回の起動以降 / -p err エラー以上 / -u <ユニット名> / -f 追尾)。"
}
# ============================================================
# B90 リマスター準備(--remaster 指定時のみ)
# 注意: 鍵・履歴・ブラウザ設定を削除する。通常運用の端末では実行しない。
# ============================================================
block_90_remaster() {
step "リマスター用ツールの導入"
# 24.04 以降の ISO は BIOS / UEFI とも GRUB で起動するため isolinux は用いない。
apt_install squashfs-tools casper
step "システムの最終更新"
apt_update || record_fatal "apt-get update に失敗(イメージ内容が確定しない)"
apt_upgrade || record_failure "apt-get dist-upgrade"
step "利用者データの消去(${TARGET_USER})"
local t
for t in .ssh .cache .thumbnails .gvfs .mozilla .config/chromium \
.bash_history .lesshst .nano_history .sqlite_history \
.local/share/Trash .local/share/recently-used.xbel; do
rm -rf "${HOME:?}/${t}"
done
mkdir -p "${HOME}/Desktop"
step "ログの消去"
sudo journalctl --vacuum-time=1d || record_failure "journalctl --vacuum-time"
apt_clean || record_failure "apt-get clean"
record_manual "イメージ作成前に再起動して構成を確定させること。ISO の作成(squashfs 生成と xorriso による作成)は本スクリプトでは行わない。"
}
# ============================================================
# B95 導入結果の確認(OK / NG 判定)
# 状態を変更しない。単体で実行する場合は --check を指定する。
# 判定項目は「Ubuntu 22.04/24.04/26.04 開発・研究環境構築ガイド」の
# 確認章に掲載した check-install.sh と同一である。
# ============================================================
chk() { # chk <表示名> <コマンド...>
local n="$1"; shift
if "$@" >/dev/null 2>&1; then
echo " [OK] ${n}"
else
echo " [NG] ${n}"
CHECK_NG+=("${n}")
fi
}
final_check() {
local PY="python3" MP="python3"
[ -x "${VENV_MAIN}/bin/python" ] && PY="${VENV_MAIN}/bin/python"
[ -x "${VENV_MP}/bin/python" ] && MP="${VENV_MP}/bin/python"
echo
echo "============================================================"
echo "[B95 ] 導入結果の確認(OK / NG)"
echo "============================================================"
CHECK_NG=()
echo "--- 開発環境 ---"
chk gcc gcc --version
chk g++ g++ --version
chk gfortran gfortran --version
chk make make --version
chk cmake cmake --version
chk ninja ninja --version
chk ccache ccache --version
chk git git --version
chk subversion svn --version
chk pkg-config pkg-config --version
chk OpenCV-dev pkg-config --exists opencv4
chk Eigen3 pkg-config --exists eigen3
chk PCL sh -c 'ls /usr/include/pcl-*/pcl/point_types.h'
chk dlib-C++ sh -c 'ldconfig -p | grep -q libdlib'
echo "--- NVIDIA GPU ---"
chk NVIDIA-driver nvidia-smi
chk CUDA-Toolkit sh -c 'nvcc --version || /usr/local/cuda/bin/nvcc --version'
chk cuDNN sh -c 'ldconfig -p | grep -q libcudnn'
echo "--- Python ---"
chk pyenv sh -c 'test -x "$HOME/.pyenv/bin/pyenv"'
chk venv-main sh -c 'test -x "$HOME/venvs/myproject_env/bin/python"'
chk venv-mediapipe sh -c 'test -x "$HOME/venvs/mediapipe_env/bin/python"'
chk pip "${PY}" -m pip --version
chk NumPy "${PY}" -c "import numpy"
chk SciPy "${PY}" -c "import scipy"
chk pandas "${PY}" -c "import pandas"
chk Matplotlib "${PY}" -c "import matplotlib"
chk scikit-learn "${PY}" -c "import sklearn"
chk scikit-image "${PY}" -c "import skimage"
chk statsmodels "${PY}" -c "import statsmodels"
chk seaborn "${PY}" -c "import seaborn"
chk SymPy "${PY}" -c "import sympy"
chk Pillow "${PY}" -c "import PIL"
chk h5py "${PY}" -c "import h5py"
chk plotly "${PY}" -c "import plotly"
chk OpenCV-Python "${PY}" -c "import cv2"
chk dlib-Python "${PY}" -c "import dlib"
chk psycopg2 "${PY}" -c "import psycopg2"
chk JupyterLab sh -c 'test -x "$HOME/venvs/myproject_env/bin/jupyter-lab" || command -v jupyter-lab'
chk Spyder sh -c 'test -x "$HOME/venvs/myproject_env/bin/spyder" || command -v spyder'
chk ONNXRuntime "${PY}" -c "import onnxruntime"
chk InsightFace "${PY}" -c "import insightface"
chk InsightFace-model test -d "${HOME}/.insightface/models/buffalo_l"
chk PyTorch "${PY}" -c "import torch"
chk PyTorch-CUDA "${PY}" -c "import torch,sys; sys.exit(0 if torch.cuda.is_available() else 1)"
chk TensorFlow "${PY}" -c "import tensorflow"
chk MediaPipe "${MP}" -c "import mediapipe"
chk MediaPipe-Tasks "${MP}" -c "import mediapipe.tasks.python"
chk MediaPipe-model test -f "${HOME}/mediapipe_models/face_landmarker.task"
chk GDAL-Python "${PY}" -c "from osgeo import gdal"
chk geopandas "${PY}" -c "import geopandas"
echo "--- エディタ・IDE ---"
chk VSCode command -v code
chk SublimeText command -v subl
chk Geany command -v geany
chk Emacs command -v emacs
chk Eclipse sh -c 'command -v eclipse || test -x /usr/local/eclipse/eclipse'
chk テキストエディタ sh -c 'command -v gnome-text-editor || command -v gedit || command -v featherpad'
echo "--- ブラウザ・リモート ---"
chk Firefox command -v firefox
chk Chrome sh -c 'command -v google-chrome || command -v google-chrome-stable'
chk Chromium sh -c 'command -v chromium || command -v chromium-browser'
chk OpenSSH-client command -v ssh
chk OpenSSH-server sh -c 'systemctl is-enabled ssh.socket || systemctl is-enabled ssh.service || systemctl is-active ssh'
chk xrdp systemctl is-active xrdp
chk Remmina command -v remmina
chk FileZilla command -v filezilla
chk sshfs command -v sshfs
echo "--- ユーティリティ ---"
chk curl command -v curl
chk wget command -v wget
chk htop command -v htop
chk tree command -v tree
chk jq command -v jq
chk tmux command -v tmux
chk screen command -v screen
chk byobu command -v byobu
chk plocate command -v plocate
chk catfish command -v catfish
chk flameshot command -v flameshot
chk inotify-tools command -v inotifywait
chk 7z command -v 7z
chk trash-cli command -v trash-put
chk deja-dup command -v deja-dup
chk GParted command -v gparted
chk Synaptic command -v synaptic
chk GNOME-Tweaks command -v gnome-tweaks
chk hardinfo sh -c 'command -v hardinfo2 || command -v hardinfo'
echo "--- ネットワーク診断・セキュリティ ---"
chk nmap command -v nmap
chk tcpdump command -v tcpdump
chk Wireshark command -v wireshark
chk ClamAV command -v clamscan
chk Lynis command -v lynis
chk netcat command -v nc
chk intel-gpu-tools command -v intel_gpu_top
echo "--- データベース ---"
chk PostgreSQL-server sh -c 'ls /usr/lib/postgresql/*/bin/postgres'
chk PostgreSQL-service pg_isready
chk psql command -v psql
chk PostGIS sh -c 'ls /usr/share/postgresql/*/extension/postgis.control'
chk PL/Python sh -c 'ls /usr/share/postgresql/*/extension/plpython3u.control'
chk pgAdmin4 sh -c 'command -v pgadmin4 || test -d /usr/pgadmin4'
chk DBeaver command -v dbeaver
chk SQLite3 command -v sqlite3
chk SQLiteBrowser command -v sqlitebrowser
chk SpatiaLite command -v spatialite
chk Redis sh -c 'redis-cli ping | grep -q PONG'
chk MongoDB command -v mongod
echo "--- 画像・映像・3D・空間データ ---"
chk ffmpeg command -v ffmpeg
chk sox command -v sox
chk VLC command -v vlc
chk Audacity command -v audacity
chk OBS-Studio command -v obs
chk OpenShot command -v openshot-qt
chk Kdenlive command -v kdenlive
chk guvcview command -v guvcview
chk GIMP command -v gimp
chk Inkscape command -v inkscape
chk Pinta command -v pinta
chk darktable command -v darktable
chk RawTherapee command -v rawtherapee
chk ImageMagick sh -c 'command -v magick || command -v convert'
chk GraphicsMagick command -v gm
chk rsvg-convert command -v rsvg-convert
chk GraphViz command -v dot
chk gnuplot command -v gnuplot
chk Blender command -v blender
chk MeshLab command -v meshlab
chk CloudCompare sh -c 'command -v CloudCompare || command -v cloudcompare.CloudCompare'
chk GDAL command -v gdalinfo
chk QGIS command -v qgis
chk GRASS-GIS command -v grass
chk Google-Earth sh -c 'command -v google-earth-pro || test -x /opt/google/earth/pro/googleearth'
chk drawio command -v drawio
echo "--- 日本語処理・文書変換 ---"
chk MeCab sh -c 'echo 日本語 | mecab'
chk nkf command -v nkf
chk pdftotext command -v pdftotext
chk html2text command -v html2text
chk 日本語フォント sh -c 'fc-list | grep -qi "Noto Sans CJK"'
chk Mozc sh -c 'dpkg -l ibus-mozc fcitx5-mozc 2>/dev/null | grep -q "^ii"'
echo "--- Java・R・その他言語 ---"
chk Java-JRE java -version
chk Java-JDK javac -version
chk R R --version
chk Rscript Rscript --version
chk RStudio command -v rstudio
chk Node.js node --version
chk npm npm --version
echo "--- ゲーム ---"
chk 0ad command -v 0ad
chk SuperTuxKart command -v supertuxkart
chk Wesnoth sh -c 'command -v wesnoth || ls /usr/games/wesnoth*'
chk GNOME-Mahjongg command -v gnome-mahjongg
chk ExtremeTuxRacer command -v etr
chk Aisleriot command -v sol
chk Warzone2100 command -v warzone2100
chk Hedgewars command -v hedgewars
chk FrozenBubble command -v frozen-bubble
echo
if [ "${#CHECK_NG[@]}" -eq 0 ]; then
echo "確認結果: すべて OK"
else
echo "確認結果: NG ${#CHECK_NG[@]} 件(レポートに一覧を表示する)"
record_manual "導入確認(B95)で NG となった項目がある。当該アプリの導入手順と普通のエラー一覧を照合すること。GPU 非搭載機では NVIDIA 関連が NG となるのは正常である。"
fi
}
# ============================================================
# 主処理
# ============================================================
for a in "$@"; do
case "$a" in
--remaster) DO_REMASTER=1 ;;
--reset) DO_RESET=1 ;;
--check) DO_CHECK=1 ;;
*) echo "使用法: $(basename "$0") [--remaster] [--reset] [--check]"; exit 2 ;;
esac
done
if [ "${DO_CHECK}" -eq 1 ]; then
detect_env
final_check
report
exit 0
fi
preflight
run_block B10 "システム基盤" block_10_base
run_block B20 "リポジトリ登録" block_20_repositories
run_block B30 "アプリ導入(apt)" block_30_apps_apt
run_block B40 "アプリ導入(ソース)" block_40_apps_source
run_block B50 "不要パッケージ削除" block_50_remove_packages
run_block B60 "システム設定・サービス停止" block_60_system
run_block B70 "ユーザ設定" block_70_user
run_block B80 "不要ファイル削除・後処理" block_80_cleanup
if [ "${DO_REMASTER}" -eq 1 ]; then
run_block B90 "リマスター準備" block_90_remaster
fi
step "APT 自動更新ユニットの再開"
apt_restore
final_check
report
echo "全ブロックが完了した。設定を反映するため再起動すること。"
echo "確認だけを再実行する場合は ${SCRIPT_PATH} --check を実行する。"
echo "最初からやり直す場合は ${SCRIPT_PATH} --reset を実行する。"
exit 0
スクリプト作成方針
構成
- 1つのスクリプトにまとめる。外部スクリプトや配布サーバ上のファイルを取得しない。必要な内容はスクリプト内に直接書く。
- 全体をブロックに分ける。分ける理由は依存関係である。後のブロックは前のブロックの成果に依存する。その切れ目でブロックを分ける。
- 破壊的な操作は既定で行わない。明示的な指定(`--remaster` 等)を要求する。root では実行しない。
エラーの扱い
- エラーを二種類に分ける。普通のエラーは記録して続行する。深刻なエラーは、後続ブロックの失敗が確実に予想される場合に限り、当該ブロックの末尾で停止する。
- 深刻なエラーで停止したブロックは未完了のまま残す。人間が原因を除去して同じスクリプトを再実行すれば、当該ブロックの先頭から再開する。完了済みのブロックは再実行しない。ブロック分割はこのためにある。
- エラーに応じて処理を変えない。エラーは記録し、人間に表示し、対処は人間に任せる。AIはこの方針を変更してはならない。分岐してよいのは環境条件(OSバージョン、デスクトップ種別、ファイルやユニットの有無)だけである。
- 一括処理で全体を巻き添えにしない。パッケージは1つずつ導入し、失敗したものだけを記録する。
- 実行結果は末尾に一覧表示する。普通のエラー、人手での確認事項、完了ブロックを示す。
実装
- apt / apt-get の連続実行ではシステムファイルがロックされたままとなり失敗しやすい。自動更新ユニットを事前に停止し、全呼び出しにロック待ちを付けて対処する。ロックファイルの削除やプロセスの強制終了はdpkgデータベースを破壊するため行わない。エラーを検出して処理を切り替えることは愚かであり、行ってはならない。
- 何度実行してもよい状態を保つ。退避は一度だけ、削除は導入済みのものだけ、追記は管理ブロックを消してから行う。
- 同一処理は同一の書き方をする。共通関数に集約する。処理が変わらないなら、単純な書き方を選ぶ。
内容
- 有用性の全くない操作は削除する。重複した導入、存在しないパッケージ、結果を使わない処理を残さない。
- 古い情報を特定し、価値の無くなったものは新しい情報に交換する。バージョン番号、コード名、配布URL、リポジトリの提供状況は実際に確認して更新する。提供が終わったものは削除し、理由を確認事項として残す。
- 不正確または不十分な箇所は修正または削除する。推測で補わない。
- 導入するアプリの一覧は「Ubuntu 22.04/24.04/26.04 開発・研究環境構築ガイド」と一致させる。自動導入しないアプリは確認事項として記録し、手順はガイド側に正確に記載する。B95 の確認項目はガイド末尾の check-install.sh と同一に保つ。