我正在尝试执行this TCL script,这应该批量规范化mp3的文件夹。我在OSX Yosemite上(完全安装了ffmpeg)。该行是:
./normalise.tcl mp3folder
(包括sudo)返回:
./normalise.tcl: line 37: proc: command not found
./normalise.tcl: line 38: global: command not found
./normalise.tcl: line 40: switch: command not found
./normalise.tcl: line 42: puts: command not found
./normalise.tcl: line 43: syntax error near unexpected token `}'
./normalise.tcl: line 43: ` }'
..然后将shell指向内置的帮助文档。我没有这种语言的经验,所以我正在阅读,但到目前为止还没有找到任何解释它的东西。非常感谢您知道出了什么问题。
修改
脚本注释中建议的-d
选项似乎没有效果。
完整的脚本:
#!/bin/sh
#\
exec tclsh "$0" ${1+"$@"}
# Copyright 2015 Tholis Biroi (tholis DOT biroi AT yahoo DOT it)
#
# This file is part of 'normalize.tcl'.
# 'normalize.tcl' is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 'normalize.tcl' is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with 'normalize.tcl'. If not, see http://www.gnu.org/licenses/.
#
#
#
# 'normalize.tcl' is a simple TCL script that drives 'ffmpeg' to normalise
# audio levels for a group of mp3 files.
# For each mp3 file found into a directory, it reads the mean volume level,
# calculates the average level among various files and adjusts the volume
# level of each of them.
#
# Global debugging variable
# To override this option set '-d' on the command line
set debugOn false
# log --
#
# puts "" wrapper
#
proc log {label args} {
global debugOn
switch $label {
"info" {
puts {*}$args
}
"error" {
puts "error: [join {*}$args]"
}
"warning" {
puts "warning: [join {*}$args]"
}
"debug" {
if {$debugOn == "true"} {
puts "debug: [join {*}$args]"
}
}
default {
# do nothing in any case
}
}
}
# get_volumes --
#
# Exec 'ffmpeg' in order to get the volume mean level
#
#
proc get_volume {mp3} {
if {$mp3 == {}} {
log error "Empty file name."
return {}
}
# Set volume variable
set volume {}
# Set 'ffmpeg' command
set cmd "ffmpeg -i \"$mp3\" -af \"volumedetect\" -f null /dev/null"
log debug "'ffmpeg' cmd= $cmd"
# Exec 'ffmpeg'
if {[catch {eval exec -ignorestderr $cmd 2>@1} out]} {
log error "'ffmpeg' execution command failed."
log debug "reason= $out"
return {}
}
# In order to avoid 'case sensitive' parsing, the output of the
# command is converted to uppercase
set Out [string toupper $out]
log debug "'ffmpeg' out= $Out"
# Now scan the out a line at time searching for 'MEAN_VOLUME:'
# output string label
set lines [split $Out "\n"]
foreach line $lines {
log debug "$line"
# first of all search for 'VOLUMEDETECT' string and if foud
# search for 'MEAN_VOLUME:' string.
if {[string first VOLUMEDETECT $line] == -1} {
# Not found, skip line parsing
continue
}
# 'VOLUMEDETECT' string found, search for 'MEAN_VOLUME' string
set pos [string first MEAN_VOLUME $line]
if { $pos != -1} {
set start [expr {$pos + 11}]
set volStr [string range $line $start end]
log debug "volStr= $volStr"
# Extract and trim the first word as volume
set words [split $volStr]
log debug "words= $words"
set volume [string trim [lindex $words 1]]
log debug "volume= $volume"
}
}
return $volume
} ;# end get_volume
# set_volume --
#
# Exec 'ffmpeg' to re-encode the mp3
#
proc set_volume {mp3 actualVol targetVol} {
if {($mp3 == {}) || ($actualVol == {}) || ($actualVol == {})} {
log error "One or more parameter are empty"
return {}
}
# Create filename output
set mp3Root [file rootname $mp3]
set mp3OutFile "${mp3Root}.norm.mp3"
# If normalized file already exists, will be deleted
if {[file exists $mp3OutFile]} {
catch {file delete -force -- $mp3OutFile}
}
# calculate the delta volume
set deltaVol [expr {$targetVol - $actualVol}]
# Set 'ffmpeg' command
set cmd "ffmpeg -y -i \"$mp3\" -af \"volume=${deltaVol}dB\" \"$mp3OutFile\""
# Exec 'ffmpeg'
if {[catch {eval exec -ignorestderr $cmd 2>@1} out]} {
log error "'ffmpeg' execution command failed."
log debug "reason= $out"
return {}
}
# For debug purposes
set Out [string toupper $out]
log debug "'ffmpeg' out= $Out"
return $deltaVol
} ;# end set_volume
# byebye --
#
proc byebye {} {
log info ""
log info "Bye!"
log info ""
} ;# end byebye
# print_help --
#
# Prints a little help
#
proc print_help {} {
global argv0
log info ""
log info "Usage: $argv0 \[-h|--help\]|<mp3 dir>"
log info ""
log info "-h|--help Print thi help"
log info "<mp3 dir> Directory path containing mp3 to normalize"
log info ""
byebye
return
} ;# end print_help
# Main -----------------------------------------------------------------
log info ""
log info "MP3 normalizer v0.9 21 mar 2015"
log info ""
log info ""
# Save current dir
set currDir [pwd]
log debug "Working dir= $currDir"
# Control input parameters to setup working dir
# If no parameter is passed a little help is printed on screen
if {$argc == 0} {
print_help
exit 0
}
# If more than one parameter is passed
if {$argc != 1} {
log error "Wrong number of arguments."
log error "Use '-h' or '--help' option to print usage info."
byebye
exit 1
}
# If only one paramter is passed, it could be the help option or the
# desired working path
if {([lindex $argv 0] == "-h") || ([lindex $argv 0] == "--help")} {
print_help
exit 0
}
# Save the passed workDir in order to make some controls
set workDir [lindex $argv 0]
# The path passed must be a directory path
if { ![file isdirectory $workDir] } {
log error "The argument passed is not a valid directory path"
byebye
exit 1
}
# The argument passed must be an existing directory
if { ![file exists $workDir] } {
log error "Directory '$workDir' does not exists"
byebye
exit 1
}
# Move on working dir
cd $workDir
# Get the list of files in the current directory
set mp3Files [glob -nocomplain *.mp3]
if {$mp3Files == {}} {
log info "No .mp3 files found on working dir: '$workDir'"
byebye
exit 1
}
# Exclude from this list files with exetension *.norm.mp3"
set mp3FileList {}
foreach mp3 $mp3Files {
set rootFname [file rootname $mp3]
set ext [file extension $rootFname]
if {$ext == ".norm"} {
# Skip already normalized files from mp3 list
continue
}
lappend mp3FileList $mp3
}
# Init the mp3 array
#set mp3Ar {}
log info "List of file mp3 to be normalized:"
# Foreach *.mp3 file
foreach mp3 $mp3FileList {
log info " '$mp3'"
# Extract volumes
set vol [get_volume $mp3]
if {$vol == {}} {
log warning "No volume information found for file: $mp3"
} else {
# Fill the array of volumes
set mp3Ar($mp3) $vol
}
}
log info ""
# parray only for debugging
#parray mp3Ar
# Calculating the average volume
set avgVol 0
set mp3List [array names mp3Ar]
set numFiles [llength $mp3List]
foreach mp3 $mp3List {
set avgVol [expr {$mp3Ar($mp3) + $avgVol}]
}
set avgVolume [expr {$avgVol/double($numFiles)}]
set avgVol [format "%0.1f" $avgVolume]
log info "Avg Volume= $avgVol"
log info ""
# Now foreach file calculate delta volume to normalize it
log info "File normalization at $avgVol dB"
foreach mp3 $mp3List {
log info " '$mp3' from $mp3Ar($mp3) to $avgVol"
if {[set_volume $mp3 $mp3Ar($mp3) $avgVol] == {}} {
log info "warning: Set volume failed for file '$mp3'"
}
}
log info ""
log info "Done."
# Before exit return to run dir
cd $currDir
byebye
exit 0
答案 0 :(得分:2)
由于某种原因,该脚本实际上是由bourne shell(/bin/sh
)完整执行的,而不是Tcl。由于这两种语言具有完全不同的语法,因此可以获得这些错误消息。
但为什么会这样呢?
嗯,关键是这些:
#!/bin/sh
#\
exec tclsh "$0" ${1+"$@"}
应该首先使用bourne shell运行脚本,然后将执行转移到Tcl(因为标准shell exec
替换当前进程可执行文件)。它基于以下事实:shell不会认为注释中一行末尾的反斜杠是特殊的,但Tcl认为这意味着以下行也是注释的一部分。不同的规则。
然而那是失败的。我猜测问题是tclsh
不在您的路径上(真的吗?它是我的OSX系统的标准部分。)那么exec
是失败,因此正在解释脚本的其余部分。这有点奇怪。你说你通过sudo
去了所以这可能是一个问题,但tclsh
放在{{1}的一个目录中确实应该有一个sudo
默认情况下(PATH
),这可能不是正在发生的事情。
此时推荐的方法是更改脚本的这三行,以使用更为现代的成语:
/usr/bin
您也可以在其中使用#!/usr/bin/env tclsh
的完全指定名称。或者您可以尝试使用以下命令启动代码:
tclsh
最后一步也是检测是否存在其他问题的好方法;它会覆盖脚本解释器的查找,并让您专注于之后发生的事情。