Reputation: 6856
An mplayer tool (midentify
) outputs "shell-ready" lines intended to be evaluated by a bash/sh/whatever interpreter.
How can I assign these var-names to their corresponding values as elisp var-names in emacs?
The data is in a string (via shell-command-to-string
)
Here is the data
ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF
Upvotes: 3
Views: 601
Reputation: 2736
Here's a function you can run on the output buffer:
(defun set-variables-from-shell-assignments ()
(goto-char (point-min))
(while (< (point) (point-max))
(and (looking-at "\\([A-Z_]+\\)=\\(.*\\)$")
(set (intern (match-string 1)) (match-string 2)))
(forward-line 1)))
Upvotes: 2
Reputation: 2335
I don't think regexp is what really need. You need to split your string by \n
and =
, so you just say exactly the same to interpreter.
I think you can also use intern
to get symbol from string(and set variables). I use it for the first time, so comment here if i am wrong. Anyways, if list is what you want, just remove top-level mapcar.
(defun set=(str)
(mapcar (lambda(arg)
(set
(intern (car arg))
(cadr arg)))
(mapcar (lambda(arg)
(split-string arg "=" t))
(split-string
str
"\n" t))))
(set=
"ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF")
Upvotes: 1
Reputation: 29772
Here's a routine that takes a string containing midentify
output, and returns an association list of the key-value pairs (which is safer than setting Emacs variables willy-nilly). It also has the advantage that it parses numeric values into actual numbers:
(require 'cl) ; for "loop"
(defun midentify-output-to-alist (str)
(setq str (replace-regexp-in-string "\n+" "\n" str))
(setq str (replace-regexp-in-string "\n+\\'" "" str))
(loop for index = 0 then (match-end 0)
while (string-match "^\\(?:\\([A-Z_]+\\)=\\(?:\\([0-9]+\\(?:\\.[0-9]+\\)?\\)\\|\\(.*\\)\\)\\|\\(.*\\)\\)\n?" str index)
if (match-string 4 str)
do (error "Invalid line: %s" (match-string 4 str))
collect (cons (match-string 1 str)
(if (match-string 2 str)
(string-to-number (match-string 2 str))
(match-string 3 str)))))
You'd use this function like so:
(setq alist (midentify-output-to-alist my-output))
(if (assoc "ID_LENGTH" alist)
(setq id-length (cdr (assoc "ID_LENGTH" alist)))
(error "Didn't find an ID_LENGTH!"))
EDIT: Modified function to handle blank lines and trailing newlines correctly.
The regexp is indeed a beast; Emacs regexps are not known for their easiness on the eyes. To break it down a bit:
^(?:valid-line)|(.*)
. It tries to match a valid line, or else matches the entire line (the .*
) in match-group 4. If (match-group 4 str)
is not nil
, that indicates that an invalid line was encountered, and an error is raised.valid-line
is (word)=(?:(number)|(.*))
. If this matches, then the name part of the name-value pair is in match-string 1, and if the rest of the line matches a number, then the number is in match-string 2, otherwise the entire rest of the line is in match-string 3.Upvotes: 5
Reputation: 13432
There's probably a better way but this should do it:
(require 'cl) (let ((s "ID_AUDIO_ID=0 ID_FILENAME=/home/axiom/abc.wav ID_DEMUXER=audio ID_AUDIO_FORMAT=1 ID_AUDIO_BITRATE=512000 ID_AUDIO_RATE=0 ID_AUDIO_NCH=1 ID_LENGTH=3207.00 ID_SEEKABLE=1 ID_CHAPTERS=0 ID_AUDIO_BITRATE=512000 ID_AUDIO_RATE=32000 ID_AUDIO_NCH=1 ID_AUDIO_CODEC=pcm ID_EXIT=EOF")) (loop for p in (split-string s "\n") do (let* ((elements (split-string p "=")) (key (elt elements 0)) (value (elt elements 1))) (set (intern key) value))))
Upvotes: 3