web-dev-qa-db-fra.com

Obtenir la tête 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 dont j'avais besoin correspond à cat myfile.txt | head -n $(($(wc -l)-1)). Mais ça n'a pas marché. Je le fais sur Darwin/OS X qui n’a pas la sémantique Nice de head -n -1 qui m’aurait eu 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é plusieurs variantes et en particulier j'ai trouvé que cela fonctionnait:

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 nombre de lignes illégale. Mais il me dit au moins que le programme head ne consomme pas l’entrée standard avant de transmettre des éléments au sous-shell/substitution de commande, une possibilité lointaine, mais que je voulais tout de même exclure.

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

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

32
gkb0986

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')

Échec est que wc utilise toutes les entrées et qu'il ne reste plus rien à voir pour head. wc hérite de son stdin du sous-shell dans lequel il s'exécute, qui lit le résultat de la echo. Une fois qu'il a consommé l'entrée, il retourne et head essaie de lire les données ... mais tout est parti. Si vous voulez lire l'entrée deux fois, les données devront être enregistrées quelque part.

39
William Pursell

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

55
dunc123

Utilisation de sed:

sed '$d' filename

supprimera la dernière ligne du fichier.

$ seq 1 10 | sed '$d' 
1
2
3
4
5
6
7
8
9
10
devnull

Pour Mac OS X en particulier, j'ai trouvé une réponse d'un commentaire à ce Q & R .

En supposant que vous utilisez Homebrew, exécutez brew install coreutils puis utilisez la commande ghead:

cat myfile.txt | ghead -n -1

Ou équivalent:

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).

6
Jimothy
cat myfile.txt | echo $(($(wc -l)-1))

Cela marche. 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 utilise toutes les entrées car il compte les lignes. Il ne reste donc aucune donnée à lire dans le tuyau au moment où head est lancé.

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

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

Si vos données peuvent provenir d'un canal, vous devez les dupliquer. L'outil habituel pour dupliquer un flux est tee, mais ce n'est pas suffisant ici, car les deux sorties de tee sont produites au même taux, alors qu'ici wc doit consommer pleinement sa sortie avant que head ne puisse commencer. Au lieu de cela, vous devrez donc utiliser un seul outil capable de détecter la dernière ligne, ce qui est tout de même une approche plus efficace.

Sed offre un moyen pratique 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 fonctionnera:

sed -n '$! p'
sed '$ d'
4
Gilles
awk 'NR>1{print p}{p=$0}'

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

0
Kent