Extrair endereço IP de um texto e armazená-lo em uma variável

5

Eu tenho um arquivo de texto chamado abd mostrado abaixo.

48878 128.206.6.136
34782 128.206.6.137
12817 23.234.22.106

Eu quero extrair apenas o endereço IP do texto e armazená-lo em uma variável e usá-lo para outra finalidade.

Eu tentei isso.

for line in 'cat abd'
do

ip='grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' $line'

echo $ip

done

Estou recebendo um erro da seguinte forma

grep: 34782: No such file or directory

grep: 128.206.6.137: No such file or directory

grep: 12817: No such file or directory

grep: 23.234.22.106: No such file or directory

Eu não sei o que está errado aqui. Qualquer ajuda seria apreciada.

    
por Swatesh Pakhare 15.11.2015 / 02:11

5 respostas

5

Você quase acertou na primeira vez. A resposta awk é boa para o seu caso específico, mas o motivo pelo qual você recebeu um erro é porque estava tentando usar grep como se estivesse procurando um arquivo em vez de uma variável.

Além disso, ao usar expressões regulares, sempre uso grep -E apenas para estar seguro. Também ouvi dizer que os backticks estão obsoletos e devem ser substituídos por $() .

A maneira correta de grep de uma variável com shells compatíveis com herestrings está sendo usada redirecionamento de entrada com 3 desses caras: < , então seu comando grep ( $ip variable) deve realmente ler da seguinte maneira:

ip="$(grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' <<< "$line")"

Se for um arquivo que você está pesquisando, eu sempre uso um while loop, já que é garantido que ele vai linha por linha, enquanto for loops frequentemente são descartados se houver algum espaçamento estranho. Você também está implementando um uso inútil de cat , que também pode ser substituído pelo redirecionamento de entrada. Tente isto:

while read line; do
  ip="$(grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' <<< "$line")"
  echo "$ip"
done < "abd"

Além disso, não sei qual SO ou versão de grep você está usando, mas o caractere de escape que você tinha antes das chaves geralmente não é necessário sempre que eu usei esse comando no passado. Pode ser usando grep -E ou porque eu uso entre aspas e sem backticks - não sei. Você pode experimentar com ou sem e ver o que acontece.

Se você usa um loop for ou while , isso é baseado em qual deles funciona para você em sua situação específica e se o tempo de execução é de extrema importância. Não me parece que OP esteja tentando atribuir variáveis separadas a cada endereço IP, mas que ele queira atribuir uma variável a cada endereço IP dentro da linha, para que possa usá-la dentro do próprio loop - em que caso ele precise apenas de uma única variável $ip por iteração. Eu estou aderindo às minhas armas em um presente.

    
por 15.11.2015 / 04:58
9

Se o endereço IP for sempre o segundo campo desse arquivo, você poderá usar awk ou cut para extraí-lo.

awk '{print $2}' abd

ou

cut -d' ' -f2 abd

Se você precisar iterar pelos endereços IP, os loops normais for ou while podem ser usados. Por exemplo:

for ip in $(cut -d' ' -f2 abd) ; do ... ; done

ou

 
awk '{print $2}' abd | while read ip ; do ... ; done

Ou você pode ler todos os endereços IP em uma matriz:

$ IPAddresses=($(awk '{print $2}' abd))
$ echo "${IPAddresses[@]}"
128.206.6.136 128.206.6.137 23.234.22.106
    
por 15.11.2015 / 03:53
6

grep procura arquivos ou entrada padrão para os padrões. Você não pode passar strings de dados para corresponder na linha de comando grep . Tente isto:

grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' abd

Se você precisar obter cada endereço IP em uma variável:

grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' abd |
while read IP
do
    echo "$IP"
done

Teste Comparativo de Desempenho do Código @ rubynorails

A resposta recomenda a execução de uma chamada separada de grep em cada linha do arquivo de entrada. Vamos ver como isso funciona com arquivos de 1000 a 5000 linhas. Os arquivos abd.1000 e abd.5000 foram criados simplesmente replicando o arquivo de exemplo original na pergunta. O código original foi alterado apenas para obter o nome do arquivo como um argumento de linha de comando ( ${1:?} ) em vez do "abd" codificado.

$ wc -l abd.1000 abd.5000
  1000 abd.1000
  5000 abd.5000
  6000 total

Teste o código de exemplo nesta resposta em um arquivo de 1.000 linhas:

$ cat ip-example.sh
#!/bin/sh
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' "${1:?}" |
while read IP
do
    echo "$IP"
done

$ time sh ip-example.sh abd.1000 > /dev/null

real    0m0.021s
user    0m0.007s
sys     0m0.017s
$

O exemplo acima mostra que o exemplo nesta resposta processou um arquivo de 1.000 linhas em menos de 1/4 de segundo. Agora vamos ver como o exemplo do @ rubynorails se comporta:

$ cat rubynorails.sh
#!/bin/bash
while read line; do
  ip="$(grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' <<< "$line")"
  echo "$ip"
done < "${1:?}"

$ time bash rubynorails.sh abd.1000 > /dev/null

real    0m3.565s
user    0m0.739s
sys     0m2.936s
$

Hmmm. O exemplo @rubynorails é executado em 3 1/2 segundos, cerca de 7-8 vezes mais lento do que o 1/4 segundo no exemplo para esta resposta.

Vamos fazer o ante e testar com 5000 linhas:

$ time sh ip-example.sh abd.5000 > /dev/null

real    0m0.052s
user    0m0.051s
sys     0m0.029s

Sobre duas vezes o tempo necessário para processar 5 vezes mais dados .

$ time bash rubynorails.sh abd.5000 > /dev/null

real    0m17.561s
user    0m3.817s
sys     0m14.333s

O código @rubynorails leva quase 5 vezes mais tempo para processar 5 vezes mais dados do que processar 1000 linhas de dados.

Conclusões

Exemplo @rubynorails leva 17 segundos, 34 vezes mais tempo para processar um arquivo de 5000 linhas do que o código ip-example.sh nesta resposta (as outras respostas nesta página devem executar de forma semelhante a ip-example.h ).

    
por 15.11.2015 / 02:17
4

Eu sugiro que você use o AWK para esse propósito. É uma ferramenta muito mais apropriada para o processamento de colunas.

xieerqi:$ vi ipAddresses

xieerqi:$ awk '{printf $2" "}' ipAddresses                                     
128.206.6.136 128.206.6.137 23.234.22.106 
xieerqi:$ ARRAY=($(awk '{printf $2" "}' ipAddresses))                          

xieerqi:$ echo ${ARRAY[@]}
128.206.6.136 128.206.6.137 23.234.22.106

xieerqi:$ echo ${ARRAY[1]} ${ARRAY[2]}
128.206.6.137 23.234.22.106

xieerqi:$ cat ipAddresses                                                      
48878 128.206.6.136
34782 128.206.6.137
12817 23.234.22.106
    
por 15.11.2015 / 02:22
3

Veja a primeira pergunta em a FAQ do Bash :

while read -r _ ip; do printf "%s\n" "${ip[@]}"; done < abd
128.206.6.136
128.206.6.137
23.234.22.106
    
por 15.11.2015 / 03:02