Reputation: 719
For generating a string command for using in uiop:run-command
I need to have a string which contains other strings (as required by the command in question). However the command (Fontforg's legacy scripting language) requires that some strings are enclosed in double quotes, e.g.:
"fontforge -lang=ff -c 'Print("A Doublequote String")'"
How can I get such a string with the "A Doublequote String" being literally part of the command-string, i.e. no doublequote being escaped?
Update
To be more concrete, the command I want to send to uiop:run-command
is fontforge -lang=ff -c 'Open($1);SelectAll();foreach Print(GlyphInfo("Name")); endloop' haydn-11.svg
, where the "Name"
argument to GlyphInfo should be written with double quotes. Using escape characters backslash to preserve the quotes
(uiop:run-program
(format nil
"fontforge -lang=ff -c 'Open($1);SelectAll();foreach Print(GlyphInfo(\"Name\")); endloop' haydn-11.svg")
:output t)
the subprocess command exits with error code 1:
Subprocess with command "fontforge -lang=ff -c 'Open($1);SelectAll();foreach Print(GlyphInfo(\"Name\")); endloop' haydn-11.svg"
exited with error code 1
[Condition of type UIOP/RUN-PROGRAM:SUBPROCESS-ERROR]
I suppose the command is seeing the escape characters too, and that is why it fails to perform, since otherwise the command is syntactically correct.
Upvotes: 0
Views: 1073
Reputation: 60004
Backslash is a single escape character in standard syntax.
I.e., what you are looking for is
"fontforge -lang=ff -c 'Print(\"A Doublequote String\")'"
Note that by default *print-escape*
is t
, i.e., the above string will be printed with backslashes even though the string itself does not contain it:
(defparameter s (string #\"))
s
==> "\""
(length s)
==> 1
(char s 0)
==> #\"
Upvotes: 1
Reputation:
I don't have fontforge
, but I can do the equivalent. First of all the way you are doing it is not one, but two levels of language-in-a-string: the shell language is in a string in CL, and whatever fontforge
language is in a string in the shell language. So let's minimise that by avoiding the whole shell language altogether. We still have one level of language-in-a-string, but that's a whole lot better than two.
I also don't understand why you're using (format nil <fixed string>)
to make ... a fixed string. I'm guessing that eventually you're intending to replace the filename using format
, but this is not needed since we're no longer going to use the shell at all.
So instead, do this
(defun runit (file)
(uiop:run-program
(list "echo" ; because I don't have fontforge
"fontforge" "-lang=ff" "-c"
"Open($1); SelectAll(); foreach Print(GlyphInfo(\"Name\")); endloop"
file)
:output t
:force-shell nil))
Note that the command here is echo
: because I don't have fontforge
I'll just get echo
to print the command line. I've also added some spaces into the big chunk of whatever-language-fontforge-uses to make it clearer.
So now
> (runit "foo.svg")
fontforge -lang=ff -c Open($1); SelectAll(); foreach Print(GlyphInfo("Name")); endloop foo.svg
nil
nil
0
And this is fine, although it's not clear what the individual arguments are because echo
doesn't do that. Well, I have a little utility called argv
whose whole job is to print its argv clearly, so rewriting runit
as
(defun runit (file)
(uiop:run-program
(list "argv" ; because I don't have fontforge
"fontforge" "-lang=ff" "-c"
"Open($1); SelectAll(); foreach Print(GlyphInfo(\"Name\")); endloop"
file)
:output t
:force-shell nil))
We get
> (runit "foo.svg")
"fontforge"
"-lang=ff"
"-c"
"Open($1); SelectAll(); foreach Print(GlyphInfo("Name")); endloop"
"foo.svg"
nil
nil
0
Note that argv
isn't smart enough to escape the quotes inside the long argument: it's just a very tiny Perl script I use for debugging things.
Upvotes: 0