Podemos usar la sintaxis ${var##pattern}
y ${var%%pattern}
extraer la última y la primera sección de una dirección 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##*.}
¿Cómo podemos extraer la segunda o tercera sección de una dirección IPv4 usando la expansión de parámetros?
Aquí está mi solución: uso una matriz y cambio la variable 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
También he escrito algunas soluciones que utilizan las awk
, sed
y cut
los comandos.
Ahora, mi pregunta es: ¿Existe una solución más simple basada en la expansión de parámetros que no use el cambio de matriz e IFS?
IFS
pararead
allí:IFS=. read -a ArrIP<<<"$IP"
IFS=. read a b c d <<< "$IP"
que no sea aceptable (si está usando Bash, eso es)? ¿Por qué tiene que hacerse con la expansión de parámetros?Respuestas:
Suponiendo el valor predeterminado de IFS, extrae cada octeto en su propia variable con:
O en una matriz con:
fuente
Su planteamiento del problema puede ser un poco más liberal de lo que pretendía. A riesgo de explotar una escapatoria, aquí está la solución a la que aludía :
Esto es algo torpe. Define dos variables desechables y no se adapta fácilmente para manejar más secciones (por ejemplo, para una dirección MAC o IPv6). La respuesta de Sergiy Kolodyazhnyy me inspiró a generalizar lo anterior a esto:
Este conjuntos
sec1
,sec2
,sec3
ysec4
, que pueden ser verificados conwhile
bucle debe ser fácil de entender: se repite cuatro veces.slice
como nombre para una variable que toma el lugar delast3
ylast2
en mi primera solución (arriba).declare sec"$count"="value"
es una manera de asignar asec1
,sec2
,sec3
ysec4
cuandocount
es1
,2
,3
y4
. Es un poco comoeval
, pero más seguro.value
,"${slice%%.*}"
es análogo a los valores de mis cesionarios respuesta original afirst
,second
ythird
.fuente
Me doy cuenta de que pediste específicamente una solución que NO redefinió temporalmente
IFS
, pero tengo una solución dulce y simple que no cubriste, así que aquí va:Ese comando corto pondrá los elementos de su dirección IP en el shell de parámetros posicionales
$1
,$2
,$3
,$4
. Sin embargo, es probable que primero desee guardar el originalIFS
y luego restaurarlo.¿Quién sabe? Quizás reconsidere y acepte esta respuesta por su brevedad y eficiencia.
(Esto se dio anteriormente incorrectamente como
IFS=. set -- $IP
)fuente
IFS
en la misma línea de comando, el nuevo valor no tendrá efecto cuando se expandan las variables en la misma línea de comando. Igual que conx=1; x=2 echo $x
set
no hace uso de$IFS
nada,$IFS
solo se usa para dividir la palabra$IP
, pero aquí se asigna demasiado tarde, por lo que no tiene ningún efecto. Esta respuesta es básicamente incorrecta.IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'
no genera nada, ya sea en modo POSIX o no. NecesitaríasIFS=. command eval 'set -- $IP'
, oIFS=. read a b c d << "$IP"
.
en una de sus pruebas anteriores. CorreIP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'
y verás que no funciona. Y miraIP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'
para ilustrar el punto de @ chepner.No es lo más fácil , pero podrías hacer algo como:
Eso debería funcionar en ksh93 (donde ese
${var//pattern/replacement}
operador viene),bash
4.3+, busyboxsh
,yash
,mksh
yzsh
, aunque, por supuesto , enzsh
, existen métodos mucho más simples . En versiones anteriores debash
, necesitaría eliminar las comillas internas. Funciona también con esas comillas internas eliminadas en la mayoría de los otros shells, pero no con ksh93.Se supone que
$IP
contiene una representación válida de cuatro decimales de una dirección IPv4 (aunque eso también funcionaría para representaciones de cuatro hexadecimales como0x6d.0x60.0x4d.0xf
(e incluso octal en algunos shells) pero generaría los valores en decimal). Si el contenido$IP
proviene de una fuente no confiable, eso equivaldría a una vulnerabilidad de inyección de comando.Básicamente, lo que estamos reemplazando cada
.
en$IP
con+256*(
, terminamos la evaluación:Así que estamos construyendo un entero de 32 bits de los 4 bytes como una dirección IPv4 en última instancia es (aunque con los bytes invertidos) ¹ y luego usando los
>>
,&
operadores bit a bit para extraer los bytes correspondientes.Usamos el
${param+value}
operador estándar (aquí en el$-
que se garantiza que siempre se establecerá) en lugar de solovalue
porque de lo contrario el analizador aritmético se quejaría de paréntesis no coincidentes. El shell aquí puede encontrar el cierre))
para la apertura$((
, y luego realizar las expansiones en el interior que resultarán en la expresión aritmética para evaluar.Con
lugar, la carcasa podría tratar la segunda y tercera$(((${IP//./"+256*("}))))&255))
)
s allí como el cierre))
para$((
y informar de un error de sintaxis.En ksh93, también puedes hacer:
bash
,mksh
,zsh
Han copiado de ksh93${var/pattern/replacement}
operador, pero no que la captura de grupos manipulación de la pieza.zsh
lo admite con una sintaxis diferente:bash
admite alguna forma de manejo de grupos de captura en su operador de coincidencia regexp , pero no en${var/pattern/replacement}
.POSIXY, usarías:
Para
noglob
evitar sorpresas negativas para valores$IP
similares10.*.*.*
, la subshell para limitar el alcance de esos cambios en las opciones y$IFS
.¹ Una dirección IPv4 es solo un número entero de 32 bits y 127.0.0.1, por ejemplo, es solo una de las muchas representaciones textuales (aunque las más comunes). Esa misma dirección IPv4 típica de la interfaz de bucle invertido también se puede representar como 0x7f000001 o 127.1 (quizás una más apropiada aquí para decir que es la
1
dirección en la red 127.0 / 8 clase A), o 0177.0.1, o las otras combinaciones de 1 a 4 números expresados como octales, decimales o hexadecimales. Puede pasar todos esos,ping
por ejemplo, y verá que todos harán ping localhost.Si no te importa el efecto secundario de establecer una variable temporal arbitraria (en este caso
$n
), enbash
oksh93
, ozsh -o octalzeroes
, olksh -o posix
, simplemente puede convertir todas esas representaciones de nuevo a un número entero de 32 bits con:Y luego extraiga todos los componentes con
>>
/&
combinaciones como arriba.mksh
usa enteros de 32 bits con signo para sus expresiones aritméticas, puede usar$((# n=32,...))
allí para forzar el uso de números de 32 bits sin signo (y laposix
opción para que reconozca constantes octales).fuente
${-+
antes. Tampoco puedo encontrar ninguna documentación al respecto. Funciona, pero tengo curiosidad por confirmar, ¿es solo para convertir la cadena en una expresión matemática? ¿Dónde puedo encontrar la definición formal? Además, las citas adicionales dentro de la sección de reemplazo de expansión de parámetros no funcionan en GNU bash, versión 4.1.2 (2), versión CentOS 6.6. Tuve que hacer esto en su lugarecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
Con zsh, puede anidar sustituciones de parámetros:
Esto no es posible en bash.
fuente
zsh
, usted puede preferir${${(s(.))ip}[3]}
Claro, juguemos al juego del elefante.
o
fuente
Con
IP=12.34.56.78
.Y
Descripción:
Uso de la expansión de parámetros
${IP// }
para convertir cada punto en la ip en un paréntesis de apertura, un punto y un paréntesis de cierre. Al agregar un paréntesis inicial y un paréntesis de cierre, obtenemos:que creará cuatro paréntesis de captura para la coincidencia de expresiones regulares en la sintaxis de prueba:
Eso permite la impresión de la matriz BASH_REMATCH sin el primer componente (toda la coincidencia de expresiones regulares):
La cantidad de paréntesis se ajusta automáticamente a la cadena coincidente. Entonces, esto coincidirá con un MAC o un EUI-64 de una dirección IPv6 a pesar de ser de diferente longitud:
Utilizándolo:
fuente
Aquí hay una pequeña solución hecha con POSIX
/bin/sh
(en mi caso eso esdash
), una función que usa repetidamente la expansión de parámetros (así que noIFS
aquí), y tuberías con nombre, e incluye lanoglob
opción por las razones mencionadas en la respuesta de Stephane .Esto funciona así:
Y con
ip
cambiado a109.*.*.*
El bucle que mantiene el contador de 4 iteraciones representa 4 secciones de una dirección IPv4 válida, mientras que las acrobacias con canalizaciones con nombre necesitan usar más secciones de la dirección IP dentro de la secuencia de comandos en lugar de tener variables atascadas en una subshell de un bucle.
fuente
¿Por qué no usar una solución simple con awk?
$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'
Resultado
$ 192 168 1 1
fuente
fuente