4

Is it possible to use a \csdef{}{} on the command line to pass in parameters to pdflatex?

Background:

If the MWE below is saved as "TeX-SE.tex", I can use the following command line to define the value of \MyCommandViaDef

pdflatex "\def\MyCommandViaDef{123} \input{TeX-SE.tex}"

and things work as desired:

enter image description here

When I need to define a more complicated command that contains file names with dashes, using \def is no longer an option.

I realize I can use

pdflatex "\def\MyCommandViaDef{123} \expandafter\def\csname MyCommand Via Csdef\endcsname{xyz} \input{TeX-SE.tex}"

to obtain the desired results

enter image description here

but using a \csdef{}{} such as would be preferrable.

pdflatex "\def\MyCommandViaDef{123} \csdef{MyCommand Via Csdef}{xyz} \input{TeX-SE.tex}"

Code:

\documentclass{article}
\usepackage{etoolbox}

\begin{document}

Value via def: \MyCommandViaDef

\ifcsdef{MyCommand Via Csdef}{%
    Value via csdef: \csuse{MyCommand Via Csdef}
}{%
    Value via csdef: Not Available
}%

\end{document}
6
  • 1
    well you can but you would need \RequirePackage{etoolbox} first to define csdef, then use --jobname to stop the jobname being etoolbox Commented Sep 11 at 16:17
  • etoolbox defines \csdef (\newrobustcmd*{\csdef}[1]{\expandafter\def\csname#1\endcsname}), so it's not available before loading it. You can define \csdef using \newcommand{\csdef}[1]{\expandafter\def\csname#1\endcsname}, but that sort of defeats the purpose... Commented Sep 11 at 16:18
  • 1
    or, depending where you want to use the thing you can use a commandline of \AtBeginDocument{\csdef\.... Commented Sep 11 at 16:18
  • Note that @Werner definition is the one of already available \@namedef. Except the latter requires @ of catcode 11. Commented Sep 11 at 21:01
  • @PeterGrill Will this be used only with posix-shells? What catcode-régime shall be used for tokenizing names? What catcode-régime shall be used for tokenizing values? Commented Sep 13 at 10:30

3 Answers 3

6

You can use a hook, so you can use \csdef just after etoolbox has been loaded.

pdflatex '\def\MyCommandViaDef{123} \AddToHook{package/etoolbox/after}{\csdef{MyCommand Via Csdef}{xyz}}\input{test}'

output

This way, the commands will also be available in the preamble.

You may want to do \RequirePackage{etoolbox} before \documentclass.

5

You can not use \csdef unless you define it first or delay its use (eg with \AtBeginDocument) but you could use built in latex features for example

\ExpandArgs{c}\def{MyCommand Via Csdef}{xyz}
8
  • Thanks. That (as well as putting the \csdef{}{} in \AtBeginDocument{}) works great. But, what is this \ExpandArgs{c} magic and where is it documented? Does \ExpandArgs{c} apply to all subsequent \def{}{}? Commented Sep 11 at 16:34
  • @PeterGrill standard latex , use texdoc usrguide section 4 Commented Sep 11 at 16:36
  • no it just works once \ExpandArgs{cc}\foo{a b c}{x y z} is \foo\a.b.c\x.y.z (where . is a space) with enough \expandafter and \csname applied to generate both command names. @PeterGrill Commented Sep 11 at 16:39
  • Isn't there a \@namedef in LaTeX 2e? so here \csname @namedef\endcsname if on command line due to @ not being letter (and nthe whole thing needing some shell escaping) Commented Sep 11 at 21:00
  • @user691586 er well given the whole idea is to avoid using \csname (as the question notes that could be used directly) using csname to define the command to access a def without using csname is a bit backwards Commented Sep 11 at 21:32
2

Using

$ pdflatex '\csdef{key1}{value1} \csdef{key2}{value2} \input{<filename>}'

is a hack to work around how TeX processes the command line; a syntax like

$ pdflatex <filename>.tex first-parameter=first-value '{another key}={\textbf{Hello, world!}}'

is much nicer for the end user to use.

To parse arguments written this way, we can use expl3 to read the command line arguments literally:

\ExplSyntaxOn
    %% Initialization code
    \cs_if_exist:NF \g__example_first_run_bool {
        \cs_generate_variant:Nn \prop_set_from_keyval:Nn { NV }

        \str_new:N \g__example_args_str
        \tl_new:N \l__example_args_tl
        \seq_new:N \l__example_args_seq
        \prop_new:N \l__example_args_prop

        \bool_new:N \g__example_first_run_bool
        \bool_gset_true:N \g__example_first_run_bool

        \cctab_const:Nn \c__example_skip_document_cctab {
            \int_set:Nn \tex_endlinechar:D { -1 }
            \int_step_inline:nnn { 0 } { 127 } {
                \char_set_catcode_ignore:n { #1 }
            }
        }
    }

    %% Main code
    \bool_if:NTF \g__example_first_run_bool {
        %% First run: read the arguments from the command line
        \tex_everyeof:D = {
            %% This will run as soon as we reach the end of the file
            \tex_everyeof:D = { } %% Reset to avoid loops

            %% Switch to a new catcode regime
            \cctab_end:
            \cctab_begin:N \c_other_cctab

            %% Read the command line arguments
            \peek_analysis_map_inline:n {
                \exp_args:Ne \token_if_eq_charcode:NNTF {
                    \char_generate:nn { `\^^M } { 11 }
                } #1 {
                    %% If we see ^^M, then we're at the end of the command line
                    %% arguments, so we switch back to normal catcodes and
                    %% input the file again.
                    \cctab_end:
                    \bool_gset_false:N \g__example_first_run_bool
                    \file_input_raw:V \c_sys_jobname_str
                } {
                    %% Otherwise, keep accumulating the arguments.
                    \str_put_right:Nn \g__example_args_str { #1 }
                }
            }
        }
        %% Ignore the rest of the document and proceed to read in the command
        %% line as document content.
        \cctab_begin:N \c__example_skip_document_cctab
    } {
        %% Second run: process the arguments and continue with the document
        \tl_set_rescan:NnV \l__example_args_tl {
            \cctab_select:N \c_document_cctab
        } \g__example_args_str

        \seq_set_split:NVV \l__example_args_seq \c_space_tl \l__example_args_tl
        \seq_map_inline:Nn \l__example_args_seq {
            \prop_put_from_keyval:Nn \l__example_args_prop { #1 }
        }

        \NewDocumentCommand \ShowParameter { m } {
            #1:~ \prop_item:Nn \l__example_args_prop { #1 }
        }
    }
\ExplSyntaxOff

%% Demonstration
\documentclass{article}

\pagestyle{empty}
\setlength{\parindent}{0pt}
\setlength{\parskip}{\baselineskip}

\begin{document}
    \ShowParameter{first-parameter}

    \ShowParameter{another key}
\end{document}

Compiling with above command line then gives the following output:

output

2
  • This works as well for bash like parameters, but is not as flexible as using a \csdef{}. My current use case includes spaces in the names and values so will probably end up with quoting issues get throught shell passing mechanism. Commented Sep 12 at 21:52
  • @PeterGrill Ok, see the edit. Commented Sep 13 at 1:14

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.