Estoy tratando de encontrar la forma más eficiente de iterar a través de ciertos valores que son un número constante de valores separados en una lista de palabras separadas por espacios (no quiero usar una matriz). Por ejemplo,
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
Por lo tanto, quiero poder iterar por la lista y solo acceder a 1,5,6,9 y 15.
EDITAR: Debería haber dejado en claro que los valores que estoy tratando de obtener de la lista no tienen que ser diferentes en formato al resto de la lista. Lo que los hace especiales es únicamente su posición en la lista (en este caso, posición 1,4,7 ...). Así que la lista podría ser,1 2 3 5 9 8 6 90 84 9 3 2 15 75 55
pero todavía quiero los mismos números. Y también, quiero poder hacerlo asumiendo que no sé la longitud de la lista.
Los métodos que he pensado hasta ahora son:
Método 1
set $list
found=false
find=9
count=1
while [ $count -lt $# ]; do
if [ "${@:count:1}" -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
done
Método 2
set list
found=false
find=9
while [ $# ne 0 ]; do
if [ $1 -eq $find ]; then
found=true
break
fi
shift 3
done
Método 3 Estoy bastante seguro de que la tubería hace que esta sea la peor opción, pero estaba tratando de encontrar un método que no use set, por curiosidad.
found=false
find=9
count=1
num=`echo $list | cut -d ' ' -f$count`
while [ -n "$num" ]; do
if [ $num -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
num=`echo $list | cut -d ' ' -f$count`
done
Entonces, ¿qué sería más eficiente, o me falta un método más simple?
fuente
Respuestas:
Bastante simple con
awk
. Esto le dará el valor de cada cuarto campo para la entrada de cualquier longitud:Esto funciona aprovechando las
awk
variables integradas comoNF
(el número de campos en el registro), y haciendo algunosfor
bucles simples para iterar a lo largo de los campos para darle las que desea sin necesidad de saber de antemano cuántos habrá.O, si realmente desea esos campos específicos como se especifica en su ejemplo:
En cuanto a la pregunta sobre la eficiencia, la ruta más simple sería probar este o cada uno de sus otros métodos y usarlo
time
para mostrar cuánto tiempo lleva; También puede usar herramientas comostrace
para ver cómo fluyen las llamadas del sistema. Uso detime
looks como:Puede comparar esa salida entre diferentes métodos para ver cuál es el más eficiente en términos de tiempo; Se pueden usar otras herramientas para otras métricas de eficiencia.
fuente
echo
vs<<<
, "idéntico" es una palabra demasiado fuerte. Se podría decir questuff <<< "$list"
es casi idéntico aprintf "%s\n" "$list" | stuff
. Con respecto aecho
vsprintf
, te dirijo a esta respuesta<<<
agrega una nueva línea al final. Esto es similar a cómo$()
elimina una nueva línea del final. Esto se debe a que las líneas están terminadas por nuevas líneas.<<<
alimenta una expresión como una línea, por lo que debe terminar con una nueva línea."$()"
toma líneas y las proporciona como argumento, por lo que tiene sentido convertir eliminando la nueva línea de terminación.awk
es un binario independiente que debe iniciarse. A diferencia de Perl o especialmente Python, el intérprete awk se inicia rápidamente (todavía todos los gastos generales habituales del enlazador dinámico de realizar bastantes llamadas al sistema, pero awk solo usa libc / libm y libdl, p. Ej.,strace
Para verificar las llamadas al sistema del inicio de awk) . Muchos shells (como bash) son bastante lentos, por lo que activar un proceso awk puede ser más rápido que recorrer los tokens en una lista con shell incorporado incluso para tamaños de lista pequeños. Y a veces puedes escribir un#!/usr/bin/awk
guión en lugar de un#!/bin/sh
guión.Primera regla de optimización de software: no lo haga .
Hasta que sepa que la velocidad del programa es un problema, no hay necesidad de pensar qué tan rápido es. Si su lista tiene aproximadamente esa longitud o solo ~ 100-1000 elementos, probablemente ni siquiera se dará cuenta de cuánto tiempo lleva. Existe la posibilidad de que pase más tiempo pensando en la optimización que cuál sería la diferencia.
Segunda regla: medida .
Esa es la forma segura de averiguarlo y la que da respuestas para su sistema. Especialmente con los proyectiles, hay tantos, y no todos son idénticos. Es posible que una respuesta para un shell no se aplique al tuyo.
En programas más grandes, la creación de perfiles también va aquí. La parte más lenta podría no ser la que crees que es.
Tercero, la primera regla de optimización de script de shell: no use el shell .
Si, en serio. Muchos shells no están hechos para ser rápidos (ya que el lanzamiento de programas externos no tiene que serlo), e incluso podrían analizar las líneas del código fuente cada vez.
Use algo como awk o Perl en su lugar. En un trivial micro-benchmark que hice,
awk
fue docenas de veces más rápido que cualquier shell común al ejecutar un bucle simple (sin E / S).Sin embargo, si utiliza el shell, use las funciones integradas del shell en lugar de los comandos externos. Aquí, está utilizando lo
expr
que no está integrado en ningún caparazón que encontré en mi sistema, pero que se puede reemplazar con la expansión aritmética estándar. Por ejemplo, eni=$((i+1))
lugar dei=$(expr $i + 1)
incrementari
. Su uso decut
en el último ejemplo también podría reemplazarse con expansiones de parámetros estándar.Ver también: ¿Por qué usar un bucle de shell para procesar texto se considera una mala práctica?
Los pasos 1 y 2 deben aplicarse a su pregunta.
fuente
awk
bucles sean necesariamente mejores o peores que los bucles de shell. Es que el shell es realmente bueno para ejecutar comandos y dirigir la entrada y salida hacia y desde los procesos, y francamente bastante torpe en todo lo demás; mientras que las herramientas comoawk
son fantásticas en el procesamiento de datos de texto, porque para esoawk
están hechos los shells y las herramientas (respectivamente) en primer lugar.dash
que congawk
, ydash
fue el shell más rápido que probé ...dash
ybusybox
no es compatible(( .. ))
, creo que es una extensión no estándar.++
también se menciona explícitamente como no requerido, por lo que puedo decir,i=$((i+1))
o: $(( i += 1))
son los seguros.Solo voy a dar algunos consejos generales en esta respuesta, y no puntos de referencia. Los puntos de referencia son la única forma de responder de manera confiable las preguntas sobre el rendimiento. Pero dado que no dice cuántos datos está manipulando y con qué frecuencia realiza esta operación, no hay forma de hacer un punto de referencia útil. Lo que es más eficiente para 10 artículos y lo que es más eficiente para 1000000 artículos a menudo no es lo mismo.
Como regla general, invocar comandos externos es más costoso que hacer algo con construcciones de shell puro, siempre que el código de shell puro no implique un bucle. Por otro lado, un ciclo de shell que itera sobre una cadena grande o una gran cantidad de cadena probablemente sea más lento que una invocación de una herramienta de propósito especial. Por ejemplo, su invocación de bucle
cut
podría ser notablemente lenta en la práctica, pero si encuentra una manera de hacer todo con una solacut
invocación, es probable que sea más rápido que hacer lo mismo con la manipulación de cadenas en el shell.Tenga en cuenta que el punto de corte puede variar mucho entre sistemas. Puede depender del kernel, de cómo está configurado el programador del kernel, del sistema de archivos que contiene los ejecutables externos, de la presión de CPU vs memoria que hay en este momento y muchos otros factores.
No llame
expr
para realizar operaciones aritméticas si le preocupa el rendimiento. De hecho, no llameexpr
para realizar operaciones aritméticas. Los proyectiles tienen aritmética incorporada, que es más clara y rápida que la invocaciónexpr
.Parece que estás usando bash, ya que estás usando construcciones de bash que no existen en sh. Entonces, ¿por qué no usarías una matriz? Una matriz es la solución más natural, y es probable que también sea la más rápida. Tenga en cuenta que los índices de matriz comienzan en 0.
Su script puede ser más rápido si usa sh, si su sistema tiene guión o ksh en
sh
lugar de bash. Si usas sh, no obtienes matrices con nombre, pero aun así obtienes una matriz de parámetros posicionales, que puedes establecer conset
. Para acceder a un elemento en una posición que no se conoce hasta el tiempo de ejecución, debe usarloeval
(¡tenga cuidado de citar las cosas correctamente!).Si solo desea acceder a la matriz una vez y va de izquierda a derecha (omitiendo algunos valores), puede usar en
shift
lugar de índices variables.El enfoque más rápido depende del shell y del número de elementos.
Otra posibilidad es utilizar el procesamiento de cadenas. Tiene la ventaja de no usar los parámetros posicionales, por lo que puede usarlos para otra cosa. Será más lento para grandes cantidades de datos, pero es poco probable que haga una diferencia notable para pequeñas cantidades de datos.
fuente
shift && shift && shift
conshift 3
en su tercer ejemplo - a menos que la cáscara está utilizando no lo soporta.shift 3
fallaría si quedaran muy pocos argumentos restantes. Necesitarías algo comoif [ $# -gt 3 ]; then shift 3; else set --; fi
awk
es una gran opción, si puede hacer todo su procesamiento dentro del script Awk. De lo contrario, terminas canalizando la salida Awk a otras utilidades, destruyendo la ganancia de rendimiento deawk
.bash
La iteración sobre una matriz también es excelente, si puede ajustar su lista completa dentro de la matriz (lo que para los shells modernos es probablemente una garantía) y no le importa la gimnasia de sintaxis de matriz.Sin embargo, un enfoque de tubería:
Dónde:
xargs
agrupa la lista separada por espacios en blanco en lotes de tres, cada nueva línea separadawhile read
consume esa lista y genera la primera columna de cada grupogrep
filtra la primera columna (correspondiente a cada tercera posición en la lista original)Mejora la comprensibilidad, en mi opinión. La gente ya sabe lo que hacen estas herramientas, por lo que es fácil leer de izquierda a derecha y razonar sobre lo que va a suceder. Este enfoque también documenta claramente la longitud del paso (
-n3
) y el patrón de filtro (9
), por lo que es fácil de variabilizar:Cuando hagamos preguntas de "eficiencia", asegúrese de pensar en la "eficiencia total de por vida". Ese cálculo incluye el esfuerzo de los mantenedores para mantener el código funcionando, y las bolsas de carne somos las máquinas menos eficientes en toda la operación.
fuente
Quizás esto?
fuente
No use comandos de shell si quiere ser eficiente. Limítese a tuberías, redirecciones, sustituciones, etc. y programas. Por eso
xargs
yparallel
utilidades existe - a causa fiesta, mientras que los bucles son ineficientes y muy lento. Use bash loops solo como la última resolución.Pero probablemente deberías ser algo más rápido con el bien
awk
.fuente
En mi opinión, la solución más clara (y probablemente la más eficiente también) es usar las variables awk de RS y ORS:
fuente
Usando script de shell GNU
sed
y POSIX :O con
bash
la sustitución de parámetros :No GNU ( es decir, POSIX )
sed
ybash
:O de manera más portátil, utilizando POSIX
sed
y script de shell:Salida de cualquiera de estos:
fuente