Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I need to pass \jobname to some external shell command and read back its input using the \input{|"<shell command>"} syntax. However \jobname may contain whitespaces. So how to quote it for the shell?

% pdflatex --shell-escape "file with spaces"
\documentclass[]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\begin{document}
  \input{|"wc \jobname"} % how to quote \jobname here?
\end{document}
share|improve this question
    
Reverse |" into "|, to begin with. –  egreg Jan 14 '13 at 23:50
    
This should be easy, but I have been trying things for more than two hours now. So I decided to ask before going to bed, frustrated :-/ –  Daniel Jan 14 '13 at 23:53
    
@egreg: Reversing does not work. wc is still complaining that it can't find the file file –  Daniel Jan 14 '13 at 23:54
    
Yes; file names with spaces are always a pain in the neck. –  egreg Jan 15 '13 at 0:03
add comment

2 Answers

up vote 10 down vote accepted

Usually \jobname is set to the main input file name; however, when the file has spaces in it, pdftex adds (double) quotes around it in \jobname.

So, for doing your business you have to strip off those quotes. Here's a way that defines a new macro \unquotedjobname in the preamble:

\documentclass{article}

\def\uqji#1#2\relax{%
  \ifx"#1%
    \uqjii#2%
  \else
    \let\unquotedjobname\jobname
  \fi}
\def\uqjii#1"{\def\unquotedjobname{#1}}
\expandafter\uqji\jobname\relax

\begin{document}

\input{"|wc '\unquotedjobname.tex'"}

\end{document}

Notice that you have to supply the .tex extension or the quoting would be wrong.


Here is a possible complete solution to this problem; note that XeTeX accepts also file names that contain " in their name and in this case it quotes \jobname with single quotes. You won't probably be able to use the filename in this case without some other somersault, so the problem is not so important.

\documentclass{article}
\usepackage{ifxetex,ifluatex}
\ifxetex
  \usepackage{fontspec}
\else
  \ifluatex
    \usepackage{fontspec}
  \else
    \usepackage[T1]{fontenc}
    \usepackage[utf8]{inputenc}
  \fi
\fi

\usepackage{xparse}
\usepackage{l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\fixjobname}{sO{\xjobname}}
 {
  \IfBooleanTF{#1}
   { \fixjobname_print:N #2 }
   { \fixjobname_string:N #2 }
 }
\tl_new:N \l_fixjobname_temp_tl
\cs_new_protected:Npn \fixjobname_print:N #1
 {
  \fixjobname_unquote:
  \tl_set_rescan:NnV #1 { } \l_fixjobname_temp_tl
 }
\cs_new_protected:Npn \fixjobname_string:N #1
 {
  \fixjobname_unquote:
  \tl_set_eq:NN #1 \l_fixjobname_temp_tl
 }

%%% Uncomment this if you don't want to use l3regex
%\cs_new_protected:Npn \fixjobname_unquote:
% {
%  \tl_set_eq:NN \l_fixjobname_temp_tl \c_job_name_tl
%  \str_case_x:nnn { \tl_head:N \l_fixjobname_temp_tl }
%   {
%    { " } { \fixjobname_trim: } % double quoted jobname
%    { ' } { \fixjobname_trim: } % single quoted jobname (XeTeX)
%   }
%   { } % do nothing
% }
%\cs_new:Npn \fixjobname_trim:
% {
%  \tl_set:Nx \l_fixjobname_temp_tl { \tl_tail:N \l_fixjobname_temp_tl }
%  \tl_set:Nx \l_fixjobname_temp_tl { \tl_reverse:V \l_fixjobname_temp_tl }
%  \tl_set:Nx \l_fixjobname_temp_tl { \tl_tail:N \l_fixjobname_temp_tl }
%  \tl_set:Nx \l_fixjobname_temp_tl { \tl_reverse:V \l_fixjobname_temp_tl }
% }
%%% This works with l3regex
\cs_new_protected:Npn \fixjobname_unquote:
 {
  \tl_set_eq:NN \l_fixjobname_temp_tl \c_job_name_tl
  \regex_replace_all:nnN { \A ("|') ( .* ) ("|') \Z } { \2 } \l_fixjobname_temp_tl
 }
%%% End of l3regex dependent part
\cs_generate_variant:Nn \tl_set_rescan:Nnn { NnV }
\ExplSyntaxOff


\begin{document}
\fixjobname \xjobname \ (string)

\fixjobname*[\foo] \foo \ (UTF8)

\end{document}

The macro \fixjobname defines \xjobname as a string, which should work for passing it to the shell (however the communication might not be so good depending on the file system used). You can say

\fixjobname*

to define \xjobname by rescanning the string, useful for printing. In case XeLaTeX or LuaLaTeX are used there's no difference. You can still add an optional argument and that will be the sequence defined to hold the result instead of \xjobname.

There are two versions: the most efficient one uses l3regex; if you don't want to live on the edge, just uncomment the previous part and comment out the request for l3regex and the corresponding code.

share|improve this answer
    
Works like a charm! –  Daniel Jan 15 '13 at 9:16
    
IMHO the extended version deserves to become a small package. –  Daniel Jan 15 '13 at 14:54
add comment

Quoting depends on the shell, but this is cygwin bash:

enter image description here

% pdflatex --shell-escape "file with spaces"
\documentclass[]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\def\requote"#1"{'#1.tex'}

\typeout{Xwc \expandafter\requote\jobname X}
\begin{document}
  \input{|"wc \expandafter\requote\jobname"} % how to quote \jobname here?
\end{document}

The above requires a space so that \jobname is quoted. If you also need to work with filenames without spaces you need to check for a " before replacing it by '

% pdflatex --shell-escape "file with spaces"
\documentclass[]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}


\def\requote#1{\ifx"#1\expandafter\xrequote\else\expandafter\yrequote\fi#1}
\def\xrequote"#1"\relax{'#1.tex'}
\def\yrequote#1\relax{#1.tex}


\begin{document}
  \input{|"wc \expandafter\requote\jobname\relax"} % how to quote \jobname here?
\end{document}
share|improve this answer
    
Oh man, \jobname is already double-quoted when it contains spaces??? No wonder that nothing I have tried did work. I really should have asked earlier... Thanks a lot! –  Daniel Jan 15 '13 at 9:16
    
@Daniel It is with pdfTeX and XeTeX 'out of the box', and with LuaTeX in most real use cases (done as a Lua callback as part of format building). –  Joseph Wright Jan 15 '13 at 12:04
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.