次のスクリプトは、現在の作業ディレクトリ内のすべてのメディアファイルを切り捨てるように設計されています。
#!/usr/bin/bash
trimmer() {
of=$(echo "${if}"|sed -E "s/(\.)([avimp4kvweb]{3,3}$)/\1trimmed\.\2/")
ffmpeg -hide_banner -loglevel warning -ss "${b}" -to "${ddecreased}" -i "${if}" -c copy "${of}"
echo "Success. Exiting .."
}
get_ddecreased() {
duration="$(ffprobe -v quiet -show_entries format=duration -hide_banner "${if}"|grep duration|sed -E s/duration=\([0-9]*\)\..*$/\\1/)"
echo ${duration}
ddecreased="$(echo "${duration} - ${trimming}"|bc -l)"
echo ${ddecreased}
}
rm_source() {
echo -e "Remove ${if}?[Y/n]?"
read ch
if [[ "${ch}" == 'y' ]]; then
rm "${if}"
fi
}
echo "How much of the beginning would you like to trim?"
read b
echo "How much of the end would you like to trim?"
read trimming
ls *.avi *.mkv *.mp4 *.vob >list_of_files
echo "Prompt before removing the source[Y/n]?"
read ch
while IFS="" read -r if || [[ -n "${if}" ]]; do
if [[ "${ch}" == 'y' ]]; then
get_ddecreased && trimmer && rm_source
elif [[ "${ch}" == 'n' ]]; then
get_ddecreased && trimmer && rm "${if}"
fi
echo $if
done <list_of_files
echo -e "Removing list_of_files."
rm list_of_files
最初のファイルは、ユーザーに要求に応じてy
トリミングを選択しPrompt before removing the source[Y/n]
て完了するかどうかを尋ねるメッセージを表示するように設計されていますtrimmer
。rm_source
彼らの入力を待つソースファイルを削除する前に、これはスクリプトが入力を待たず、echo -e "Removing list_of_files."
whileループがまったくないかのようにすぐに続くため、機能しません。ユーザーがn
要求したときに選択すると、whileループも実行されませんPrompt before removing the source[Y/n]
。スクリプトがecho -e "Removing list_of_files."
繰り返すのではなく直接続行されますlist_of_files
。なぜですか?しかし、これらすべての行をコメントアウトすると
if [[ "${ch}" == 'y' ]]; then
get_ddecreased && trimmer && rm_source
elif [[ "${ch}" == 'n' ]]; then
get_ddecreased && trimmer && rm "${if}"
fi
whileループ内では、すべての行がlist_of_files
画面に印刷されます。
私のコードに問題がありますか?
答え1
コードはデフォルトで次のことを行います。
foo () {
read variable
}
while read something; do
foo
done <input-file
目的は、端末から何かをread
読むfoo
ことですが、いくつかのファイルからリダイレクトされる標準入力ストリームのコンテキストで呼び出されます。
read
これは、inがfoo
端末ではなく入力ファイルの入力ストリームから読み取られることを意味します。
標準入力以外のファイル記述子からループを読み取ると、この問題を回避できます。
foo () {
read variable
}
while read something <&3; do
foo
done 3<input-file
ここで、read
ループはキーワードの後に入力ファイルに関連付けられたファイル記述子3から読み取られますdone
。これにより、関数read
内の関数が生のfoo
標準入力ストリームを自由に使用できます。
bash
シェルでは、追加のファイル記述子にハードコードされた値を使用する代わりに、シェル変数に記述子を割り当てることができます。
foo () {
read variable
}
while read something <&"$fd"; do
foo
done {fd}<input-file
$fd
10以上の整数に設定できます。正確な値は重要ではありません。
問題の現在のコードでは、ファイルのリストを生成して読み取ることを避け、代わりにファイルglobを直接使用して問題を解決することもできます。
for filename in *.avi *.mkv *.mp4 *.vob; do
if [ ! -e "$filename" ]; then
# skip non-existing names
continue
fi
# use "$filename" here
# ... and call your rm_source function
done
これにより、リダイレクトが完全に防止されます。これにより、コード内の名前に改行文字を含む奇妙なファイルを処理することもできます。
指定されたファイルが存在するかどうかをテストするループのステートメントは、パターンに一致する名前がif
ない場合、デフォルトではシェルがワイルドカードパターンを保持するために必要です。以下を設定して、シェルif
からステートメントを削除できます。bash
nullglob
シェルオプションの使用 shopt -s nullglob
。このオプションを設定すると、bash
シェルは一致しないグローブを完全に削除します。
また、ワイルドカードパターンと一致する名前目次。たとえば、ディレクトリがある場合は一覧表示されますmydir.mp3
。ls
コンテンツこのディレクトリの。また、パターンに一致するファイル名がダッシュで始まる場合、使用されたコードはls
オプションセットの名前を間違える可能性があります。