Considérez ce script:
tmpfile=$(mktemp)
cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS
cat <(tail -1 "$tmpfile") "$tmpfile"
Cela fonctionne et génère:
line 3
line 1
line 2
line 3
Disons que notre source d'entrée, plutôt que d'être un fichier réel, était plutôt stdin:
cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS
Comment modifions-nous la commande:
cat <(tail -1 "$tmpfile") "$tmpfile"
Alors qu'il produit toujours la même sortie, dans ce contexte différent?
REMARQUE: Le Heredoc spécifique que je raconte, ainsi que l'utilisation d'un Heredoc lui-même, sont simplement illustratifs. Toute réponse acceptable doit supposer qu'elle reçoit des données arbitraires via stdin .
Réponses:
Essayer:
Exemple
Définissez une variable avec notre entrée:
Exécutez notre commande:
Alternativement, bien sûr, nous pourrions utiliser un ici-doc:
Comment ça fonctionne
x=x $0 ORS
Cela ajoute chaque ligne d'entrée à la variable
x
.Dans awk,
ORS
est le séparateur d'enregistrement de sortie . Par défaut, il s'agit d'un caractère de nouvelle ligne.END{printf "%s", $0 ORS x}
Après la que nous avons lu dans le fichier entier, ce imprime la dernière ligne,
$0
, suivi par le contenu du fichier entier,x
.Puisqu'il lit l'intégralité de l'entrée en mémoire, il ne conviendrait pas pour les entrées volumineuses ( par exemple, gigaoctets).
la source
tee
, mais d'un stdin et d'un fichier, nous serions en train de canaliser le même stdin en deux substitutions de processus différentes. ou quelque chose qui serait à peu près équivalent à cela?Si stdin pointe vers un fichier pouvant être recherché (comme dans le cas des documents bash (mais pas tous les autres shell) ici qui sont implémentés avec des fichiers temporaires), vous pouvez obtenir la queue puis rechercher avant de lire le contenu complet:
les opérateurs de recherche sont disponibles dans les shells
zsh
ouksh93
, ou dans des langages de script comme tcl / perl / python, mais pas dansbash
. Mais vous pouvez toujours appeler ces interprètes plus avancésbash
si vous devez utiliserbash
.Ou
Maintenant, cela ne fonctionnera pas lorsque stdin pointe vers des fichiers non recherchables comme un tuyau ou une socket. Ensuite, la seule option est de lire et de stocker (en mémoire ou dans un fichier temporaire ...) l'ensemble de l'entrée.
Certaines solutions de stockage en mémoire ont déjà été proposées.
Avec un fichier temporaire, avec
zsh
, vous pouvez le faire avec:Si sous Linux, avec
bash
ouzsh
ou n'importe quel shell qui utilise des fichiers temporaires pour les documents ici, vous pouvez réellement utiliser le fichier temporaire créé par un document ici pour stocker la sortie:la source
Le problème avec la traduction de ceci en quelque chose qui utilise
tail
est qu'iltail
faut lire le fichier entier pour en trouver la fin. Pour utiliser cela dans votre pipeline, vous deveztail
.cat
.L'astuce n'est pas de dupliquer le contenu du document (
tee
fait cela) mais de faire en sorte que la sortie detail
se produise avant la sortie du reste du document, sans utiliser de fichier temporaire intermédiaire.L'utilisation
sed
(ouawk
, comme le fait John1024 ) supprime la double analyse des données et le problème de commande en stockant les données en mémoire.La
sed
solution que je propose est de1{h;d;}
, stockez la première ligne dans l'espace d'attente, telle quelle, et passez à la ligne suivante.H
, ajoutez-vous une ligne à l'espace d'attente avec une nouvelle ligne incorporée.${G;p;}
, ajoutez l'espace d'attente à la dernière ligne avec une nouvelle ligne incorporée et imprimez les données résultantes.Il s'agit d'une traduction assez littérale de la solution de John1024
sed
, avec la mise en garde que la norme POSIX garantit uniquement que l'espace de blocage est d'au moins 8192 octets (8 Ko), mais il recommande que ce tampon soit alloué dynamiquement et étendu selon les besoins, ce que GNU tous les deuxsed
et BSDsed
fait).Si vous vous autorisez à utiliser un canal nommé:
Cela permet
tee
d'envoyer les données vers le basmypipe
et en même temps verscat
. L'cat
utilitaire lira d'abord la sortie detail
(qui litmypipe
, quitee
écrit), puis ajoutera la copie du document provenant directement detee
.Il y a cependant un sérieux défaut, dans le cas où si le document est trop volumineux (plus grand que la taille de la mémoire tampon du tube), il
tee
écritmypipe
etcat
se bloquerait en attendant que le tube (sans nom) se vide. Il ne serait pas vidé avant d'êtrecat
lu.cat
ne le lirait pas avant d'tail
avoir terminé. Ettail
ne finirait pas avant d'tee
avoir fini. Il s'agit d'une situation de blocage classique.La variation
a le même problème.
la source
sed
ci ne fonctionne pas si l'entrée n'a qu'une seule ligne (peut-êtresed '1h;1!H;$!d;G'
). Notez également que plusieurssed
implémentations ont une limite basse sur la taille de leur modèle et de l'espace de stockage.Il existe un outil nommé
pee
dans une collection d'utilitaires de ligne de commande généralement fournis avec le nom "moreutils" (ou autrement récupérable sur son site Web ).Si vous pouvez l'avoir sur votre système, l'équivalent pour votre exemple serait comme:
L'ordre des commandes exécutées
pee
est important car elles sont exécutées dans l'ordre indiqué.la source
Essayer:
Étant donné que le tout est constitué de données littérales (un "document ici"), et que la différence entre celles-ci et la sortie souhaitée est triviale, il suffit de masser ces données littérales juste là pour correspondre à la sortie.
Supposons maintenant que cela
line 3
vienne de quelque part et soit stocké dans une variable appeléelastline
:Dans un doc ici, nous pouvons générer du texte en substituant des variables. Non seulement cela, mais nous pouvons calculer du texte en utilisant la substitution de commandes:
On peut interpoler plusieurs lignes:
En général, évitez de traiter le texte du modèle de doc ici; essayez de le générer en utilisant du code interpolé.
la source
cat <<EOS...
le PO n'était qu'un exemple de standin pour "attraper un fichier arbitraire", pour rendre le post spécifique et la question claire. N'était-ce pas vraiment évident pour vous, ou pensiez-vous simplement qu'il serait intelligent d'interpréter la question littéralement?Si vous ne vous souciez pas de la commande. Ensuite, cela fonctionnera
cat lines | tee >(tail -1)
. Comme d'autres l'ont dit. Vous devez lire le fichier deux fois, ou mettre en mémoire tampon le fichier entier, pour le faire dans l'ordre que vous avez demandé.la source