¿Se le permite al shell optimizar comandos de terminación inútiles?

27

Si se le pide a un shell que ejecute un comando probablemente inútil ( o parcialmente inútil ) que se sabe que termina, como cat hugeregularfile.txt > /dev/null, ¿puede omitir la ejecución de ese comando ( o ejecutar un equivalente más barato, por ejemplo touch -a hugeregularfile.txt )?

En términos más generales, ¿es el shell similar a los compiladores de C en el sentido de que puede realizar cualquier transformación en el código fuente, siempre que el comportamiento observable externamente sea como si la máquina abstracta lo evaluara?

EDITAR

Nota Bene: Mi pregunta, como se planteó originalmente, tenía un título que preguntaba si el shell puede hacer estas optimizaciones, no si debería o incluso si existen implementaciones que puedan hacerlas. Me interesa más la teoría que la práctica, aunque ambas son bienvenidas.

Iwillnotexist Idonotexist
fuente
No, el shell no es tan inteligente como los compiladores modernos. De hecho, es bastante tonto. No optimizaría ningún código inútil.
devnull
12
Adivinar cuál es la intención del usuario no es algo que el shell debería hacer. El usuario podría estar tratando de hacer casi cualquier cosa con ese comando, optimizarlo sería lo incorrecto, incluso si fuera posible.
Chris Down
1
No importa decir que si el archivo era un dispositivo, el catting hace una gran diferencia. El shell puede llegar a saber que el archivo es un dispositivo, pero no necesita ser confiable.
yo '
3
Los compiladores de @StephaneChazelas C no necesitan "pedir permiso a alguien" para optimizar sus programas compilados; Hay una regla como si en el estándar C que les permite hacerlo. El estándar POSIX parece haber estandarizado al menos un shell ( pubs.opengroup.org/onlinepubs/009695399/utilities/… ), así como numerosas otras utilidades ( pubs.opengroup.org/onlinepubs/009604499/utilities/wc.html para wc, por ejemplo). Pero, que yo sepa, POSIX no toma posición en la optimización de shell; O lo hace?
Iwillnotexist Idonotexist
2
La optimización está mejorando el rendimiento con accesos directos sin afectar la funcionalidad. Mientras la funcionalidad esté garantizada, no puedo ver la objeción de POSIX. Sin embargo, su optimización propuesta rompería la especificación del gato . Hay palabras específicas en la especificación POSIX que están ahí para acomodar el tipo de optimización realizada por ksh. Como si no dijeran un proceso separado sino un entorno de subshell para permitir optimizaciones de ahorro de fork.
Stéphane Chazelas

Respuestas:

26

No, eso sería una mala idea.

cat hugeregularfile.txt > /dev/nully touch -a hugeregularfile.txtno son lo mismo catleerá todo el archivo, incluso si redirige la salida a /dev/null. Y leer todo el archivo puede ser exactamente lo que quieres. Por ejemplo, para almacenarlo en caché para que las lecturas posteriores sean significativamente más rápidas. El caparazón no puede conocer tu intención.

Del mismo modo, un compilador de C nunca optimizará la lectura de un archivo, incluso si no mira las cosas que lee.

scai
fuente
2
@Iwillnotexist: cada comando útil (excepto posiblemente truey false) tiene efectos secundarios potenciales, y los efectos secundarios son casi siempre el punto de invocar el comando. El shell no podía conocer esos efectos secundarios de antemano (para programas externos como cat) sin resolver el problema de detención. Entonces, con razón, no lo intenta, y asume que quisiste decir lo que dijiste.
cHao
55
@IwillnotexistIdonotexist No, el shell no puede ver todo lo que está por suceder. No tiene idea sobre cat. De hecho, catpodría hacer cualquier cosa, desde formatear su disco duro hasta descargar Internet.
scai
55
"Unix no fue diseñado para evitar que sus usuarios hagan cosas estúpidas, ya que eso también les impediría hacer cosas inteligentes". - Doug Gwyn
Agi Hammerthief
77
@cHao E incluso truey falselisto $?.
Kyle Strand
3
Como @scai señaló anteriormente, los ejecutables no son como palabras clave de lenguaje: caty /dev/nulltienen significados típicos, pero no se garantiza que se comporten de esa manera. Para realizar optimizaciones sin garantizar ningún cambio en el comportamiento esperado, la optimización solo puede permitirse que implique construcciones implementadas dentro del propio shell y no elementos encontrados en el entorno de ejecución ... sin importar cuán intuitivos puedan parecer sus nombres.
andybuckley
20

No, dado que /dev/nulles solo un nombre, que podría usarse para cualquier otro dispositivo o para un archivo distinto de lo que "normalmente" es un sumidero de datos.

Por lo tanto, un shell (o cualquier otro programa) no tiene idea, según el nombre, de si el archivo al que está escribiendo está haciendo algo "real" con los datos. Hay AFAIK y tampoco hay llamadas al sistema que el programa de shell puede hacer, para determinar que, por ejemplo, el descriptor de archivos no está haciendo nada.

Su comparación con la optimización del código ausente en un programa en C no funciona, ya que un shell no tiene la visión general total que un compilador de C tiene sobre un fragmento de código fuente. Un shell no sabe lo suficiente /dev/nullpara optimizar su ejemplo, más como un compilador de C no sabe lo suficiente sobre el código en una función a la que se vincula dinámicamente, para no hacer la llamada.

Anthon
fuente
44
Como resultado, ksh93 tratará /dev/nullespecialmente, a veces. Una unidad incorporada que tiene su stdout dirigida /dev/null, por ejemplo echo foo >/dev/null, no dará lugar a ninguna escritura /dev/null. No hace nada especial si invoca un comando no integrado (como cat file >/dev/null).
Mark Plotnick
De hecho, cattambién podría ser otra cosa. Cualquier otra cosa, de hecho.
orion
3
En realidad /dev/nulles uno de los muy pocos caminos estandarizados , junto con /dev/tty, /dev/console, /tmp, /dev/y /.
Gilles 'SO- deja de ser malvado'
2
@MarkPlotnick En realidad cat es un ksh93 incorporado (no habilitado a menos que lo coloque /opt/ast/binantes /bin(o donde catesté disponible) $PATH). Y sí, aunque cat file > /dev/nullcon ese readcontenido incorporado file, no lo escribe en / dev / null (aunque lo abre y lo establece).
Stéphane Chazelas
14

No optimizará los comandos en ejecución (y ya ha recibido una serie de buenas respuestas que le dicen por qué no debería), pero puede optimizar las horquillas, tuberías / pares de pares, lecturas en algunos casos. El tipo de optimizaciones que puede hacer:

  • Con la mayoría de los shells modernos, el último comando en un script generalmente se ejecutará en el proceso del shell a menos que trapse hayan configurado algunos s. Por ejemplo, en sh -c ls, la mayoría de shlas implementaciones ( bash, mksh, ksh, zsh, yash, algunas versiones de ash) no bifurcar un proceso para ejecución ls.
  • en ksh93, la sustitución de comandos no creará una tubería o bifurcación de un proceso hasta que se llame a un comando externo ( $(echo foo)por ejemplo, se expandirá a foosin una tubería / par de conexiones o bifurcación).
  • la readincorporación de algunos shells ( bash, AT&T ksh) no realizará lecturas de un solo byte si detectan que el stdin es buscable (en cuyo caso harán lecturas grandes y buscarán hasta el final de lo que deben leer).
Stéphane Chazelas
fuente
Me gusta esta respuesta, pero no está claro si esta es la razón original, o si la información se toma de alguna referencia (que me gustaría profundizar)
yoniLavi
3
@yoniYalovitsky, esa es la razón original . ksh93es el shell que lidera el camino en el frente de la optimización, ya que su objetivo es / debía verse a la par con lenguajes de programación como perl. Para que pueda consultar la kshdocumentación, el código (buena suerte) y la lista de correo para obtener más información.
Stéphane Chazelas
1
@HenkLangeveld, sí, puedes verificar eso con lo sh -c 'ps -p "$$"'que te dará psy no shcon esas shimplementaciones, o con strace / truss / tusc ...
Stéphane Chazelas
1
La diferencia entre ksh -c 'ps; ps'y bash -c 'ps; ps' es interesante. Ksh93 va más allá en su optimización.
Henk Langeveld
1
@HenkLangeveld, depende de qué implementación kshestamos hablando aquí. mkshse comporta como bash. Ese comportamiento está destinado principalmente a optimizar cosas como system("some command"). Tenga en cuenta que hay un efecto secundario de esa optimización cuando se trata del estado de salida de los procesos terminados por una señal (en algunos shells). ksh93solía tener un error ya que estaba haciendo la optimización incluso cuando se establecieron trampas.
Stéphane Chazelas
7

Al ver cat hugeregularfile.txt > /dev/null, el shell no puede creer que la acción es inútil, catno es parte del shell y podría hacer cualquier cosa en teoría, y también en la práctica.

Por ejemplo, el usuario puede haber cambiado el nombre del ejecutable rma cat, y de repente la línea realiza un comportamiento observable externamente, es decir, eliminando el archivo.

El usuario puede haber compilado una versión catque entra en un bucle infinito, por lo que el shell no puede asumir que se 'sabe que termina' como usted sugiere.

Es posible que alguien haya instalado una versión catque funcione según lo previsto, pero con un efecto secundario adicional de instalar un rootkit si alguna vez se ejecuta con los privilegios adecuados; nuevamente, el shell debería ejecutarlo debidamente.

Pedro es
fuente
2
En realidad, mkshde hecho se optimiza V=$(cat file)al convertirlo en un generador incorporado. Por lo tanto, el shell puede optimizarlo, pero no transformarlo en solo a touch -a.
Steve Schnepp
1
@SteveSchnepp, cat está incorporado mksh, pero ese recurso recurre al sistema catsi pasa alguna opción, por lo que con GNU cat, mksh -c 'cat /dev/null --help'no produce el mismo resultado que bash -c 'cat /dev/null --help', pero mksh -c 'cat --help /dev/null'le da lo mismo que bash -c 'cat --help /dev/null'(ya que el mkshgato incorporado analiza las opciones POSIX manera, mientras que GNU cat los analiza de la manera GNU).
Stéphane Chazelas
En bash y ksh93, V=$(cat file)se puede optimizar con V=$(< file). Esto acelera las cosas incluso sin una construcción cat.
Henk Langeveld