Inleiding
Laatst vulde ik een usb-stick met mp3-bestanden voor tijdens de volgende lange
autorit. Eenmaal gereed wilde ik er graag in willekeurige volgorde doorheen
luisteren om te horen of normalize-audio
zijn werk goed had gedaan. Het
moet toch mogelijk zijn om find
, sort
en mplayer
aan elkaar
te knopen?
Onderzoek (1)
Als eerste zocht ik in de handleiding van mplayer (man 1 mplayer
) naar een
optie om een map recursief uit te lezen; tevergeefs. Ergens anders had ik
gelezen over het programma xargs
, waarmee je grote hoeveelheden
parameters aan programma's kunt doorgeven. Ik probeerde de volgende one-liner:
find . -type f -print0 | sort -z -R | xargs -0 mplayer
Het werkte! find
zoekt in de huidige map (.
) naar bestanden (-type f
) en
voert deze aan sort
, die ze in een willekeurige volgorde (-R
) plaatst.
Uiteindelijk geeft xargs
de lijst door aan mplayer. Om problemen met
bestandsnamen met spaties of newline karakters te vermijden, gebruik ik de
opties -print0
, -z
en -0
. Hierdoor worden de namen gescheiden door een
null
-byte in plaats van een newline.
Maar wat was dat? De toetsenbordbediening van mplayer
werkte niet meer! Ik kon
niets anders dan met Ctrl + C de uitvoer afbreken.
Onderzoek (2)
Mplayer heeft ook een -playlist
optie, die als argument een lijst van af te
spelen bestanden verwacht. In een ver grijs verleden had ik ooit een Bash
subshell hiervoor gebruikt in de volgende vorm:
mplayer -playlist <( find ${PWD} -type f | sort -R );
Dit werkt, en ook de toetsenbordbediening van mplayer functioneert naar behoren. Maar, wat als je buiten de huidige map wil zoeken?
Script
Uiteindelijk wilde ik een functie die één optionele parameter accepteert: de map
waarin moet worden gezocht. Is deze parameter leeg, dan wordt de huidige map
(${PWD}
) gebruikt.
Daarna wordt een tijdelijk bestand aangemaakt (mktemp
), waarin find
s uitvoer
in terecht komt. sort -R
zorgt, net als in bovenstaand script, voor de
willekeurige volgorde.
Omdat mplayer
niet per sé in dezelde map start als find
, moeten de
bestandsnamen en paden in de afspeellijst absoluut zijn. Daarom wordt voor elke
treffer een -exec readlik -f \;
uitgevoerd.
function mus()
{
FOLDER="$1";
if [ "${FOLDER}"x = x ]; then
FOLDER="${PWD}";
fi
TMP="$( mktemp )";
find "${FOLDER}" -type f -exec readlink -f "{}" \; | sort -R > "${TMP}";
mplayer -playlist "${TMP}";
rm "${TMP}";
}
Ik heb nog geprobeerd om dit script zonder tijdelijk bestand, met behulp van een subshell te maken. Dat werd me te lastig :-) Het blijkt behoorlijk moeilijk om variabelen vanuit de hoofd- naar de subshell door te geven.
Conclusie
Het bovenstaand script heb ik aan mijn ~/.bashrc
-bestand toegevoegd. Daardoor
kan ik in elk terminalvenster of tekstconsole de functie mus
oproepen; al
dan niet met de af te spelen map als argument.
mus ~/Music