¿Qué tan estables son las "API stdin / stdout" de shell de Unix?

20

grepping, awking, sedding y piping son la rutina diaria de un usuario de cualquier sistema operativo similar a Unix, ya sea en la línea de comando o dentro de un script de shell (colectivamente llamados filtros de ahora en adelante).

En esencia, cuando se trabaja con programas "estándar" de Unix CLI y con builtins de shell (denominados colectivamente comandos de ahora en adelante), los filtros necesitan un formato esperado preciso para stdin, stdout y stderr en cada paso del filtro para que funcionen correctamente. Llamo a este formato esperado preciso de algún comando una API de este comando a continuación.

Como alguien con experiencia en desarrollo web, comparo este tipo de recopilación y procesamiento de datos técnicamente con el raspado web , una técnica que es muy inestable cada vez que se produce el más mínimo cambio en la presentación de datos.

Mi pregunta ahora se refiere a la estabilidad de las API de comandos de Unix.

  1. ¿Los comandos en un sistema operativo tipo Unix se adhieren a una estandarización formal con respecto a su entrada y salida?
  2. ¿Ha habido casos en el historial en los que las actualizaciones de algún comando importante causaron la interrupción de la funcionalidad de algún filtro que se creó con una versión anterior de dicho comando?
  3. ¿Han madurado los comandos de Unix con el tiempo que es absolutamente imposible cambiar de tal manera que se pueda romper algún filtro?
  4. En caso de que los filtros se rompan de vez en cuando debido al cambio de las API de comandos, ¿cómo puedo, como desarrollador, proteger mis filtros contra este problema?
Abdull
fuente

Respuestas:

17

El estándar POSIX 2008 tiene una sección que describe "Shell y utilidades" . En general, si se adhiere a eso, sus scripts deberían ser bastante seguros para el futuro, excepto posiblemente por desaprobaciones, pero eso difícilmente sucederá de la noche a la mañana, por lo que debería tener suficiente tiempo para actualizar sus scripts.

En algunos casos en los que el formato de salida para una única utilidad varía ampliamente entre plataformas y versiones, el estándar POSIX puede incluir una opción típicamente llamada -po -Pque especifica un formato de salida garantizado y predecible. Un ejemplo de esto es la timeutilidad , que tiene implementaciones muy diferentes. Si necesita un formato de salida / API estable, lo usaría time -p.

Si necesita usar una utilidad de filtro que no esté cubierta por el estándar POSIX, entonces está a merced de los empaquetadores de distribución / desarrolladores ascendentes, así como está a merced de los desarrolladores web remotos cuando realizan el raspado web.

jw013
fuente
12

Trataré de responder desde mi experiencia.

  1. Los comandos realmente no se adhieren a una especificación formal, pero sí se adhieren a un requisito para consumir y generar texto orientado a líneas.

  2. Sí, por supuesto. Antes de que las utilidades GNU se convirtieran en un estándar de facto, muchos proveedores tendrían una producción peculiar, especialmente con respecto a psy ls. Esto causó mucho dolor. Hoy, solo HP ofrece comandos súper extravagantes. Históricamente, las utilidades de Berkeley Software Distribution (BSD) fueron una ruptura importante con el pasado. La especificación POSIX fue una ruptura con el pasado, pero ahora es ampliamente aceptada.

  3. Los comandos de Unix han madurado con el tiempo. Todavía no es imposible romper alguna secuencia de comandos escrita para una versión anterior. Piense en la tendencia reciente hacia UTF-8 como una codificación de archivo de texto. Este cambio requirió cambiar utilidades básicas como tr. En el pasado, el texto simple casi siempre era ASCII (o algo parecido), por lo que las letras mayúsculas formaban un rango numérico, al igual que las letras minúsculas. Eso ya no es cierto con UTF-8, por lo trque acepta diferentes opciones de línea de comandos para especificar cosas como "mayúsculas" o "alfanuméricas".

  4. Una de las mejores formas de "reforzar" sus filtros es no depender de un diseño de texto particular. Por ejemplo, no hacer cut -c10-24, que depende de las posiciones de una línea. Use en su cut -f2lugar, lo que cortaría el segundo campo separado por tabuladores. awkdivide cualquier línea de entrada en $ 1, $ 2, $ 3 ... que están separados por espacios en blanco por defecto. Depende de conceptos de nivel superior como "campos" en lugar de conceptos de nivel inferior como posición de columna. Además, use expresiones regulares: sedy awkambos pueden hacer cosas con expresiones regulares que no se preocupan por alguna variación en la entrada. Otro truco es procesar la entrada en algo cuyo formato puede ser exigente con su filtro. Use tr -cs '[a-zA-z0-9]' '[\n]'para dividir el texto en una sola palabra por línea, sin puntuación. Simplemente no

Bruce Ediger
fuente
9

Primero, respuestas muy breves a sus preguntas:

  1. Normalización formal de convenciones de entrada / salida: no
  2. Rotura en el pasado debido a cambios en la producción:
  3. Absolutamente imposible romper futuros filtros: no
  4. ¿Cómo puedo protegerme de los cambios? Ser conservador.

Cuando dice "API", está utilizando un término que (para bien o para mal) implica demasiada formalidad en torno a las convenciones de entrada / salida de filtro. Muy (y quiero decir "muy") en términos generales, las convenciones principales para los datos que son susceptibles de filtrado fácil son

  • cada línea de entrada es un registro completo
  • dentro de cada registro, los campos están separados por un caracter delimitador conocido

Un ejemplo clásico sería el formato de / etc / passwd. Pero, estas convenciones predeterminadas probablemente se violan hasta cierto punto con más frecuencia de lo que se siguen al pie de la letra.

  • Hay muchos filtros (a menudo escritos en awk o perl) que analizan formatos de entrada multilínea.
  • Hay muchos patrones de entrada (por ejemplo, / var / log / messages) donde no hay una estructura de campo bien definida, y se deben usar técnicas más generales basadas en expresiones regulares.

Su cuarta pregunta, cómo protegerse contra las variaciones en la estructura de salida, es realmente la única sobre la que puede hacer algo.

  • Como dijo @ jw013 , mira lo que dicen los estándares posix. Por supuesto, posix no especifica todos los comandos que querrá usar como fuentes de entrada.
  • Si desea que sus scripts sean portables, intente evitar las idiosincrasias de cualquier versión del comando que tenga instalado. Por ejemplo, muchas versiones de GNU de comandos estándar de Unix tienen extensiones no estándar. Estos pueden ser útiles, pero debe evitarlos si desea la máxima portabilidad.
  • Intente aprender qué subconjuntos de argumentos de comandos y formatos de salida tienden a ser estables en todas las plataformas. Desafortunadamente, esto requiere acceso a múltiples plataformas junto con el tiempo, porque estas diferencias no se escribirán en ningún lado, ni siquiera de manera informal.

Al final, no puede protegerse por completo de los problemas que le preocupan, y no hay un solo lugar para buscar una declaración "definitiva" de lo que debe hacer un determinado comando. Para muchos scripts de shell, especialmente aquellos escritos para uso personal o de pequeña escala, esto simplemente no es un problema

Dale Hagglund
fuente
5

Solo cubriendo 1) de su pregunta.

Naturalmente, las API siempre pueden cambiar a voluntad de sus creadores y, por lo tanto, romper el software dependiente en cualquier idioma. Dicho esto, la gran idea de las "API" de E / S de las herramientas de Unix es que prácticamente no hay ninguna (tal vez 0x0acomo final de línea). Un buen script filtra los datos con las herramientas de Unix en lugar de crearlos. Eso significa que su secuencia de comandos puede romperse porque la especificación de entrada o salida cambió, pero no porque el formato de E / S (nuevamente, en realidad no hay una) de las herramientas individuales utilizadas en la secuencia de comandos cambió (porque algo que realmente no existe realmente no puede cambiar).

Al revisar una lista de herramientas básicas, hay algunas que también atribuiría productor , en lugar de solo filtrar:

  • wc - imprime el número de bytes, palabras, líneas - formato muy simple, por lo tanto absolutamente improbable que cambie, y además no es muy probable que se use en un script.
  • diff : han evolucionado diferentes formatos de salida, pero no he oído hablar de ningún problema. Tampoco se usa normalmente sin supervisión.
  • fecha : ahora aquí realmente tenemos que cuidar lo que producimos, especialmente en lo que respecta a la configuración regional del sistema. Pero, de lo contrario, el formato de salida es RFC dado que usted no lo especifica exactamente.
  • cal - no hablemos de eso, sé que el formato de salida difiere mucho entre sistemas.
  • ls , que , w , última - no puedo evitar si se quiere analizar ls, simplemente no estaba destinado a ser. Además, quienes, por último, son más interactivos. Si los usa en un script, debe cuidar lo que hace.
  • el tiempo fue señalado en otro post. Pero sí, es lo mismo que con ls. Más para uso interactivo / local. Y el bash incorporado es muy diferente de la versión de GNU, y la versión de GNU ha tenido errores no corregidos durante muchos años. Simplemente no confíes en ello.

Aquí hay herramientas que esperan un formato de entrada particular más específico que ser una secuencia de bytes:

  • bc , dc - calculadoras. Ya en el lado más hostil de las cosas (realmente, no los uso en scripts), y presumiblemente en formatos de E / S muy estables.

Hay otra área con un riesgo mucho mayor de rotura, a saber, la interfaz de línea de comandos. La mayoría de las herramientas tienen características diferentes tanto en los sistemas como en la línea de tiempo. Ejemplos son

  • Todas las herramientas que utilizan regex : regex puede cambiar el significado en función de la configuración regional del sistema (por ejemplo, LC_COLLATE) y existen muchas sutilezas y particularidades en las implementaciones de expresiones regulares.
  • Simplemente no use interruptores elegantes. Puede usar fácilmente, man 1p findpor ejemplo, para leer la página de manual de búsqueda POSIX en lugar de la página de manual del sistema. En mi sistema, necesito manpages-posix instalado.

E incluso cuando se usan tales interruptores, normalmente no se introducirán errores sutilmente y envenenarán sus datos. La mayoría de los programas simplemente se negarán a trabajar con un interruptor desconocido.

Para concluir, diría que Shell tiene el potencial de ser uno de los lenguajes más portátiles (es portátil cuando se escribe de forma portátil). Compare con sus lenguajes de script favoritos donde ocurren errores sutiles, o su programa compilado favorito que dejará de compilarse.

Además, en los lugares raros donde puede ocurrir la rotura debido a incompatibilidades, probablemente no se deba al tiempo inducido, sino a la diversidad en los diferentes sistemas (es decir, si funciona para usted, lo hizo 20 años antes y lo hará en 20 años). , también). Ese es un corolario de la simplicidad de las herramientas.

Jo So
fuente
1

Solo hay estándares de IO de facto: espacios en blanco y salida separada por nulos.

En cuanto a la compatibilidad, generalmente volvemos a verificar los números de versión de los filtros individuales. No es que cambien mucho, pero cuando quieres usar una nueva característica y aún quieres que el script se ejecute en versiones anteriores, tienes que "ifdef" de alguna manera. Prácticamente no existe un mecanismo de informe de capacidad, salvo para escribir manualmente casos de prueba.

lynxlynxlynx
fuente
0

Los guiones se rompen, algunos más a menudo que otros. El software antiguo y famoso tiende a permanecer relativamente igual, y a menudo tiene indicadores de compatibilidad cuando cambia de todos modos.

Los guiones escritos en un sistema tienden a seguir funcionando, pero a menudo rompen otro.

Alex Chamberlain
fuente