GNU/Linux >> Tutoriels Linux >  >> Linux

Comment Linux gère-t-il les scripts shell ?

Si vous utilisez strace vous pouvez voir comment un script shell est exécuté lorsqu'il est exécuté.

Exemple

Supposons que j'ai ce script shell.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Exécution en utilisant strace :

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

Jetez un œil à l'intérieur du strace.log le fichier révèle ce qui suit.

...
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)     
...

Une fois le fichier lu, il est ensuite exécuté :

...
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)                           = ?

Dans ce qui précède, nous pouvons clairement voir que le script entier semble être lu comme une seule entité, puis exécuté ensuite. Ainsi, il "apparaîtra" au moins dans le cas de Bash, il lit le fichier, puis l'exécute. Donc, vous penseriez que vous pourriez éditer le script pendant qu'il est en cours d'exécution ?

REMARQUE : Ne le faites pas, cependant ! Lisez la suite pour comprendre pourquoi vous ne devriez pas jouer avec un fichier de script en cours d'exécution.

Qu'en est-il des autres interprètes ?

Mais votre question est légèrement décalée. Ce n'est pas Linux qui charge nécessairement le contenu du fichier, c'est l'interpréteur qui charge le contenu, donc c'est vraiment à la façon dont l'interpréteur est implémenté qu'il charge le fichier entièrement ou par blocs ou lignes à la fois.

Alors pourquoi ne pouvons-nous pas modifier le fichier ?

Si vous utilisez un script beaucoup plus volumineux, vous remarquerez que le test ci-dessus est un peu trompeur. En fait, la plupart des interpréteurs chargent leurs fichiers par blocs. C'est assez standard avec de nombreux outils Unix où ils chargent des blocs d'un fichier, le traitent, puis chargent un autre bloc. Vous pouvez voir ce comportement avec ce Q&A U&L que j'ai écrit il y a quelque temps concernant grep , intitulé :Combien de texte grep/egrep consomme-t-il à chaque fois ?.

Exemple

Disons que nous créons le script shell suivant.

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

Résultat dans ce fichier :

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

Qui contient le type de contenu suivant :

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

Maintenant, lorsque vous exécutez ceci en utilisant la même technique ci-dessus avec 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

Vous remarquerez que le fichier est lu par incréments de 8 Ko, donc Bash et les autres shells ne chargeront probablement pas un fichier dans son intégralité, mais le liront plutôt par blocs.

Références

  • Le # ! magie, détails sur le mécanisme shebang/hash-bang sur différentes versions d'Unix

Cela dépend plus du shell que du système d'exploitation.

Selon la version, ksh lire le script à la demande par bloc de 8k ou 64k octets.

bash lire le script ligne par ligne. Cependant, étant donné que les lignes de faits peuvent être de longueur arbitraire, il lit à chaque fois 8176 octets à partir du début de la ligne suivante à analyser.

C'est pour des constructions simples, c'est-à-dire une suite de commandes simples.

Si des commandes structurées du shell sont utilisées (un cas où la réponse acceptée n'est pas prise en compte ) comme un for/do/done boucle, un case/esac switch, un document here, un sous-shell entouré de parenthèses, une définition de fonction, etc. et toute combinaison de ce qui précède, les interpréteurs shell lisent jusqu'à la fin de la construction pour s'assurer d'abord qu'il n'y a pas d'erreur de syntaxe.

Ceci est quelque peu inefficace car le même code peut être lu encore et encore un grand nombre de fois, mais atténué par le fait que ce contenu est normalement mis en cache.

Quel que soit l'interpréteur shell, il est très imprudent de modifier un script shell pendant son exécution car le shell est libre de relire n'importe quelle partie du script et cela peut entraîner des erreurs de syntaxe inattendues en cas de désynchronisation.

Notez également que bash peut planter avec une violation de segmentation lorsqu'il est incapable de stocker une construction de script trop volumineuse que ksh93 peut lire sans problème.


Cela dépend du fonctionnement de l'interpréteur exécutant le script. Le noyau ne fait que remarquer que le fichier à exécuter commence par #! , exécute essentiellement le reste de la ligne en tant que programme et lui donne l'exécutable comme argument. Si l'interpréteur listé ici lit ce fichier ligne par ligne (comme le font les shells interactifs avec ce que vous tapez), c'est ce que vous obtenez (mais les structures de boucle multilignes sont lues et conservées pour être répétées); si l'interpréteur glisse le fichier en mémoire, le traite (peut-être le compile en une représentation intermédiaire, comme le font Perl et Pyton), le fichier est lu en entier avant de s'exécuter.

Si vous supprimez le fichier entre-temps, le fichier n'est pas supprimé tant que l'interpréteur ne le ferme pas (comme toujours, les fichiers disparaissent lorsque la dernière référence, qu'il s'agisse d'une entrée de répertoire ou d'un processus le gardant ouvert) disparaît.


Linux
  1. Comment j'utilise Vagrant avec libvirt

  2. Comment chiffrer des fichiers avec gocryptfs sous Linux

  3. Linux - Comment la charge moyenne fonctionne-t-elle avec les processeurs modernes ?

  4. Comment analyser Json avec des scripts Shell sous Linux ?

  5. Comment changer de shell sous Linux

Comment comparer des répertoires avec Meld sous Linux

Comment exécuter une commande Shell avec Python

Shell Scripting pour les débutants - Comment écrire des scripts Bash sous Linux

Comment sécuriser les serveurs Linux avec SE Linux

Comment changer un shell d'utilisateurs sous Linux

Comment utiliser if-else dans les scripts shell ?