1

running Ubuntu Linux here.

I have a terminal command that finds all mp3 files in the PWD, gets their individual durations in minutes with mp3info, sums them, and prints the summed duration of all the mp3s in the pwd.

for file in *.mp3; do 
  mp3info -p "%S\n" "$file"
done | paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc

Example output:

$ for file in *.mp3; do 
  mp3info -p "%S\n" "$file"
done | paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc
47

So, 47 minutes's worth of mp3s in the PWD.

I want to make this into a bash script that will recurse into all lower directories, print their names, and list the summed durations of all the mp3s found in each folder, like:

foldernameA 
45 
foldernameB 
89 
foldernameC 
17

etc.

What I've tried ("durations.sh"):

#!/bin/bash
find . -type d -execdir sh -c 'for file in *.mp3; 
do 
  mp3info -p "%S\n" "$file"; 
done 
| paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc

But that just fails miserably:

$ ./durations.sh
./durations.sh: line 6: syntax error near unexpected token `('
./durations.sh: line 6: `| paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc'

I clearly have no idea what I'm doing.

2
  • 1
    Welcome, you have a single quote mismatch: your sh command starts, without end, and then your sed command starts again. O ne approach would be to double quote your sed command, and add the missing single quote to the end of the sh command. Jan 24, 2021 at 20:39
  • 1
    Using the find command to loop through dirs to then loop through files is not optimal. If you are going to use find, use it to find the files you need directly. Jan 24, 2021 at 20:43

2 Answers 2

1

You can use a for-loop directly with the shopt -s globstar:

globstar

If set, the pattern ‘**’ used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/’, only directories and subdirectories match.

shopt -s globstar

d=0;
for file in **/*.mp3; do
  d=$((d + $(mp3info -p "%S" "$file")))
done
mins=$(echo "$d / 60" | bc)
secs=$(echo "$d % 60" | bc)

echo "Total $mins minutes and $secs seconds"
1
  • Thank you. I turned the above codes into a bash script ("another.sh"), ran it, and it doesn't appear to want to recurse. I got: $ ./another.sh: line 6: d + : syntax error: operand expected (error token is "+ ") Total 77 minutes and 23 seconds (BTW: 77 minutes is accurate for the mp3s in the PWD, it's just not recursing.) Jan 24, 2021 at 23:39
0

If you want to list the lenght of individual folder you need a double loop. The first loop listing directory and the second one listing files in each directory:

#!/bin/bash
OIFS="$IFS"
IFS=$'\n'

function secondToTime () { #Convert second to Day, Hours, Minutes, Seconds
    seconds=$1
    min=0
    hour=0
    day=0
    if((seconds>59));then
        ((sec=seconds%60))
        ((seconds=seconds/60))
        if((seconds>59));then
            ((min=seconds%60))
            ((seconds=seconds/60))
            if((seconds>23));then
                ((hour=seconds%24))
                ((day=seconds/24))
            else
                ((hour=seconds))
            fi
        else
            ((min=seconds))
        fi
    else
        ((sec=seconds))
    fi
    echo "$day"d "$hour"h "$min"m "$sec"s
  }

case $1 in #loop though the first argument
  '-h'|'--help')     # Display the help and exit
    echo "Usage: $0 [PATH]"
    echo "Display the total play time of each folder"
    exit 0
    ;;

  !'')      # Will use the argument as target path
    target=$1
    ;;

  *)        # If no argument is specify it will use the current path
    target='.'
    ;;
esac



for folders in `find $1 -type d ` # Find all sub folders in the specifyed path
do
    for folder in $folders # Loop though each folders
    do
      echo Folder $folder:
    folderTime=0;
        for file in `ls $folder/*.mp3 2> /dev/null` #loop though each files in each folders
        do
            fileTime=`mp3info -p "%S\n" "$file"` #get the time lenght of $file
            isNumber=`echo $fileTime | grep -E '^\-?[0-9]+.?[0-9]*$'` #grep only numbers, if it's not a number isNumber will be empty
            if [ "$isNumber" != '' ]  # Check if $isNumber is NOT empty (which mean that it's a number)
            then
              let "folderTime=$fileTime+$folderTime" #Calculate Total duration in seconds
            fi
        done
        secondToTime $folderTime # Convert seconds to days hours minutes seconds and print it out
    done
done
IFS=$OIFS
9
  • 1
    If the filenames have spaces – and that is not unusual for mp3 files – this will break. You should not loop over find's output, and the only totally foolproof way to parse it reliably is to use the non-portable -print0 option.
    – Quasímodo
    Jan 24, 2021 at 21:04
  • I just fix the space issue ;-)
    – Angel115
    Jan 24, 2021 at 21:19
  • Okay, thank you. Using the script above (with the space issue fixed) I get this: ./durations.sh: line 13: syntax error near unexpected token |' ./durations.sh: line 13: | paste -sd+ | sed 's+(.*)+(\1)/60+' | bc Jan 24, 2021 at 23:28
  • @"That Jack Elliott" I just fix the script
    – Angel115
    Jan 26, 2021 at 14:07
  • That's brilliant! I hope you didn't spend a lot of time helping with my dinky little question! I wonder . . . with this code the output is not sorted by foldername. Possible to sort, ascending? Jan 26, 2021 at 19:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .