経緯
先日ふと画像整理を思い立って始めたのだが、画像が多すぎて捗らない。
別々のスマホで撮った写真や加工して解像度がデフォルトではなくなっている画像がちらほら。
...とてもめんどくさい!!
なんかこう、うまいこと、shellでできないかなぁ...。
けど布団の外は寒いし出たくないなぁ...。
そんなかんなでperplexity.aiにいい方法がないか尋ねてみた。
me:
Androidスマホ単体でコマンドは実行できますか?
perplexity.ai先生:
Androidスマホ単体でも「コマンド(シェル/Linuxコマンド)」は実行できます。・Termux や TermOne Plus などの「ターミナルエミュレータ」アプリを入れると、スマホ上でシェルを直接開いてコマンドを実行できます。
・TermuxはAPTパッケージマネージャを持っていて、bash・zsh・ssh・python など多数のLinuxパッケージをインストールできる、ほぼLinux環境として使えるアプリです。
ほう?
なんか出来そうである。
環境
使用端末: Galaxy S24
Androidバージョン: 16
事前準備
必要なアプリ
- Termux
- ファイルマネージャー(ファイルの移動や作成ができるアプリであればなんでも良い)
![]() |
![]() |
1.Termuxを触ってみる
まずTermuxで何ができるのか色々調べてみた。
Q."Hello World"を出力するには?
A.以下のコマンドを実行
$ cd ~
$ touch hello.sh
$ vi hello.sh
↓↓↓以下はvim内で書いたもの↓↓↓
#!/data/data/com.termux/files/usr/bin/bash
echo "Hello World"
↑↑↑ここまで↑↑↑
Q.どこのディレクトリ階層で実行される?
A./data/data/com.termux/files/home
(要はAndroidのTermuxアプリのデータフォルダの中にあるみたい)
Q.Androidのdownloadsフォルダで何かをしたときは?
A.termux-setup-storageというコマンドをTermux内で打つとAndroidストレージへのリンクが作られる(~/storage)。
なので以下コマンドで移動し、実行できる
# リンク作成
$ termux-setup-storage
# 移動
$ cd storage/downloads
# 適当なファイルを作成
$ touch aaa.txt
Q.ImageMagicやpythonのようなパッケージは使える?
A.パッケージをインストールすることで使用可能
$ pkg install imagemagick
$ pkg install python
Q.アプリのキャッシュ削除や、データの削除を行うとインストールしたパッケージはどうなる?
A.キャッシュ削除では消えないが、データの削除を行うと消える
なんとなく分かってきた。
普通のPCのターミナルみたいに使えそう。
色々パッケージ入れて、使い終わったらデータ削除で綺麗さっぱり。
最高かよ。
早速使っていこう!
2.デバイスごとに分ける(カメラで撮った画像用)
まずは画像を仕分け処理するためのフォルダやshellファイルを作成する。
今回はDownloads配下にファイルマネージャーを使用し、以下のような階層で作ってみた。
downloads
└── execute(フォルダ)
├── input(フォルダ。この中に仕分けたい画像を突っ込む)
└── sort_by_model.sh
sort_by_model.sh (perplexity.ai先生作)
#!/data/data/com.termux/files/usr/bin/bash
INPUT_DIR="input"
OUTPUT_DIR="output/Camera"
# スクリプト自身が置かれているディレクトリに移動
# どこからシェルを実行しても相対パスinputやoutputが正しく機能するように
cd "$(dirname "$0")"
# ==== オプション解析 (-r で解像度仕分けON) ====
# r = Resolution = 解像度
RESOLUTION_SORT=false
while getopts "r" opt; do
case "$opt" in
r) RESOLUTION_SORT=true ;;
*) echo "Usage: $0 [-r]"; exit 1 ;;
esac
done
echo "=== カメラモデル仕分け開始 (解像度仕分け: $RESOLUTION_SORT) ==="
echo "カレント: $(pwd)"
# 出力先ディレクトリの作成
# 既に存在してもエラーにならないよう -p オプション付き
mkdir -p "$OUTPUT_DIR"
# エラー防止のため、input/*.jpg などの該当ファイルがない場合は、パターン文字列そのものではなく「空」にする
shopt -s nullglob
for f in input/*.jpg input/*.JPG \
input/*.jpeg input/*.JPEG \
input/*.png input/*.PNG \
input/*.heic input/*.HEIC \
input/*.mp4 input/*.MP4 \
input/*.mov input/*.MOV; do
# ファイルが通常ファイルでない(例えばディレクトリやリンク)の場合はスキップ
[[ ! -f "$f" ]] && continue
# 今処理しているファイル名を表示
echo "処理中: $(basename "$f")"
# カメラモデル取得
# -s3で値だけを出力し、2>/dev/nullでエラー出力を無視
model=$(exiftool -s3 -Model "$f" 2>/dev/null | tail -1)
echo " モデル: '$model'"
# モデル名が空、または「No Exif data」と出た場合はExifが無いと判断し、そのままinputフォルダに残す
if [[ -z "$model" || "$model" == "No Exif data" ]]; then
echo "⚪Exifなし → input残留"
continue
fi
# モデル名サニタイズ & モデルフォルダ作成
# カメラモデル名をそのままフォルダ名に使うと危険なので、安全化処理(サニタイズ) を実行
safe_model=$(echo "$model" | tr ' /\\:*?"<>|' '_' | sed 's/[^a-zA-Z0-9_]/_/g' | cut -c1-30)
model_dir="$OUTPUT_DIR/$safe_model"
mkdir -p "$model_dir"
# ==== 解像度仕分けをしない場合 (-rなし) ====
if [[ "$RESOLUTION_SORT" != true ]]; then
mv "$f" "$model_dir/"
echo "✓ → $safe_model/"
continue
fi
# ==== 解像度仕分けする場合 (-rあり) ====
resolution=""
if [[ "$f" =~ \.(mp4|mov|avi|mkv|MOV|AVI|MKV)$ ]]; then
# 動画: ffprobe
resolution=$(ffprobe -v quiet -select_streams v:0 \
-show_entries stream=width,height \
-of csv=s=x:p=0 "$f" 2>/dev/null)
else
# 静止画: exiftool (ImageWidth / ImageHeight)
resolution=$(exiftool -s3 -ImageWidth -s3 -ImageHeight "$f" 2>/dev/null \
| paste - - | tail -1)
fi
echo "解像度: '$resolution'"
if [[ -z "$resolution" ]]; then
# 解像度取得失敗 → モデル配下の専用フォルダ
fail_dir="$model_dir/解像度の取得に失敗"
mkdir -p "$fail_dir"
mv "$f" "$fail_dir/"
echo "⚪解像度取得失敗 → $safe_model/解像度の取得に失敗/"
continue
fi
# 解像度文字列を WxH に整形
if [[ "$resolution" =~ ([0-9]+)x([0-9]+) ]]; then
width=${BASH_REMATCH[1]}
height=${BASH_REMATCH[2]}
res_dir="${width}x${height}"
else
read width height <<< "$resolution"
[[ -n "$width" && -n "$height" ]] && res_dir="${width}x${height}" || res_dir="unknown"
fi
safe_res=$(echo "$res_dir" | tr ' /\\:*?"<>|' '_' | sed 's/[^0-9x]//g' | cut -c1-20)
target_dir="$model_dir/$safe_res"
mkdir -p "$target_dir"
mv "$f" "$target_dir/"
echo "✓ → $safe_model/$safe_res/"
done
echo "=== 仕分け完了: $OUTPUT_DIR/ ==="
実行
1.inputフォルダ配下に仕分けたい画像を格納
2.以下のコマンドを順番に実行
# ストレージへのアクセス許可(すでに実行済みならスキップ)
$ termux-setup-storage
# Exif情報取得ツール(すでに実行済みならスキップ)
$ pkg install exiftool
# ImageMagick(すでに実行済みならスキップ)
$ pkg install imagemagick
# 動画対応(すでに実行済みならスキップ)
$ pkg install ffmpeg
# 移動
$ cd ~/storage/downloads/execute
# 実行(もし解像度でも階層を切りたい場合は -r オプションを付ける)
bash ./sort_by_model.sh
![]() |
![]() |
![]() |
![]() |
実行後、execute/output配下に仕分けた画像が出力される。
モデル情報がない画像(スクショ画像等)はexecute/input配下に残るので次の工程へ。
3.解像度ごとに分ける(スクショ画像用)
先ほどのexecuteフォルダ配下に新しいshellファイルを作成する
downloads
└── execute
├── input
├── sort_by_model.sh
└── sort_by_size.sh ← New!!
sort_by_size.sh (perplexity.ai先生作)
#!/data/data/com.termux/files/usr/bin/bash
INPUT_DIR="input"
OUTPUT_BASE="output/Screenshots"
cd "$(dirname "$0")"
echo "=== 画像サイズ別仕分け開始 ==="
echo "カレント: $(pwd)"
mkdir -p "$OUTPUT_BASE"
shopt -s nullglob
for f in input/*.{jpg,jpeg,JPG,JPEG,png,PNG,heic,HEIC}; do
[[ ! -f "$f" ]] && continue
echo "処理中: $(basename "$f")"
# 画像サイズ取得(identifyコマンド使用)
size=$(identify -format "%wx%h" "$f" 2>/dev/null)
if [[ -z "$size" ]]; then
echo "⚪ サイズ取得失敗 → input残留"
continue
fi
echo "サイズ: $size"
# 縦横サイズのフォルダ名(例: 1080x1920)
size_folder="$size"
mkdir -p "$OUTPUT_BASE/$size_folder"
# 同名時はエラー(上書きせず)
if [[ -f "$OUTPUT_BASE/$size_folder/$(basename "$f")" ]]; then
echo "❌ 同名ファイル存在 → input残留"
continue
fi
mv "$f" "$OUTPUT_BASE/$size_folder/"
echo "✓ → $size_folder/"
done
echo "=== 仕分け完了: $OUTPUT_BASE/ ==="
実行
1.inputフォルダ配下に仕分けたい画像を格納
2.以下のコマンドを順番に実行
# ストレージへのアクセス許可(すでに実行済みならスキップ)
$ termux-setup-storage
# 移動(すでに移動済みならスキップ)
$ cd ~/storage/downloads/execute
# 実行
bash ./sort_by_size.sh
まとめ
これで楽して画像仕分けできる!
個人的にアプリのデータ削除を行うだけでクリーンな環境に戻るのが最高!
コマンド使いたいけど布団の外に出たくない人はぜひTermux使ってみてね〜





