Salut touT le mond0e,
Je souhaite soumettre ce script à votre analyse. Il fonctionne comme je veux même s’il n’est pas fini (pas de getopts notamment) mais je voulais le soumettre à vos critiques, parfois acerbes mais toujours constructives !
#!/bin/bash
test "${1}" || { echo -e "\nUsage: ${0} <file>\n"; exit 1; }
if test -f "${1}"; then
REC_OUTPUT_FILE="$$-${1}"
else
REC_OUTPUT_FILE="${1}"
fi
REC_DEVICE='hw:0,1'
REC_FORMAT='S32_LE'
REC_SAMPLERATE='96000'
REC_NB_CHANNELS='2'
FLAC_COMPRESSION_LEVEL='8'
a_period_passed() {
# 10 seconds by default
local period="${1:-10}"
test $(( "$(date +%s)" % period )) -eq 0
}
get_elapsed_time() {
echo -n "$(date +%H:%M:%S -u -d@"$(( $(date +%s) - REC_START_TIME ))")"
}
get_file_size() {
du -sh "${1}" | awk '{print $1}'
}
convert_to_flac() {
wait
echo -e "\nConvertion to FLAC…"
mv "${1}" "/tmp/$$-${1}"
sox -t wav "/tmp/$$-${1}" -t flac -C"${FLAC_COMPRESSION_LEVEL}" "${1}"
retval="$?"
rm "/tmp/$$-${1}"
return "${retval}"
}
stop_recording() {
[[ "${REC_OUTPUT_FILE}" =~ .*\.flac$ ]] && convert_to_flac "${REC_OUTPUT_FILE}"
echo -e "\nAudio recorded to “${REC_OUTPUT_FILE}” (size: $(get_file_size "${REC_OUTPUT_FILE}")) [$(file "${REC_OUTPUT_FILE}" | cut -d':' -f2 | sed -e 's/^\s//')]"; exit
}
arecord -D"${REC_DEVICE}" -f"${REC_FORMAT}" -r"${REC_SAMPLERATE}" -c"${REC_NB_CHANNELS}" "${REC_OUTPUT_FILE}" &> /dev/null &
REC_START_TIME="$(date +%s)"
echo "Recording from device ${REC_DEVICE} at ${REC_SAMPLERATE} Hz on ${REC_NB_CHANNELS} channel(s) to file “${REC_OUTPUT_FILE}”"
echo "Hit Ctrl+C to end recording"
while true; do
echo -n '.'
sleep 1
a_period_passed && echo -en "\n$(get_elapsed_time)|$(get_file_size "${REC_OUTPUT_FILE}")"
trap stop_recording SIGINT
done
exit 0
Il ya un warning shellcheck qui me laisse dubitatif :
record_loopback.sh:53:5: note: Use a_period_passed "$@" if function's $1 should mean script's $1. [SC2119]
J’ai envie de répondre : « Non ¹ ici here isn’t main script $1, pourquoi tu me casses les couilles ici mais pas pour, entre autres, la fonction get_file_size ? »
Le shell, en l’occurrence bash, est un language interprété que beaucoup dédaignent, lui préférant des langages plus élégants, plus modernes, tels que Python ou Perl. Moi je vous avoue que pour avoir pratiqué les trois, je préfère clairement, finalement, les illogismes de bash/sh, ses aspects barbares et son manque rationalité. Parce que même si c’est moins productif, c’est aussi beaucoup plus palpitant.
# timeout ?
Posté par cg . Évalué à 2.
Question peut-être bête, mais est-ce que avec
timeout
tu ne peux pas simplifier drastiquement la gestion du temps ? Genre :[^] # Re: timeout ?
Posté par cg . Évalué à 3.
Ah et je vois que
arecord
a directement une option-d, --duration=#
qui permet de donner une durée directement en secondes, aussi.Si tu souhaites déclencher un enregistrement rapidement, j'avais vu un paquet sur Debian, dont j'ai oublié le nom, qui permettait en substance de déclencher un enregistrement de l'audio quelques secondes avant qu'on lance la commande !
En gros, il y a un démon qui tourne et converse l'audio dans un buffer circulaire de plusieurs secondes/minutes en permanence, et un second outil qui permet de capturer ce buffer et de prolonger l'enregistrement. Comme ça, quand on entend un truc intéressant, on peut l'enregistrer a posteriori.
[^] # Re: timeout ?
Posté par cg . Évalué à 2.
J'ai retrouvé, c'est timemachine mais c'est un plugin Jack, moins simple que ALSA.
[^] # Re: timeout ?
Posté par Marotte ⛧ . Évalué à 5.
Yes je connais. Une appli très astucieuse.
Quelque part Jack permet de faire des choses de manière plus simple qu’ALSA, et sans devoir arrêter la reproduction du flux audio pour recharger une nouvelle configuration. Parce qu’ALSA, rien que pour permettre d’avoir ce « playback device », c’est franchement cryptique comme configuration à mettre en place. Je me demande d’ailleurs pourquoi il n’existe pas une appli pour configurer ALSA de manière plus intuitive que l’édition directe du fichier de configuration.
Évidemment le confort apporté par Jack a un coût en terme d’utilisation CPU. Je l’utilise parfois, mais quand je travaille sur un logiciel « monolithique » comme LMMS, j’apprécie de ne pas avoir Jack qui me prend une partie de la ressource CPU.
Si ça intéresse quelqu’un : la configuration d’ALSA qui permet d’enregistrer le son qui est envoyé à la carte son. Autant j’ai appris à utiliser Jack de façon intuitive, autant pour faire ça avec ALSA je n’y serais sûrement jamais arrivé sans le lien ci-dessus.
[^] # Re: timeout ?
Posté par Luc-Skywalker . Évalué à 1.
Oui moi, mais quelle usine !
Pipewire serait-il la solution ?
"Si tous les cons volaient, il ferait nuit" F. Dard
[^] # Re: timeout ?
Posté par Marotte ⛧ . Évalué à 4.
Je ne me suis pas encore penché sur Pipewire, je croyais que c’était un bête "drop-in replacement" de Pulseaudio, mais à priori c’est un projet de serveur audio visant à remplacer Jack et Pulseaudio, une solution universelle pour mettre tout le monde d’accord !?
Tu m’étonnes ! Clairement pas adapté à l’utilisateur final. C’est pour ça que je suis surpris qu’il y ai pas une belle GUI pour générer un
asound.conf
qui va bien pour tel ou tel usage.Et Jack parlons-en, en réalité il n’y a pas Jack, il y a Jack 1 et Jack 2, chacun ayant des fonctionnalités que l’autre n’a pas.
[^] # Re: timeout ?
Posté par Marotte ⛧ . Évalué à 3.
Je ne fais pas vraiment de gestion du temps. Une fois l’enregistrement démarré, il se poursuit indéfiniment jusqu’à interruption par un Ctrl+C. La fonction
a_period_passed
elle me sert juste à afficher à intervalle régulier à quelle durée on est rendu, avec la taille du fichier atteinte jusqu’ici.Mais merci pour la commande timeout, je ne la connaissais pas (ou plus probablement oublié son existence). Mais comme dit plus bas,
arecord
prévoit déjà qu’on puisse enregistrer seulement pour une période donnée avec l’option-d
.Je devrais d’ailleurs peut-être l’utiliser pour définir une période maximum, de sécurité, parce que sinon, même si la source audio cesse, si on oublie d’arrêter le processus il doit se poursuivre jusqu’à saturation de l’espace disque.
# Passage explicite du paramètre
Posté par cg . Évalué à 3.
J'ai essayé sur ma machine, et en fait la remarque fonctionne avec un autre warning :
Donc il te recommande de passer un paramètre à
a_period_passed()
explicitement pour éviter tout ambiguïté sur la nature de$1
, des fois que tu croies que$1
est le premier paramètre de ligne de commande (perso je me suis fait avoir). Ta fonction met une valeur par défaut, donc pas de problème de programmation à ce niveau.Il ne dit rien pour
get_file_size()
car tu passes le paramètre et$1
est donc défini sans l'ombre d'un doute, pas la peine de houspiller le programmeur :).Sinon, oui désolé, j'avais lu un peu en diagonale et cru que tu voulais couper au bout de
$1
secondes. Bref.[^] # Re: Passage explicite du paramètre
Posté par Marotte ⛧ . Évalué à 3.
Ah OK ! Donc le warning dont je parlais est une conséquence du fait de ne pas passer l’argument pourtant prévu (le warning que tu indiques, que j’avais aussi mais dont je n’ai pas jugé bon de parler vu que je le comprenais).
Un grand merci à toi.
[^] # Re: Passage explicite du paramètre
Posté par Marotte ⛧ . Évalué à 3.
C’est ça tout le charme du shell, c’est rempli de pièges tous plus saugrenus les uns que les autres ! ^^
Il est vrai que j’ai pris l’habitude (mais je ne l’ai pas fait ici, à tort) ds systématiquement mettre un commentaire au tout début du corps de la fonction du genre:
ça aide…
Le dernier piège dans lequel je suis tombé il y quelques temps : un nombre qui commence par 0 est de l’octal. Si tu utilises le format
+V%
dedate
pour récupérer le numéro de la semaine tu as un bug qui survient uniquement pour un mois sur douze, pour septembre ! :)# Remarques
Posté par xshell . Évalué à 2. Dernière modification le 06 octobre 2023 à 18:35.
trap
devrait être avant la boucle. Un seul suffit.sleep
et faire sauter « a_period_passed ». Et tu mets à jours toutes les secondes sans manger de ligne (pas de caractère\n
linefeed).arecord
captureSIGINT
qui est retransmis au process du groupe du shell (tous les processus fils qui ont leurPGID
égal auPID
du shell), ça explique pourquoi il s’arrête. Mais dans le cas général ce n’est pas garanti, et c’est un peu perturbant de ne pas avoir un arrêt explicite (unsleep
à la place duarecord
n’est pas interrompu chez moi et continue à tourner en tâche de fond — rattaché auPID 1
, lorsque le scriptexit
). D’un autre côté la solution “propre” me paraît singulièrement compliquée./tmp/$$-$1
en lieu et place d’unmktemp
tu ne devrais pas, pour éviter les I/O inutiles passer partmp
. Lors desmv
il faut privilégier les déplacements dans le même répertoire, pour rester sur le même filesystem (en protégeant parmv -n ... || exit 1
afin de ne rien écraser par inadvertance — l’option est très probablement robuste sur Linux car il suffit d’un appel C àrenameat2
avecRENAME_NOREPLACE
, mais pas en standard POSIX — faut passer par unmkdir
dans ce cas)."${path%.flac}" != "$path"
awk
est overkill, pour faire uncut -f1
-e
pour tester l’existence d’un fichier, pas-f
. L’intention est louable mais casse-gueule (l’utilisateur peut créer un fichier entre letest
et learecord
). Encore une foismktemp -p
,mv -n
, oumkdir
fournissent les garanties nécessaires.PATH="$1"
(par exemple). Ça auto-documente les arguments de la ligne de commande.[^] # Re: Remarques
Posté par xshell . Évalué à 1. Dernière modification le 06 octobre 2023 à 18:44.
Pour mon 8. c’est pas une bonne idée de nommer la variable
PATH
^^
[^] # Re: Remarques
Posté par Marotte ⛧ . Évalué à 3. Dernière modification le 07 octobre 2023 à 02:08.
Oui mais est-ce que ce n’est pas « dommage » d’effectuer une affectation de variable inutile ? Je pense notamment au cas d’une fonction, qui pourrait être appelée de manière intensive (ce qui n’est pas le cas ici mais je parle dans le cas général). Après je ne sais pas, c’est peut-être négligeable.
[^] # Re: Remarques
Posté par cg . Évalué à 3.
Deux programmes simples, avec un million d'exécutions d'une fonction :
Sur ma machine, la version avec assignation prend presque toujours une seconde de plus que celle sans assignation. Genre 6 secondes au lieu de 5, ce qui est en effet énorme !
Dans un langage compilé, précompilé ou avec du JIT, c'est sans doute moins un sujet. Le même genre de programme en C prend sans doute moins de quelques millisecondes (flemme totale d'essayer).
[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 4.
Dans tous les cas, l’affectation a un coût (certes négligeable) ; et si tu ne le ressens pas dans ton programme compilé ce sera plutôt du aux optimisations… (où le compilateur aura probablement généré le même code en détectant qu’il y a une affectation inutile.) :)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Remarques
Posté par cg . Évalué à 4.
Finalement j'ai fait le test en C, et mon ordi met 5ms pour un million d'itérations, et 2,4s pour 1 milliard.
Mais comme tu dis, si je précise
-O2
ou-O3
, ça devient inférieur à la milliseconde, car l'optimiseur supprime alors la boucle et les appels à la fonction, vu que ça ne fait rien.On peut voir l'assembleur en faisant
cc -S -O2 var.c
, ça génère unvar.s
, pour celles et ceux qui ne connaissent pas.[^] # Re: Remarques
Posté par Marotte ⛧ . Évalué à 3.
Pour 6, on ne peut pas faire un
cut -d"\n"
, on peut faire un cut en mettant une tabulation (en utilisant Ctrl+v), mais dans un script je trouve ça pas terrible. Et je crois que j’ai bêtement crû que c’était une suite d’espaces…Tu m’apprends qu’on n’a pas besoin d’indiquer un délimiteur. Et que
cut
se débrouille. Merci !Je me pencherai sûrement sur les autres remarque un de ces quatre mais pour ce soir, c’est mort. ^^
[^] # Re: Remarques
Posté par xshell . Évalué à 2.
Au besoin, $'\t' est interprété comme un tab.
[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3. Dernière modification le 07 octobre 2023 à 18:08.
Je n’ai pas compris cela, et si c’était l’intention bah c’est faux : il y a une valeur par défaut, la tabulation, quand on n’indique pas de délimiteur. No magic, RTFM.
Comme ton script est en bash, tu peux utiliser
cut -d $'\t'
comme mentionné par un autre commentaire. Mais bon, comme c’est le délimiteur par défaut, pas besoin de s’embêter…Par contre, oui :
It's not a bug, that's by design …pour la simple et bonne raison que la commande opère sur les lignes…
C’est hélas un souci courant (pas du tout propre aux scripts shell.) Mon éditeur est toujours configuré pour distinguer visuellement les simples espaces des tabulations.
Dans le cas présent, on pouvait se douter qu’il s’agit de tabulation (ou d’une erreur qui ne va pas fonctionner) car le délimiteur doit être un caractère !
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Remarques
Posté par Marotte ⛧ . Évalué à 3. Dernière modification le 07 octobre 2023 à 19:07.
Oui je me suis mal exprimé.
J’ai tendance à sortir systématiquement awk au lieu de cut (et c’est une mauvaise habitude je reconnais) du fait que parfois les données à traiter ont des champs séparées par un nombre variables d’espaces (du moins plusieurs), et cut, si on lui indique l’espace comme séparateur, considère alors qu’il s’agit de plusieurs champs vides. Comportement qui a sûrement une bonne raison d’être, mais que je ne saisi pas. Alors que awk considère les espaces multiples comme un seul séparateur.
Je n’avais effectivement pas en tête que la tabulation était le délimiteur par défaut (en plus d’avoir assumé qu’il s’agissait d’espaces multiples sans même vérifer). Je suis pétri de honte, je vais recopier cent fois le manuel de bash pour expier ma faute.
Ça reste intéressant à connaître, on peut par exemple faire :
$ echo -e "plop\0toto" | cut -d $'\0' -f2
toto
[^] # Re: Remarques
Posté par cg . Évalué à 4.
Au passage, ça peut servir, pour couper sur les espaces multiples et les tabs, je fais souvent cette séquence :
Le
-s
detr
"squeeze" les répétitions.Je ne sais pas à quel moment ça devient plus rentable de sortir sed, awk ou Perl pour le faire avec des regex, ceci dit.
[^] # Re: Remarques
Posté par Marotte ⛧ . Évalué à 3.
Sympa l’option
-s
. J’ai pour habitude, pour faire cela, d’utilisersed -e 's/\s\s*/ /g'
, qui est à mon avis moins lisible.En tous cas pour simplement « squeezer » les espaces/tabulations multiples
tr
doit être plus rentable quesed
. Parce que le programme fait un peu moins de la moitié desed
en taille, donc j’imagine en temps de chargement (?), et faisant moins de choses je suppose également qu’il est plus simple et s’exécute donc plus vite. Ça doit commencer à s’inverser si tu utilisessed
pour faire d’autres transformations, des transformations qu’il faudrait sinon réaliser par de multiples pipes recourant à différents programmes simples tels quetr
.[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 4.
Je présume que tu utilise l’implémentation GNU sed car il me semble (mais faut que je vérifie) que
\s
n’est pas standard…Pour la performance, ce n’est pas lié qu’à la taille (qui reste un atout important au niveau du chargement et de l’exécution quand il y a de la charge) mais plus aux actions effectuées (et avec quels algorithmes.)
TR va parcourir le flot et faire des remplacements à la volée (du moins c’était aussi trivial avant de devoir prendre en compte les encodages multiple octets.)
SED doit séquentiellement parcourir les lignes du fichier en retenir celles qui correspondent puis appliquer le traitement demandé à la ligne. Cette approche est par nature forcément plus lente.
Il y a typiquement des choses que les petits utilitaires ne savent pas faire (par exemple substituer un caractère par un autre c’est bon, mais remplacer un mot par un autre non), et en général (presque tout) ce que tu ferais manuellement dans un éditeur de texte sied à Sed …qui a été pensé comme pendant de scriptage de Ed/Ex. D’un autre côté, les enchaînements de pipe ne sont plus performants quand il y en a trop (j’ai compté une dégradation à partir de cinq sur une vieille bécane il y a longtemps, et puis de toute façon pour la gestion des erreurs on repassera) et surtout sur de gros volumes. Là, pouvoir faire dans le même programme plutôt que plusieurs processus qui s’attendent est mieux.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
C’est que la plupart de ces outils viennent d’une époque où les choses devaient être KISS et là il s’agit de pouvoir manipuler des fichiers DSV tous bêtes (contrairement à cette immondice de CSV…) Un exemple connu de ce type est le fichier
/etc/passwd
(et aussi la variablePATH
) avec-d ':'
l’équivalent pour Markdown, sauce GFM et similaires, serait avec
-d '|'
et tu pourrais avoir une application qui utilise des espaces, soit
-d ' '
(dans tous les cas, il faut éviter la collision de délimiteur —par exemple avec le blanc souligné ici mais ce pourrait être un autre espacement comme l’insécable)
Avec ce type de format, DSV, la convention a toujours été que tous les champs soient présents, même vides. Dans ce cas, on a deux successions de délimiteur…
…règle qui ne change pas selon le délimiteur (on a dit kiss, pas avec des exceptions comme en français)
(dans cet exemple, en ayant changé le délimiteur, on ne casse pas notre
cut -d ' ' -f 6
héhé)C’est un poil plus subtil. Quand AWK ont mis au point l’outil, c’était pour traiter des fichiers avec une structuration un peu plus complexe et ils ont prévu de pouvoir pratiquement tout définir (comprendre décrire le format du fichier…) Du coup, au lieu de travailler ligne par ligne, leur programme gobe tout le fichier et se fait un tableau interne dont les lignes sont découpées au niveau de la valeur de
RS
(qui est le saut de ligne par défaut) et les colonnes/champs (cette fois-ci variables —i.e. contrairement à du DSV toutes les lignes ne sont pas obligées d’avoir le même nombre de champs) au niveau deFS
(qui est toute succession de blancs par défauts, comme la variableIFS
pour le shell…) :) Tu as bien un seul séparateur, et c’est là sa différence, le séparateur n’est plus un seul caractère-délimiteur mais une expression rationnelle (soit[:blank:]
…)C’est à cause de ce traitement plus coûteux que cet utilitaire n’a pas simplement remplacé les autres : chacun répond à des besoins différents, malgré la ressemblance de prime abord.
awk -F ' ' '{print $3}'
(on utilise comme séparateur de champs, le caractère espace, et non la regexp par défaut) devra te donner le même résultat quecut -d ' ' -f 3
awk -F ':' '{print $1}'
devra te donner le même résultat quecut -d ':' -f 1
Ressemblance de prime abord parce-que AWK ont prévu aussi qu’on puisse personnaliser à la sortie/affichage les séparateurs de champs (valeur de
OFS
qui est par défaut l’espace unique) et d’enregistrement (valeur deORS
qui est par défaut le saut de ligne)awk -F '|' '{print $2,$4}'
va, par défaut, afficher les deux champs séparés par une espace… tandis quecut -d '|' -f 2,4
va garder le même séparateur (et il n’est pas prévu de changer cela par défaut sauf en enchainant avec untr '|' ' '
par exemple.)awk -F ' ' '{print $3,$1}'
va bien nous sortir successivement les troisièmes et premiers champs… tandis quecut -d ' ' -f 3,1
est pareil àcut -d ' ' -f 1,3
car les champs ne sont pas réordonnés…“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
Parmi les remarques, aucun commentaire n’a tilté sur la première ligne qui m’a bloquée perso
Il manque l’option du type de test fait, qui je devine est de savoir s’il y a un argument ?
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Remarques
Posté par Marotte ⛧ . Évalué à 4.
Extrait du man test :
Mais je note que cela peut potentiellement être déstabilisant.
[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Arf, j’aurais du vérifier avant d’écrire une connerie
^^
En c’est POSIX et il me semble l’avoir déjà rencontré mais bon mon refus des implicites doit faire que j’oublie chaque fois.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Remarques
Posté par Marotte ⛧ . Évalué à 3.
Ne t’essaie pas à Perl alors, je crois que c’est le langage champion en terme d’implicites (même si on conserve la possibilité d’expliciter dans tous les cas).
[^] # Re: Remarques
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Ah mais j’en fais et j’aime beaucoup ce langage :D Justement, j’évite les trucs implicites (et du coup je ne comprends pas quand les gens balancent sur Internet que Perl = automatiquement illisible etc.)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.