1

I get a string from an external program, and I would like to get each part of that string:

\documentclass{article}
\usepackage{bashful}

\begin{document}

% Test.sh would do echo "4 8 15 16 23 42"
\let\data{\splice{./test.sh}}

\begin{itemize}
    \item \getOccurences{2}{\data} %15
    \item \getOccurences{5}{\data} %23
\end{itemize}

\end{document}

The information read from stdout is a series of numbers space separated.

How should I implement the \getOccurences macro?

10
  • Since you already load bashful, why not count the occurrences in bash? Commented Jun 14, 2022 at 8:23
  • Because I am not using bash. This is just a Minimal, Reproducible Example.
    – nowox
    Commented Jun 14, 2022 at 8:30
  • Would it be safe to assume that your external program writes a file containing (say) 4 8 15 16 23 42 and that you want to extract data therefrom?
    – egreg
    Commented Jun 14, 2022 at 8:38
  • 1
    can you provide an example that does not require --shell-escape it is not reasonable to ask people to run tex fragments copied from the internet with shell escape enabled Commented Jun 14, 2022 at 8:52
  • 1
    if you have control over the format and can use commas not spaces you could use expl3 clist item directly Commented Jun 14, 2022 at 8:55

2 Answers 2

2

This answer addresses how to parse the string, once you have inputted it. For this example, therefore, I merely define the string using \def. I use the listofitems package to create an array of the individual string elements.

\documentclass{article}
%\usepackage{bashful}
\usepackage{listofitems}
\setsepchar{ }
\ignoreemptyitems
\begin{document}

% Test.sh would do echo "4 8 15 16 23 42"
%\let\data{\splice{./test.sh}}
\def\data{4 8 15 16 23 42}

\readlist\mylist{\data}

\mylist[3] should be 15

\mylist[5] should be 23

\end{document}

enter image description here

3
  • It doesn't work with the output from bash. I tried to put echo 4 8 15 16 23 42 in \splice.
    – nowox
    Commented Jun 20, 2022 at 19:25
  • 1
    @nowox This answer works if you first detokenize the bash output like in my answer, i.e., \edef\data{\detokenize\expandafter{\bashStdout}}\readlist\mylist{\data}\mylist[3]. And it is also a better solution than my answer because the indexing is much more straightforward.
    – Marijn
    Commented Jun 21, 2022 at 7:09
  • @nowox Also, the \mylist[3], etc. array elements fully expand into their original values. Commented Jun 21, 2022 at 17:41
2

The xstring package has a macro StrBetween[n,m]{full string}{string A}{string B} that may help here. It will select the substring between the nth occurrence of string A and the mth occurrence of string B. In this case you can set both string A and string B to be a space and extract the string between the 2nd and 3rd space to get 15 and the string between the 4th and 5th space to get 23.

The second part of the question is to get the external output into a format that xstring can handle. This is a bit tricky. \splice is doing a lot under the hood that causes trouble when expanding the macro for string processing. A better alternative is to use \bashStdout as defined by bashful that captures the output of the last \bash \END block as a plain string. The only problem here is that spaces have category code 12 which also confuses xstring. As a solution you can 'flatten' the string using \detokenize. This has the added advantage that you can use \edef which expands the argument, allowing for other \bash \END blocks (which will overwrite \bashStdout) in between the execution and the actual use of the values.

MWE:

\documentclass{article}
\usepackage{bashful}
\usepackage{xstring}

\begin{document}

\bash
echo "4 8 15 16 23 42"
\END
\edef\data{\detokenize\expandafter{\bashStdout}}
All data: \data

\begin{itemize}
    \item \StrBetween[2,3]{\data}{ }{ } %15
    \item \StrBetween[4,5]{\data}{ }{ } %23
\end{itemize}
\end{document}

Result:

enter image description here

8
  • You probably want to write edef data instead of def data. // remark, expl3 equivalent statement is \str_set:NV \data \bashStdout
    – user202729
    Commented Jun 15, 2022 at 13:35
  • @user202729 just \edef\data{\bashStdout} does not solve the catcode problem, while \def{\detokenize ...} works fine with xstring. Is there another reason to prefer edef here (besides it being a bit cleaner perhaps)?
    – Marijn
    Commented Jun 15, 2022 at 13:39
  • I mean \edef\data{\detokenize\expandafter{\bashStdout}}.
    – user202729
    Commented Jun 15, 2022 at 13:39
  • 1
    Because... for example if you do another bash command bashStdout will be rewritten? Also only need to detokenize once (alternative is to insert two expandafter in, but these expandafter are ugly)
    – user202729
    Commented Jun 15, 2022 at 13:39
  • Ah yes, very good point. I'll edit.
    – Marijn
    Commented Jun 15, 2022 at 13:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .