tags:

views:

244

answers:

3

What I'm trying to achieve is the difference between chapter 1 start time and chapter 2 start time and so on subtracting each chapter start time from the next in the array e.g. 00:05:57 - 00:01:03 = 00:04:54

$ cat ChapterStart 
00:00:00 00:01:03 00:05:57 00:08:27 00:11:58 00:14:50 00:20:19 00:25:06 00:33:17 00:38:21 00:42:30 00:46:11 00:51:33 01:00:04 01:00:56 01:04:15 01:09:13 01:16:51 01:20:03 01:27:58

This simply doesn't work:

#!/bin/bash
awk 'BEGIN{
{
 for(i=1;i<=NF;i++){
    m=split($i,t,":")
    n=split($(i+1),w,":")
    chap = (t[1]*3600) + (t[2]*60) + t[3]
    chap_next = (w[1]*3600) + (w[2]*60) + w[3]
    duration = (chap_next - chap)
    print $duration
    }
 }
}'ChapterStart

Any suggestions?

+1  A: 

The problem is that you're running the whole thing in a BEGIN block so it never sees the data. Just remove "BEGIN{" and the last "}" and it should work fine.

The BEGIN block is run before any data is read and is used for initialization. Awk program structure looks like this (BEGIN, the main data loop - consisting of one or more blocks - and END are each optional):

BEGIN { 
}
{
}
END {
}

The program blocks in the main loop can have various condional expressions, regular expressions or patterns that select whether they are executed.

Also, your loop needs to stop before the last field since it can't get the next one after the last:

for(i=1;i<NF;i++){

And this line won't work unless you remove the dollar sign:

    print $duration

Since you're not doing anything with the return values of split you can eliminate the variable assignment or reuse the variable:

split($i,t,":")
split($(i+1),w,":")

or

m=split($i,t,":")
m=split($(i+1),w,":")

Also, when you post a question "doesn't work" isn't very informative.

Dennis Williamson
A: 

the correct code should be

awk '{
 for(i=1;i<NF;i++){
    m=split($i,t,":")
    n=split($(i+1),w,":")
    chap = (t[1]*3600) + (t[2]*60) + t[3]
    chap_next = (w[1]*3600) + (w[2]*60) + w[3]
    duration = (chap_next - chap)
    print duration
 }
}' file

The BEGIN block is processed before the input file is read by awk. Therefore if you put your code inside BEGIN block, the file will not be processed.

ghostdog74
See my answer regarding the loop ending test.
Dennis Williamson
A: 

Also, when you post a question "doesn't work" isn't very informative.

You are of course correct. Perhaps it would have been preferable to have said "does nothing" which would have been quite accurate because exactly nothing happened. =)

Thanks to both of you.

While I still have not fully come to understand the many mysterious ways of awk I did find a simpler (to me) solution and learned a fair bit in the process. This works but surely could be done better.

#!/bin/bash
#~ Prelude to a DVD audio ripper.
#~ This simply prints Audio ID's & start/end/duration times for the longest DVD title. 

MPLAYER=$(which mplayer)
DRIVE=${1:-/dev/dvd} ## Check for softlink to DVD drive, otherwise let user specify
    if ! [ -b "$DRIVE" ]; then 
        read -p "DVD drive name? (/dev/scd0 for example): " DRIVE
    fi

#~ Find, then scan the longest title

    LTITLE=$($MPLAYER dvd:// -dvd-device $DRIVE -identify -vo null -ao null -nolirc -nojoystick \
    -frames 0 2>/dev/null| awk '/ID_DVD_TITLE/'|awk '/LENGTH/'|sort -n -t = -k 2 |tail -1 |cut -f 4 -d _)
    MPLAYER_OUT=$($MPLAYER dvd://$LTITLE -dvd-device $DRIVE -identify -vo null -ao null -nolirc \
    -nojoystick -frames 0 2>/dev/null) ## mplayer tells us about the longest title on the disc/image

#~ Set some variables per $MPLAYER_OUT 

    VID=$(echo "$MPLAYER_OUT"|awk '/ID_DVD_VOLUME_ID=/{sub(/ID_DVD_VOLUME_ID=/,"");print}') ## Get the DVD Volume ID
    CHAPTERS=$(echo "$MPLAYER_OUT"|awk '/ID_CHAPTERS/ {sub(/ID_CHAPTERS=/,"");print}') ## Total number of chapters in longest title
    ChapStart=($(echo "$MPLAYER_OUT"|awk '/CHAPTERS:/'|sed 's/CHAPTERS://; s/,/\n /g'))
    ChapEnd=($(echo "$MPLAYER_OUT"|awk '/CHAPTERS:/'|sed 's/CHAPTERS://; s/,/\n /g')) 
    DiscTimeTotal=$(echo "$MPLAYER_OUT" |awk '/ID_DVD_TITLE_'$LTITLE'_LENGTH/ {sub(/ID_DVD_TITLE_'$LTITLE'_LENGTH=/,"");print}')
    DiscTime=$(echo "$MPLAYER_OUT" |echo - |awk '{printf "%02d:%02d:%02d","'"$DiscTimeTotal"'"/(60*60),"'"$DiscTimeTotal"'"%(60*60)/60,"'"$DiscTimeTotal"'"%60}';echo) ## Converted to HH:MM:SS
    Disc=$(date +%s -d "$DiscTime") ## Convert to seconds since 1970-01-01 - this becomes that last ChapTimeEnd array element
    Streams=$(echo "$MPLAYER_OUT" |awk '/audio stream/'|sed 's/.$//;s/language/Language/'|awk '{ print "AID: " $10, "Format: " $5, $7, $8, $6 }')

#~ Convert ChapStart HH:MM:SS to ChapTimeStart seconds since 1970-01-01 

for ((s=0; s<=$CHAPTERS; s++)) 
    do 
        ChapTimeStart[$s]=$(date +%s -d "${ChapStart[$s]}") 
        ChapTimeEnd[$s]=$(date +%s -d "${ChapEnd[$s]}")
done

    unset ChapTimeEnd[0] ## Remove the first chapter start time (always 00:00:00) in the array
    ChapTimeEnd[$CHAPTERS]=$Disc ## Add $Disc as last array element
    ChapTimeEnd=( "${ChapTimeEnd[@]}" ) ## Pack the array

    unset ChapEnd[0] ## Offset the time differences - this is printed to screen as "End: HH:MM:SS"
    ChapEnd[$CHAPTERS]=$DiscTime ## Add $DiscTime as last array element
    ChapEnd=( "${ChapEnd[@]}" )     ## Pack the array

#~ Provide some feedback:

echo
echo "  --- DVD Volume ID ---";echo
echo "$VID";echo
echo "  --- Longest Title ----";echo
echo "Title $LTITLE is longest with $CHAPTERS Chapters"
echo "Title $LTITLE Length: $DiscTime";echo
echo "  ---- Audio ID's ----";echo
echo "$Streams";echo

#~ Print start/end/duration

echo "  ---- Chapters ----";echo
 for ((t=0; t<$CHAPTERS; t++)) 
    do TimeDiff[$t]=$((${ChapTimeEnd[$t]} - ${ChapTimeStart[$t]}))  ## Do the integer math with values expressed as seconds since 1970-01-01 
        Duration=$(echo - |awk '{printf "%02d:%02d:%02d","'"${TimeDiff[$t]}"'"/(60*60),"'"${TimeDiff[$t]}"'"%(60*60)/60,"'"${TimeDiff[$t]}"'"%60}';echo)
    echo "Chapter: `printf "%02d" $(($t+1))`  Start: "${ChapStart[$t]}" End: "${ChapEnd[$t]}" Duration: $Duration"
 done 
 echo "_____________________________________________________________"
echo
mzilikazi
`sed` can do the `grepping` that you're using `awk` for. Instead of `awk '/CHAPTERS:/'|sed 's/.../g` just do `sed -n '/CHAPTERS:/ s/.../gp`. It's interesting that you're using `awk` to do `grep's` job elsewhere, too. However, you should know that you can use `awk` to do a lot of what you're using `sed` for. On my system, neither Bash's builtin `echo` or the external `/bin/echo` accept stdin so your `echo $var|echo -` only echoes a dash without the contents of the variable. In some cases you can put your `awk` scripts in BEGIN blocks instead of piping `echo -` into it.
Dennis Williamson
You should use `awk` variable passing instead of complex quoting. It's easier to read and maintain. `awk -v TimeDiff=${TimeDiff[$t]} '{printf ... TimeDiff/(60*60),TimeDiff%(60*60)/60...`
Dennis Williamson