
Ubuntu日本語フォーラム

ログインしていません。
現在、音楽ファイルをflac形式で保存しています。アーティスト名でフォルダを作り、その下の階層にアルバム名でフォルダを作ってファイルを入れています。
ファイルの名前を、タグ情報を引用して[Artist] - [Album] - [Track Number].[Title]という形式にしたいと考えています。今、flacコマンドを使用するためにインストールし、様々なHPを参考に以下のコードを作成しました。
for a in *.flac;do ARTIST=`metaflac "$a" --show-tag=ARTIST | sed s/.*=//g` ALBUM=`metaflac "$a" --show-tag=ALBUM | sed s/.*=//g` TITLE=`metaflac "$a" --show-tag=TITLE | sed s/.*=//g` TRACKNUMBER=`metaflac "$a" --show-tag=TRACKNUMBER | sed s/.*=//g` mv "$a" "$ARTIST - $ALBUM - `printf %02g $TRACKNUMBER`.$TITLE.flac"; done
上記のコードを実行すれば、カレントディレクトリ内のフォルダは希望通りにリネームできました。しかし、これを200以上のフォルダにcdコマンドで移動しながら実行するというのは骨が折れます。
そこで良いコードを考えていただけないでしょうか。おそらくフリーのソフトウェアで便利なものもたくさんあるとは思いますが、ターミナルの練習としてコードでやろうと考えています。
findコマンドを使うことでうまく行くかと考えたのですが、xargsコマンドを用いても引数の受け渡しの部分で詰まってしまいました。
オフライン
難しいコマンドを使わなくとも、わかる範囲のコマンドで、作られたほうが良いのではないですか?
単純に考え、
アーティスト名のディレクトリが入っているディレクトリで、ディレクトリを取り、順に、作ってあるリネーム処理をし、ディレクトリを移動していけば良いのではないでしょうか?
簡単に作れば、for の2重ループ構造でもいいでしょうし、リネーム処理コード部分を、関数にして、ディレクトリ移動処理ループ内から呼び出しても良いでしょう。
注意するとすれば、カレントディレクトリと上位ディレクトリを表す、”.”と”..”で処理をしないことくらいでしょうか?
オフライン
si による投稿:
注意するとすれば、カレントディレクトリと上位ディレクトリを表す、”.”と”..”で処理をしないことくらいでしょうか?
for x in *
do
echo $x
done
で確認したら、”.”と”..” は、出てきませんでしたね。
失礼
オフライン
siさん、回答ありがとうございます。実は投稿の後にOSの情報を書き忘れたことに気づきました。ubuntu13.10を使用しています。
どうも正規表現のあたりでうまく行かなくて諦めかけていました。アドバイスを参考に以下のコードを書いたのですが、やはりうまく行きません。
for a in $(ls -F | grep /)
do
`cd "$a"`
for b in $(ls -F | grep /)
do
`cd "$b"`
for c in *.flac
do
ARTIST=`metaflac "$c" --show-tag=ARTIST | sed s/.*=//g`
ALBUM=`metaflac "$c" --show-tag=ALBUM | sed s/.*=//g`
TITLE=`metaflac "$c" --show-tag=TITLE | sed s/.*=//g`
TRACKNUMBER=`metaflac "$c" --show-tag=TRACKNUMBER | sed s/.*=//g`
mv "$c" "$ARTIST - $ALBUM - `printf %02g $TRACKNUMBER`.$TITLE.flac"
done
done
done出力結果は以下のようになります。とても長いので一部抜粋です。
bash: cd: THE: そのようなファイルやディレクトリはありません *.flac: ERROR: reading metadata, status = "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE" The FLAC file could not be opened. Most likely the file does not exist or is not readable. *.flac: ERROR: reading metadata, status = "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE" The FLAC file could not be opened. Most likely the file does not exist or is not readable. *.flac: ERROR: reading metadata, status = "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE"
きりがないので途中で実行を止めると、カレントディレクトリはコマンドを実行したディレクトリのままです。
THEというディレクトリがないこと、cdコマンドがTHEというディレクトリを見つけられずにエラーになっていることから、変数が空白ごとに別々に読み込まれていることが原因のエラーと考えました。
様々なHPを参考に上記のようにバッククォーテーションを用いて見たのですが症状は変わらず。リストコマンドを単純に*で置き換えてもダメでした。
もう少しヒントをいただけないでしょうか。
オフライン
for a in $(ls -F | grep /)
do
`cd "$a"` ---> これに対応する、cd .. が無い
for b in $(ls -F | grep /) ---> このループは要るの?
do
`cd "$b"` ---> これもまた対応する、cd .. が無い
for c in *.flac
do
ARTIST=`metaflac "$c" --show-tag=ARTIST | sed s/.*=//g`
ALBUM=`metaflac "$c" --show-tag=ALBUM | sed s/.*=//g`
TITLE=`metaflac "$c" --show-tag=TITLE | sed s/.*=//g`
TRACKNUMBER=`metaflac "$c" --show-tag=TRACKNUMBER | sed s/.*=//g`
mv "$c" "$ARTIST - $ALBUM - `printf %02g $TRACKNUMBER`.$TITLE.flac"
done
done
doneオフライン
2重目のループ要るのかなと思いましたが、
ディレクトリ構成が
アーチスト ー アルバム ー 曲
だから、3重のループで良いのですね。
オフライン
siさん、何度も回答ありがとうございます。実は最初にアドバイスを頂いた際に、cdコマンドで上の階層に戻るようにコードを書いてみました。いろいろ試行錯誤の末、cd ..はないほうがいいのではないかとも考えていましたが、今考えなおすとご指摘通りないとだめですね。
改めて改善したコードが以下です。まだ問題点があります。
for a in $(ls -F | grep /)
do
`cd "$a"`
for b in $(ls -F | grep /)
do
`cd "$b"`
for c in *.flac
do
ARTIST=`metaflac "$c" --show-tag=ARTIST | sed s/.*=//g`
ALBUM=`metaflac "$c" --show-tag=ALBUM | sed s/.*=//g`
TITLE=`metaflac "$c" --show-tag=TITLE | sed s/.*=//g`
TRACKNUMBER=`metaflac "$c" --show-tag=TRACKNUMBER | sed s/.*=//g`
mv "$c" "$ARTIST - $ALBUM - `printf %02g $TRACKNUMBER`.$TITLE.flac"
done
cd ..
done
cd ..
doneこれを実行すると、また大量に印字されるので途中で実行を中止するとホームディレクリにいました。この現象から想像するに、cd $aコマンドやcd $bがうまく実行されず、cd ..コマンドだけが実行されていると考えられます。
なぜcd $aなどのコマンドがうまくいかないかを考えてみました。自分の至る結論はやはり空白ごとに区切って引数になっているということです。少し状況の説明が不足していたので以下に詳細を書きます。
実行結果のログを見ると、"The"というディレクトリに移動しようとして、そのようなディレクトリがないとエラーが返されています。これは、"The Beatles"というアーティスト名のディレクトリがあり、それを変数aとしてcdコマンドの引数とする際に、cd The とcd Beatlesというように2つのコマンドに別れてしまっていることが原因と考えました。
この現象を防ぐためにバッククォーテーションを使ってみたのですが改善されません。もしこのコードの問題点が指摘した部分であれば、正しい表現をアドバイスいただければと思います。
もしその他にも問題点がありましたら、そのご指摘もしていただければと思います。
オフライン
こんな感じかな?
find '/path/to/files' -type f -name '*.flac' | while read f
do
n=変更後ファイル名
d=$(dirname "$f")
mv -v "$f" "$d/$n"
doneSTGSAGWANさん、回答ありがとうございます。参考に、というよりもほとんどそのまま自分の環境に当てはめて実行してみました。
find . -type f -name '*.flac' | while read f
do
ARTIST=`metaflac "$f" --show-tag=ARTIST | sed s/.*=//g`
ALBUM=`metaflac "$f" --show-tag=ALBUM | sed s/.*=//g`
TITLE=`metaflac "$f" --show-tag=TITLE | sed s/.*=//g`
TRACKNUMBER=`metaflac "$f" --show-tag=TRACKNUMBER | sed s/.*=//g`
d=$(dirname "$f")
mv "$f" "$d/$ARTIST - $ALBUM - $TRACKNUMBER.$TITLE.flac"
donefor文でうまく行かなかった理由が未だわからないままですが、while文にすることでうまく行ったようです。本当に回答ありがとうございました。満足行く結果が得られました。
ここまでで十分なのですが、実はSTGSAGWANさんの回答を参考にしている際に、間違えて変数dなしで実行してしまいました。結果、見事にカレントディレクトリに希望通りのファイル名にリネームされたファイルの数々が作られました。mvコマンドをよく理解していなかったからですね。いくつかカレントディレクトリに移動されていないファイルもあるのでそれがよく分からない点ですが、今後理解していこうと思います。また、間違えて移動してしまったファイルを元のディレクトリに戻すコードも考えてみます。もしこれを見かけたかたで良い案があれば意見いただければとも考えていますが、まだ試行錯誤もしていない段階なので、問題が起きたらまた質問させていただきます。今回はsiさん、STGSAGWANさんともにありがとうございました。
オフライン
for文でうまく行かなかった理由が未だわからないままですが、
#7でご自身で答えを出していますが、スペース混じりのデータにfor文を使うと、IFS(インターナルフィールドセパレーター:区切り文字)を変更しなければなりません。
スペースが区切り文字として認識されてしまい、"aaaa bbbb"の様に1つとして認識させたい物が"aaaa"と"bbbb"の様に2つに認識されてしまう為です。
カレントディレクトリに希望通りのファイル名にリネームされたファイルの数々が作られました。
パスが、アーチスト/アルバム と言う構成との事ですが、flacファイルに埋め込まれているアーチストとアルバム情報が「パスと全く同じ文字構成」で書き込まれているならば、同じ様にそこから情報を取得してパス名を作り出せば良いのではないでしょうか。
mv "カレントにあるflacファイル" "${ARTIST}/${ALBUM}"
といった感じ。
findはカレントディレクトリのみ検索させる為に
find . -maxdepth 1 -name "*.flac" -type f
の様にすれば良いかと思います。
始めは mv を echo に変えて画面上の出力だけで確認し、それが正しいと判断できたら mv に変更する方が被害が出ないと思います。
dd による投稿:
自分の至る結論はやはり空白ごとに区切って引数になっているということです。
空白文字は、デリミタになるので
for a in $(ls -F | grep /)
等とすると、word 単位に分割されてしまします。
こういった場合の、回避策の、1例です
#!/bin/bash
for a in *
do
if test -d "$a"
then
cd "$a"
pwd
cd ".."
fi
doneオフライン
dd による投稿:
また、間違えて移動してしまったファイルを元のディレクトリに戻すコードも考えてみます。
ここまで来ると、多分、シェルスクリプトだけでは、シンドイと思われますので、文字列処理ライブラリの豊富なスクリプト言語、
例えば、perl, ruby, scheme, python 等を、覚えてトライしたほうが良いかも知れません。
オフライン
katsu07さん、siさん回答ありがとうございます。最初に結論から書くと、全てうまく行きました。回答して下さった皆さんは、自分でコードを書きたいというわがままに答える形でヒントのみを与えて下さり、物分かりの悪い自分にイライラしていたかもしれませんがようやく解決です。
今後同じ問題にぶつかった方のためにも、備忘録としても書き込みを頂いてからの試行錯誤を記載しておきます。まずkatsu07さんの書き込みを見て良い案だと思いました。しかし問題もありました。flacファイルに情報をタグ付けした際に、データベースの情報を利用するとcdによって同じアーティストでも大文字小文字等の表記が異なる場合があり、自分でディレクトリを作り統一するという作業をしていました。そのためタグの情報とディレクトリ名が少し異なるファイルがいくつかありました。そのためechoコマンドを使うというアドバイスは大いに活用させていただくとして、他の戦略をとることにしました。
siさんの書き込みを見て、諦めかけましたが、なんとか実現出来ました。挙げていた言語だとruby、schemeを齧ったことがありましたが、本当に触りだけなので自信がありませんでした。しかしターミナル上でできる自信はありました笑。一度ディレクトリを全て削除し、タグの情報を元にディレクトリを作り直し、移動するという手順をとりました。ディレクトリの削除はGUIで行い、ディレクトリの作成、移動は以下のコードで行いました。
find . -type f -name '*.flac' | while read f;
do
ARTIST=`metaflac "$f" --show-tag=albumartist|sed s/.*=//g`
ALBUM=`metaflac "$f" --show-tag=album | sed s/.*=//g`
mkdir -p "$ARTIST"/"$ALBUM"
done
find . -maxdepth 1 -name "*.flac" -type f | while read f;
do
ARTIST=`metaflac "$f" --show-tag=albumartist|sed s/.*=//g`
ALBUM=`metaflac "$f" --show-tag=album | sed s/.*=//g`
mv "$f" "$ARTIST"/"$ALBUM"
doneまとめて一つのwhile文にできますが、うまく行くか少し不安でこのような形にしました。このコードを作るまでにechoを使う手法で何度か確認もしました。結果は大成功でした。今回はsiさんをはじめ多くの方に助けていただき、希望通りのリネーム及びターミナル上でのファイル、ディレクトリの扱いに慣れることができました。本当にありがとうございました。
オフライン