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 would like to include part of the contents of a .tex file into a different .tex file. I don't have the option of modifying the file to include. The file to include may change so simply copying and pasting the part I need is not ideal.

Essentially, I'm wondering if there is something that would work like

\input[start-end]{file.tex}

where start and end are the first and last lines to include from the file?

share|improve this question
4  
If the file will change, wouldn't some kind of start/end tags be safer than specifying line numbers? –  Seamus Nov 3 '10 at 21:18
7  
An alternative to a TeX solution might be to add some sed-fu to your makefile. pipe the output of sed -n 'x,yp file to a temporary file and input that. (where x and y are start and end lines respectively) –  Seamus Nov 3 '10 at 21:23
    
(Or something like that. I just looked up how to do this and I'm not sure I'd be able to do it myself...) –  Seamus Nov 3 '10 at 21:26
7  
sed -ne 42,99p will copy lines 42–99 (inclusive) of its input to its output. –  Harald Hanche-Olsen Nov 3 '10 at 22:00
    
@Harald, could you add your comment as an answer? –  Juan A. Navarro Nov 3 '10 at 22:37

4 Answers 4

up vote 15 down vote accepted

Here is an approach in pure TeX (well, e-TeX). The main idea is: to select a range of lines in a file, sed is overkill, TeX is more than enough. As a bonus, no temporary file is needed.

\documentclass[a4paper]{article}
\usepackage[ascii]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}

\makeatletter
\newread\pin@file
\newcounter{pinlineno}
\newcommand\pin@accu{}
\newcommand\pin@ext{pintmp}
% inputs #3, selecting only lines #1 to #2 (inclusive)
\newcommand*\partialinput [3] {%
  \IfFileExists{#3}{%
    \openin\pin@file #3
    % skip lines 1 to #1 (exclusive)
    \setcounter{pinlineno}{1}
    \@whilenum\value{pinlineno}<#1 \do{%
      \read\pin@file to\pin@line
      \stepcounter{pinlineno}%
    }
    % prepare reading lines #1 to #2 inclusive
    \addtocounter{pinlineno}{-1}
    \let\pin@accu\empty
    \begingroup
    \endlinechar\newlinechar
    \@whilenum\value{pinlineno}<#2 \do{%
      % use safe catcodes provided by e-TeX's \readline
      \readline\pin@file to\pin@line
      \edef\pin@accu{\pin@accu\pin@line}%
      \stepcounter{pinlineno}%
    }
    \closein\pin@file
    \expandafter\endgroup
    \scantokens\expandafter{\pin@accu}%
  }{%
    \errmessage{File `#3' doesn't exist!}%
  }%
}
\makeatother

% for testing purpose
\begin{filecontents}{pitest}
5 You shouldn't see this.\par
6 Let the fun begin!
7 \iffalse
8 bla
9 \fi

11 New paragraph.
12 \verb*+\foo (two  spaces)~_$^+ bla
13 You shouldn't see this.\par
\end{filecontents}

\begin{document}

\partialinput{6}{12}{pitest} random text

\end{document}

I think/hope this approach is robust, mostly thanks to e-TeX's \readline. See the included test file for potential edge cases that do work.

share|improve this answer
    
Is performance going to be an issue for long excerpts: the \edef\pin@accu{\pin@accu\pin@line} is going to repeatedly expand the same text, and looks like it will cost O(n^2)? –  Charles Stewart Nov 5 '10 at 13:37
2  
Yes, performance may be an issue. I don't think it's possible to do list kind of list operation in less than O(n²) with TeX. For long excerpts, it may be better to use a temporary file rather than a macro: open it before the second loop, write to it instead of adding to \pin@accu, close it and \input it after the loop. As a bonus, you don't need to change \newlinechar so the group becomes useless. But don't forget to use \immediate on all file operations! –  mpg Nov 5 '10 at 16:19
    
Performance is not such an issue for me, and a pure-TeX solution is preferable, so this is what I need - thanks! –  astrofrog Nov 15 '10 at 14:19
1  
To improve the performance you could change \edef\pin@accu{\pin@accu\pin@line} to \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\pi‌​n@accu\expandafter\expandafter\expandafter{\expandafter\pin@accu\pin@line} so that both macros are only expanded once. Using a token register (\newtoks\pin@accu) would be even better: \pin@accu\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pin@‌​accu\pin@line}. –  Martin Scharrer Apr 6 '11 at 12:17
    
@mpg I get some strange results using this. If I write some text before using \partialinput, there is a horizontal space between that word and whatever is on the line(s) fetched with \partialinput. How is that solved –  Hugo May 10 '13 at 13:58

As is stated in the comments, sed -ne 42,99p will copy lines 42–99 (inclusive) from its input to its output. That is assuming you're on some sort of unix (though I suppose you can have sed on windows too). You can do this in a script or a makefile, putting the result in a temporary file that you include.

Alternatively, assuming you are on unix, you can resort to some mild unix wizardry: Say the TeX file you want to include part of, is named foo.tex. Then you can do

mkfifo foosnips.tex
while sed -ne 42,99p foo.tex > foosnips.tex || [ $? == 141 ]; do sleep 1; done &

(modify for your shell if it's not (ba)sh) and then input foosnips in your master TeX file. Each time you TeX it, the sed command will extract the requested lines. The background job should keep on running until you reboot the computer, but thanks to the way fifos work, it will not actually do anything until the fifo is opened for reading. Kill it by hand when it is no longer needed, or if you need to adjust the parameters.

Doing it that way is perhaps not worth it, unless foo.tex changes very often, or the extract is very large and you don't want to keep it as a separate file.

(The test for exit status 141 (128+13, where 13 is the signal number of the PIPE signal) is there to make sure the loop is not terminated if you read only part of the output from the fifo, as when TeX is quit due to some error. And the sleep is needed to make sure the process at the receiving end of the fifo has a chance to close the file before a new sed process starts.)

share|improve this answer
    
Could this be modified to read particular start/end tags in the file? I ask because I personally think using line numbers to select the text seems a bad idea. (Although for a specific context, I guess it could be fine...) –  Seamus Nov 4 '10 at 13:33
2  
@Seamus: Sure, just replace 42,99p by '/start/,/end/p'. (The quotes are only needed if the text start or end is replaced by something containing spaces or other characters special to the shell.) Beware that these texts are interpreted as regular expressions, though, so square brackets, asterisks and question marks in particular are special. This prints lines between occorrences of start and end inclusive, with repetitions if several such pairs exist. One alternative is sed -ne '/end/q;/start/,$p' which does not repeat (and skips the end line). Sed syntax is weird and powerful. –  Harald Hanche-Olsen Nov 4 '10 at 14:16

Here's something that's totally untested!

\newread\pin
\newcount\pcount
\def\partialinput[#1-#2]#3{%
    \openin\pin #3
    \global\pcount0
    \loop\ifnum\pcount<#1\relax
        \read\pin to\l
        \global\advance\pcount1
    \repeat
    \loop\ifnum\pcount<#2\relax
        \read\pin to\l\l
        \global\advance\pcount1
    \repeat
    \read\pin to\l\l
    \closein\pin
}

Modulo errors, that defines a new macro \partialinput that should work as you describe.

Actually, that numbers lines starting at 0. replace the \global\pcount0 with \global\pcount1 to start at 1 instead.

share|improve this answer
2  
I don't think \read\pin to\l\l is going to be robust: what happens if the next line is something like \ifx\cs\relax? –  Charles Stewart Nov 4 '10 at 9:35
    
Additionnaly, the catcodes are fixed when the line is read, so this approach will break if the somthing in the file tries to change them. –  mpg Nov 4 '10 at 14:18
    
@Charles, @mpg: Yes, both are true. I can't imagine reading specific ranges of files is going to be particularly robust anyway, so I didn't bother going the e-TeX route with \readline and \scantokens, but as mpg demonstrates, it's not really that hard to do. –  TH. Nov 5 '10 at 1:11

Manuel has posted an answer that uses line numbers; there's a recent package on CTAN that lets you do it with tags in the input file: catchfilebetweentags.

An example is

\usepackage{catchfilebetweentags}
...
\ExecuteMetaData[file.tex]{tag}

where the contents of the external file is surrounded by the "tags":

%<*tag>
...
%</tag>
share|improve this answer
3  
I upvoted your answer because I didn't know this package and it sounds very useful, but, just nitpicking: it doesn't solve the OP's problem proper, since he mentioned he can't change the external file. But I agree that tags are more robust in general. –  mpg Nov 5 '10 at 12:41
1  
Yes, I agree your answer is better! I just added this answer for completeness. –  Will Robertson Nov 5 '10 at 22:05
    
This is very cool, I will definitely look into this package some more for other projects. –  astrofrog Nov 15 '10 at 14:20
    
Finally i found an elegant way to include my m4-circuit-macros figures in a document! Thanks Mr. Robertson for this post –  user1146332 Jun 8 '13 at 21:11
    
This is really great - I'm building a poster and with this it is trivial to produce an alternative layout and be able to edit the content separately. –  AnjoMan Sep 17 at 14:33

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.