Subliniem că totuși, „articol” s-ar chema ceva bine închegat și care merită eventual, atenția vreunei publicații – nu ca articol.tex
din [1]. De regulă, redacțiile revistelor științifice (și ale editurilor, uneori) vor cere "sursa LaTeX" (sau poate un fișier PDF, dar dacă se poate nu cel compilat din sursa-LaTeX, ci… din Microsoft-Word); tocmai vizând asemenea cerință de sursă-LaTeX, în pygjust
am vizat numai formatul LaTeX (chit că n-ar fi greu de administrat și o opțiune de alegere a formatului, pasând-o parametrului -f
din pygmentize
și modificând Default.sty
).
E greu de crezut că redactorul revistei va accepta să compileze fișierul LaTeX primit de la tine, folosind -shell-escape
– cum cere pachetul (aici, pygjust
) implicat pentru pigmentarea secvențelor de cod… Ar însemna să accepte scrierea pe discul propriu a „ceva” provenit de la un necunoscut – ceea ce trebuie evitat, pentru a nu avea vreo surpriză neplăcută.
Ce-i de făcut dacă ținem să păstrăm pigmentarea făcută secvențelor de cod?!
Prin pygjust
creasem fișierele "1.tex
", "2.tex
" ș.a.m.d. care conțin comenzile LaTeX pentru pigmentarea secvențelor de cod care apăreau una după alta, în diverse locuri din articol.tex
; n-avem decât să includem aceste fragmente LaTeX în locul secvențelor de cod cărora le sunt asociate – adică să înlocuim aparițiile "\begin{Pyg}
linii_de_cod-sursă \end{Pyg}
" (unde "Pyg
" este mediul cerut și gestionat de către pygjust
), cu textul din fișierul "N.tex
" unde N
este numărul de ordine al acelei secvențe.
Mai întâi, folosim sed pentru a înlocui orice bloc de linii în care prima linie conține \begin{Pyg}
, iar ultima conține \end{Pyg}
, cu o linie pe care scriem "\input{ .tex }%####
" (ulterior, vom putea identifica aceste linii după comentariul "%####"):
sed '/\\begin{Pyg}/ , /\\end{Pyg}/ c \\\\input{ \.tex }%####' < articol.tex \ > articol_1.tex
'c
' (de la "change") operează înlocuirea menționată; după aceste înlocuiri, corpul inițial din articol.tex
devine în cazul nostru (într-un nou fișier, articol_1.tex
):
\begin{document} Un program în limbajul \textsf{Perl}: \input{ .tex }%#### Un program în limbajul \textsf{Python}: \input{ .tex }%#### \end{document}
Puteam face înlocuirile respective și printr-o comandă bazată pe perl, sau pe awk – dar acestea operează de regulă „linie cu linie”, în timp ce sed
operează în mod natural și pe blocuri de linii, după sintaxa:
/ [șablon_prima_linie] / [,] / [șablon_ultima_linie] / change_with [Linie]
În schimb, nu vedem cum puteam, folosind sed
, să înscriem și numărul de ordine al blocului înlocuit – încât să avem "\input{1.tex}
", ș.a.m.d.. În loc de numărul respectiv, avem deocamdată spațiu…
Obs. Vor fi de înlocuit liniile "\input{N.tex}
", prin conținutul fișierelor "N.tex
"; ne putem aștepta ca autorul articolului să se refere în diverse locuri la vreun fișier "N.tex
" și chiar să evoce pe undeva comanda "\input{N.tex}
" — dar ni se pare incredibil că va folosi în altă parte din document, forma artificială înscrisă mai sus; deci sufixul "%####
" ne va permite să identificăm în mod unic locul în care trebuie adus conținutul fișierului "N.tex
".
Mai departe, pe fișierul articol_1.tex
rezultat mai sus, folosim awk, pentru a înlocui liniile "\input{ .tex }%####
" prin "\input{ N.tex }%####
" unde N
contorizează liniile respective:
awk '{ if($0 ~ /%####/) { Nr=Nr+1; print $1" "Nr $2" }%####" } else print }' articol_1.tex \ > articol_2.tex
Această comandă decurge cam așa: se parcurge linie după linie, fișierul articol_1.tex
; dacă linia curentă – referită prin $0
– conține "%####
", atunci în fișierul articol_2.tex
(pe care s-a redirectat în final ieșirea comenzii awk
) se înscrie primul câmp (referit prin $1
) al liniei (până la spațiu: "\input{
"); apoi se înscrie un spațiu și valoarea curentă a variabilei-contor 'Nr
' (care pleacă implicit de la 0
și este incrementat pentru fiecare nouă linie care conține "%####
") și apoi se înscrie câmpul referit prin $2
, anume ".tex
", urmat de un spațiu și "}%####
"; în caz contrar (ramura else
), în articol_2.tex
se înscrie linia curentă, nemodificată.
Deci în articol_2.tex
avem acum "\input{ 1.tex }%####
", "\input{ 2.tex }%####
" ș.a.m.d.
Subliniem că '#
' este „rezervat” în LaTeX, pentru a desemna parametrii unui macrou — de aceea, am prefixat cu '%
' (care desemnează un „comentariu”) – astfel încât compilatorul va ignora semnificațiile și nu ne va semnala "eroare".
Putem compila articol_2.tex
fără a folosi -shell-escape
— ba chiar, pentru siguranță, folosind explicit "-no-shell-escape
"; compilatorul va căuta în directorul curent fișierele "N.tex
" indicate în comenzile \input
și va face (în memorie, desigur) „înlocuirile” – producând un fișier PDF identic celui obținut anterior din articol.tex
(v. [1]) folosind -shell-escape
.
Dar aceasta… pentru că am păstrat linia \usepackage{pygjust}
!
Programul extern pygmentize
nu va mai fi invocat în cursul compilării, dat fiind că am eliminat mediile \begin{Pyg}
— atunci… de ce să mai încărcăm pachetul pygjust
?
Definițiile de opțiuni și definiția mediului "Pyg
" din pygjust.sty
nu mai sunt necesare – însă trebuie să păstrăm pachetele încorporate de pygjust
, anume Default
(care definește macrourile \PY
invocate în fișierele "N.tex
") și fancyvrb
(căruia i se pasaseră comenzile de numerotare a liniilor secvenței de cod); deasemenea, trebuie păstrat xcolor
(din care provine de exemplu \textcolor
, folosit în Default.sty
).
Bineînțeles că în articol_2.tex
putem înlocui linia "\usepackage{pygjust}
" cu:
\usepackage{fancyvrb, xcolor, Default} % abandonează pygjust
eliminând deci, pygjust
. Recompilând (iarăși, cu "-no-shell-escape
"), rezultă desigur (fără erori) un PDF identic – măcar ca vizualizare – celui anterior.
Subliniem că dacă am fi omis fancyvrb
, atunci am fi obținut eroarea "Environment Verbatim undefined" – explicabil, fiindcă fișierele importate "N.tex
" au fiecare pe prima linie "\begin{Verbatim}[...]
", conținând eventual și opțiunile "numbers
" și "numbersep
" care fuseseră pasate macroului \fvset
din fancyvrb
(care definește și mediul Verbatim
).
Dar tot ne rămâne o dilemă, adică o problemă…
Putem proceda oricum (inclusiv manual, prin "Copy&Paste") dacă articol.tex
conține numai două-trei secvențe de cod; dar dacă ne-am învrednicit a scrie ceva carte de programare care conține vreo sută-două secvențe de cod (v. de exemplu cărțile mele Orare școlare echilibrate și limbajul R, sau Caractere vechi si noi, cu PostScript, Latex și R) — atunci ar fi neelegant (ar fi o prostie) să trimitem cele o sută de fișiere "N.tex
" necesare compilării lui articol.tex
…
Fișierele „auxiliare” (inclusiv "N.tex
") servesc poate autorului, nicidecum redactorului care a receptat articolul (sau cartea); se cuvine să trimitem numai fișierul-sursă "articol.tex
", având încorporate în prealabil, toate fișierele "N.tex
".
În loc să lăsăm lui xelatex
să citească pe parcursul compilării documentului articol_2.tex
, fișierele "N.tex
" (executând comenzile \input
respective) — constituim un program "helper.sh
" în care invocăm awk
pentru a înlocui fiecare linie "input{ N.tex }%####
" întâlnită în articol_2.tex
, cu setul de linii existent în fișierul "N.tex
"; prin operatorul de redirectare '>
', reținem rezultatul în fișierul "articol_3.tex
", pe care îl pasăm apoi compilatorului xelatex
:
#!/bin/bash awk '/%####/ { while(getline tx < $2 >0) print tx next } 1' articol_2.tex \ > articol_3.tex xelatex -no-shell-escape articol_3.tex
awk
preia în variabila internă $0
linia curent citită din fișierul indicat, articol_2.tex
și o desparte în câmpuri delimitate (în modul implicit) de spațiu – referindu-le prin $1
, $2
ș.a.m.d. Când $0
vizează linia "\input{ 1.tex }%####
" – care se potrivește șablonului indicat /%####/
– în $1
avem "input{
", în $2
apare "1.tex
" și în $3
avem "}%####
"; prin getline tx < $2
se citește în variabila "tx
" linia „curentă” din fișierul indicat de $2
(returnând 0
dacă s-a ajuns la sfârșitul fișierului, sau -1
dacă fișierul nu există). Deci while(getline tx < $2 > 0) print tx
va citi pe rând toate liniile din "1.tex
" și le va „afișa”; apoi, next
va determina revenirea în procesul principal: se preia în $0
următoarea linie din articol_2.tex
și se testează dacă aceasta conține "%####
" — dacă da, se execută acțiunea din blocul {while(...) print; next}
, dacă nu atunci se execută acțiunea următoare acestui bloc, adică în cazul nostru '1
', ceea ce desemnează convențional acțiunea {print $0}
de printare a liniei respective.
Fișierele "N.tex
" nu mai prezintă interes – conținutul lor fiind acum încorporat în articol_3.tex
– și le putem șterge, astfel că după ce vom mai fi adăugat în articol.tex
niște noi secvențe de cod – rezultând după compilare noi fișiere de pigmentare N.tex
– să putem aplica iarăși operațiile de mai sus: obținem noul articol_1.tex
, apoi articol_2.tex
și articol_3.tex
, apoi ștergem fișierele auxiliare "N.tex
".
Bineînțeles că putem „automatiza” toate aceste operații, de repetat din când în când (pe măsură ce tot adăugăm noi secvențe de cod în cartea noastră) – rescriind "helper.sh
" astfel:
#!/bin/bash xelatex -shell-escape articol.tex # produce (prin pygjust) și fișierele N.tex sed '/\\begin{Pyg}/ , /\\end{Pyg}/ c \\\\input{ \.tex }%####' < articol.tex \ > articol_1.tex # cu \input{...} în locul mediilor Pyg awk '{ if($0 ~ /%####/) { Nr=Nr+1; print $1" "Nr $2" }%####" } else print }' articol_1.tex \ > articol_2.tex # se compilează și fără -shell-escape awk '/%####/ { while(getline tex < $2 >0) print tex next } 1' articol_2.tex \ > articol_3.tex # s-au încorporat fișierele-pigment mv articol_3.tex articol.tex rm [[:digit:]+].tex # șterge fișierele N.tex (încorporate deja) xelatex -no-shell-escape articol.tex # rezultă articol.pdf (cu pigmentare)
Marcând helper.sh
ca fișier-executabil (prin chmod +x helper.sh
), îl putem lansa din directorul curent de lucru prin ./helper.sh
și vor decurge succesiv operațiile discutate mai sus — în urma cărora articol.tex
nu mai conține „secvențe de cod” propriu-zise, ci conține codurile de pigmentare corespunzătoare acestora (astfel că articol.tex
poate fi compilat fără
) și desigur, rezultă fișierul PDF corespunzător stadiului curent al cărții; putem extinde în continuare articol.tex
, adăugând noi secvențe de cod, iar relansând ./helper.sh
le putem pigmenta „intern” și pe acestea – încât în final va fi suficientă „sursa-LaTeX” a cărții (fără și fișierele-pigmentoare "N.tex
" create prin pachetul pygjust
).
Bineînțeles că vom avea grijă ca în fișierul final articol.tex
, să eliminăm din preambul linia "\usepackage{pygjust}
" (încărcând în schimb pachetele fancyvrb
, xcolor
și Default
).
Și aceasta ar fi (împreună cu [1]) întreaga poveste a pachetului pygjust
– dar desigur, esențial este să poți scrie o „carte de programare” care să merite atenție… Poți?
vezi Cărţile mele (de programare)