Muchas personas usan líneas y scripts que contienen código a lo largo de las líneas.
cat "$MYFILE" | command1 | command2 > "$OUTPUT"
El primero a cat
menudo se llama "uso inútil del gato" porque técnicamente requiere comenzar un nuevo proceso (a menudo /usr/bin/cat
) en el que esto podría evitarse si el comando hubiera sido
< "$MYFILE" command1 | command2 > "$OUTPUT"
porque entonces shell solo necesita comenzar command1
y simplemente apuntarlo stdin
al archivo dado.
¿Por qué el shell no realiza esta conversión automáticamente? Creo que la sintaxis de "uso inútil del gato" es más fácil de leer y Shell debería tener suficiente información para deshacerse del gato inútil automáticamente. El cat
está definido en el estándar POSIX, por lo que se debe permitir que Shell lo implemente internamente en lugar de usar un binario en la ruta. El shell incluso podría contener la implementación solo para una versión de argumento exactamente y recurrir a la ruta binaria.
fuente
lseek
todavía es un comportamiento definido y podría causar un resultado diferente, el diferente comportamiento de bloqueo puede ser semánticamente significativo, etc. Sería posible realizar el cambio si supiera cuáles eran los otros comandos y supiera que no les importaba, o si simplemente no le importaba la compatibilidad a ese nivel, pero el beneficio es bastante pequeño. Me imagino que la falta de beneficios impulsa la situación más que el costo de conformidad.cat
embargo, el shell está absolutamente autorizado a implementarse , o cualquier otra utilidad. También se permite saber cómo funcionan las otras utilidades que pertenecen al sistema (por ejemplo, puede saber cómo se comporta lagrep
implementación externa que vino con el sistema ). Esto es completamente viable, por lo que es completamente justo preguntarse por qué no lo hacen.grep
. Ysed
. Yawk
. Ydu
. ¿Y cuántos cientos, si no miles, de otras utilidades?Respuestas:
Los 2 comandos no son equivalentes: considere el manejo de errores:
cat <file that doesn't exist> | less
producirá una secuencia vacía que se pasará al programa canalizado ... como tal, terminará con una pantalla que no muestra nada.< <file that doesn't exist> less
no podrá abrir la barra, y luego no se abrirá menos.Intentar cambiar el primero por el último podría romper cualquier número de scripts que esperen ejecutar el programa con una entrada potencialmente en blanco.
fuente
cat
siempre ejecutará el segundo comando en la tubería mientras que la variante con solo la redirección de entrada no ejecutará el comando si falta el archivo de entrada.<"missing-file" grep foo | echo 2
no se ejecutará,grep
pero se ejecutaráecho
.El "uso inútil de
cat
" se trata más de cómo escribe su código que de lo que realmente se ejecuta cuando ejecuta el script. Es una especie de antipatrón de diseño , una forma de hacer algo que probablemente podría hacerse de una manera más eficiente. Es un fracaso en la comprensión de cómo combinar mejor las herramientas dadas para crear una nueva herramienta. Yo diría que unir variossed
y / oawk
comandos juntos en una tubería también a veces podría decirse que es un síntoma de este mismo antipatrón.Arreglar instancias de "uso inútil de
cat
" en un script es principalmente una cuestión de arreglar el código fuente del script manualmente. Una herramienta como ShellCheck puede ayudar con esto al señalar los casos obvios:Hacer que el shell haga esto automáticamente sería difícil debido a la naturaleza de los scripts de shell. La forma en que se ejecuta un script depende del entorno heredado de su proceso padre y de la implementación específica de los comandos externos disponibles.
El caparazón no necesariamente sabe qué
cat
es. Potencialmente podría ser cualquier comando desde cualquier parte de su$PATH
, o una función.Si se tratara de un comando incorporado (que puede estar en algunos shells), tendría la capacidad de reorganizar la canalización, ya que conocería la semántica de su
cat
comando incorporado . Antes de hacer eso, adicionalmente tendría que hacer suposiciones sobre el siguiente comando en la tubería, después del originalcat
.Tenga en cuenta que la lectura de la entrada estándar se comporta de manera ligeramente diferente cuando está conectada a una tubería y cuando está conectada a un archivo. Una tubería no se puede buscar, por lo que, dependiendo de lo que haga el siguiente comando en la tubería, puede o no comportarse de manera diferente si la tubería se reorganizó (puede detectar si la entrada es buscable y decidir hacer las cosas de manera diferente si lo es o si no lo es, en cualquier caso, entonces se comportaría de manera diferente).
Esta pregunta es similar (en un sentido muy general) a " ¿Hay algún compilador que intente arreglar los errores de sintaxis por sí mismo? " (En el sitio de Software Engineering StackExchange), aunque esa pregunta es obviamente sobre errores de sintaxis, no patrones de diseño inútiles . Sin embargo, la idea de cambiar automáticamente el código en función de la intención es en gran medida la misma.
fuente
cat
es, y los otros comandos en la tubería, (la regla as-if) y se comporten en consecuencia, simplemente no lo hacen aquí porque no tiene sentido y es demasiado difícil.cat /dev/tty
es el interesante con el que sería diferente<
.cat
hace realmente el comando sin ejecutarlo . Por todo lo que (y la cáscara) conocimientos, el PO tiene un comandocat
en su camino, que es una simulación interactiva gato, "miarchivo" es sólo el estado del juego almacenado, ycommand1
ycommand2
son posprocesamiento algunas estadísticas acerca de la sesión de juego actual ...Porque no es inútil.
En el caso de
cat file | cmd
, el fd0
(stdin) decmd
será una tubería, y en el caso decmd <file
que sea un archivo, dispositivo, etc.Una tubería tiene una semántica diferente a la de un archivo normal, y su semántica no es un subconjunto de las de un archivo normal:
un archivo normal no puede ser
select(2)
editado opoll(2)
editado de manera significativa; unselect(2)
sobre siempre devolverá "listo". Las interfaces avanzadas comoepoll(2)
en Linux simplemente no funcionarán con archivos normales.en Linux hay llamadas de sistema (
splice(2)
,vmsplice(2)
,tee(2)
), que sólo trabajo en tuberías [1]Como
cat
se usa mucho, podría implementarse como un shell incorporado que evitará un proceso adicional, pero una vez que comience en ese camino, se podría hacer lo mismo con la mayoría de los comandos: transformar el shell en un proceso más lento y más complicadoperl
opython
. Probablemente sea mejor escribir otro lenguaje de secuencias de comandos con una sintaxis similar a una tubería fácil de usar para continuar ;-)[1] Si quieres un ejemplo simple que no esté hecho para la ocasión, puedes mirar mi git gist "exec binary from stdin" con algunas explicaciones en el comentario aquí . La implementación
cat
en su interior para que funcione sin UUoC lo habría hecho 2 o 3 veces más grande.fuente
cat
internos.cat /dev/urandom | cpu_bound_program
ejecuta lasread()
llamadas del sistema en un proceso separado. En Linux, por ejemplo, el trabajo real de la CPU de generar más números aleatorios (cuando el grupo está vacío) se realiza en esa llamada del sistema, por lo que usar un proceso separado le permite aprovechar un núcleo de CPU separado para generar datos aleatorios como entrada. Por ejemplo, en ¿Cuál es la forma más rápida de generar un archivo de texto de 1 GB que contenga dígitos aleatorios?lseek
que no funcionará.cat foo.mp4 | mpv -
funcionará, pero no puede buscar hacia atrás más allá del búfer de caché de mpv o mplayer. Pero con la entrada redirigida desde un archivo, puede hacerlo.cat | mpv -
es una forma de verificar si un MP4 tiene sumoov
átomo al comienzo del archivo, para que pueda reproducirse sin buscar al final y viceversa (es decir, si es adecuado para la transmisión). Es fácil imaginar otros casos en los que desea probar un programa en busca de archivos no buscables ejecutándolo/dev/stdin
concat
un redireccionamiento.xargs cat | somecmd
. Si las rutas de archivo se extienden más allá del límite del búfer de comando,xargs
pueden ejecutarsecat
varias veces, lo que resulta en una secuencia continua, mientras que el usoxargs somecmd
directo a menudo falla porquesomecmd
no se puede ejecutar en múltiplos para lograr un resultado perfecto.Porque detectar un gato inútil es realmente muy difícil.
Tenía un script de shell donde escribí
La secuencia de comandos shell falló en la producción si el
cat
fue eliminado porque se invoca a travéssu -c 'script.sh' someuser
. Lo aparentemente superfluocat
causó que el propietario de la entrada estándar cambiara al usuario que el script se estaba ejecutando, de modo que su reapertura/proc
funcionó.fuente
cat
seguido de exactamente un parámetro, por lo que el shell debe usar uncat
ejecutable real en lugar de un acceso directo optimizado. Sin embargo, un buen punto sobre credenciales posiblemente diferentes o stdin no estándar para procesos reales.tl; dr: Los depósitos no lo hacen automáticamente porque los costos exceden los beneficios probables.
Otras respuestas han señalado la diferencia técnica entre stdin ser una tubería y ser un archivo. Teniendo esto en cuenta, el shell podría hacer uno de:
cat
como un archivo incorporado, conservando la distinción entre el archivo y la tubería. Esto ahorraría el costo de un ejecutivo y tal vez, posiblemente, un tenedor.A continuación, debe considerar los costos y beneficios de cada enfoque. Los beneficios son bastante simples:
cat
)Por lo tanto, ahorra un poco de tiempo y memoria de la CPU, especialmente si puede evitar la bifurcación. Por supuesto, solo guarda este tiempo y memoria cuando la función se usa realmente. Y realmente solo está ahorrando el tiempo fork / exec; con archivos más grandes, el tiempo es principalmente el tiempo de E / S (es decir, gato leyendo un archivo desde el disco). Entonces, debe preguntar: ¿con qué frecuencia se
cat
usa (inútilmente) en scripts de shell donde el rendimiento realmente importa? Compárelo con otras construcciones de conchas comunes comotest
: es difícil imaginar quecat
se use (inútilmente) incluso una décima parte tan a menudo comotest
se usa en lugares importantes. Eso es una suposición, no lo he medido, que es algo que querría hacer antes de cualquier intento de implementación. (O de manera similar, pedirle a otra persona que implemente, por ejemplo, una solicitud de función).A continuación, pregunta: ¿cuáles son los costos? Los dos costos que vienen a la mente son (a) código adicional en el shell, que aumenta su tamaño (y, por lo tanto, posiblemente el uso de memoria), requiere más trabajo de mantenimiento, es otro lugar para errores, etc .; y (b) sorpresas de compatibilidad con versiones anteriores, POSIX
cat
omite muchas características de, por ejemplo, GNU coreutilscat
, por lo que debe tener cuidado con exactamente locat
que implementaría el incorporado.La opción de construcción adicional probablemente no sea tan mala: agregar una construcción más donde ya exista un montón. Si tuviera datos de perfil que mostraran que ayudaría, probablemente podría convencer a los autores de su shell favorito para que lo agreguen.
En cuanto al análisis de la tubería, no creo que los shells hagan algo como esto actualmente (algunos reconocen el final de una tubería y pueden evitar una bifurcación). Esencialmente, estaría agregando un optimizador (primitivo) al shell; Los optimizadores a menudo resultan ser un código complicado y la fuente de muchos errores. Y esos errores pueden ser sorprendentes: pequeños cambios en el script de shell podrían terminar evitando o activando el error.
Posdata: puede aplicar un análisis similar a sus usos inútiles de cat. Beneficios: más fácil de leer (aunque si command1 tomará un archivo como argumento, probablemente no). Costos: fork extra y exec (y si command1 puede tomar un archivo como argumento, probablemente mensajes de error más confusos). Si su análisis le dice que use gato inútilmente, continúe.
fuente
El
cat
comando puede aceptar-
como marcador para stdin . ( POSIX , " Si un archivo es '-', la utilidad cat leerá de la entrada estándar en ese punto de la secuencia "). Esto permite el manejo simple de un archivo o stdin donde, de lo contrario, esto no se permitiría.Considere estas dos alternativas triviales, donde el argumento de shell
$1
es-
:Otro momento
cat
útil es cuando se usa intencionalmente como un no-op simplemente para mantener la sintaxis de shell:Finalmente, creo que el único momento en que realmente se puede invocar correctamente UUOC es cuando
cat
se usa con un nombre de archivo que se sabe que es un archivo normal (es decir, no un dispositivo o canalización con nombre), y que no se dan banderas al comando:En cualquier otra situación, las oroperties de
cat
sí mismo pueden ser necesarias.fuente
El comando cat puede hacer cosas que el shell no necesariamente puede hacer (o al menos, no puede hacer fácilmente). Por ejemplo, suponga que desea imprimir caracteres que de otro modo serían invisibles, como pestañas, retornos de carro o líneas nuevas. * Podría * haber una manera de hacerlo con solo comandos integrados de shell, pero no puedo pensar en ninguno fuera de mi cabeza. La versión GNU de cat puede hacerlo con el
-A
argumento o los-v -E -T
argumentos (aunque no sé sobre otras versiones de cat). También puede prefijar cada línea con un número de línea usando-n
(nuevamente, IDK si las versiones que no son GNU pueden hacer esto).Otra ventaja de cat es que puede leer fácilmente múltiples archivos. Para hacerlo, uno simplemente puede escribir
cat file1 file2 file3
. Para hacer lo mismo con un shell, las cosas se pondrían difíciles, aunque un ciclo cuidadosamente diseñado probablemente podría lograr el mismo resultado. Dicho esto, ¿realmente quieres tomarte el tiempo de escribir un ciclo así, cuando existe una alternativa tan simple? ¡Yo no!Leer archivos con cat probablemente usaría menos CPU que el shell, ya que cat es un programa precompilado (la excepción obvia es cualquier shell que tenga un gato incorporado). Al leer un gran grupo de archivos, esto puede ser evidente, pero nunca lo he hecho en mis máquinas, por lo que no puedo estar seguro.
El comando cat también puede ser útil para forzar a un comando a aceptar entradas estándar en casos en los que no lo haga. Considera lo siguiente:
echo 8 | sleep
El comando "sleep" no aceptará el número "8", ya que nunca tuvo la intención de aceptar una entrada estándar. Por lo tanto, el sueño no tendrá en cuenta esa entrada, se quejará de la falta de argumentos y saldrá. Sin embargo, si uno escribe:
echo 8 | sleep $(cat)
Muchos proyectiles expandirán esto
sleep 8
y el sueño esperará 8 segundos antes de salir. También puedes hacer algo similar con ssh:command | ssh 1.2.3.4 'cat >> example-file'
Este comando con un archivo de ejemplo adjunto en la máquina con la dirección de 1.2.3.4 con lo que salga del "comando".
Y eso (probablemente) solo está rascando la superficie. Estoy seguro de que podría encontrar más ejemplos de gatos útiles si quisiera, pero esta publicación es lo suficientemente larga como es. Entonces, concluiré diciendo esto: pedirle al shell que anticipe todos estos escenarios (y varios otros) no es realmente factible.
fuente
Recuerde que un usuario podría tener un
cat
en su$PATH
que no es exactamente el POSIXcat
(pero quizás alguna variante que podría registrar algo en alguna parte). En ese caso, no desea que el shell lo elimine.El
PATH
podría cambiar dinámicamente, y entoncescat
no es lo que crees que es. Sería bastante difícil escribir un shell haciendo la optimización que sueñas.Además, en la práctica,
cat
es un programa bastante rápido. Hay pocas razones prácticas (excepto la estética) para evitarlo.Vea también la excelente charla del infierno Parsing POSIX [s] de Yann Regis-Gianas en FOSDEM2018. Proporciona otras buenas razones para evitar intentar hacer lo que sueñas en una concha.
Si el rendimiento fuera realmente un problema para los shells, alguien habría propuesto un shell que utiliza una sofisticada optimización del compilador de todo el programa, análisis de código fuente estático y técnicas de compilación justo a tiempo (estos tres dominios tienen décadas de progreso y publicaciones científicas y dedicadas conferencias, por ejemplo, bajo SIGPLAN ). Lamentablemente, incluso como un tema de investigación interesante, que actualmente no está financiado por agencias de investigación o capitalistas de riesgo, y deduzco que simplemente no vale la pena el esfuerzo. En otras palabras, probablemente no haya un mercado significativo para optimizar los depósitos . Si tiene medio millón de euros para gastar en dicha investigación, encontrará fácilmente a alguien que lo haga, y creo que daría resultados valiosos.
Desde un punto de vista práctico, la reescritura, para mejorar su rendimiento, se hace comúnmente un pequeño script de shell (un centenar de líneas) en cualquier lenguaje de script mejor (Python, AWK, Guile, ...). Y no es razonable (por muchas razones de ingeniería de software) escribir scripts de shell grandes: cuando está escribiendo un script de shell que supera las cien líneas, debe considerar reescribirlo (incluso por razones de legibilidad y mantenimiento) en un lenguaje más adecuado : como lenguaje de programación, el shell es muy pobre. Sin embargo, hay muchos scripts de shell generados de gran tamaño , y por buenas razones (por ejemplo,
configure
scripts generados de autoconfiguración de GNU ).Con respecto a los archivos de texto enormes, pasarlos
cat
como un solo argumento no es una buena práctica, y la mayoría de los administradores de sistemas lo saben (cuando cualquier script de shell tarda más de un minuto en ejecutarse, comienza a considerar la optimización). Para archivos de gigabytes grandes, nuncacat
es la buena herramienta para procesarlos.fuente
cat some-huge-log | tail -n 5
correr (dondetail -n 5 some-huge-log
podría saltar directamente al final, mientras quecat
solo lee de adelante hacia atrás) no estaría de acuerdo.cat
un archivo de texto grande en un rango de decenas de GB (que fue creado para probar) lleva un poco más de tiempo. No lo recomendaríaAgregando a la respuesta de @Kusalananda (y el comentario de @alephzero), cat podría ser cualquier cosa:
o
No hay ninguna razón para que cat (solo) o / usr / bin / cat en el sistema sea realmente la herramienta concatenada cat.
fuente
cat
está definido por POSIX, por lo que no debería ser muy diferente.PATH=/home/Joshua/bin:$PATH cat ...
¿Estás seguro de que sabes lo quecat
hace ahora?cat
se puede anular, pero también sabemos que no debería reemplazarse sin motivo por algo más. Mi comentario señala que POSIX exige un comportamiento particular (subconjunto de) que razonablemente puede esperarse que exista. A veces, he escrito un script de shell que extiende el comportamiento de una utilidad estándar. En este caso, el script de shell actuó y se comportó como la herramienta que reemplazó, excepto que tenía capacidades adicionales./bin/cat
. (Y lo convertiría en una opción que podría desactivar). O crearíacat
un shell incorporado (¿que quizás recurra a/bin/cat
múltiples argumentos?) Para que los usuarios puedan controlar si querían o no la versión externa normal camino, conenable cat
. Me gusta parakill
. (Estaba pensando que bashcommand cat
funcionaría, pero eso no se salta los builtins)cat
en ese entorno ya no se refiere a lo habitualcat
. Obviamente, la optimización debe implementarse después de que se hayan procesado los alias. Considero que los complementos de shell representan comandos en el directorio virtual que siempre se antepone a su ruta. Si desea evitar la versión integrada de shell de cualquier comando (por ejemplotest
), debe usar una variante con una ruta.Dos usos "inútiles" para el gato:
... aquí
cat
se usa para mezclar archivos y entradas canalizadas.... aquí
xargs
puede aceptar un número virtualmente infinito de nombres de archivo y ejecutarsecat
tantas veces como sea necesario mientras hace que todo se comporte como una secuencia. Por lo tanto, esto funciona para listas de archivos grandes donde el uso directo dexargs sort
no lo hace.fuente
cat
se llama con exactamente un argumento. Especialmente en el caso en quesh
se pasa una cadena yxargs
llamarácat
directamente, no hay forma de que el shell pueda usar su implementación incorporada.Además de otras cosas,
cat
-check agregaría sobrecarga de rendimiento adicional y confusión sobre qué uso decat
es realmente inútil, en mi humilde opinión, porque tales comprobaciones pueden ser ineficientes y crear problemas con elcat
uso legítimo .Cuando los comandos tratan con las secuencias estándar, solo tienen que preocuparse por leer / escribir en los descriptores de archivo estándar. Los comandos pueden saber si stdin se puede buscar / buscar o no, lo que indica una tubería o un archivo.
Si agregamos a la mezcla comprobando qué proceso realmente proporciona ese contenido estándar, necesitaremos encontrar el proceso al otro lado de la tubería y aplicar la optimización adecuada. Esto se puede hacer en términos de shell en sí, como se muestra en la publicación SuperUser de Kyle Jones, y en términos de shell que es
como se muestra en la publicación vinculada. Estos son 3 comandos más (tan
fork()
s yexec()
s extra ) y recorridos recursivos (muchasreaddir()
llamadas).En términos de C y el código fuente del shell, el shell ya conoce el proceso secundario, por lo que no hay necesidad de recurrencia, pero ¿cómo sabemos cuándo optimizar y cuándo
cat
es realmente inútil? De hecho, hay usos útiles del gato , comoProbablemente sería un desperdicio y una sobrecarga innecesaria agregar tal optimización al shell. Como ya mencionó la respuesta de Kusalanda, UUOC trata más sobre la falta de comprensión del usuario sobre cómo combinar mejor los comandos para obtener los mejores resultados.
fuente