Quanto sono stabili le API "stdin / stdout" della shell Unix?


20

grepping, awking, sedding e piping sono la routine quotidiana di un utente di qualsiasi sistema operativo simile a Unix, sia esso dalla riga di comando o all'interno di uno script di shell (collettivamente chiamati filtri da ora in poi).

Essenzialmente, quando si lavora con programmi CLI Unix "standard" e builtin di shell (chiamati collettivamente comandi da ora in poi), i filtri necessitano di un formato previsto preciso per stdin, stdout e stderr in ogni fase del filtro per funzionare correttamente. Chiamo questo preciso formato previsto di alcuni comandi un'API di questo comando nel seguito.

Come persona con un background di sviluppo web, paragone tecnicamente questo tipo di raccolta ed elaborazione dei dati con il web scraping , una tecnica molto instabile ogni volta che si verifica il minimo cambiamento nella presentazione dei dati.

La mia domanda ora riguarda la stabilità delle API dei comandi Unix.

  1. I comandi in un sistema operativo simile a Unix aderiscono a una standardizzazione formale rispetto al loro input e output?
  2. Ci sono stati casi nella storia in cui gli aggiornamenti di alcuni comandi importanti hanno causato l'interruzione della funzionalità di alcuni filtri creati utilizzando una versione precedente di detto comando?
  3. I comandi Unix sono maturati nel tempo ed è assolutamente impossibile cambiarli in modo tale che alcuni filtri possano rompersi?
  4. Nel caso in cui i filtri possano rompersi di tanto in tanto a causa della modifica delle API dei comandi, come posso proteggere i miei filtri come sviluppatore da questo problema?

Risposte:


17

Lo standard POSIX 2008 ha una sezione che descrive "Shell and Utilities" . Generalmente, se ti attieni a ciò, i tuoi script dovrebbero essere abbastanza a prova di futuro, tranne forse per le deprecazioni, ma quelli difficilmente si verificano durante la notte, quindi dovresti avere un sacco di tempo per aggiornare i tuoi script.

In alcuni casi in cui il formato di output per una singola utility varia ampiamente tra piattaforme e versioni, lo standard POSIX può includere un'opzione tipicamente chiamata -po -Pche specifica un formato di output garantito e prevedibile. Un esempio di ciò è l' timeutilità , che ha implementazioni molto diverse. Se hai bisogno di un formato API / output stabile, dovresti utilizzarlo time -p.

Se devi usare un'utilità di filtro che non è coperta dallo standard POSIX, allora sei praticamente in balia dei pacchetti di distribuzione / sviluppatori upstream, proprio come sei in balia degli sviluppatori web remoti quando esegui lo scraping web.


12

Proverò a rispondere dalla mia esperienza.

  1. I comandi non aderiscono realmente a una specifica formale, ma aderiscono a un requisito per consumare e generare testo orientato alla linea.

  2. Sì, naturalmente. Prima che le utility GNU diventassero uno standard di fatto, molti fornitori avrebbero avuto un output stravagante, specialmente per quanto riguarda pse ls. Ciò ha causato molto dolore. Oggi solo HP offre comandi stravaganti. Storicamente, le utility Berkeley Software Distribution (BSD) sono state una rottura importante con il passato. Le specifiche POSIX sono state una rottura con il passato, ma ora sono ampiamente accettate.

  3. I comandi Unix sono effettivamente maturati nel tempo. Non è ancora impossibile interrompere alcuni script scritti per una versione precedente. Pensa alla recente tendenza verso UTF-8 come codifica di file di testo. Questo cambiamento ha reso necessario cambiare utilità di base come tr. In passato, il testo semplice era quasi sempre ASCII (o qualcosa di simile), quindi le lettere maiuscole formavano un intervallo numerico, così come le lettere minuscole. Questo non è più vero con UTF-8, quindi traccetta diverse opzioni della riga di comando per specificare cose come "maiuscole" o "alfanumeriche".

  4. Uno dei modi migliori per "rinforzare" i filtri è non dipendere da un particolare layout di testo. Ad esempio, non farlo cut -c10-24, che dipende dalle posizioni di una linea. Usa cut -f2invece, che taglierebbe il secondo campo separato da tabulazioni. awksuddivide qualsiasi riga di input in $ 1, $ 2, $ 3 ... che sono separati da spazi bianchi per impostazione predefinita. Dipende da concetti di livello superiore come "campi" piuttosto che da concetti di livello inferiore come la posizione della colonna. Inoltre, usa le espressioni regolari: sede awkpuoi fare entrambe le cose con espressioni regolari che non si preoccupano della varianza nell'input. Un altro trucco è quello di elaborare l'input in qualcosa di cui il filtro può essere esigente. Utilizzare tr -cs '[a-zA-z0-9]' '[\n]'per dividere il testo in una sola parola per riga, senza punteggiatura. Non devi semplicemente


9

Innanzitutto, risposte molto brevi alle tue domande:

  1. Standardizzazione formale delle convenzioni di input / output: no
  2. Rottura in passato dovuta alla modifica dell'output:
  3. Assolutamente impossibile rompere i filtri futuri: no
  4. Come posso proteggermi dai cambiamenti: essere prudente

Quando dici "API", stai usando un termine che (nel bene o nel male) implica troppa formalità attorno alle convenzioni di input / output del filtro. Molto (e intendo "molto" in senso lato), sono le convenzioni primarie per i dati che possono essere filtrati facilmente

  • ogni riga di input è un record completo
  • all'interno di ciascun record, i campi sono separati da un carattere delimitatore noto

Un classico esempio sarebbe il formato di / etc / passwd. Ma queste convenzioni predefinite sono probabilmente violate in una certa misura più spesso di quanto siano seguite alla lettera.

  • Esistono molti filtri (spesso scritti in awk o perl) che analizzano i formati di input multilinea.
  • Esistono molti schemi di input (ad es., / Var / log / messages) in cui non esiste una struttura di campo ben definita e devono essere utilizzate tecniche più generali basate sull'espressione regolare.

La tua quarta domanda, come proteggerti dalle variazioni nella struttura di output, è davvero l'unica su cui puoi fare qualcosa.

  • Come diceva @ jw013 , guarda cosa dicono gli standard posix. Ovviamente, posix non specifica tutti i comandi che vorrai usare come fonti di input.
  • Se vuoi che i tuoi script siano portabili, cerca di evitare le idiosincrasie della versione di qualche comando che ti capita di avere. Ad esempio, molte versioni GNU di comandi unix standard hanno estensioni non standard. Questi possono essere utili, ma dovresti evitarli se vuoi la massima portabilità.
  • Prova a scoprire quali sottoinsiemi di argomenti di comandi e formati di output tendono ad essere stabili tra le piattaforme. Sfortunatamente, ciò richiede l'accesso a più piattaforme nel tempo, poiché queste differenze non verranno annotate da nessuna parte, nemmeno in modo informale.

Alla fine, non puoi proteggerti completamente dai problemi di cui sei preoccupato, e non c'è un solo posto in cui cercare un'affermazione "definitiva" su cosa dovrebbe fare un determinato comando. Per molti script di shell, in particolare quelli scritti per uso personale o su piccola scala, questo semplicemente non è un problema


5

Coprendo solo 1) della tua domanda.

Naturalmente le API possono sempre cambiare a piacimento dei loro creatori e quindi rompere il software dipendente, in qualsiasi lingua. Detto questo, la grande idea delle "API" degli I / O degli strumenti Unix è che praticamente non ce ne sono (forse 0x0acome fine della linea). Un buon script filtra i dati con gli strumenti Unix invece di crearli. Ciò significa che il tuo script potrebbe interrompersi perché le specifiche di input o output sono cambiate, ma non perché il formato I / O (di nuovo, non ce n'è davvero uno) dei singoli strumenti utilizzati nello script è cambiato (perché qualcosa che in realtà non esiste non posso davvero cambiare).

Scorrendo un elenco di strumenti di base, ce ne sono alcuni che attribuirei anche a produttore , invece di filtrare solo:

  • wc - stampa il numero di byte, parole, righe - formato molto semplice, quindi assolutamente improbabile che cambi, e inoltre non molto probabilmente usato in uno script.
  • diff - ci sono stati diversi formati di output ma non ho sentito parlare di problemi. Inoltre, normalmente non utilizzato senza supervisione.
  • data - Ora qui dobbiamo davvero prenderci cura di ciò che produciamo, soprattutto per quanto riguarda le impostazioni locali del sistema. Ma per il resto il formato di output è RFC dato che non lo si specifica esattamente da soli.
  • cal - non parliamone, so che il formato di output differisce molto da un sistema all'altro.
  • ls , che , w , ultima - non posso aiuto se si vuole analizzare ls, semplicemente non ero destinato a essere. Inoltre, chi, w, ultimo, sono lister più interattivi; Se li usi in uno script devi prenderti cura di quello che fai.
  • il tempo è stato indicato in un altro post. Ma sì, è lo stesso di ls. Altro per uso interattivo / locale. E il built-in bash è molto diverso dalla versione GNU, e la versione GNU ha avuto bug non corretti per molti anni. Basta non fare affidamento su di esso.

Ecco alcuni strumenti che prevedono un particolare formato di input più specifico di un flusso di byte:

  • bc , dc - calcolatrici. Già dal lato più intricato delle cose (davvero, non le uso negli script) e presumibilmente formati I / O molto stabili.

C'è un'altra area con un rischio molto più elevato di rottura, ovvero l'interfaccia della riga di comando. La maggior parte degli strumenti ha funzionalità diverse sia tra i sistemi sia attraverso la sequenza temporale. Ne sono esempi

  • Tutti gli strumenti che usano regex - regex possono cambiare significato in base alle impostazioni locali del sistema (ad esempio LC_COLLATE) e ci sono molte sottigliezze e peculiarità nelle implementazioni di regex.
  • Semplicemente non usare interruttori di fantasia. Ad man 1p findesempio, si può facilmente usare per leggere la manpage di POSIX find invece della manpage di sistema. Sul mio sistema, ho bisogno che manpages-posix sia installato.

E anche quando si utilizzano tali interruttori, normalmente non vengono introdotti sottilmente errori e non si avvelenano i dati. La maggior parte dei programmi semplicemente rifiuta di funzionare con un interruttore sconosciuto.

Per concludere, direi che la shell ha effettivamente il potenziale di essere uno dei linguaggi più portatili (è portatile quando si esegue lo script in modo portabile). Confronta con i tuoi linguaggi di script preferiti in cui si verificano errori sottili o il tuo programma compilato preferito che verrà compilato.

Inoltre, nei rari luoghi in cui possono verificarsi rotture a causa di incompatibilità, probabilmente non a causa del tempo indotto, ma a causa della diversità tra i diversi sistemi (il che significa che se funziona per te, lo ha fatto 20 anni prima e lo farà in 20 anni , pure). Questo è un corollario della semplicità degli strumenti.


1

Esistono solo standard IO di fatto: spazi bianchi e output nullo separato.

Per quanto riguarda la compatibilità, di solito torniamo a verificare i numeri di versione dei singoli filtri. Non che cambino molto, ma quando si desidera utilizzare una funzionalità nuova di zecca e si desidera comunque eseguire lo script su versioni precedenti, è necessario "ifdef" in qualche modo. Non esiste praticamente alcun meccanismo di segnalazione delle capacità, ad eccezione della scrittura manuale dei casi di test.


0

Gli script si rompono, alcuni più spesso di altri. Il vecchio e famoso software tende a rimanere relativamente lo stesso e spesso presenta flag di compatibilità quando cambia comunque.

Gli script scritti su un sistema tendono a continuare a funzionare, ma spesso ne rompono un altro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.