GNU/Linux >> Linux Esercitazione >  >> Linux

In che modo Linux gestisce gli script di shell?

Se usi strace puoi vedere come viene eseguito uno script di shell quando viene eseguito.

Esempio

Diciamo che ho questo script di shell.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Eseguilo usando strace :

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

Uno sguardo all'interno del strace.log file rivela quanto segue.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Una volta che il file è stato letto, viene eseguito:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

In quanto sopra possiamo vedere chiaramente che l'intero script sembra essere letto come una singola entità, e poi eseguito lì dopo. Quindi sarebbe "apparirà" almeno nel caso di Bash che legge il file e poi lo esegue. Quindi penseresti di poter modificare lo script mentre è in esecuzione?

NOTA: Non farlo, però! Continua a leggere per capire perché non dovresti pasticciare con un file di script in esecuzione.

E gli altri interpreti?

Ma la tua domanda è leggermente sbagliata. Non è Linux che carica necessariamente il contenuto del file, è l'interprete che carica il contenuto, quindi dipende davvero da come l'interprete è implementato se carica il file interamente o in blocchi o righe alla volta.

Allora perché non possiamo modificare il file?

Tuttavia, se utilizzi uno script molto più grande, noterai che il test precedente è un po' fuorviante. Infatti la maggior parte degli interpreti carica i propri file in blocchi. Questo è piuttosto standard con molti degli strumenti Unix in cui caricano blocchi di un file, lo elaborano e quindi caricano un altro blocco. Puoi vedere questo comportamento con questa domanda e risposta di U&L che ho scritto qualche tempo fa riguardo a grep , intitolato:Quanto testo consuma ogni volta grep/egrep?.

Esempio

Supponiamo di creare il seguente script di shell.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

Risultato in questo file:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Che contiene il seguente tipo di contenuto:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Ora quando lo esegui usando la stessa tecnica sopra con strace :

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

Noterai che il file viene letto con incrementi di 8 KB, quindi Bash e altre shell probabilmente non caricheranno un file nella sua interezza, piuttosto lo leggeranno in blocchi.

Riferimenti

  • Il #! magic, dettagli sul meccanismo shebang/hash-bang su vari tipi di Unix

Questo dipende più dalla shell che dal sistema operativo.

A seconda della versione, ksh leggere lo script su richiesta per blocco di 8k o 64k byte.

bash leggi lo script riga per riga. Tuttavia, dato che le linee di fatto possono essere di lunghezza arbitraria, legge ogni volta 8176 byte dall'inizio della riga successiva da analizzare.

Questo è per costruzioni semplici, cioè una suite di semplici comandi.

Se vengono utilizzati comandi strutturati in shell (un caso che la risposta accettata manca di considerare ) come un for/do/done ciclo, un case/esac switch, un here document, una subshell racchiusa tra parentesi, una definizione di funzione, ecc. e qualsiasi combinazione di quanto sopra, gli interpreti di shell leggono fino alla fine della costruzione per assicurarsi che non ci siano errori di sintassi.

Questo è alquanto inefficiente in quanto lo stesso codice può essere letto più e più volte un gran numero di volte, ma mitigato dal fatto che questo contenuto è normalmente memorizzato nella cache.

Qualunque sia l'interprete della shell, non è molto saggio modificare uno script della shell mentre viene eseguito poiché la shell è libera di rileggere qualsiasi parte dello script e questo può portare a errori di sintassi imprevisti se non sincronizzati.

Nota anche che bash potrebbe bloccarsi con una violazione della segmentazione quando non è in grado di memorizzare una costruzione di script troppo grande che ksh93 può leggere in modo impeccabile.


Dipende da come funziona l'interprete che esegue lo script. Tutto ciò che il kernel fa è notare che il file da eseguire inizia con #! , essenzialmente esegue il resto della riga come un programma e gli fornisce l'eseguibile come argomento. Se l'interprete elencato legge quel file riga per riga (come fanno le shell interattive con ciò che digiti), questo è ciò che ottieni (ma le strutture di loop multilinea vengono lette e mantenute in giro per la ripetizione); se l'interprete recupera il file in memoria, lo elabora (forse lo compila in una rappresentazione intermedia, come fanno Perl e Pyton) il file viene letto per intero prima dell'esecuzione.

Se nel frattempo elimini il file, il file non viene eliminato fino a quando l'interprete non lo chiude (come sempre, i file scompaiono quando l'ultimo riferimento, sia esso una voce di directory o un processo che lo tiene aperto) scompare.


Linux
  1. Come uso Vagrant con libvirt

  2. Come crittografare i file con gocryptfs su Linux

  3. Nozioni di base su Linux:come scaricare file sulla shell con Wget

  4. Come cambiare una parola in un file con lo script della shell linux

  5. Come controllare la password con Linux?

Come confrontare le directory con Meld su Linux

Come eseguire un comando Shell con Python

Script di shell per principianti - Come scrivere script Bash in Linux

Come proteggere i server Linux con SE Linux

Come modificare una shell utente in Linux

Come utilizzare if-else negli script della shell?