package
0.0.0-20240929065015-8ede5d41a07d
Repository: https://github.com/florianbegusch/golang-tools.git
Documentation: pkg.go.dev

# README

Execute on files

Run arbitrary commands on files simultaneously/concurrently.

-config
Files are specified by -config. It contains filenames separate by newlines, wildcards are also allowed.
-config ... can be command redirect (a filedescriptor) <(find -type d -name '*something*') or a regular file.

Hint: There is no order to how we output command output for files, whichever go routine finishes first wins.

Speed comparison

amount of files:

/r/m/f/1TB/scanlime-in-progress-new-channel
$ ls | wc -l
205

find

$ time find -name '*.mp4' -exec bash -c 'stat "$0"; ffprobe-get-date "$0"' {} \;
...
...
20180414
  File: ./._Does it even MBR??? Let's try DOS or Linux on the AVC Edge again, now with Sigrok + bus probe-ako_wEfj9gs.mp4
  Size: 4096            Blocks: 256        IO Block: 4096   regular file
Device: 8,2     Inode: 17816       Links: 1
Access: (0777/-rwxrwxrwx)  Uid: ( 1000/     flo)   Gid: ( 1000/     flo)
Access: 2024-08-23 07:47:52.000000000 +0000
Modify: 2024-08-23 07:47:52.000000000 +0000
Change: 2024-08-23 07:47:52.000000000 +0000
 Birth: -

real    2m27,942s
user    0m45,595s
sys     1m39,264s

execute-on-files

contents of /tmp/it:

$ cat /tmp/it
#!/usr/bin/env bash
ffprobe-get-date "$1"
stat "$1"

--

/r/m/f/1TB/scanlime-in-progress-new-channel
$ time ~/Documents/golang/tools/execute-on-files/execute-on-files -config <(ls *.mp4) /tmp/it
...
...
'Unbox Pinebook and e-paper ⧸ Designing a simple FPGA GPU ⧸ Can we 720p with iCEBreaker? [2018-11-23]-whrFOIMcK8w.mp4':pwd:'/run/media/flo/1TB/scanlime-in-progress-new-channel'
20181123
  File: Unbox Pinebook and e-paper ⧸ Designing a simple FPGA GPU ⧸ Can we 720p with iCEBreaker? [2018-11-23]-whrFOIMcK8w.mp4
  Size: 2134490042      Blocks: 4168960    IO Block: 4096   regular file
Device: 8,2     Inode: 14237       Links: 1
Access: (0777/-rwxrwxrwx)  Uid: ( 1000/     flo)   Gid: ( 1000/     flo)
Access: 2024-08-23 12:19:27.000000000 +0000
Modify: 2018-11-27 21:50:57.000000000 +0000
Change: 2018-11-27 21:50:57.000000000 +0000
 Birth: -


real    0m39,174s
user    1m9,154s
sys     2m46,024s

Examples

Basic

./execute-on-files -config <(ls *.mp4) /tmp/it
./execute-on-files -config <(ls *.mp4) ffprobe-get-date
./execute-on-files -config <(ls *.mp4) stat
./execute-on-files -config <(find -name '*network reor*.mp4') stat
./execute-on-files -config <(ls *.m4a) ffmpeg-dynamic-range-compress-file

$ ./execute-on-files -config <(echo main.go) ls -alh
[INFO]: config file: /dev/fd/63
'main.go':pwd:'/home/flo/Documents/golang/tools/execute-on-files'
-rw-r--r-- 1 flo flo 3,0K Aug 23 13:34 main.go

$ ./execute-on-files -config <(echo main.go) stat
[INFO]: config file: /dev/fd/63
'main.go':pwd:'/home/flo/Documents/golang/tools/execute-on-files'
  File: main.go
  Size: 3019            Blocks: 8          IO Block: 4096   regular file
Device: 0,47    Inode: 1911786     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/     flo)   Gid: ( 1000/     flo)
Access: 2024-08-23 12:02:30.754224342 +0000
Modify: 2024-08-23 13:34:23.153871516 +0000
Change: 2024-08-23 13:34:23.157204802 +0000
 Birth: 2024-08-23 12:02:30.754224342 +0000

More sophisticated

Sum the duration of all mp4 files in a directory

# plain old. slow
for f in *; do ffprobe-get-duration "$f" | awk -F '.' '{ printf("%s seconds + ", $1); }'; done | qalc

# OR

# fast
$ cat /tmp/it
#!/usr/bin/env bash
ffprobe-get-duration "$1" | awk -F '.' '{ printf("%s seconds + ", $1); }' >> /tmp/all
/r/m/f/1TB/scanlime-in-progress-new-channel

$ execute-on-files -config <(find -name '*.mp4') /tmp/it && qalc < /tmp/all

Merge video files into one (across several directories)

/tmp/command.sh:

#!/usr/bin/env bash

# all of these stem from https://www.shellcheck.net/wiki/
set -o pipefail  # propagate errors
set -u  # exit on undefined
set -e  # exit on non-zero return value
#set -f  # disable globbing/filename expansion
shopt -s failglob  # error on unexpaned globs
shopt -s inherit_errexit  # Bash disables set -e in command substitution by default; reverse this behavior

set -x
dir="$1"
shift

temp="$(mktemp -d)"
cleanup () { rm -rf "$temp"; }
trap cleanup EXIT

config="$temp"/config.txt


for f in "$dir"/*.m4a; do
  echo "file '$f'" >> "$config"
done

name="$(basename "$dir")"
out="$temp/How It All Ends: $name.m4a"
pwd
echo "$temp"
cat "$config"
ffmpeg -f concat -safe 0 -i "$config" -c copy "$out"
mv "$out" "$dir"

command to run across directories:

$ execute-on-files -config <(find "$PWD" -type d) /tmp/command.sh