Obtenir head pour afficher tout sauf la dernière ligne d'un fichier: substitution de commande et redirection d'E/S standard

J'ai essayé d'obtenir l'utilitaire head pour afficher tout sauf la dernière ligne d'entrée standard. Le code réel dont j'avais besoin est quelque chose du type cat myfile.txt | head -n $(($(wc -l)-1)). Mais cela ne fonctionne pas. Je fais cela sur Darwin / OS X qui n'a pas la belle sémantique de head -n -1 qui m'aurait obtenu une sortie similaire.

Aucune de ces variations ne fonctionne non plus.

cat myfile.txt | head -n $(wc -l | sed -E -e 's/s//g')
echo "hello" | head -n $(wc -l | sed -E -e 's/s//g')

J'ai testé plus de variations et en particulier trouvé cela pour fonctionner:

cat <<EOF | echo $(($(wc -l)-1))
>Hola
>Raul
>Como Esta
>Bueno?
>EOF
3

Voici quelque chose de plus simple qui fonctionne aussi.

echo "hello world" | echo $(($(wc -w)+10))

Celui-ci me donne naturellement une erreur de comptage de ligne illégale. Mais il me dit au moins que le programme head ne consomme pas l'entrée standard avant de passer des choses à la substitution de sous-shell/commande, une possibilité à distance, mais que je voulais exclure de toute façon.

echo "hello" | head -n $(cat && echo 1)

Qu'est-ce qui explique le comportement de head et wc et leur interaction à travers des sous-coquilles ici? Merci pour votre aide.

25
demandé sur Gilles 2013-08-08 17:46:09

6 réponses

head est le mauvais outil. Si vous voulez voir tout sauf la dernière ligne, utilisez:

sed \$d

La raison pour laquelle

# Sample of incorrect code:
echo "hello" | head -n $(wc -l | sed -E -e 's/\s//g')

Échoue est que wc consomme toute l'entrée et il n'y a plus rien à voir pour head. wc hérite de son stdin du sous-shell dans lequel il est en cours d'exécution, qui est en lecture à partir de la sortie du echo. Une fois qu'il consomme l'entrée, il retourne puis head essaie de lire les données...mais il est tout a disparu. Si vous voulez lire l'entrée deux fois, les données devront être enregistré quelque part.

30
répondu William Pursell 2016-05-06 04:15:50

head -n -1 vous donnera tous sauf la dernière ligne de son entrée.

47
répondu dunc123 2013-08-08 13:50:14

En utilisant sed:

sed '$d' filename

Supprime la dernière ligne du fichier.

$ seq 1 10 | sed '$d' 
1
2
3
4
5
6
7
8
9
7
répondu devnull 2013-08-08 13:56:35

Pour Mac OS X spécifiquement, j'ai trouvé une réponse à partir d'un commentaire à cette question-Réponse .

En Supposant que vous utilisez Homebrew, exécutez brew install coreutils, alors utilisez ghead commande:

cat myfile.txt | ghead -n -1

Ou, de manière équivalente:

ghead -n -1 myfile.txt

Enfin, consultez brew info coreutils Si vous souhaitez utiliser les commandes sans le préfixe g (par exemple, head au lieu de ghead).

5
répondu Jimothy 2017-05-23 12:10:39
cat myfile.txt | echo $(($(wc -l)-1))

Cela fonctionne. C'est trop compliqué: vous pouvez simplement écrire echo $(($(wc -l)-1)) <myfile.txt ou echo $(($(wc -l <myfile.txt)-1)). Le problème est la façon dont vous l'utilisez.

cat myfile.txt | head -n $(wc -l | sed -E -e 's/\s//g')

wc consomme toute l'entrée car il compte les lignes. Il n'y a donc plus de données à lire dans le tuyau au moment où head est démarré.

Si votre entrée provient d'un fichier, vous pouvez rediriger à la fois wc et head à partir de ce fichier.

head -n $(($(wc -l <myfile.txt) - 1)) <myfile.txt

Si vos données peuvent provenir d'un tuyau, vous devez les dupliquer. L'outil habituel pour dupliquer un flux est tee, mais cela ne suffit pas ici, car les deux sorties de tee sont produites au même rythme, alors qu'ici wc doit consommer complètement sa sortie avant que head puisse démarrer. Donc, à la place, vous devrez utiliser un seul outil qui peut détecter la dernière ligne, ce qui est une approche plus efficace de toute façon.

Idéalement, sed offre un moyen de faire correspondre la dernière ligne. L'impression de toutes les lignes sauf la dernière, ou la suppression de la dernière ligne de sortie, fonctionnera:

sed -n '$! p'
sed '$ d'
4
répondu Gilles 2015-05-13 14:54:38
awk 'NR>1{print p}{p=$0}'

Pour ce travail, un awk one-liner est un peu plus long qu'un sed un.

0
répondu Kent 2013-08-08 14:16:43