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 55pero 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
awkvariables integradas comoNF(el número de campos en el registro), y haciendo algunosforbucles 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
timepara mostrar cuánto tiempo lleva; También puede usar herramientas comostracepara ver cómo fluyen las llamadas del sistema. Uso detimelooks 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
echovs<<<, "idéntico" es una palabra demasiado fuerte. Se podría decir questuff <<< "$list"es casi idéntico aprintf "%s\n" "$list" | stuff. Con respecto aechovsprintf, 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.awkes 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.,stracePara 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/awkguión en lugar de un#!/bin/shguió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,
awkfue 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
exprque 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 decuten 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
awkbucles 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 comoawkson fantásticas en el procesamiento de datos de texto, porque para esoawkestán hechos los shells y las herramientas (respectivamente) en primer lugar.dashque congawk, ydashfue el shell más rápido que probé ...dashybusyboxno 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
cutpodría ser notablemente lenta en la práctica, pero si encuentra una manera de hacer todo con una solacutinvocació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
exprpara realizar operaciones aritméticas si le preocupa el rendimiento. De hecho, no llameexprpara 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
shlugar 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
shiftlugar 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 && shiftconshift 3en su tercer ejemplo - a menos que la cáscara está utilizando no lo soporta.shift 3fallaría si quedaran muy pocos argumentos restantes. Necesitarías algo comoif [ $# -gt 3 ]; then shift 3; else set --; fiawkes 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.bashLa 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:
xargsagrupa la lista separada por espacios en blanco en lotes de tres, cada nueva línea separadawhile readconsume esa lista y genera la primera columna de cada grupogrepfiltra 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
xargsyparallelutilidades 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
sedy POSIX :O con
bashla sustitución de parámetros :No GNU ( es decir, POSIX )
sedybash:O de manera más portátil, utilizando POSIX
sedy script de shell:Salida de cualquiera de estos:
fuente