rétrospective sur la mise en page en console

des utilitaires Unix méconnus ou oubliés

Salut les agrumes.

Dans une enfilage sur le clavier, j’avais avoué ma passion pour l’histoire des systèmes et matériels informatiques ; et dans diverses autres discussion, j’ai souvent évoqué l’influence des téléscripteurs (en tant que périphériques d’entrée-sortie) sur le système Unix, un processeur de textes commandé textuellement… C’est cet aspect que j’aimerais rappeler à travers quelques commandes oubliées.

intro au kilomètre

J’ai souvenir qu’on enseignait à une certaine époque (j’ai l’impression que ce n’est plus vrai quand je regarde autour de moi) que dans la création de documents textes (lettre, rédaction, roman, etc.) il y a deux étapes distinctes : la saisie (dite au kilomètre) d’une part, et la mise en forme d’autre part. Un éditeur modal, comme vi, permet d’observer cette règle facilement : on est en mode saisie (insertion ou ajout) pour la partie de saisie, et en mode commandes pour la partie édition avant mise en forme…
Lors de cette phase de saisie brute, les traitements de texte moderne nécessitent de distinguer le simple saut de ligne du changement de paragraphe (qui est associé à la touche ). Dans un éditeur de texte, que ce soit pour avec un balisage ou directement pour l’imprimante comme dans le cas présent, c’est plus simple : la touche permet de juste passer à la ligne (ce qui est rarement nécessaire), et il faut sauter au moins une ligne pour indiquer un nouveau paragraphe.

Une fois son texte saisi (input mode) et corrigé (command mode), il faut le mettre en forme pour l’impression (ou l’affichage sur le terminal…) Les systèmes Unix offrent quelques outils pour cela.

replis sur bord de lignes

Le premier, peu connu, est le filtre POSIX « fold » (qui signifie plier/replier), apparu dans 1BSD en 1977 (sortie officielle de la distribution en mars 1978).

utilisation simple

On lui indique la largeur voulue avec l’option « -w » (par défaut c’est du 80 caractères…) Il va être utile pour adapter la largeur du texte, en particulier lorsqu’on l’envoie vers une imprimante (sans retour de ligne, les lignes trop longues sont juste tronquées…) On peut l’utiliser aussi pour voir le résultat du même texte avec une largeur d’écran différente. Exemple :

$ echo "The _fold_ utility is a filter that shall fold lines from its\
input files, but breaking the lines to have a maximum of _width_\
column position (or bytes, if the *-b* option is specified)." | fold
The _fold_ utility is a filter that shall fold lines from its input files, but b
reaking the lines to have a maximum of _width_ column position (or bytes, if the
 *-b* option is specified).

$ echo "The _fold_ utility is a filter that shall fold lines from its\
input files, but breaking the lines to have a maximum of _width_\
column position (or bytes, if the *-b* option is specified)." | fold -w 75
The _fold_ utility is a filter that shall fold lines from its input files,
but breaking the lines to have a maximum of _width_ column position (or byt
es, if the *-b* option is specified).

$ echo "The _fold_ utility is a filter that shall fold lines from its\
input files, but breaking the lines to have a maximum of _width_\
column position (or bytes, if the *-b* option is specified)." | fold -w 60
The _fold_ utility is a filter that shall fold lines from it
s input files, but breaking the lines to have a maximum of _
width_ column position (or bytes, if the *-b* option is spec
ified).

Le fonctionnement est un peu simplet : noter la coupe abrupt des mots …sauf si on utilise l’option « -s » qui demande de couper aux espaces !

$ echo "The _fold_ utility is a filter that shall fold lines from its\
input files, but breaking the lines to have a maximum of _width_\
column position (or bytes, if the *-b* option is specified)." > wrap.txt
$ fold wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~its~input~files,~but~b
reaking~the~lines~to~have~a~maximum~of~_width_~column~position~(or~bytes,~if~the
~*-b*~option~is~specified).
Use~*-s*~option~to~split~at~latest~space~before~maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each~paragraph~from~its~input~
files,~but~break~the~lines~to~have~a~maximum~of~_width_~column~position.

$ fold -s wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~its~input~files,~but~
breaking~the~lines~to~have~a~maximum~of~_width_~column~position~(or~bytes,~if~
the~*-b*~option~is~specified).
Use~*-s*~option~to~split~at~latest~space~before~maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each~paragraph~from~its~input~
files,~but~break~the~lines~to~have~a~maximum~of~_width_~column~position.

$ fold -s -w 75 wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~its~input~files,~
but~breaking~the~lines~to~have~a~maximum~of~_width_~column~position~(or~
bytes,~if~the~*-b*~option~is~specified).
Use~*-s*~option~to~split~at~latest~space~before~maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each~paragraph~from~its~
input~~files,~but~break~the~lines~to~have~a~maximum~of~_width_~column~
position.

$ fold -w 60 -s wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~
its~input~files,~but~breaking~the~lines~to~have~a~maximum~
of~_width_~column~position~(or~bytes,~if~the~*-b*~option~is~
specified).
Use~*-s*~option~to~split~at~latest~space~before~maximum~
_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each~
paragraph~from~its~input~files,~but~break~the~lines~to~have~
a~maximum~of~_width_~column~position.

usage détourné

J’utilise cette commande dans mes scripts pour des traitements caractère par caractère, de petites chaînes :

# from variable
for char in $(echo "$var" | fold -w 1); do stuff; done
# from file
for char in $(fold -w 1 "$filepath"); do stuff; done

…chose qu’on peut faire aussi à l’aide de grep :

# from variable
for char in $(echo "$var" | grep -os '.'); do stuff; done
# from file
for char in $(grep -os '.' "$filepath"); do stuff; done

Mais préférez plutôt (plus robuste et accepte de longues chaînes de caractères) :

# from variable 
echo "$var" | fold -w 1 | {
  while read char; do stuff; done
}
# from file
fold -w 1 "$filepath" | {
  while read char; do stuff; done
}

et faites gaffe à IFS

paragraphes ajustées à une largeur

Il existe un autre filtre, apparu dans 2BSD en 1978 et vite adopté par d’autres distributions. Il porte le nom de « fmt » pour « ForMat Text »… (En dehors du système, c’est un sigle qui peut avoir plusieurs sens en français comme en anglais et ailleurs. Dans de nombreux langages de programmation, c’est souvent le nom de la bibliothèque pour faire du formatage de données —textes/nombres/etc., cousin de « printf » et « sprintf » anciennement connu sous le nom de « cppformat » et qui est utilisé par exemple dans Go et dans Java EE en tant que JSTL etc.)

utilisation et options

On lui indique aussi la largeur voulue avec l’option « -w » (cf. encadré d’information plus bas pour la valeur par défaut.)

$ fmt wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~its~input~files,
but~breaking~the~lines~to~have~a~maximum~of~_width_~column~position~
(or~bytes,~if~the~*-b*~option~is~specified).~~Use~*-s*~option~to~split
at~latest~space~before~maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each~paragraph~from
its~input~ files,~but~break~the~lines~to~have~a~maximum~of~_width_
column~position.

$ fmt -w 80 wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~its~input~files,
but~breaking~the~lines~to~have~a~maximum~of~_width_~column~position~(or~bytes,
if~the~*-b*~option~is~specified).~~Use~*-s*~option~to~split~at~latest~space
before~maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each~paragraph~from~its
input~files,~but~break~the~lines~to~have~a~maximum~of~_width_~column~position.

$ fmt -w 60 wrap.txt | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from
its~input~files,~but~breaking~the~lines~to~have~a~maximum
of~_width_~column~position~(or~bytes,~if~the~*-b*~option
is~specified).~~Use~*-s*~option~to~split~at~latest~space
before~maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each
paragraph~from~its~input~files,~but~break~the~lines~to
have~a~maximum~of~_width_~column~position.

Il met en œuvre la notion de mots comme suite de caractères séparés par des blancs, et reconnait la notion de paragraphe comme étant séparés par une ou plusieurs lignes vides (comme « ex/vi » hé hé.) Du coup, il peut appliquer un retrait à la première ligne du paragraphe (option « -p » de l’implémentation BSD) …ou carrément l’inverse (option « -t » de l’implémentation GNU.) Eh oui, la typographie n’a pas attendu les traitements de texte… (voir ci et ou par exemple.)
On notera qu’il connait aussi la notion de phrase comme étant terminées par une ponctuation de fin (point… final, d’interrogation, d’exclamation, en fait comme dans « ex/vi » hé hé) et peut alors appliquer la règle typographique anglophone d’espacement (datant de l’époque des machines à écrire et devant faciliter la lecture) : une seule espace entre les mots et deux espaces entre les phrases. (BSD « -s » ou GNU « -u ») Noter que la commande « J » de « ed/ex/vi » procède normalement ainsi…
D’autres implémentations peuvent offrir de centrer le texte sur la ligne ou d’autres options sympathiques, comme de conserver les lignes dans les paragraphes (GNU « -s »)

OpenBSD/FreeBSD GNU/Linux Sun Solaris Plan 9 IBM AIX Minix
-w Ⅰ -w Ⅰ -w Ⅰ -w Ⅰ -w Ⅰ -w Ⅰ use Width output (default goal is around 93% of width)
-Ⅰ -Ⅰ -Ⅰ -l Ⅰ -Ⅰ -Ⅰ use width output
-g Ⅰ goal is (and max width is Ⅰ+10 unless specified)
Ⅰ Ⅱ -g Ⅰ -w Ⅱ goal is and max width is
-c Center
-d Ⅹ sentence ending Delimiters are
-l Ⅰ replace spaces with tabs at beginning of line
-m try to format Mail headers too
-n disable Nroff compatibility so not skip lines starting with '.'
-p indent Paragraph first line
-t Tagged paragraph (indent lines but first)
-s -u Uniformize white spaces by Single one/two between words/sentences
-t Ⅰ replace Tabs with spaces instead of 1
-p Ⅹ reformat only lines beginning with Prefix
-c -c -c preserve indentation of first two lines = try to be smarter about Crown margin
-s -s -j -s Split/fold only long lines but do not refill (i.e. Join short lines)
-i Ⅰ Indent each line with spaces
-i Ⅹ allow the Indentation text to include any character from (quote the chars list to protect it from the shell), in addition to spaces and tabs
-C is short for -c -i'/*' and is useful for reformatting C/C++ Comments
-M is short for -c -i'>' and is useful for reformatting eMail Messages

Ceci dit, il ne faut se limiter qu’à une option, « -w… » (voire même juste « -… » puisque d’anciennes implémentation AIX ne connaissent que ce dernier mais que cette dernière forme n’est pas reconnue sur Plan 9…)
Pour le travail de base, « fmt » est quasiment équivalent à « fold -s ». Ainsi, « fold » reste intéressant quand il faut respecter les sauts de lignes, tandis que « fmt » va formater un paragraphe en essayant d’avoir un texte homogène…

Petites précisions concernant la largeur par défaut

La largeur par défaut n’est pas la même selon les implémentations (et ce n’est pas une commande POSIX), mais est toujours en accord avec la nétiquette et les RFC du courriel

  • 75 pour BSD

    with lines as close to the goal length as possible without exceeding the maximum. The goal length defaults to 65 and the maximum to 10 more than the goal length.

  • 75 pour GNU

    maximum line width (default of 75 columns)

  • 72 pour AIX et Minix

    Specifies the line length. The default value for Width is 72 characters.

  • 70 pour Plan9

    Output line length is n, including indent (default 70).

les bornes limites

Il y a, dans les implémentations BSD et GNU, deux limites…

  • le maximum est le mur infranchissable… (dans l’analogie de la gestion des quotas c’est l’équivalent du « hard limit ».)
  • le goal (but ou objectif en français) est la limite désirée, désirable mais légèrement dépassable… (donc l’équivalent du « soft limit » dans l’analogie avec la gestion des quotas.)

Dans ces implémentations, trois cas peuvent donc se produire :

  • Quand on indique max seul, avec « -w », le programme doit en déduire le goal
    • semble que BSD utilise la même valeur,
    • tandis que GNU calcule que ça fait 93%.
  • Quand on indique goal seul, le programme doit en déduire le max
    • BSD fait plus dix caractères, et
    • GNU fait plus 7% (sept pour cent.)
  • Quand on spécifie les deux, le programme va essayer de couper un peu après (ou juste à) goal et toujours avant max !
$ # ceci est une implémentation BSD
$ fmt -v
fmt: illegal option -- v
usage:   fmt [-cmps] [-d chars] [-l num] [-t num]
             [-w width | -width | goal [maximum]] [file ...]
Options: -c     center each line instead of formatting
         -d <chars> double-space after <chars> at line end
         -l <n> turn each <n> spaces at start of line into a tab
         -m     try to make sure mail header lines stay separate
         -n     format lines beginning with a dot
         -p     allow indented paragraphs
         -s     coalesce whitespace inside lines
         -t <n> have tabs every <n> columns
         -w <n> set maximum width to <n>
         goal   set target width to goal
$ # mon fichier de test
$ wc lorem
       1      69     446 lorem
$ ## soft 52 (soit max vers 62?)
$ # BSD: fmt 52    lorem
$ # GNU: fmt -g 52 lorem
$ #        1         2         3         4         5 G       6 m
$ #456789012345678901234567890123456789012345678901234567890123456789
$ fmt 52 lorem
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
$ ## soft 47 (soit max vers 57?)
$ #        1         2         3         4      G  5      m  6
$ #456789012345678901234567890123456789012345678901234567890123456789
$ fmt 47 lorem
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi
ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim
id est laborum.
$ ## hard 65 (soit but vers 55?)
$ # BSD: fmt -w 65 lorem
$ # GNU: fmt -w 65 lorem
4 #      1         2         3         4         5    g    6    M
$ #456789012345678901234567890123456789012345678901234567890123456789
$ fmt -w 65 lorem
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
$ ## hard 57 (soit but vers 47?)
$ #      1         2         3         4      g  5      M  6
$ #456789012345678901234567890123456789012345678901234567890123456789
$ fmt -w 57 lorem
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est
laborum.
$ ## intervalle 48--52
$ # BSD: fmt 48 52 lorem
$ # GNU: fmt -w 52 -g 48 lorem
$ # GNU: fmt -g 48 -w 52 lorem
$ #      1         2         3         4       G 5 M       6
$ #456789012345678901234567890123456789012345678901234567890123456789
$ fmt 48 52 lorem
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa
qui officia deserunt mollit anim id est laborum.
$ ## intervalle 47--54
$ #      1         2         3         4      G  5   M     6
$ #456789012345678901234567890123456789012345678901234567890123456789
$ fmt 47 54 lorem
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi
ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim
id est laborum.

expansion au taquet

Pour les tabulations, il existe des filtres POSIX qu’il vaut mieux utiliser en amont ; ça rend les scripts plus portables.

BSD fmt alternative POSIX
-t Ⅰ expand -t Ⅰ
-l Ⅰ unexpand -t Ⅰ

Les filtres dédiés permettent d’ailleurs de faire beaucoup plus…

$ cat -t taquets.txt
Fournitures^IQuantité^IPrix
Crayons^I12^I15,76
Feuilles A4^I1000^I137,5
Correcteurs^I10^I18

$ # par défaut on positionne en colonne modulo 8...
$ echo "1------.-1-------.-2-------.-3" ; cat taquets.txt
1------.-1-------.-2-------.-3-------.-
Fournitures     Quantité        Prix
Crayons 12      15,76
Feuilles A4     1000    137,5
Correcteurs     10      18
$ echo "1------.-1-------.-2-------.-3" ; cut -f 1-3 taquets.txt
1------.-1-------.-2-------.-3-------.-
Fournitures     Quantité        Prix
Crayons 12      15,76
Feuilles A4     1000    137,5
Correcteurs     10      18

$ # cas par défaut de la commande qui remplace TAB par 8
$ echo "1------.-1-------.-2-------.-3" ; expand taquets.txt
1------.-1-------.-2-------.-3-------.-
Fournitures     Quantité        Prix
Crayons 12      15,76
Feuilles A4     1000    137,5
Correcteurs     10      18

$ # mais on peut spécifier une autre largeur de TAB comme 7
$ echo "1------.-1-------.-2-------.-3" ; expand -t 7 taquets.txt
1------.-1-------.-2-------.-3-------.-
Fournitures   Quantité     Prix
Crayons       12      15,76
Feuilles A4   1000    137,5
Correcteurs   10      18

$ # On peut mettre des Taquets par exemple en colonnes 17 et 33
$ echo "1------.-1-------.-2-------.-3" ; expand -t 17,33 taquets.txt
1------.-1-------.-2-------.-3-------.-
Fournitures      Quantité      Prix
Crayons          12             15,76
Feuilles A4      1000           137,5
Correcteurs      10             18

$ # On peut mettre des Taquets par exemple en colonnes 13 et 23
$ echo "1------.-1-------.-2-------.-3" ; expand -t 13,23 taquets.txt
1------.-1-------.-2-------.-3-------.-
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # On peut redefinir les taquets pour le terminal
$ # (profite à tous les programmes)
$ tabs 13,23 ; echo "1------.-1-------.-2-------.-3" ; cat taquets.txt ; tabs -8
1------.-1-------.-2-------.-3-------.-
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

On savait déjà faire tout cela en ligne de commande dans ces années là (pour la petite histoire, « expand » est apparue avec 1BSD en mars 1978, et « tabs » est apparu dans PWB/UNIX publié en juillet 1977.) Encore une fois, pas eu besoin d’attendre la machinerie moderne (voir ici et ou par exemple.)

$ # attention a bien les enchaîner....
$ fold -w 60 taquets.txt
Fournitures     Quantité        Prix
Crayons 12      15,76
Feuilles A4     1000    137,5
Correcteurs     10      18
$ fmt -w60 taquets.txt
Fournitures     Quantité        Prix Crayons 12      15,76
Feuilles A4     1000    137,5 Correcteurs     10      18
$ fmt -s -w 60 taquets.txt
Fournitures     Quantité        Prix
Crayons 12      15,76
Feuilles A4     1000    137,5
Correcteurs     10      18
$ expand -t 13,23 taquets.txt | fmt -s -w 60
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

Pour la route, si on n’est pas préoccupé par la portabilité, il y a d’autres alternatives à « expand » :

paragraphes au large

Il semble qu’il y a matière à améliorations, surtout dans le cadre du courriel, ce qui a conduit Adam M. Costello à produire « par » en 1993 (juste l’abréviation de « PARagraph » je crois, mais c’aurait pu être le sigle de « Paragraphs Alternative Reformater » aussi.) Je ne maitrise pas sa syntaxe peu évidente ; mais il sait traiter :

  • les mails (option « q » ajustable avec « Q… » et combinable avec « i » et « e »)
  • les marges (options « p » et « s » pouvant être combinées avec « t » ou « f »)
  • les imbrications de commentaires et messages cités (options « d »)
  • la préservation des espaces initiaux (option « r »)
  • les justification (option « j ») et la dernière ligne (option « l »)
  • le remplissage quasi complet (option « f ») sans justification
  • l’expansion de la tabulation (option « T… »)
  • et le retrait des lignes superflues (option « e »)
  • et d’autres choses (options « c », « h », « g », par exemple)

…le tout en gérant l’Unicode (en UTF-8) ! La largeur est aussi indiquée avec « -w … » (on peut utiliser juste « w… » ou « … ») et est par défaut 72

$ # attention par n'opère que comme filtre...
$ par -w 60 wrap.txt
par error:
bad argument: wrap.txt

options for par:

help       print option summary        ---------- Boolean parameters. ---------
version    print version number        b<body>   let non-trailing body chars in
B<op><set> as <op> is =/+/-                      prefix, non-leading in suffix
           replace/augment/diminish    c<cap>    count all words as capitalized
           body chars by <set>         d<div>    use indentation as a delimiter
P<op><set> ditto for protective chars  E<Err>    send messages to stderr
Q<op><set> ditto for quote chars       e<expel>  discard superfious lines
-------- Integer parameters: --------  f<fit>    narrow paragraph for best fit
h<hang>    skip IP's 1st <hang> lines  g<guess>  preserve wide sentence breaks
           in scan for common affixes  i<invis>  hide lines inserted by <quote>
p<prefix>  prefix length               j<just>   justify paragraphs
r<repeat>  if not 0, force bodiless    l<last>   treat last lines like others
           lines to length <width>     q<quote>  supply vacant lines between
s<suffix>  suffix length                         different quote nesting levels
T<tab>     tab stops every <tab> cols  R<report> print error for too-long words
w<width>   max output line length      t<touch>  move suffixes left

See par.doc or par.1 (the man page) for more information.

$ # ...il lit l'entrée standard et écrit sur la sortie standard
$ echo "_par_ is a filter which reformates each paragraph from its input
to its output" | par
_par_ is a which reformates each paragraph from its input to its
output

$ # donc envoyer d'abord le fichier sur l'entrée standard...
$ cat wrap.txt | par -w 60 | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from~~.
its~input~files,~but~breaking~the~lines~to~have~a~maximum~~.
of~_width_~column~position~(or~bytes,~if~the~*-b*~option~is.
specified)~Use~*-s*~option~to~split~at~latest~space~before~.
maximum~_width_~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each
paragraph~from~its~input~files,~but~break~the~lines~to~have
a~maximum~of~_width_~column~position.

$ # les points systématiques à la fin de ligne sont reproduits
$ # ...donc lui signaler qu'il n'y a pas de délimiteur de fin
$ cat wrap.txt | par -w 60 -s 0 | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from
its~input~files,~but~breaking~the~lines~to~have~a~maximum
of~_width_~column~position~(or~bytes,~if~the~*-b*~option~is
specified)~Use~*-s*~option~to~split~at~latest~space~before
maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each
paragraph~from~its~input~files,~but~break~the~lines~to~have
a~maximum~of~_width_~column~position.

$ # ...ou ajouter le point dans la liste des caractères normaux
$ cat wrap.txt | par -w 60 -B+. | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from
its~input~files,~but~breaking~the~lines~to~have~a~maximum
of~_width_~column~position~(or~bytes,~if~the~*-b*~option~is
specified)~Use~*-s*~option~to~split~at~latest~space~before
maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each
paragraph~from~its~input~files,~but~break~the~lines~to~have
a~maximum~of~_width_~column~position.

$ # essayons d'avoir des lignes plus équilibrées, comme fmt...
$ cat wrap.txt | par -w60fs0 | tr ' ' '~'
The~_fold_~utility~is~a~filter~that~shall~fold~lines~from
its~input~files,~but~breaking~the~lines~to~have~a~maximum
of~_width_~column~position~(or~bytes,~if~the~*-b*~option~is
specified)~Use~*-s*~option~to~split~at~latest~space~before
maximum~_width_.

The~_fmt_~utility~is~a~filter~that~shall~reformat~each
paragraph~from~its~input~files,~but~break~the~lines~to
have~a~maximum~of~_width_~column~position.

$ # on peut carrément vouloir justifier les lignes...
$ cat wrap.txt | par -w60j | tr ' ' '~'
The~_fold_~utility~~is~a~filter~that~shall~~fold~lines~from.
its~input~files,~~but~breaking~the~lines~to~~have~a~maximum.
of~_width_~column~position~(or~bytes,~if~the~*-b*~option~is.
specified)~Use~*-s*~option~to~~split~at~latest~space~before.
maximum~_width_~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.

The~~_fmt_~utility~~is~~a~filter~~that~~shall~reformat~~each
paragraph~from~its~input~files,~~but~break~the~lines~to have
a~maximum~of~_width_~column~position.

$ # ...sans oublier de combiner le reste...
$ cat wrap.txt | par 60js0 | tr ' ' '~'
The~_fold_~utility~~is~a~filter~~that~shall~~fold~lines~from
its~input~files,~but~breaking~the~lines~to~have~a~maximum~of
_width_~column~~position~(or~~bytes,~if~the~~*-b*~~option~is
specified).~Use~*-s*~option~to~~split~at~latest~space~before
maximum~_width_.

The~~_fmt_~utility~~is~~a~filter~~that~~shall~reformat~~each
paragraph~from~its~input~files,~~but~break~the~lines~to have
a~maximum~of~_width_~column~position.

par est aussi bien pour reformater des mails (format texte) ! Mais dans beaucoup de cas, il est assez compliqué à mon avis… À réserver donc aux rares cas où les autres ne satisfont pas.

$ # comme "fmt" mais les tabulations deviennent une espace...
$ cat taquets.txt | par -w 60
Fournitures Quantité Prix Crayons 12 15,76 Feuilles A4 1000
137,5 Correcteurs 10 18

$ # je ne suis pas parvenu à utiliser cette option
$ cat taquets.txt | par -w 60 -T 8
Fournitures Quantité Prix 12 5,76 A4 137,5 urs 10 8

$ # on peut au moins arriver à préserver les sauts de ligne...
$ par -w 60 -d  < taquets.txt
Fournitures Quantité Prix
Crayons 12 15,76
Feuilles A4 1000 137,5
Correcteurs 10 18

$ # on peut demander de préserver les 23 premiers caractères...
$ expand -t 13,23 taquets.txt | par w60p23
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

Il a le mérite d’exister et d’être présent dans de plus en plus de distributions, sinon se porte facilement. À surveiller.

balle au centre et égalisation

Pour le centrage, il faut hélas recourir à « printf » (ou « perl » ou « sed » ou autre) puis boucler sur toutes les lignes (ce qui peut se faire avec « awk » qui est mon filtre favori dans ce cas ci…) Nota : « vim » a la commande « :center » qui fait bien cela ; ainsi que « :right » et « :left », et tous les trois peuvent s’abréger à 2 caractères et on peut indiquer ensuite la largeur voulue (80 sinon).

Concernant la pleine justification, c’est un processus plus couteux qui se fait en répartissant astucieusement des espaces entre les mots (chose que savent faire « emacs » et « nano » ; et sinon ça se scripte dans un langage un peu avancé.) Ce procédé est bien connu, mais le résultat n’est pas toujours des plus heureux et se retrouve facilement avec des fleuves qui sillonnent le texte…
Idéalement, pour faire de la justification, il faut certes ajouter des blancs ci et là, mais arriver à limiter le nombre de ceux-ci et jouer également avec la césure des mots (ou « hyphenation » en anglais, qui se fait avec le trait d’union…) dont les règles varient selon les langues. On peut par exemple s’appuyer sur un « dictionnaire » dédié. C’est d’ailleurs l’approche des logiciels pionniers : « Justify », puis « TJ-1 » et enfin « TJ-2 » en 1963, pour la machine PDP-1, ne traitent que l’anglais en utilisant un dictionnaire de césures… On le considère parfois comme le premier « processeur de texte » (ou « traitement de texte », buzzword du NY Times en 1971) en tant que machine dédiée (comme les « AES-90 et Micom-2000 de Stephen B. Dorsey » et la « série Vydec de Exxon ») et non logiciel (comme les « MultiMate, Electric Pencil, WordStar, Scripsit, etc. »)…

impressions standards

Voyons maintenant une dernière commande, apparue avec la première version officiellent distribuée aux universités vers 1971 mais déjà utilisé au Bell Labs (en fait il nait en août 1969 mais son état civile est enregistré pour le 1er janvier de 1970 ha-ha …et dès le début le système est utilisé en interne entre par les secrétaires.) Du nom de « pr », elle prépare la « mise en page » pour l’impression papier… (est-ce que son nom est l’abréviation de « print » ou le sigle de « print request » est une des questions qui me turlupinent…)
Cette mise en page va consister surtout en la « pagination » (i.e. le découpage en « page ») comme le font les éditeurs plein écran (« vi/emacs/nano/etc. ») et visualiseurs de texte (« pager/pg/more/less/most/etc. ») Sauf qu’eux raisonnent par rapport à l’écran tandis que « pr » raisonne par rapport à la page papier.

réglages de papier

Tout comme l’écran du terminal ou de la console a un certain nombre de lignes × colonnes (par exemple 25×80) la page aussi. Ces dimensions (longueur × largeur de papier, par défaut 66×72) peuvent être spécifiées respectivement avec les options « -l » (pour « page length ») et « -w » (pour « page Width »).
Mais pour le papier, il y aussi la notion de « marge interne » (nulle par défaut, et à ne pas confondre avec la marge technique ou le débord qui s’ajoute), qui est retiré de la largeur, avec l’option « -o » (pour « line Offset »)

page de base

Comme il s’agit de document à imprimer, « pr » rajoute un en-tête de 5 lignes (prises dans la longueur totale…) et y place au milieu (troisième ligne) : la date de génération (à gauche), le numéro de page (à droite), et le nom du fichier (au milieu) ou titre du document… Il prend également 5 lignes pour le pied de page
Ce n’est certainement pas parfait, mais moins prise de tête àmha.

$ # noter la belle entête faite toute seule
$ expand -t 13,23 taquets.txt | pr -w 50 | head


2017-10-30 13:27                           Page 1


Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # ajouter un décalage de 2 espaces à gauche
$ expand -t 13,23 taquets.txt | pr -o 2 -w 50 | head


  2017-10-30 13:33                         Page 1


  Fournitures  Quantité Prix
  Crayons      12        15,76
  Feuilles A4  1000      137,5
  Correcteurs  10        18

$ # pr mange 5 lignes en haut et autant en bas
$ expand -t 13,23 taquets.txt | pr -l 12 -w 50 | cat -n
    1
    2
    3 2017-10-30 13:43                           Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9
   10
   11
   12
   13
   14
   15
   15
   16 2017-10-30 13:43                           Page 2
   17
   18
   19 Correcteurs  10        18
   20
   21
   22
   23
   24

$ # pr mange 10 lignes et pagine automatiquement
$ expand -t 13,23 taquets.txt | pr -l 13 -w 50 | cat -n
    1
    2
    3 2017-10-30 13:43                           Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9
   10
   11
   12
   13
   14
   15
   15
   16 2017-10-30 13:43                           Page 2
   17
   18
   19 Correcteurs  10        18
   20
   21
   22
   23
   24
   25
   26

Le pied de page n’est pas utilisé et je trouve que c’est un gâchis de ces cinq lignes. Mais c’est très utile pour les sociétés, qui impriment sur du papier avec un certain nombre de mentions qui sont plus ou moins obligatoires dans cette zone… On en rencontre qui utilisent l’en-tête du document pour des informations autre que leur logo et leur nom.

paramétrages de page

L’option « -t » permet de désactiver les en-têtes et pieds de page ajoutés automatiquement, comme souvent requis sur les sorties papier. Noter que ceux-ci sont automatiquement désactivés quand le nombre de lignes n’est pas supérieur à dix !

Question : Est-ce que les commandes déjà évoquées sont utiles ici ?
Réponse : Oui, « -w » positionne le numéro de page sans adapter le texte, et la largeur minimale utilisée est de 24 (en dessous ce n’est pas pris en compte.)
L’implémentation GNU offre en plus l’option « -W » qui permet d’appliquer un effet « truncate »… Cette implémentation offre également l’option « -T » qui fait comme « -t » mais envoie le code de saut saut de page au lieu de plusieurs saut de ligne : c’est normalement plus rapide car l’imprimante éjecte la page au lieu de la dérouler ligne par ligne. Il est à noter que la norme propose « -F » et « -f » pour ce faire (donc « -T » a pour forme portable « -tF » simplement.)

$ # plus d'entête et pied de page avec "`-t`"
$ expand -t 13,23 taquets.txt | pr -t | head
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # plus d'entête et pied de page quand l<=10
$ expand -t 13,23 taquets.txt | pr -l 5
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # "-w" ne tronque pas (utile ici)
$ expand -t 13,23 taquets.txt | pr -w 26 | head


2017-10-30 13:27    Page 1


Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # "-w" ne s'applique pas au texte
$ echo "ceci est une ligne de 30 car." | pr -l 2 -w 20
ceci est une ligne de 30 car.
$ echo "ceci est une ligne de 30 car." | pr -l 2 -W 20
ceci est une ligne d
$ echo "ceci est une ligne de 30 car." | fold -s -w 20 | pr -l 2 -w 20
ceci est une ligne
de 30 car.

$ # "-w" utilise une valeur minimale de 24...
$ # (juste ce qu'il faut pour l'horodatage et la pagination)
$ expand -t 13,23 taquets.txt | pr -w 10 | head


2017-10-30 13:27  Page 1


Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # "-F" si on ne veut pas enregistrer dans un fichier
$ # mais envoyer directement à l'imprimante
$ expand -t 13,23 taquets.txt | pr -w 10 | wc
     66      17     196
$ expand -t 13,23 taquets.txt | pr -w 10 -F | wc
      9      17     140

On ne le voit pas dans les précédents exemples, mais dans l’entête il y a le nom du fichier centré. Du moins, par défaut, car on peut mettre un texte personnalisé en utilisant l’option « -h » (pour « personalized Header ».)
L’implémentation GNU propose en plus l’option « -D » pour personnaliser le champ de date par son propre texte… (on peut bien entendu utiliser « $(date +...) » pour changer le format de la date si on le désire.)

$ # préparons notre fichier...
$ expand -t 13,23 taquet.txt > formated1

$ # faisons notre impression
$ pr -w 40 -l 15 formated1 | head | cat -n
    1
    2
    3 2017-10-30 13:45    formated1    Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9 Correcteurs  10        18
   10

$ # changeons le titre tant qu'à faire
$ pr -w 40 -l 15 -h "liste d'achats" formated1 | head | cat -n
    1
    2
    3 2017-10-30 13:47  liste d'achats  Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9 Correcteurs  10        18
   10

$ # vérifions pour la largeur minimale...
$ pr -w 30 -l 15 -h "liste d'achats" formated1 | head | cat -n
    1
    2
    3 2017-10-30 13:47 liste d'achats Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9 Correcteurs  10        18
   10

$ # changeons le champ de date aussi...
$ pr -w 20 -l 15 -h "liste d'achats" -D "now" formated1 | head | cat -n
    1
    2
    3 now liste d'achats Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9 Correcteurs  10        18
   10

$ # gardons juste la date
$ # (obligatoire sinon utiliser cat au lieu de pr)
$ pr -w 20 -l 15 -h "" -D "" formated1 | head | cat -n
    1
    2
    3                    Page 1
    4
    5
    6 Fournitures  Quantité Prix
    7 Crayons      12        15,76
    8 Feuilles A4  1000      137,5
    9 Correcteurs  10        18
   10

On voit au passage, que la largeur minimale n’est pas 24 mais (10+1+5)+1+N+1+(4+1+1)=17+N+7(10+1+5)+1+N+1+(4+1+1) = 17+N+7 avec :

  • champ de gauche : 10+1+510+1+5 est le nombre de caractères pour la date (10) et l’horaire (5) conformément aux normes
  • champ de droite : 4+1+14+1+1 est le nombre de caractères pour le numéro de page (<10 sinon augmenter en conséquence)
  • champ du milieu : NN est le nombre de caractères composant le titre

On n’a certes pas le choix pour l’emplacement des différents éléments, mais cela correspond bien aux pratiques en vigueur que l’on bafoue avec les traitement de texte en s’improvisant typographe.

autres paramétrages

C’est beau n’est-ce pas ? Mais cette commande peut faire plus !
Dans notre exemple, il peut être intéressant de numéroter les lignes du tableau, et il sait le faire avec l’option « -n » ; pas besoin de passer par « nl » au préalable (mais ce filtre a ses petits plus…)
Par défaut, elle utilise une colonne de 5 caractères, mais on peut ajuster cela en accolant un entier à l’option. Le contenu est séparé du numéro par une tabulation, ce qui le fait commencer en huitième colonne. On peut aussi changer ce caractère par un autre accolé à l’option.
On peut combiner les deux mais dans un ordre précis !

$ # numéroter les lignes du tableau
$ pr -n -l 5 formated1
     1   Fournitures  Quantité Prix
     2   Crayons      12        15,76
     3   Feuilles A4  1000      137,5
     4   Correcteurs  10        18

$ # numéroter sur 3 colonnes
$ pr -n3 -l 5 formated1
   1     Fournitures  Quantité Prix
   2     Crayons      12        15,76
   3     Feuilles A4  1000      137,5
   4     Correcteurs  10        18

$ # il faut acoller l'argument...
$ pr -n 3 -l 5 formated1
pr: 3: No such file or directory
     1   Fournitures  Quantité Prix
     2   Crayons      12        15,76
     3   Feuilles A4  1000      137,5
     4   Correcteurs  10        18

$ # utiliser le point au lieu de la tabulation
$ pr -n. -l 5 formated1
       1.Fournitures  Quantité Prix
       2.Crayons      12        15,76
       3.Feuilles A4  1000      137,5
       4.Correcteurs  10        18

$ # utiliser l'espace au lieu de la tabulation
$ pr -n\   -l 5 formated1
    1 Fournitures  Quantité Prix
    2 Crayons      12        15,76
    3 Feuilles A4  1000      137,5
    4 Correcteurs  10        18

$ # toujours le point et sur deux colonnes
$ pr -n.2 -t -l 5 formated1
 1.Fournitures  Quantité Prix
 2.Crayons      12        15,76
 3.Feuilles A4  1000      137,5
 4.Correcteurs  10        18

$ # l'ordre importe ici...
$ pr -n3. -t -l 5 formated1
pr: `-n' extra characters or invalid number in argument: `3.'
Try `pr --help' for more information

Il peut, de la même façon convertir des espaces successifs en un seul caractère (par défaut la tabulation, comme « unexpand »), avec l’option « -i » et son corolaire avec (par défaut la tabulation en espaces, comme « expand ») avec l’option « -e ». Ces deux options s’utilisent comme « -n» : il y a des paramètres par défaut (espace/tabulation et huit) mais on peut indiquer son caractère et son nombre (de blanc ou espacement des taquets.)

$ # quand il y a plus de 2 espaces remplacer par 1 tab
$ # taquet en position muliple de 8 (8, 16, 24, 32, etc.)
$ echo "Quatre    Trois   Deux  Un Zero" | pr -l 1 -i | cat -t
Quatre^I    Trois^I Deux^IUn Zero

$ # quand il y a plus de 2 espaces remplacer par 1 tab
$ # taquet en position muliple de 2 (2, 4, 6, 8, 10, 12, etc.)
$ echo "Quatre    Trois   Deux  Un Zero" | pr -l 1 -i2 | cat -t
Quatre^I^ITrois^I^IDeux^IUn Zero

$ # quand il y a plus de 2 espaces remplacer par 1 tab
$ # taquet en position muliple de 12 (12, 24, 32, 64, etc.)
$ echo "Quatre    Trois   Deux  Un Zero" | pr -l 1 -i2 | cat -t
Quatre    Trois   Deux^IUn Zero

$ # quand il y a plus de 2 espaces remplacer par 1 point
$ # taquet en position muliple de 8 (8, 16, 24, 32, etc.)
$ echo "Quatre    Trois   Deux  Un Zero" | pr -l 1 -i. | cat -t
Quatre.   Trois.  Deux.Un Zero

$ # quand il y a plus de 2 espaces remplacer par 1 point
$ # taquet en position muliple de 6 (6, 12, 18, 24, etc.)
$ echo "Quatre    Trois   Deux  Un Zero" | pr -l 1 -i.6 | cat -t
Quatre    Trois.Deux.Un Zero

$ # chez moi, Ctrl+V puis Tab, sinon utiliser un fichier
$ echo "tab    tabtab        stop" | cat -t
tab^Itabtab^I^Istop

$ # remplacer les tabulations par 4 espaces
$ # en fait on positionne aux taquets multiples de 8...
$ echo "tab    tabtab        stop" | pr -l 1 -e | tr ' ' '~'
tab~~~~~tabtab~~~~~~~~stop

$ # remplacer les tabulations par 3 espaces
$ # en fait on positionne aux taquets multiples de 3...
$ echo "tab    tabtab        stop" | pr -l 1 -e3 | tr ' ' '~'
tab~~~tabtab~~~~~~stop

$ # remplacer les underscores par 3 espaces
$ # en fait on positionne aux taquets multiples de 3...
$ echo "tab_tabtab__stop" | pr -l 1 -e_3 | tr ' ' '~'
tab~~~tabtab~~~~~~stop

$ # remplacer les underscores par 4 espaces
$ # en fait on positionne aux taquets multiples de 8...
$ echo "tab_tabtab__stop" | pr -l 1 -e_ | tr ' ' '~'
tab~~~~~tabtab~~~~~~~~stop

Il y a peu de cas où cela sert vraiment, d’après mon expérience, mais la possibilité existe ; et je n’ai pas pu retrouver la cause historique.

impressions avancées

répartition en colonnes

Une des possibilités incontournables est le multi colonage ! Les lignes du texte qui est passé à la commande, sont divisées en autant de colonnes que demandées par l’option numérique (qui est donc « -1 » par défaut, et on utilisera « -2 » pour faire une double colonne et « -3 » pour une triple, etc.), puis ces colonnes sont juxtaposées horizontalement (comme avec « paste » ou « rs » par exemple.)
Le séparateur entre colonnes est l’espace, chose que l’on peut changer avec l’option « -s » auquel on accole le caractère voulu (mais contrairement au « -d » de « paste », on ne peut pas alterner plusieurs séparateurs.)
L’implémentation GNU propose l’option « -S » qui ici ne change rien… (tout comme « -W » ne change rien dans le cas présent) on y reviendra.

$ # GNU/Linux: quelle est la plus longue ligne?
$ # et on voit qu'une largeur de 30 convient
$ wc -L formated1
28 formated1

$ # cat formated1
$ pr -l 4 -w 30 formated1
Fournitures  Quantité Prix
Crayons      12        15,76
Feuilles A4  1000      137,5
Correcteurs  10        18

$ # sur 2 colonnes de 30/2
$ # lignes "Fourn..." et "Crayo..." d'une part
$ # lignes "Feuil..." et "Corre..." d'autre part
$ pr -2 -l 4 -w 30 formated1
Fournitures  Q Feuilles A4  1
Crayons             1 Correcteurs  1

$ # sur 2 colonnes de 60/2
$ # lignes "Fourn..." et "Crayo..." d'une part
$ # lignes "Feuil..." et "Corre..." d'autre part
$ pr -l 4 -w 60 -2 formated1
Fournitures  Quantité Prix     Feuilles A4  1000      137,5
Crayons             12        5,76   Correcteurs  10        18

$ # sur 2 colonnes de 60/2
$ # avec le point au lieu d'espace
$ pr -l 4 -2 -w 60 -s. formated1
Fournitures  Quantité Prix    .Feuilles A4  1000      137,5
Crayons             12        5,76  .Correcteurs  10        18

$ # attention à bien le coller...
$ pr -l 4 -2 -w 60 -s . formated1
pr: .: Is a directory

$ # sur 2 colonnes de 30/2
$ # avec rien au lieu d'espace
$ pr -l 4 -2 -w 30 -s formated1
Fournitures  QuFeuilles A4  1
Crayons             12Correcteurs  1

$ # sur 2 colonnes de 30/2
$ # avec point au lieu d'espace
$ # et en numérotant (on voit la répartition des lignes)
$ pr -l 4 -2 -w 30 -s. -n formated1
    1  Fourni.    3   Feuill
    2  Crayon.    4   12Corr

J’avoue que cet exemple n’est pas ce qu’il y a de mieux, mais ça montre toute la difficulté de la chose et la nécessité d’avoir une commande dédiée pour « mettre en page » avant d’imprimer. Prenons un autre exemple, qui va nous permettre de mettre en évidence l’option « -a » (change le sens de colonage) qui peut être utile dans certains rares cas :

$ # préparons notre fichier...
$ # pour être à 28 comme formated1
$ fold -s -w 28 wrap.txt > formated0

$ # imprimons en 2 colonnes de 60/2
$ # remplissage normal, en N par page: P1C1, P1C2, P2C1, P2C2
$ pr -2 -w 60 -l 15 -f formated0 | cat -nt
    1
    2
    3 2017-11-01 13:05             formated0             Page 1
    4
    5
    6 The _fold_ utility is a      column position (or bytes.
    7 filter that shall fold       if the *-b* option is
    8 lines from its input files,  espicified).
    9 but breaking the lines to    Use *-* option to split at
   10 have a maximum of _width_    latest space before maximum
   11 ^L
   12
   13 2017-11-01 13:05             formated0             Page 2
   14
   15
   16 _width_.                     input files, but break the
   17                              lines to have a maximum of
   18 The _fmt_ utility is a       _width_ column position.
   19 filter that shall reformat
   20 each paragraph from its
   21 ^L


$ # imprimons en 2 colonnes de 60/2
$ # remplissage alternatif, en Z par lignes successives...
$ pr -2 -w 60 -l 15 -f formated0 | cat -nt
    1
    2
    3 2017-11-01 13:05             formated0             Page 1
    4
    5
    6 The _fold_ utility is a      filter that shall fold
    7 lines from its input files,  but breaking the lines to
    8 have a maximum of _width_    column position (or bytes,
    9 if the *-b* option is        specified).
   10 Use *-s* option to split at  latest space before maximum
   11 ^L
   12
   13 2017-11-01 13:05             formated0             Page 2
   14
   15
   16 _width_.
   17 The _fmt_ utility is a       filter that shall reformat
   18 each paragraph from its      input files, but break the
   29 lines to have a maximum of   _width_ column position.
   20
   21 ^L

$ # imprimons en 2 colonnes de 60/2
$ # remplissage alternatif, en Z par lignes successives...
$ pr -2 -w 60 -l 12 -f formated1 | cat -nt
    1
    2
    3 2017-11-01 13:09             formated1             Page 1
    4
    5
    6 Fournitures Quantité Prix     Crayons     12      15,75
    7 Feuilles A4 10000     137,5  Correcteurs  10      18
    8 ^L

Je n’ai eu à utiliser ce truc qu’une seule fois, pour imprimer un index de noms (à raison d’un par ligne dans le fichier) triés (par « sort ».)
Il y a aussi l’option « -d », sans paramètre, que j’invite à tester.

sélection de pages

Il y une autre option un peu étrange qu’il peut être utile de connaître : « + » qui permet de commencer à partir d’une page donnée… L’implémentation GNU permet permet en plus d’indiquer de s’arrêter à une page donnée…
En plus de cela, cette implémentation propose une option « -N » pour réinitialiser le compteur quand on utilise l’option « -n » et c’est pas mal.

$ # par défaut, toutes les 4 pages
$ # (de la première à la dernière donc)
$ pr -w 30 -l 15 -f formated0 | cat -nt
    1
    2
    3 2017-10-30 13:54 formated0 Page 1
    4
    5
    6 The _fold_ utility is a
    7 filter that shall fold
    8 lines from its input files,
    9 but breaking the lines to
   10 have a maximum of _width_
   11 ^L
   12
   13 2017-10-30 13:54 formated0 Page 2
   14
   15
   16 column position (or bytes,
   17 if the *-b* option is
   18 specified).
   19 Use *-s* option to split at
   20 latest space before maximum
   21 ^L
   22
   23 2017-10-30 13:54 formated0 Page 3
   24
   25
   26 _width_.
   27
   28 The _fmt_ utility is a
   29 filter that shall reformat
   30 each paragraph from its
   31 ^L
   32
   33 2017-10-30 13:54 formated0 Page 4
   34
   35
   36 input files, but break the
   37 lines to have a maximum of
   38 _width column position.
   39
   40
   41 ^L

$ # possible seulement à partir de la 3ème...
$ # (de la 3ème à la dernière page donc)
$ pr -w 30 -l 15 -f +3 formated0 | cat -nt
    1
    2
    3 2017-10-30 13:54 formated0 Page 3
    4
    5
    6 _width_.
    7
    8 The _fmt_ utility is a
    9 filter that shall reformat
   10 each paragraph from its
   11 ^L
   12
   13 2017-10-30 13:54 formated0 Page 4
   14
   15
   16 input files, but break the
   17 lines to have a maximum of
   18 _width column position.
   19
   20
   21 ^L

$ # il n'y a pas de 5ème page...!
$ # (j'utilise l'astuce avec 999
$ # pour avoir le nombre de pages)
$ pr -w 40 -l 17 +5 formated0
pr: starting page number 5 exceeds page count 4

$ # GNU pr permet de la 2ème à la 3ème...
$ pr -w 30 -l 15 -f +2:3 formated0 | cat -n
    1
    2
    3 2017-10-30 13:54 formated0 Page 2
    4
    5
    6 column position (or bytes,
    7 if the *-b* option is
    8 specified).
    9 Use *-s* option to split at
   10 latest space before maximum
   11 ^L
   12
   13 2017-10-30 13:54 formated0 Page 3
   14
   15
   16 _width_.
   17
   18 The _fmt_ utility is a
   19 filter that shall reformat
   20 each paragraph from its
   21 ^L

$ # GNU pr permet juste la 3ème...
$ pr -w 30 -l 15 -f +3:3 formated0 | cat -nt
    1
    2
    3 2017-10-30 13:54 formated0 Page 3
    4
    5
    6 _width_.
    7
    8 The _fmt_ utility is a
    9 filter that shall reformat
   10 each paragraph from its
   11 ^L

$ # pour juste les 2 premières bien indiquer 1:2
$ pr -w 30 -l 15 -f +:2 formated0
pr: invalid + argument `:2'

$ # quand on numérote la page 4
$ pr -w 30 -l 15 -f +4 -n formated0 | cat -nt
    1
    2
    3 2017-10-30 13:54 formated0 Page 4
    4
    5
    6    16   input files, but break the
    7    17   lines to have a maximum of
    8    18   _width column position.
    9    19
   10    20
   11 ^L

$ # GNU nous permet de dire qu'on commence
$ # à la 4ème ligne (du second paragraphe)
$ pr -w 30 -l 15 -f +4 -n -N 4 formated0 | cat -nt
    1
    2
    3 2017-10-30 13:54 formated0 Page 4
    4
    5
    6     4   input files, but break the
    7     5   lines to have a maximum of
    8     6   _width column position.
    9     7
   10     8
   11 ^L

On remarque au passage (première ligne du texte ou sixième imprimée, à la troisième page) que « pr » ne connait pas la notion de paragraphes, ou en tout cas ne gère pas le difficile problème de veuvage et d’orphelinage des lignes (lignes creuses/isolées/désolidarisées.)

impression de livrets

Tout comme « fold » et « fmt », « pr » peut prendre plusieurs fichiers en entrée. Sans l’utilisation de l’option « -h », chaque fichier se voit indiqué car les fichiers sont parcourus « séquentiellement » de sorte que chacun commence sur une nouvelle page. Mieux, les fichiers suivants commencent toujours sur une page impaire (c’est pratique quand chaque fichier représente un chapitre de votre manuscrit par exemple) ce qui est « top » ! Par contre, il recommence la pagination (ce qui est pratique si on imprime des documents séparés mais pas génial si on imprime un livre éclaté en plusieurs fichiers) et peut produire des pages vides (l’algorithme est relativement simple et correspond à la plupart des cas courants.) En tout cas, tout cela est fait automatiquement…

$ # préparons notre fichier...
$ fold -w 40 -s wrap.txt > formated2

$ # faisons notre impression 1 colonne
$ # c'est séquentiel, normal.
$ pr -w 40 -l 17 formated2 formated1 | cat -nt
    1
    2
    3 2017-10-30 13:45    formated2    Page 1
    4
    5
    6 The _fold_ utility is a filter that
    7 shall fold lines from its input files,
    8 but breaking the lines to have a
    9 maximum of~ width ~column position (or
   10 bytes, if the *-b* option is specified)
   11 Use *-s* option to split at latest
   12 space before maximum _width_.
   13
   14
   15
   16
   17
   18
   19
   20 2017-10-30 13:45    formated2    Page 2
   21
   22
   23
   24 The _fmt_ utility is a filter that
   25 shall reformat each paragraph from its
   26 input files, but break the lines to
   27 have a maximum of _width_ column
   28 position.
   29
   30
   31
   32
   33
   34
   35
   36
   37 2017-10-30 13:45    formated2    Page 3
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54 2017-10-30 13:45    formated1    Page 1
   55
   56
   57 Fournitures  Quantité Prix
   58 Crayons      12        15,76
   59 Feuilles A4  1000      137,5
   60 Correcteurs  10        18
   61
   62
   63
   64
   65
   66
$ pr -w 40 -l 17 -f formated2 formated1 | cat -nt
    1
    2
    3 2017-10-30 13:47    formated2    Page 1
    4
    5
    6 The _fold_ utility is a filter that
    7 shall fold lines from its input files,
    8 but breaking the lines to have a
    9 maximum of~ width ~column position (or
   10 bytes, if the *-b* option is specified)
   11 Use *-s* option to split at latest
   12 space before maximum _width_.
   13 ^L
   14
   15 2017-10-30 13:47    formated2    Page 2
   16
   17
   18
   19 The _fmt_ utility is a filter that
   20 shall reformat each paragraph from its
   21 input files, but break the lines to
   22 have a maximum of _width_ column
   23 position.
   24
   25 ^L
   26
   27 2017-10-30 13:47    formated2    Page 3
   28
   29
   30
   31 ^L
   32
   33 2017-10-30 13:47    formated1    Page 1
   34
   35
   36 Fournitures  Quantité Prix
   37 Crayons      12        15,76
   38 Feuilles A4  1000      137,5
   39 Correcteurs  10        18
   40 ^L

$ # il n'y a pas de 4ème page...
$ pr -w 40 -l 17 +4 formated1 formated2
pr: starting page number 4 exceeds page count 1
pr: starting page number 4 exceeds page count 3

$ # faisons notre impression 2 colonnes
$ # c'est toujours séquentiel.
$ pr -w 60 -l 17 -2 formated0 formated1 | cat -nt

Il y a cependant une option sympathique quand on imprime plusieurs fichiers : « -m » qui va faire autant de colonnes que de fichiers, et c’est incompatible avec l’option de colonage.

$ # mettre côte à côte les 2 fichiers
$ pr -w 60 -l 15 -f -m formated0 formated1 | head
    1
    2
    3 2017-10-30 13:54                                    Page 1
    4
    5
    6 The _fold_ utility is a       Fournitures Quantité Prix
    7 filter that shall fold        Crayons     12        15,76
    8 lines from its input files,   Feuilles A4 10000     137,5
    9 but breaking the lines to     Correcteurs 10        18
   10 have a maximum of _width_

$ # mettre côte à côte les 2 fichiers
$ pr -w 60 -l 15 -f -m formated0 formated2 | cat -nt
    1
    2
    3 2017-10-30 13:55                                     Page 1
    4
    5
    6 The _fold_ utility is a       The _fold_ utility is a filte
    7 filter that shall fold        shall fold lines from its inp
    8 lines from its input files,   but breaking the lines to hav
    9 but breaking the lines to     maximum of _width_ column pos
   10 have a maximum of _width_     bytes, if the *-b* option is
   11 ^L
   12
   13 2017-10-30 13:55                                     Page 2
   14
   15
   16 column position (or bytes,    Use *-s* option to split at l
   17 if the *-b* option is         space before maximum _width_.
   18 specified).
   19 Use *-s* option to split at   The _fmt_ utility is a filter
   20 latest space before maximum   shall reformat each paragraph
   21 ^L
   22
   23 2017-10-30 13:55                                     Page 3
   24
   25
   26 _width_.                      input files, but break the li
   27                               have a maximum of _width_ col
   28 The _fmt_ utility is a        position.
   29 filter that shall reformat
   30 each paragraph from its
   31 ^L
   32
   33 2017-10-30 13:55                                     Page 4
   34
   35
   36 input files, but break the
   37 lines to have a maximum of
   38 _width column position.
   39
   40
   41 ^L

$ # mettre côte à côte les 3 fichiers
$ pr -w 70 -l 15 -m formated0 formated1 formated2 | head
    1
    2
    3 2017-10-30 13:54                                               Page 1
    4
    5
    6 The _fold_ utility is  Fournitures  Quantité P  The _fold_ utility is
    7 filter that shall fold Crayons      12          shall fold lines from
    8 lines from its input f Feuilles A4  10000       but breaking the lines
    9 but breaking the lines Correcteurs  10          maximum of _width_ col
   10 have a maximum of _wid                          bytes, if the *-b* opt

$ # ce n'est pas compatible avec le colonnage forcé...
$ pr -w 70 -l 15 -m -4 formated0 formated1 formated2
pr: cannot specify number of columns when printing in parallel

$ # ...pareil pour le format alternatif (Z au lieu de W)...
$ pr -w 70 -l 15 -m -a formated0 formated1 formated2
pr: cannot specify printing accross and printing in parallel

$ # ...mais le changement de séparateur fonctionne
$ pr -w 70 -l 15 -m -s\| formated? | head
    1
    2
    3 2017-10-30 13:56                                               Page 1
    4
    5
    6 The _fold_ utility is |Fournitures  Quantité P |The _fold_ utility is
    7 filter that shall fold|Crayons      12         |shall fold lines from
    8 lines from its input f|Feuilles A4  10000      |but breaking the lines
    9 but breaking the lines|Correcteurs  10         |maximum of _width_ col
   10 have a maximum of _wid|                        |bytes, if the *-b* opt

C’est une variante intéressante de « diff -y »…
Noter qu’aucun nom de fichier n’est privilégié et qu’il n’y a pas de « mix » particulier. Mais on peut utiliser « -h » pour ajouter son propre titre.

L’implémentation GNU propose aussi une option « -J » qui avec annule la troncature des lignes qui dépassent la largeur spécifiée par « -w ».

impression en chasse fixe

L’utilitaire « pr » permet diverses mises en pages plus ou moins avancées pour une impression brute/directe (i.e. sans passer par un pilote, l’imprimante pouvant gérer du pur texte et étant dans le mode qui va bien, on y reviendra.) Elle a pléthore d’options pour satisfaire la plupart des caprices. Elles n’ont pas toutes été évoquées ici, et j’invite à parcourir le « man page » associé dans la distribution que l’on utilise.

OpenBSD/FreeBSD GNU AIX POSIX
+Ⅰ +Ⅰ +Ⅰ +Ⅰ begin output at page number
+Ⅰ:Ⅱ output pages from number to
-Ⅰ -Ⅰ -Ⅰ -Ⅰ produce columns wide
-a -a -a -a fill columns Across the page in a round robin order
-c show Control chars
-d -d -d -d produce Double spaced output
-D Ⅹ use as header Date format
-eⅭⅠ -eⅭⅠ -eⅭⅠ -eⅭⅠ Expand each input (default tab) to n×Ⅰ+1 (default 8)
-F -F -F -F use Form feed char. for new pages, not multiple newline
-f -f -f -f same as -F plus pause on stdout
-h Ⅹ -h Ⅹ -h Ⅹ -h Ⅹ use instead of filename as centered Header
-iⅭⅠ -iⅭⅠ -iⅭⅠ -iⅭⅠ replace multiple blanks by char. (default tab) at n×Ⅰ+1 (default 8) in output
-J Join lines, i.e. merge full lines and turn off -W truncation
-l Ⅰ -l Ⅰ -l Ⅰ -l Ⅰ set page Length to lines
-m -m -m -m Merge contents of multiple files (lines side by side)
-nⅭⅠ -nⅭⅠ -nⅭⅠ -nⅭⅠ print line Number separated by char. (default tab) using width column (default 5)
-N Ⅰ first line Number printed with +Ⅰ
-o Ⅰ -o Ⅰ -o Ⅰ -o Ⅰ precede each line of output with space as Offset
-p -p Pause before each page on TTY
-r -r -r -r no diagnostic Reports on file open failure
-sⅭ -sⅭ -sⅭ -sⅭ Separate text columns with char. (default tab) instead of spaces, turn off truncation when no -s
-SⅭ Separate text columns with char, (default space with -J or tab), but no effect on columns
-t -t -t -t omit five+five lines of header+Trailers on each page
-T omit headers+Trailers and pagination by form feed in input
-w Ⅰ -w Ⅰ -w Ⅰ -w Ⅰ set the Width of the line to instead of 72 (when not -s) or 512 (when -s)
-W Ⅰ set page Width to instead of 72, truncate except -J but no interference with -s or -S

Avant de finir, revenons sur les largeurs par défaut :

  • 80 pour fold
  • 75/72/70 pour fmt
  • 72 pour par
  • 72 pour pr

Cela va faire bizarre aujourd’hui à ceux et celles qui n’ont pas connu les « polices à chasse fixe » — comme Consolas, Courier New, DejaVu Sans Mono, Droid Sans Mono, Inconsolata, Menlo, Lucida Console, Monaco, Terminal, etc.— et les fontes qui caractérisent les imprimantes à impacts :

Les limites (entre 70 et 80 caractères) sont historiques, adaptées aux consoles (plus exactement les moniteurs) standards comme le IBM 3270 et ses variantes et compatibles (80 caractères) ainsi que les téléscripteurs (72 caractères) même s’il y a eu rapidement des équipements pouvant atteindre 132 caractères… Il se trouve, que pour pour une petite fonte lisible (soit une taille de caractère standard de 10 pitch ou ~20 CPI —environ ⅙ pouce ou un pica—) on a bien 72 CPL …qui correspond au format standard de papier américain « letter de 8½×11 pouces ».
Cependant, certains suggèrent de faire moins (entre 50 et 70 CPL), ce qu’on obtient avec une plus grande fonte (soit une taille de caractère standard de 12 pitch ou ~17 CPI —alias un elite pour les IBM Selectric—).
Selectric iii balls
Les journaux ne s’y sont pas trompé en privilégiant le multi colonage (avec un optimum entre 40 et 50 par colonne…) !

Alors, à l’heure des polices proportionnelles (qui posent des soucis très différents et où il faut raisonner en « pixels » et non plus en caractères), est-ce que tout ceci est dépassé ? Pas vraiment. D’une part la chasses fixe peut s’utiliser avec le matériel récent réglé comme il se doit, d’autre part il y a encore pas mal de machines pro qui sont matricielles dont beaucoup utilisent de très petites largeurs de papier
Twitter receipt printer
(Parmi les principaux fabricants, on trouve Sharp, Casio, et bien d’autres. Ceci dit, comme toujours, il n’est pas obligatoire que l’imprimante et le clavier et le logiciel ne fassent qu’un —les uns et les autres aiment vendre des solutions tout-en-un mais dont on se retrouve prisonnier…) Ces dernières sont souvent des imprimantes thermiques (nombreux direct ou à transfert et quelques uns à sublimation) ou à diodes, connectable par USB ou sans fil, fonctionnant sur secteur, etc.

J’ai eu pendant quelques temps la HP 82240B qui est une imprimante de poche connectée par IR que je n’ai pu utiliser (en dehors de ma calculatrice) qu’avec Linux ! Comme le rappelle Sylvain cote sur HPMuseum, elle a 2 jeux de caractères et 3 fontes (6 et 7 points de haut ainsi qu’une moins connue de 5 points) permettant d’avoir 24 et 32 CPL ainsi que 48 !
Mis à part le packaging vraiment bien fichu et l’électronique très bien maîtrisée par l’équipe de Corvallis, on peut faire la même chose avec d’autres composants ou en faire d’autres usages (suffit d’avoir un adaptateur IR…)
hp-82240b
Sur cette note de nostalgie, j’arrête ce qui n’est plus loin de devenir un roman. À bientôt peut-être pour la suite de cette exploration ; et si des points t’ont interpelé-e ou parus peu clairs, n’hésite pas : ça se passe en commentaires.


Aucun commentaire

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte