使用 AWK 减去时间

发布于 2024-08-17 08:36:37 字数 650 浏览 3 评论 0原文

我想要实现的是第 1 章开始时间和第 2 章开始时间之间的差异,依此类推,从数组中的下一章开始时间减去每个章节开始时间,例如 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

这根本行不通:

#!/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

有什么建议吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

转身泪倾城 2024-08-24 08:36:37

问题是您在 BEGIN 块中运行整个过程,因此它永远看不到数据。只需删除“BEGIN{”和最后一个“}”,它就应该可以正常工作。

BEGIN 块在读取任何数据之前运行,并用于初始化。 awk 程序结构如下所示(BEGIN,主数据循环 - 由一个或多个块组成 - 和 END 都是可选的):

BEGIN { 
}
{
}
END {
}

主循环中的程序块可以有各种条件表达式、正则表达式或模式来选择是否执行它们。

另外,您的循环需要在最后一个字段之前停止,因为它无法获取最后一个字段之后的下一个字段:

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

除非您删除美元符号,否则这一行将不起作用:

    print $duration

因为您没有对返回值执行任何操作split 您可以消除变量分配或重用变量:

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

或者

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

此外,当您发布问题“不起作用”时,信息量并不大。

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.

月寒剑心 2024-08-24 08:36:37

正确的代码应该是

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

在 awk 读取输入文件之前处理 BEGIN 块。因此,如果您将代码放在 BEGIN 块内,则该文件将不会被处理。

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.

殊姿 2024-08-24 08:36:37

此外,当您发布问题时
“不起作用”并没有提供太多信息。

你当然是对的。也许最好说“什么也不做”,这会非常准确,因为实际上什么也没发生。 =)

谢谢你们俩。

虽然我还没有完全理解 awk 的许多神秘方式,但我确实找到了一个更简单的(对我来说)解决方案,并在这个过程中学到了一些东西。这可行,但肯定可以做得更好。

#!/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

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
救赎№ 2024-08-24 08:36:37

其他解决方案仍然一遍又一遍地重新分割相同的列。该方法一次性分割整个输入,假设每个值都是正确的 HH:MM:SS,然后从左到右迭代它们,并将每个 chap_next 保存为下一个周期的chap


awk '
function ______(__, _, ___, ____, _____) {

    gsub(/:/, _ = " ", __)
    ____ = (__ = (_ += _ = (_____ = split(__, ___, _))^_) * \
           (++_^_ + _--)) * (__ * ___[!!_] + ___[_]) + ___[++_]

    while (_++ < _____)
        print " duration :: ", -____ + (\
                ____ = __ * (__ * ___[_++] + ___[_]) + ___[++_])
} 
BEGIN { FS = RS ; CONVFMT = "%.250g" } ($++NF = ______($!_))^_'

echo '
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'

 duration :: 63
 duration :: 294
 duration :: 150
 duration :: 211
 duration :: 172

 duration :: 329
 duration :: 287
 duration :: 491
 duration :: 304
 duration :: 249

 duration :: 221
 duration :: 322
 duration :: 511
 duration :: 52
 duration :: 199

 duration :: 298
 duration :: 458
 duration :: 192
 duration :: 475

事实上,您甚至可以直接读出HHMMSS的原始值,按照适当的顺序,因为

  ____ = __ * (__ * ___[_++] + ___[_]) + ___[++_] 
    ⬇                                           
  chap = 60 * (60 *     HH   +    MM ) +     SS

The other solutions are still re-splitting the same columns over and over again. The approach splits the entire input all at once, assuming every value is a proper HH:MM:SS, then iterate them left-to-right, and saving each chap_next as next cycle's chap :


awk '
function ______(__, _, ___, ____, _____) {

    gsub(/:/, _ = " ", __)
    ____ = (__ = (_ += _ = (_____ = split(__, ___, _))^_) * \
           (++_^_ + _--)) * (__ * ___[!!_] + ___[_]) + ___[++_]

    while (_++ < _____)
        print " duration :: ", -____ + (\
                ____ = __ * (__ * ___[_++] + ___[_]) + ___[++_])
} 
BEGIN { FS = RS ; CONVFMT = "%.250g" } ($++NF = ______($!_))^_'

echo '
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'

 duration :: 63
 duration :: 294
 duration :: 150
 duration :: 211
 duration :: 172

 duration :: 329
 duration :: 287
 duration :: 491
 duration :: 304
 duration :: 249

 duration :: 221
 duration :: 322
 duration :: 511
 duration :: 52
 duration :: 199

 duration :: 298
 duration :: 458
 duration :: 192
 duration :: 475

in fact, you can even directly read out the original values for HH, MM, SS, in proper order, since

  ____ = __ * (__ * ___[_++] + ___[_]) + ___[++_] 
    ⬇                                           
  chap = 60 * (60 *     HH   +    MM ) +     SS
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文