Como recuperar a primeira palavra da saída de um comando no bash?

126

Eu tenho um comando, por exemplo: echo "word1 word2". Eu quero colocar um pipe ( |) e obter word1 do comando.

echo "word1 word2" | ....

Não sei o que colocar depois do cano.

Neuquino
fonte

Respostas:

201

O Awk é uma boa opção se você precisar lidar com espaços em branco à direita, porque ele cuidará disso para você:

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

O Cut não vai resolver isso:

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

'cut' aqui não imprime nada / espaço em branco, porque a primeira coisa antes de um espaço era outro espaço.

mattbh
fonte
O ponto e vírgula é necessário?
Alice Purcell
1
Deve ser um espaço em branco "inicial" (no início da sequência), não "final".
user202729
@AlicePurcell eu tentei sem; e funcionou para mim (MBP 10.14.2)
Samy Bencherif
1
Isso não funciona se a cadeia é, por exemplo "firstWord, secondWord" como que delimitadores comando awk por espaço
Roger Oba
@RogerOba Essa não foi a pergunta do OP, mas você pode -F","substituir o separador de campo padrão (um espaço) por vírgula.
pjd 27/02
70

não há necessidade de usar comandos externos. O próprio Bash pode fazer o trabalho. Supondo que "word1 word2" você tenha obtido de algum lugar e armazenado em uma variável, por exemplo

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

agora você pode atribuir $ 1 ou $ 2 etc a outra variável, se quiser.

ghostdog74
fonte
11
+1 por usar apenas os recursos internos do shell e stdin. @Mat M. --significa stdin, então $stringestá sendo passado como stdin. stdiné um espaço em branco-separados em argumentos $1, $2, $3, etc. - assim como quando uma festança Avalia programa argumentos (por exemplo, cheque $1, $2etc.), esta abordagem tira proveito da tendência do shell para dividir o stdinem argumentos automaticamente, eliminando a necessidade de awkou cut.
Caleb Xu
3
@CalebXu Not stdin, setdefine os argumentos do shell.
Guido
9
word1=$(IFS=" " ; set -- $string ; echo $1)Defina o IFS para reconhecer corretamente o espaço entre as palavras. Coloque parênteses para evitar espantar o conteúdo original de $ 1.
Steve Pitchers
Isso está quebrado porque está sujeito à expansão do nome do caminho. Experimente com string="*". Surpresa.
21418 Gnourf_gniourf
32

Eu acho que uma maneira eficiente é o uso de matrizes 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

Além disso, você pode ler diretamente matrizes em uma linha de tubulação:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done
Isaías
fonte
1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home
Isso está quebrado porque está sujeito à expansão do nome do caminho. Experimente com string="*". Surpresa.
21818 Gnourf_gniourf
Use a whilesintaxe para recuperar cada primeira palavra em cada linha. Caso contrário, use a abordagem do Boontawee Home. Além disso, observe que echo "${array[0]}"foi citado para impedir a expansão, conforme observado pelo gniourf-gniourf.
Isaías
Se você tentar acessar um índice da matriz que seja maior que o número de palavras, não receberá um erro. Você só terá uma linha vazia #
Dhumil Agarwal
26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Isso tem a vantagem de não usar comandos externos e deixa intactas as variáveis ​​$ 1, $ 2 etc.

John Marter
fonte
Deixar as variáveis $1, $2, …intactas é um recurso extremamente útil para escrever scripts!
Serge Stroobandt 21/10
14

Se você tiver certeza de que não há espaços à esquerda, poderá usar a substituição do parâmetro bash:

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

Cuidado para escapar do espaço único. Veja aqui mais exemplos de padrões de substituição. Se você tem bash> 3.0, também pode usar correspondência de expressão regular para lidar com espaços à esquerda - veja aqui :

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

Você poderia tentar awk

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

Com o awk, é muito fácil escolher qualquer palavra que você goste (US $ 1, US $ 2, ...)

mfloryan
fonte
11

Usando expansão de parâmetro do shell %% *

Aqui está outra solução usando a expansão de parâmetros do shell . Ele cuida de vários espaços após a primeira palavra. A manipulação de espaços na frente da primeira palavra requer uma expansão adicional.

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

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

Explicação

Os %%significa eliminar a maior correspondência possível de  *(um espaço seguido por qualquer número de quaisquer outros caracteres) no arrasto parte string.

Serge Stroobandt
fonte
9

Eu me perguntava como várias das principais respostas se comparam em termos de velocidade. Eu testei o seguinte:

1 @ mattbh's

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

2 @ ghostdog74

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

3 @ boontawee-casa

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

e 4 @ boontawee-home's

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

Eu os medi com o tempo do Python em um script Bash em um terminal Zsh no macOS, usando uma sequência de teste com 215 palavras de 5 letras. Realizou cada medição cinco vezes (os resultados foram todos para 100 voltas, melhor de 3) e calculou a média dos resultados:

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

Bom trabalho, eleitores 👏 Os votos (até o momento em que escrevemos) correspondem à velocidade das soluções!

Henry
fonte
Estranho que você possa medir 3 no traço, pois o traço não suporta matrizes ( read -aé inválido no traço).
21818 Gnourf_gniourf #
Sim, isso é estranho. Eu descartei isso, fiz os testes de velocidade, depois pensei "por que deixei esse de fora" e o adicionei. Removendo agora, e posso executar novamente as coisas mais tarde para ter certeza de que não tenho nenhum erro
henry
6
echo "word1 word2" | cut -f 1 -d " "

cut corta o 1º campo (-f 1) de uma lista de campos delimitados pela string "" (-d "")

lajuette
fonte
essa é uma maneira, mas sua declaração de corte não irá distinguir múltiplos espaços entre as palavras, se ele quer obter word2 mais tarde
ghostdog74
sim, a solução awk é a melhor.
lajuette
3

read é seu amigo:

  • Se a string estiver em uma variável:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Se você estiver trabalhando em um pipe: primeiro caso: você quer apenas a primeira palavra da primeira linha:

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

    segundo caso: você quer a primeira palavra de cada linha:

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

Eles funcionam se houver espaços à esquerda:

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

Como o perl incorpora a funcionalidade do awk, isso também pode ser resolvido com o perl:

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

Eu estava trabalhando com um dispositivo incorporado que não possuía perl, awk ou python e o fez com o sed. Ele suporta vários espaços antes da primeira palavra (que as soluções cute bashnão manipularam).

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

Isso foi muito útil quando grepping pspara IDs de processo, pois as outras soluções aqui usando apenas o bash não conseguiram remover os primeiros espaços psusados ​​para alinhar.

Johan Bjäreholt
fonte