2015-07-10 156 views
2

我正在嘗試執行this TCL script,它應該批量規範mp3文件夾的規格。我在OSX Yosemite(ffmpeg完全安裝)上。該生產線是:在OSX中執行TCL腳本時發現「命令未找到」錯誤

./normalise.tcl mp3folder 

這(增量須藤)返回:

./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: `  }' 

..然後引導殼內置的幫助文檔。我沒有這種語言的經驗,所以我正在閱讀,但迄今尚未遇到任何解釋它的內容。非常感謝一個想法出了什麼問題。


編輯

-d選項建議在腳本評論似乎沒有任何效果。

完整腳本:

#!/bin/sh 
#\ 
exec tclsh "$0" ${1+"[email protected]"} 

# 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 
+1

我對TCL並不熟悉,但是發生了什麼是'/ bin/sh'正在執行那些TCL指令,並且它不理解它。我認爲你可以將hash-bang行改爲'#!/usr/bin/env tcl'並註釋掉'tclsh'行。 – trojanfoe

+2

第二行斜線後有空格嗎? –

+0

謝謝。我嘗試了新的hash-bang而沒有成功,並且註釋掉了'tclsh'這一行。然後恢復到原來的代碼,莫名其妙地這次它工作!也許一些我需要學習的語法微妙.. 我會很快刪除這個問題.. – geotheory

回答

2

出於某種原因,該腳本實際正在以其整體通過伯恩殼(/bin/sh),而不是Tcl的執行。由於這兩種語言的語法相當不同,因此可以爲您提供這些錯誤消息。

但爲什麼會發生這種情況?

好了,關鍵線路是這些:

#!/bin/sh 
#\ 
exec tclsh "$0" ${1+"[email protected]"} 

這應該是與Bourne shell的最初運行的腳本,然後轉移執行給Tcl的(因爲標準shell exec取代當前進程的可執行文件) 。它基於shell不認爲註釋中行尾的反斜槓是特殊的,但Tcl認爲這意味着下面的行也是註釋的一部分。不同的規則。

然而,這是失敗的。我猜測問題是tclsh不在你的路上(真的嗎?它是我的OSX系統的一個標準部分),所以exec失敗了,因此腳本的其餘部分被解釋。這有點奇怪。你說你要通過sudo這樣可能會出現問題,但是默認設置爲PATH/usr/bin)的目錄中應該有一個tclsh,所以可能不會發生什麼。

在這一點上推薦的方法是改變那些三行的腳本使用更現代的成語,這是一個單行:

#!/usr/bin/env tclsh 

您可以在那裏使用完全指定名稱tclsh爲好。或者你可以嘗試啓動代碼:

tclsh ./normalise.tcl mp3folder 

這最後一步,也有其他的問題,檢測的好辦法;它會覆蓋腳本解釋器的查找,並讓您專注於此後發生的事情。

+0

謝謝 - 有用的問題描述。最初的劇本(有些莫名其妙地)確實發揮了作用,所以一定會有所改變。 – geotheory

相關問題