I know there already are several answers in here, but neither completely answers the question if you need to use command line on macOS, so I am adding mine too.
This was actually spotted by mokubai in his comment under this question: there's a similar question on AskUbuntu forums, and one of the answers supplies a complete script, however it is not working.
I have rewritten the script from scratch, and also properly commented it in case someone needs to tweak it later. You only need to have MKVToolNix installed, no other dependencies.
EDIT [2023-08-03]: I created a separate Gist for this script, where I will update it in the future when/if needed.
#!/bin/sh
# Extract subtitles from each MKV/MP4 file in the given directory
# MKVToolNix path - Leave empty if you have the tools added to $PATH.
# This is needed e.g. on macOS, if you downloaded MKVToolNix app
# and just dragged it to the Applications folder.
toolPath='/Applications/MKVToolNix.app/Contents/MacOS/'
# If no directory is given, work in local dir
if [ "$1" = "" ]; then
DIR="."
else
DIR="$1"
fi
# Get all MKV/MP4 files in this dir and its subdirs
find "$DIR" -type f \( -iname '*.mkv' -o -iname '*.mp4' \) | while read filename
do
echo "Processing file $filename:"
# Get base file name (without extension)
fileBasename=${filename%.*}
# Parse info about all subtitles tracks from file. This will output lines in the
# following format, one line per subtitle track, fields delimited by tabulator:
# trackID <tab> trackLanguage <tab> trackCodecID <tab> trackCodec
"${toolPath}mkvmerge" -J "$filename" | python -c "exec(\"import sys, json;\nfor track in json.load(sys.stdin)['tracks']:\n\tif track['type'] == 'subtitles':\n\t\tprint(str(track['id']) + '\t' + track['properties']['language'] + '\t' + track['properties']['codec_id'] + '\t' + track['codec'])\")" | while IFS=$'\t' read -r trackNumber trackLanguage trackCodecID trackCodec;
do
# optional: process only some types of subtitle tracks (according to $trackCodecID)
# See codec types here: https://tools.ietf.org/id/draft-lhomme-cellar-codec-00.html#rfc.section.6.5
if [[ $trackCodecID == 'S_VOBSUB' || $trackCodecID == 'unwantedID_#2' ]] ; then
echo " skipping track #${trackNumber}: $trackLanguage ($trackCodec, $trackCodecID)"
continue;
fi
echo " extracting track #${trackNumber}: $trackLanguage ($trackCodec, $trackCodecID)"
# extract track with language and track id
`"${toolPath}mkvextract" tracks "$filename" $trackNumber:"$fileBasename $trackNumber($trackLanguage).srt" > /dev/null 2>&1`
done
done
mkvmerge -i <filename>
is the better way to get the subtitle track numbers to then feed intomkvextract
ffmpeg -i filename.mkv
.ffmpeg -i filename.mkv
to be able to provide an accurate answer.