to_replace

the most nightmarish one-liner i've ever written

date: may 8, 2024


for i in *; do ffprobe -i $i 2>&1 ; done | grep Duration | cut -d' ' -f 4 | cut \
-d',' -f1 | xargs -n 1 -I AAA date +%s -d "TZ=\"+00:00\" 1970-01-01 AAA" | (n=0; \
while true; do read i; n=$(($n + $i)); echo $n; done)

what the fuck.

alright here we go. i wanted to see how long my copy of The Slim Shady LP was, and here we are.

we start off with a for loop, just running ffprobe on all files in the current directory. 2>&1 because ffprobe outputs to stderr for "reasons". then, we pipe that into grep to just get the lines containing Duration, which look like this:

  Duration: 00:00:33.12, start: 0.000000, bitrate: 2636 kb/s
  Duration: 00:04:28.21, start: 0.000000, bitrate: 2860 kb/s
  Duration: 00:03:19.71, start: 0.000000, bitrate: 2895 kb/s
  Duration: 00:03:45.59, start: 0.000000, bitrate: 2947 kb/s
  Duration: 00:00:16.89, start: 0.000000, bitrate: 2640 kb/s
  Duration: 00:04:04.61, start: 0.000000, bitrate: 2924 kb/s
  Duration: 00:05:17.12, start: 0.000000, bitrate: 2920 kb/s
  Duration: 00:00:19.64, start: 0.000000, bitrate: 2690 kb/s
  Duration: 00:03:25.22, start: 0.000000, bitrate: 2915 kb/s
  Duration: 00:00:47.07, start: 0.000000, bitrate: 2766 kb/s
  Duration: 00:03:59.95, start: 0.000000, bitrate: 2931 kb/s
  Duration: 00:01:16.85, start: 0.000000, bitrate: 2670 kb/s
  Duration: 00:03:38.76, start: 0.000000, bitrate: 3055 kb/s
  Duration: 00:03:34.01, start: 0.000000, bitrate: 2862 kb/s
  Duration: 00:04:02.42, start: 0.000000, bitrate: 3304 kb/s
  Duration: 00:00:36.16, start: 0.000000, bitrate: 2738 kb/s
  Duration: 00:04:25.05, start: 0.000000, bitrate: 2976 kb/s
  Duration: 00:03:32.25, start: 0.000000, bitrate: 3106 kb/s
  Duration: 00:04:12.63, start: 0.000000, bitrate: 2997 kb/s
  Duration: 00:04:15.13, start: 0.000000, bitrate: 2960 kb/s
  Duration: 00:00:00.12, start: 0.000000, bitrate: 37 kb/s

pipe that into cut -- the timecode is after 3 spaces, and fields are numbered from 1. now we have the timecodes!

00:00:33.12,
00:04:28.21,
00:03:19.71,
...

wait, why are there commas?

ffprobe adds a comma after each little nugget of information. let's do another cut to get rid of it. (tr would work too)

so, we cut -d',' -f1, which just outputs the start of each line, until the first comma.

00:00:33.12
00:04:28.21
00:03:19.71
...

now for the real nightmare.

the xargs command from hell

xargs is a wonderful little part of the command line -- it takes input from stdin, and passes it as arguments to a command of your choosing.

-n ensures that only one line from stdin is passed per run of date, otherwise it wouldn't format correctly.

i decided to use date to convert the timecode to seconds:

AAA

xargs has an option called replace, where instead of appending input from stdin, it will replace a string of your choosing with the arguments. -I AAA means that wherever the string AAA is encountered in the command string, it will be replaced with input from stdin.

the output at this stage is as follows:

$ for i in *; do ffprobe -i $i 2>&1 ; done | grep Duration | cut -d' ' -f 4 | \
cut -d',' -f1 | xargs -n 1 -I AAA date +%s -d "TZ=\"+00:00\" 1970-01-01 AAA"                     
xargs: warning: options --max-args and --replace/-I/-i are mutually exclusive, ignoring previous --max-args value
33
268
199
225
16
244
317
19
205
47
239
76
218
214
242
36
265
212
252
255
0

finally, we need to sum these values. a simple while loop will do.

| (n=0; while true; do read i; n=$(($n + $i)); echo $n; done)

this just uses math expansions (or whatever they're called) to do int math right on the cli. the loop will read each line, then add that value to n, and finally print n on each iteration.

and at the end, we have our result.

$ for i in *; do ffprobe -i $i 2>&1 ; done | grep Duration | cut -d' ' -f 4 | \
cut -d',' -f1 | xargs -n 1 -I AAA date +%s -d "TZ=\"+00:00\" 1970-01-01 AAA" | \
(n=0; while true; do read i; n=$(($n + $i)); echo $n; done)
xargs: warning: options --max-args and --replace/-I/-i are mutually exclusive, ignoring previous --max-args value
33
301
500
725
741
985
1302
1321
1526
1573
1812
1888
2106
2320
2562
2598
2863
3075
3327
3582
3582
$

The Slim Shady LP is 3582 seconds long, or 59:42. (excluding fractional seconds at the end, because int math.)

and if we search up the length, as would've been much easier from the start, we get the answer of...

drum roll

59:39.

close enough.

~ spv

courtesy of @benny@social.linux.pizza: By-the-by, it took 45 million hours to write this one-liner, and 10 minutes to write this blog post. Anyway, document your code.

corrections