試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

書庫ファイルに含まれる複数のオーディオファイルを一時ディレクトリに展開して再生する

複数のオーディオファイルをまとめたファイルをマウントして再生する(無圧縮tarボールを推奨)」の最後で触れたのだが、書庫ファイルに含まれる複数のオーディオファイルの再生は、一時ディレクトリに展開して処理するのが(うまくはないが)手っ取り早いため、前回のシェルスクリプトをベースにしてそのように修正してみた。
[任意]ファイル名: play-archive-tmp.sh

#! /bin/bash

shopt -s extglob  # case

## players
PLAYERS=(audacious totem)
## default
PLAYER_NO=0
TMPDIR=/dev/shm
#TMPDIR=/tmp

usage()
{
  local x i=0
  local players_list_str=""  # 0:player1|1:player2|2:player3...
  for x in ${PLAYERS[*]}; do
    players_list_str=${players_list_str}$(printf "|%d:%s" ${i} ${x})
    i=$((${i} + 1))
  done
  printf "Usage: %s [-h] [-m MOUNTPOINT] [-p PLAYER] [-w] [-z] archivefile
  -h             print help
  -p PLAYER      use PLAYER (%s)
  -z             use Zenity file selection dialog\n" $(basename $0) ${MOUNTPOINT} ${players_list_str/|/} >&2
}
die()
{
  echo "ERROR: "$1 >&2
  exit 1
}

USE_ZENITY=0
ARCHIVEFILE=""
while getopts 'hp:z' OPT
do
  case ${OPT} in
   p)
    PLAYER_NO=$((${OPTARG//[^0-9]/}))
    if [ ${PLAYER_NO} -gt $((${#PLAYERS[@]} - 1)) ]; then
      printf "ERROR: Illegal player number(%d) [max %d]\n" ${PLAYER_NO} $((${#PLAYERS[@]} - 1)) >&2
      exit 1
    fi
    ;;
   z)
    ARCHIVEFILE=$(zenity --file-selection)
    if [ $? -ne 0 ]; then exit 1; fi  # cancel
    USE_ZENITY=1
    ;;
   ?)
    usage
    exit 1
    ;;
  esac
done
shift $((${OPTIND} - 1))

if [ ${USE_ZENITY} -eq 0 ]; then ARCHIVEFILE=$1; fi
case ${ARCHIVEFILE} in
 *.t?(ar.)bz2)
  EXTRACT_CMD=tar
  EXTRACT_OPT=jxf
  ;;
 *.t?(ar.)gz)
  EXTRACT_CMD=tar
  EXTRACT_OPT=zxf
  ;;
 *.tar)
  EXTRACT_CMD=tar
  EXTRACT_OPT=xf
  ;;
 *.zip?(.mp3))
  EXTRACT_CMD=unzip
  EXTRACT_OPT=""
  ;;
 *)
  die "unknown file type"
  ;;
esac

IFS="	"
ARCHIVE_BASENAME=$(basename	${ARCHIVEFILE})
TMPDIR_ARCHIVENAME=${TMPDIR}/${ARCHIVE_BASENAME}
# relative path ==> absolute path (ARCHIVEFILE)
ARCHIVEFILE=$(cd	$(dirname	${ARCHIVEFILE}) && pwd)/${ARCHIVE_BASENAME}
# extract
mkdir	${TMPDIR_ARCHIVENAME} || die	"mkdir failed"
cd	${TMPDIR_ARCHIVENAME}
${EXTRACT_CMD}	${EXTRACT_OPT}	${ARCHIVEFILE}	>/dev/null || die	"extract failed"
# play
${PLAYERS[${PLAYER_NO}]}	${TMPDIR_ARCHIVENAME} || die	"cannot launch ${PLAYERS[${PLAYER_NO}]}"
# cleanup
rm	${TMPDIR_ARCHIVENAME}	-fr

処理の流れとしては

  1. 一時ディレクトリを作成
  2. 一時ディレクトリに入り、その中に入力ファイルを展開
  3. 一時ディレクトリ内を再生
  4. プレーヤ終了時に一時ディレクトリとその中身を削除

のようになり、難しくはないのだが、幾つか注意点があった。

  • 展開コマンドやオプション指定は書庫の種類ごとに異なるため、拡張子で判別して決めるようにした
  • bashにおけるcase文でのパターンマッチングは「パス名展開」のルールで、細かくは指定できないが、「shopt -s extglob」(拡張パターンマッチングを有効にする)指定で、「*.tar.bz2」と「*.tbz2」を「*.t?(ar.)bz2」という1つのパターンにするなどのことはできるようになる
  • 作業ディレクトリを移動して展開する関係で、入力ファイルが相対パスで指定された場合は絶対パスに置き換えないと展開に失敗する。そのため、入力ファイルに対して相対パスから絶対パスへの変換処理を行った(あまり使用しないpwdコマンドが役に立った)

実用性としては、展開の時間と負荷はかかるものの、書庫ファイルのサイズがそれほど大きなものでなければarchivemountでマウントするよりもよいのではないかという気がした。メモリが十分にあれば、/dev/shm以下を使用すればディスクアクセスも減らせる。メモリに余裕がなくても、一時ディレクトリに使用するためのディスク領域があれば展開もできる(変数TMPDIRを変更する)。