Perché le utility obbligatorie POSIX non sono integrate nella shell?


45

Lo scopo di questa domanda è rispondere a una curiosità, non risolvere un particolare problema di elaborazione. La domanda è: perché le utility obbligatorie POSIX non sono comunemente integrate nelle implementazioni della shell?

Ad esempio, ho uno script che sostanzialmente legge alcuni piccoli file di testo e verifica che siano formattati correttamente, ma ci vogliono 27 secondi per essere eseguiti, sulla mia macchina, a causa di una notevole quantità di manipolazione delle stringhe. Questa manipolazione di stringhe crea migliaia di nuovi processi chiamando varie utility, quindi la lentezza. Sono abbastanza sicuro che se alcuni dei programmi di utilità sono stati costruiti in, vale a dire grep, sed, cut, tr, e expr, quindi lo script verrebbe eseguito in un secondo o meno (in base alla mia esperienza in C).

Sembra che ci sarebbero molte situazioni in cui la creazione di questi programmi di utilità farebbe la differenza tra il fatto che una soluzione nello script di shell abbia prestazioni accettabili.

Ovviamente, c'è una ragione per cui è stato scelto di non integrare queste utilità. Forse avere una versione di un'utilità a livello di sistema evita di avere più versioni disuguali di tale utilità utilizzate da varie shell. Non riesco davvero a pensare a molte altre ragioni per mantenere il sovraccarico di creare così tanti nuovi processi, e POSIX definisce abbastanza sulle utilità che non sembra un problema avere implementazioni diverse, purché siano POSIX ciascuna conforme. Almeno non è un grosso problema quanto l'inefficienza di avere così tanti processi.


15
Se 27 secondi sono troppo lenti puoi usare Python, Perl o qualche altro linguaggio semi-compilato. In alternativa, pubblica le parti lente dello script e chiedi miglioramenti. È possibile che tu stia utilizzando tre o quattro comandi in cui uno (uno più veloce) potrebbe fare.
roaima,

8
Sfortunatamente le conchiglie non sono state fatte per compiti pesanti, purtroppo e il mondo è cambiato molto dai tempi in cui potevi cavartela con una semplice shell script. Sono d'accordo con roaima - ogni ragionevole amministratore di sistema dovrebbe scegliere Python o Perl e non aspettarsi che la shell gestisca tutto
Sergiy Kolodyazhnyy,

16
Lo scopo principale della shell è eseguire altri programmi, non manipolare direttamente i dati. Nel corso degli anni, alcuni programmi o funzionalità esterni forniti da essi (globbing, aritmetica printf, ecc.) Sono stati incorporati nelle shell quando sono stati ritenuti abbastanza utili.
chepner,

8
Se pubblichi il tuo script su codereview.stackexchange.com, sono sicuro che i revisori potrebbero dare alcuni suggerimenti per accelerare drasticamente il tuo script (o almeno sottolineare perché dovrebbe essere scritto in Python / etc invece che nella shell).
Chepner,

5
@ Kyle: awkè un programma di utilità obbligatoria in POSIX, e particolarmente adatto (che è, molto veloce) per implementare script che altrimenti potrebbero implementare utilizzando sed, cut, tr, grep, e exprin uno script di shell.
Animale nominale

Risposte:


11

Non si prevede che gli script di shell vengano eseguiti con quel tipo di velocità. Se vuoi migliorare la velocità del tuo script, provalo in perl. Se è ancora troppo lento, dovrai passare a un linguaggio tipicamente statico come java oc, oppure scrivere un modulo C per perl che esegue le parti che sono troppo lente.

Shell è il primo livello di prototipazione, se puoi provare il concetto con shell, quindi passa a un linguaggio di scripting migliore che può fare più controlli sui limiti che richiederebbero acri di shell.

Un sistema operativo Unix dovrebbe includere molti piccoli programmi che svolgono compiti ben definiti che compongono un quadro più ampio. Questa è una buona cosa in quanto compartimenta i programmi più grandi. Dai un'occhiata a qmail, per esempio e confrontalo con sendmail. qmail è composto da molti programmi:

http://www.nrg4u.com/qmail/the-big-qmail-picture-103-p1.gif

Sfruttare il demone di rete non ti aiuterebbe a sfruttare il gestore code.


Il PO specificamente NON ha chiesto suggerimenti per migliorare la velocità del codice. La domanda era perché alcune utility non sono integrate come cdo pwd.
Stephen C,

4
Vero. La risposta era esprimere la differenza tra monolitico e compartimentato e mostrare una ragione a questo favore.
Ed Neville,


1
@StephenC cdè un builtin - e in realtà deve esserlo, perché la modifica della directory di lavoro in un sottoprocesso non influisce sui processi parent.
Jonas

67

Perché le utility obbligatorie POSIX non sono integrate nella shell?

Perché per essere conforme a POSIX, è necessario un sistema 1 per fornire la maggior parte delle utility come comandi autonomi.

Il fatto di averli incorporati implicherebbe che devono esistere in due posizioni diverse, all'interno della shell e all'esterno. Ovviamente, sarebbe possibile implementare la versione esterna utilizzando un wrapper di script shell nell'integrato, ma ciò svantaggerebbe le applicazioni non shell che chiamano le utility.

Si noti che BusyBox ha seguito il percorso suggerito implementando internamente molti comandi e fornendo la variante standalone utilizzando i collegamenti a se stesso. Un problema è mentre il set di comandi può essere piuttosto grande, le implementazioni sono spesso un sottoinsieme dello standard quindi non sono conformi.

Si noti inoltre che, almeno ksh93, bashe zshandare oltre, fornendo metodi personalizzati per la shell in esecuzione di comandi incorporati caricare dinamicamente da librerie condivise. Tecnicamente, nulla impedisce a tutte le utility POSIX di essere implementate e rese disponibili come built-in.

Infine, la generazione di nuovi processi è diventata un'operazione abbastanza veloce con i moderni sistemi operativi. Se sei davvero colpito da un problema di prestazioni, potrebbero esserci alcuni miglioramenti per rendere i tuoi script più veloci.

1 POSIX.1-2008

Tuttavia, tutte le utilità standard , inclusi i normali incorporati nella tabella, ma non gli speciali incorporati descritti in Utilità speciali incorporate, devono essere implementate in modo tale da poter essere accessibili tramite la famiglia di funziona come definito nel volume Interfacce di sistema di POSIX.1-2008 e può essere richiamato direttamente dalle utilità standard che lo richiedono (env, find, nice, nohup, time, xargs).


4
Questa è la risposta giusta, ma vorrei solo aggiungere che, poiché l'interfaccia di queste utility è generalmente via stdin / stdout, che anche se ognuna di esse fosse implementata come routine incorporata in bash, avrebbe comunque bisogno biforcarsi e creare pipe per ogni comando in una pipeline comunque, quindi ci sarebbero solo guadagni marginali
Chunko

2
@Chunko Sì. i subshells sono più leggeri dei processi fork / eseguiti.
jlliagre,

3
@slebetman Ti manca il mio punto. I sotto shell non sono né thread né processi eseguiti, indipendentemente dal fatto che siano in esecuzione su Linux o meno. I subshells sono solo il clone dei loro genitori, creati da un fork non seguito da exec; forkè oggi un'operazione molto leggera rispetto a exec.
jlliagre,

3
Ho misurato i noforkbuiltin di busybox come nell'ordine di 10 volte in meno rispetto ai noexecbuiltin, che a loro volta avevano ~ 5 volte in meno di overhead rispetto a fork + exec di un binario separato. Definizioni come da unix.stackexchange.com/a/274322/29483 È interessante notare che busybox non fa noforktutto, anche se so che un po 'di codice busybox viene abbreviato non ripulendo la memoria e si basa solo su un processo di breve durata.
FonteJedi

1
@jlliagre: su Linux un fork crea un processo. Il punto che forse ti manca è che su Linux hanno ottimizzato i processi così tanto che gli sviluppatori hanno stabilito che non vi è ulteriore vantaggio nel creare qualcosa di più leggero. Fondamentalmente in Linux un processo è leggero come un thread.
Slebetman,

9

Dal manuale di riferimento BASH ,

I comandi integrati sono necessari per implementare funzionalità impossibili o scomode da ottenere con utility separate.

Come sono sicuro che hai sentito, la filosofia UNIX si basa fortemente su più applicazioni che hanno tutte funzionalità limitate. Ogni built-in ha un'ottima ragione per cui è integrato. Tutto il resto non lo è. Penso che una classe di domande più interessante sia sulla falsariga di "perché esattamente è pwd integrato?"


2
In una parola: modularità
Peschke,

2
/ bin / pwd esiste. Penso che cdsarebbe un esempio migliore qui di qualcosa che è impossibile da implementare come strumento separato.
Oskar Skog,

1
@OskarSkog Questo era il punto. cddeve essere integrato, pwdno. Quindi perché gli bashimplementatori hanno scelto di includerlo?
Stig Hemmer,


@StigHemmer /bin/bashesiste, ma è ancora incorporato. Vedi l'elenco dei builtin su gnu.org/software/bash/manual/html_node/…
Stephen C

8

I ragazzi di AT&T si sono chiesti la stessa cosa

Se osservi la storia di AT&T Software Toolkit (attualmente inattivo su Github da quando il team principale se ne è andato), questo è esattamente ciò che hanno fatto con la shell AT&T Korn, aka ksh93.

Le prestazioni sono sempre state parte della motivazione per i manutentori di ksh93 e quando si costruisce ksh è possibile scegliere di creare molte utility POSIX comuni come librerie caricate dinamicamente. Associando questi comandi a un nome di directory simile /opt/ast/bin, è possibile controllare quale versione del comando verrà utilizzata, in base alla posizione del nome di tale directory in $PATH.

Esempi:

cat chmod chown cksum cmp cp cut date expr fmt head join ln
mkdir mkfifo mktemp mv nl od paste rm tail tr uniq uuencode wc

L'elenco completo è disponibile nel repository github ast .

Si noti che la maggior parte degli strumenti ast ha la propria provenienza e differirebbe fortemente dalle implementazioni gnu più comuni. Il team di ricerca AT&T ha rispettato gli standard ufficiali, che era il modo per raggiungere l'interoperabilità quando non si poteva condividere il codice.


6

Quindi non abbiamo impiegato risorse per ottimizzare lo strumento originale, per soddisfare ogni desiderio specifico. Immagino che ciò che dobbiamo spiegare è quanto questo desiderio specifico avrebbe avuto un costo da attuare.

POSIX definisce abbastanza le utilità che non sembra essere un problema avere implementazioni diverse.

questa è una pessima ipotesi :-P.

I sistemi Post-POSIX continuano a diventare più potenti e convenienti per buoni motivi; come standard after-the-fact non raggiunge mai effettivamente.

Ubuntu ha iniziato uno sforzo per passare a una shell POSIX ridotta per gli script, per ottimizzare il vecchio processo di avvio di System V init. Non sto dicendo che sia fallito, ma ha innescato molti bug che dovevano essere ripuliti: "bashismi", script che correvano sotto l' /bin/shipotesi che bashfossero disponibili funzionalità.

POSIX sh non è un buon linguaggio di programmazione generico. Il suo scopo principale è di funzionare bene come shell interattiva. Non appena inizi a salvare i tuoi comandi in uno script, tieni presente che ti avvicini a un tarpit di Turing . Ad esempio, non è possibile rilevare guasti nel mezzo di una normale conduttura . bashaggiunto set -o pipefailper questo, ma questo non è in POSIX.

Simili funzioni utili ma non standardizzate sono fornite da quasi tutte le utility più complesse di true.

Per la classe di attività che si delinea, è possibile tracciare una linea di massima per Awk, Perl e oggi Python. Diversi strumenti sono stati creati ed evoluti in modo indipendente. Ti aspetteresti, ad esempio, che GNU Awk venga inserito in un libutilposix esteso?

Non sto dicendo che ora abbiamo un approccio universalmente migliore che posso indicarti. Ho un debole per Python. Awk è sorprendentemente potente, anche se sono stato frustrato da alcune funzionalità specifiche di GNU Awk. Ma il punto è che l'elaborazione di un gran numero di stringhe singolarmente (presumibilmente da linee di file) non era un obiettivo di progettazione della shell POSIX.


Mi chiedo se ci sarebbe qualche difficoltà con una shell che presumerebbe che qualsiasi comando eseguito da un elenco configurabile di posizioni sarebbe trattato come un built-in nei casi in cui la shell ha capito tutto sul comando? Se uno script esegue cat -@fnord foola shell dovrebbe decidere che dal momento che non sa cosa -@significhi che avrebbe bisogno di invocare il comando effettivo, ma dato solo cat <foo >barla shell non dovrebbe aver bisogno di generare un altro processo.
supercat,

1
@supercat complessità.
sourcejedi,

2

C'è anche la domanda di: in quale shell lo costruiresti?

La maggior parte dei sistemi Unix / Linux hanno più shell diverse che sono sviluppate indipendentemente (sh / bash / korn / ???). Se si creano gli strumenti nella shell, si otterrebbe un'implementazione diversa di questi strumenti per ciascuna shell. Ciò causerebbe un sovraccarico e potresti finire con diverse funzionalità / bug, ad esempio grep, a seconda della shell che hai usato per invocarlo.


zsh è piuttosto popolare in alcuni ambienti al giorno d'oggi. Storicamente csh / tcsh ha avuto un grande seguito, ma non penso che tu ne veda molto oggi. E c'è un intero gruppo di conchiglie meno conosciute ...
un CVn del

Modularità. Con i builtin, è necessario ricompilare o reinstallare la shell ogni volta che viene apportata una modifica a uno di questi builtin.
can-ned_food,

1

Molti hanno risposto bene. Intendo solo complimentarmi con quelle risposte. Penso che la filosofia UNIX sia che uno strumento dovrebbe fare una cosa e farlo bene. Se si cerca di creare uno strumento onnicomprensivo, ci sono molti più posti per fallire. Limitare la funzionalità in questo modo rende un set di strumenti affidabile.

Inoltre, considera, se funzionalità come sed o grep fossero integrate nella shell, sarebbe facile invocare dalla riga di comando quando vuoi?

In conclusione, considera, alcune delle funzionalità che desideri essere in BASH, sono in BASH . Ad esempio, l'abilità per la corrispondenza RE in BASH è implementata usando l' operatore binario = ~ (vedere Shell Grammar nella Pagina del manuale per ulteriori informazioni, in particolare, fare riferimento alla discussione del costrutto [[]] per if ). Come esempio molto veloce, supponiamo che stia cercando un file per 2 cifre esadecimali:

while read line; do
    if [[ $line =~ 0x[[:xdigit:]]{2} ]]; then
        # do something important with it
    fi
done < input_file.txt

Per quanto riguarda la funzionalità sed-like , guarda sotto Parameter Expansion nella sezione Expansion della stessa pagina man. Vedrai moltissime cose che puoi fare che ricordano sed. Molto spesso uso sed per apportare alcune modifiche al tipo di sostituzione nel testo. Sulla base di quanto sopra:

# this does not take into account the saving of the substituted text
# it shows only how to do it
while read line; do
    ${line/pattern/substitution}
done < input_file.txt

Alla fine però, quanto sopra è "migliore" di?

grep -E "[[:xdigit:]]{3}" input_file.txt
sed -e 's/pattern/substitution/' input_file.txt

Un argomento contro l'ultima domanda è disponibile su unix.stackexchange.com/questions/169716/…
phk,

1

Questo è, immagino, un incidente storico.

Quando UNIX fu creato alla fine degli anni '60 e all'inizio degli anni '70, i computer non avevano quasi la stessa memoria di oggi. Sarebbe stato possibile, al momento, implementare tutte queste funzionalità come built-in della shell, ma a causa delle limitazioni della memoria, avrebbero dovuto limitare la quantità di funzionalità che potevano implementare o rischiare di esaurire la memoria e / o scambiare il cestino i problemi.

D'altra parte, implementando la funzionalità data come programmi separati e rendendo le due chiamate di sistema richieste per l'avvio di un nuovo processo il più leggero possibile, potrebbero creare un ambiente di scripting che non presenta questi problemi e che funziona ancora a prezzi ragionevoli velocità.

Naturalmente, una volta implementate queste cose come processi separati, le persone le avvieranno da programmi che non sono shell e quindi dovranno rimanere così, o improvvisamente tutto questo software inizierà a rompersi.

Questo non vuol dire che non è possibile implementare alcune funzionalità due volte, tuttavia, e in effetti alcune shell implementano alcune funzionalità che si suppone siano un programma esterno come built-in della shell; ad esempio, bash implementa il echocomando come incorporato, ma c'è anche a/usr/bin/echo

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.