Podemos usar a sintaxe ${var##pattern}
e ${var%%pattern}
extrair a última e a primeira seção de um endereço IPv4:
IP=109.96.77.15
echo IP: $IP
echo 'Extract the first section using ${var%%pattern}: ' ${IP%%.*}
echo 'Extract the last section using ${var##pattern}: ' ${IP##*.}
Como podemos extrair a segunda ou terceira seção de um endereço IPv4 usando a expansão de parâmetros?
Aqui está minha solução: eu uso uma matriz e altero a variável IFS.
:~/bin$ IP=109.96.77.15
:~/bin$ IFS=. read -a ArrIP<<<"$IP"
:~/bin$ echo ${ArrIP[1]}
96
:~/bin$ printf "%s\n" "${ArrIP[@]}"
109
96
77
15
Também tenho escrito algumas soluções que utilizam os awk
, sed
e cut
comandos.
Agora, minha pergunta é: Existe uma solução mais simples com base na expansão de parâmetros que não usa mudança de matriz e IFS?
IFS
pararead
lá:IFS=. read -a ArrIP<<<"$IP"
IFS=. read a b c d <<< "$IP"
não é aceitável (se você estiver usando o Bash)? Por que isso precisa ser feito com a expansão de parâmetros?Respostas:
Supondo que o valor padrão do IFS você extraia cada octeto em sua própria variável com:
Ou em uma matriz com:
fonte
Sua declaração do problema pode ser um pouco mais liberal do que você pretendia. Correndo o risco de explorar uma brecha, aqui está a solução que Muru aludiu :
Isso é um pouco desajeitado. Ele define duas variáveis descartáveis e não é prontamente adaptado para lidar com mais seções (por exemplo, para um endereço MAC ou IPv6). A resposta de Sergiy Kolodyazhnyy me inspirou a generalizar o que foi exposto acima:
Isso define
sec1
,sec2
,sec3
esec4
, o que pode ser verificado comwhile
loop deve ser fácil de entender - itera quatro vezes.slice
como o nome de uma variável que substituilast3
elast2
na minha primeira solução (acima).declare sec"$count"="value"
é uma maneira de atribuir asec1
,sec2
,sec3
esec4
quandocount
é1
,2
,3
e4
. É um pouco parecidoeval
, mas mais seguro.value
,"${slice%%.*}"
, é análogo ao valoriza meus cessionários resposta original parafirst
,second
ethird
.fonte
Percebo que você pediu especificamente uma solução que NÃO redefiniu temporariamente
IFS
, mas eu tenho uma solução simples e doce que você não cobriu, então aqui vai:Esse comando curta vai colocar os elementos do seu endereço IP no shell de parâmetros posicionais
$1
,$2
,$3
,$4
. No entanto, você provavelmente desejará primeiro salvar o originalIFS
e restaurá-lo posteriormente.Quem sabe? Talvez você reconsidere e aceite essa resposta por sua brevidade e eficiência.
(Isso foi fornecido incorretamente anteriormente como
IFS=. set -- $IP
)fonte
IFS
na mesma linha de comando, o novo valor não terá efeito quando as variáveis na mesma linha de comando forem expandidas. O mesmo que comx=1; x=2 echo $x
set
não utiliza$IFS
nada,$IFS
é usado apenas para a divisão de palavras$IP
, mas aqui é atribuído tarde demais, de modo que não tem efeito. Esta resposta está basicamente errada.IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'
não produz nada, seja no modo POSIX ou não. Você precisariaIFS=. command eval 'set -- $IP'
, ouIFS=. read a b c d << "$IP"
.
em um dos seus testes anteriores. ExecuteIP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'
e você verá que não funciona. E vejaIP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'
para ilustrar o argumento de @ chepner.Não é o mais fácil , mas você pode fazer algo como:
Isso deve funcionar no ksh93 (onde esse
${var//pattern/replacement}
operador vem),bash
4.3+, busyboxsh
,yash
,mksh
ezsh
, embora, naturalmente , emzsh
, existem abordagens muito mais simples . Nas versões anterioresbash
, você precisaria remover as aspas internas. Também funciona com as aspas internas removidas na maioria dos outros shells, mas não no ksh93.Isso pressupõe que
$IP
contenha uma representação quad-decimal válida de um endereço IPv4 (embora isso também funcione para representações quad-hexadecimais como0x6d.0x60.0x4d.0xf
(e até octal em alguns shells), mas produziria os valores em decimal). Se o conteúdo$IP
vier de uma fonte não confiável, isso equivaleria a uma vulnerabilidade de injeção de comando.Basicamente, como estamos substituindo cada
.
no$IP
com+256*(
, acabamos avaliação:Então, nós estamos construindo um inteiro de 32 bit fora dessas 4 bytes como um endereço IPv4 em última análise, é (embora com os bytes invertida) ¹ e, em seguida, usando os
>>
,&
operadores bit a bit para extrair os bytes relevantes.Usamos o
${param+value}
operador padrão (aqui no$-
qual é garantido que seja sempre definido) em vez de apenasvalue
porque, caso contrário, o analisador aritmético reclamaria de parênteses incompatíveis. O shell aqui pode encontrar o fechamento))
para a abertura$((
e , em seguida, executar as expansões dentro que irá resultar na expressão aritmética para avaliar.Com
vez, o shell trataria o segundo e terceiro$(((${IP//./"+256*("}))))&255))
)
s lá como o fechamento))
de$((
e relatar um erro de sintaxe.No ksh93, você também pode:
bash
,mksh
,zsh
Copiou de ksh93${var/pattern/replacement}
operador, mas não que a captura do grupo manipulação parte.zsh
suporta com uma sintaxe diferente:bash
suporta alguma forma de manipulação de grupo de captura em seu operador de correspondência regexp , mas não em${var/pattern/replacement}
.POSIXly, você usaria:
Para
noglob
evitar surpresas ruins para valores de$IP
like10.*.*.*
, o subshell para limitar o escopo dessas alterações às opções e$IFS
.Address Um endereço IPv4 é apenas um número inteiro de 32 bits e 127.0.0.1, por exemplo, é apenas uma das muitas (embora as mais comuns) representações textuais. O mesmo endereço IPv4 típico da interface de loopback também pode ser representado como 0x7f000001 ou 127.1 (talvez um mais apropriado aqui para dizer que é o
1
endereço na rede classe 127.0 / 8 classe A), ou 0177.0.1 ou as outras combinações de 1 a 4 números expressos em octal, decimal ou hexadecimal. Você pode passar todos para,ping
por exemplo, e verá que todos executarão ping no host local.Se você não se importa com o efeito colateral de definir uma variável temporária arbitrária (aqui
$n
), embash
ouksh93
ouzsh -o octalzeroes
oulksh -o posix
, você pode simplesmente converter todas essas representações em um número inteiro de 32 bits com:E então extraia todos os componentes com
>>
/&
combinações como acima.mksh
usa números inteiros de 32 bits assinados para suas expressões aritméticas, você pode usá$((# n=32,...))
-lo para forçar o uso de números de 32 bits não assinados (e aposix
opção para reconhecer constantes octais).fonte
${-+
antes. Também não consigo encontrar nenhuma documentação. Funciona, mas estou curioso para confirmar, é apenas para transformar a string em uma expressão matemática? Onde posso encontrar a definição formal? Além disso, as cotações extras dentro da seção de substituição de expansão de parâmetros não funcionam no GNU bash, versão 4.1.2 (2), versão CentOS 6.6. Eu tive que fazer issoecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
Com o zsh, você pode aninhar substituições de parâmetros:
Isso não é possível no bash.
fonte
zsh
, você pode preferir${${(s(.))ip}[3]}
Claro, vamos jogar o jogo do elefante.
ou
fonte
Com
IP=12.34.56.78
.E
Descrição:
O uso da expansão de parâmetro
${IP// }
para converter cada ponto no ip em um parêntese de abertura um ponto e um parêntese de fechamento. Adicionando um parêntese inicial e um parêntese de fechamento, obtemos:que criará quatro parênteses de captura para a correspondência de regex na sintaxe de teste:
Isso permite a impressão da matriz BASH_REMATCH sem o primeiro componente (toda a regex corresponde):
A quantidade de parênteses é ajustada automaticamente para a sequência correspondente. Portanto, isso corresponderá a um MAC ou a EUI-64 de um endereço IPv6, apesar de serem de tamanho diferente:
Usando isso:
fonte
Aqui está uma pequena solução feita com POSIX
/bin/sh
(no meu casodash
), uma função que usa repetidamente a expansão de parâmetros (então nãoIFS
aqui) e pipes nomeados, e inclui anoglob
opção pelos motivos mencionados na resposta de Stephane .Isso funciona da seguinte maneira:
E com
ip
alterado para109.*.*.*
O contador de manutenção de loops de 4 iterações é responsável por 4 seções de um endereço IPv4 válido, enquanto as acrobacias com pipes nomeados são necessárias para usar ainda mais as seções do endereço IP no script, em vez de ter variáveis presas no subshell de um loop.
fonte
Por que não usar solução simples com o awk?
$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'
Resultado
$ 192 168 1 1
fonte
fonte