Comment récupérer le premier mot de la sortie d'une commande en bash?

127

J'ai une commande, par exemple: echo "word1 word2". Je veux mettre un tube ( |) et obtenir mot1 de la commande.

echo "word1 word2" | ....

Je ne sais pas quoi mettre après la pipe.

Neuquino
la source

Réponses:

201

Awk est une bonne option si vous devez gérer des espaces blancs de fin, car il s'en chargera pour vous:

echo "   word1  word2 " | awk '{print $1;}' # Prints "word1"

Cut ne s'en chargera pas:

echo "  word1  word2 " | cut -f 1 -d " " # Prints nothing/whitespace

«couper» ici n'imprime rien / espace, car la première chose avant un espace était un autre espace.

mattbh
la source
Le point-virgule est-il nécessaire?
Alice Purcell
1
Il doit s'agir d'un espace blanc «de début» (au début de la chaîne) et non de «fin».
user202729
@AlicePurcell Je l'ai essayé sans; et ça a marché pour moi (MBP 10.14.2)
Samy Bencherif
1
Cela ne fonctionne pas si la chaîne est par exemple "firstWord, secondWord" car cette commande awk délimite par espace
Roger Oba
@RogerOba Ce n'était pas la question du PO, mais vous pouvez utiliser -F","pour remplacer le séparateur de champ par défaut (un espace) par une virgule.
pjd le
70

pas besoin d'utiliser des commandes externes. Bash lui-même peut faire le travail. En supposant que "mot1 mot2" vous avez obtenu de quelque part et stocké dans une variable, par exemple

$ string="word1 word2"
$ set -- $string
$ echo $1
word1
$ echo $2
word2

maintenant vous pouvez assigner $ 1, ou $ 2 etc à une autre variable si vous le souhaitez.

ghostdog74
la source
11
+1 pour utiliser uniquement les modules intégrés du shell et stdin. @Matt M. --signifie stdin, ainsi $stringest transmis comme stdin. stdinest des espaces séparés en arguments $1, $2, $3, etc. - comme quand un argument de Évalue du programme Bash (chèques $1, $2etc.), cette approche profite de la tendance à diviser la du shell stdindans les arguments automatiquement, supprimant la nécessité awkou cut.
Caleb Xu
3
@CalebXu Non stdin, setdéfinit les arguments du shell.
Guido
9
word1=$(IFS=" " ; set -- $string ; echo $1)Réglez IFS pour reconnaître correctement l'espace entre les mots. Mettez entre parenthèses pour éviter d'écraser le contenu original de 1 $.
Steve Pitchers
Ceci est cassé car il est sujet à l'expansion des chemins. Essayez-le avec string="*". Surprise.
gniourf_gniourf
32

Je pense qu'un moyen efficace est l'utilisation de tableaux bash:

array=( $string ) # do not use quotes in order to allow word expansion
echo ${array[0]}  # You can retrieve any word. Index runs from 0 to length-1

En outre, vous pouvez lire directement les tableaux dans un pipe-line:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done
Isaías
la source
1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home
Ceci est cassé car il est sujet à l'expansion des chemins. Essayez-le avec string="*". Surprise.
gniourf_gniourf
Utilisez la whilesyntaxe pour récupérer chaque premier mot à chaque ligne. Sinon, utilisez l'approche Boontawee Home. Veuillez également noter que cela echo "${array[0]}"a été cité pour empêcher l'expansion comme l'a remarqué gniourf-gniourf.
Isaías
Si vous essayez d'accéder à un index de tableau supérieur au nombre de mots, vous n'obtiendrez pas d'erreur. Vous n'obtiendrez qu'une ligne vide
Dhumil Agarwal
26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Cela présente l'avantage de ne pas utiliser de commandes externes et de laisser intactes les variables $ 1, $ 2, etc.

John Marter
la source
Laisser les variables $1, $2, …intactes est une fonctionnalité extrêmement utile pour l'écriture de scripts!
Serge Stroobandt
15

Si vous êtes sûr qu'il n'y a pas d'espaces de début, vous pouvez utiliser la substitution de paramètre bash:

$ string="word1  word2"
$ echo ${string/%\ */}
word1

Attention à ne pas vous échapper de l'espace unique. Voir ici pour plus d'exemples de modèles de substitution. Si vous avez bash> 3.0, vous pouvez également utiliser la correspondance d'expressions régulières pour gérer les espaces de début - voir ici :

$ string="  word1   word2"
$ [[ ${string} =~ \ *([^\ ]*) ]]
$ echo ${BASH_REMATCH[1]}
word1
dsl101
la source
11

Tu pourrais essayer awk

echo "word1 word2" | awk '{ print $1 }'

Avec awk, il est vraiment facile de choisir le mot que vous aimez (1 $, 2 $, ...)

mfloryan
la source
11

Utilisation de l'expansion des paramètres du shell %% *

Voici une autre solution utilisant l' expansion des paramètres du shell . Il prend en charge plusieurs espaces après le premier mot. La gestion des espaces devant le premier mot nécessite une extension supplémentaire.

string='word1    word2'
echo ${string%% *}
word1

string='word1    word2      '
echo ${string%% *}
word1

Explication

Le %%signifie la suppression de la plus longue correspondance possible de  *(un espace suivi d'un nombre quelconque de tout autre caractère) dans la partie de fin de string.

Serge Stroobandt
la source
9

Je me suis demandé comment plusieurs des meilleures réponses se mesuraient en termes de vitesse. J'ai testé ce qui suit:

1 @ mattbh's

echo "..." | awk '{print $1;}'

2 @ ghostdog74's

string="..."; set -- $string; echo $1

3 @ boontawee-home's

echo "..." | { read -a array ; echo ${array[0]} ; }

et 4 @ boontawee-home

echo "..." | { read first _ ; echo $first ; }

Je les ai mesurés avec le temps de Python dans un script Bash dans un terminal Zsh sur macOS, en utilisant une chaîne de test avec 215 mots de 5 lettres. A fait chaque mesure cinq fois (les résultats étaient tous pour 100 boucles, le meilleur de 3) et a fait la moyenne des résultats:

method       time
--------------------------------
1. awk       9.2ms
2. set       11.6ms (1.26 * "1")
3. read -a   11.7ms (1.27 * "1")
4. read      13.6ms (1.48 * "1")

Bon travail, électeurs 👏 Les votes (au moment de la rédaction de cet article) correspondent à la vitesse des solutions!

Henri
la source
Bizarre que vous puissiez mesurer 3 dans le tiret, car dash ne prend pas en charge les tableaux ( read -an'est pas valide dans le tiret)
gniourf_gniourf
Ouais c'est bizarre. J'ai exclu celui-là, j'ai fait les tests de vitesse, puis j'ai pensé "pourquoi ai-je laissé cela de côté" et l'ai ajouté. En le supprimant maintenant, je pourrai relancer les choses plus tard pour m'assurer de ne pas avoir commis d'erreur
henry
6
echo "word1 word2" | cut -f 1 -d " "

cut coupe le 1er champ (-f 1) d'une liste de champs délimités par la chaîne "" (-d "")

lajuette
la source
c'est une façon, mais votre déclaration de coupe ne distinguera pas plusieurs espaces entre les mots s'il veut obtenir word2 plus tard
ghostdog74
oui, la solution awk est la meilleure.
lajuette
3

read est votre ami:

  • Si la chaîne est dans une variable:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Si vous travaillez dans un tuyau: premier cas: vous ne voulez que le premier mot de la première ligne:

    printf '%s\n' "word1 word2" "line2" | { read -r first _; printf '%s\n' "$first"; }

    deuxième cas: vous voulez le premier mot de chaque ligne:

    printf '%s\n' "word1 word2" "worda wordb" | while read -r first _; do printf '%s\n' "$first"; done

Ceux-ci fonctionnent s'il y a des espaces de tête:

printf '%s\n' "   word1 word2" | { read -r first _; printf '%s\n' "$first"; }
gniourf_gniourf
la source
0

Comme perl incorpore la fonctionnalité de awk, cela peut également être résolu avec perl:

echo " word1 word2" | perl -lane 'print $F[0]'
tssch
la source
0

Je travaillais avec un périphérique embarqué qui n'avait ni perl, awk ou python et je l'ai fait avec sed à la place. Il prend en charge plusieurs espaces avant le premier mot (que les solutions cutet bashn'ont pas géré).

VARIABLE="  first_word_with_spaces_before_and_after  another_word  "
echo $VARIABLE | sed 's/ *\([^ ]*\).*/\1/'

Cela a été très utile lors de la recherche psdes ID de processus, car les autres solutions utilisant uniquement bash n'étaient pas en mesure de supprimer les premiers espaces psutilisés pour l'alignement.

Johan Bjäreholt
la source