web-dev-qa-db-fra.com

Quel est le but de: (deux points) GNU Bash intégré?

Quel est le but d'une commande qui ne fait rien, n'étant guère plus qu'un chef de commentaire, mais qui est en réalité un shell intégré en soi?

C'est plus lent que d'insérer un commentaire dans vos scripts d'environ 40% par appel, ce qui varie probablement beaucoup selon la taille du commentaire. Les seules raisons possibles que je peux voir sont les suivantes:

# poor man's delay function
for ((x=0;x<100000;++x)) ; do : ; done

# inserting comments into string of commands
command ; command ; : we need a comment in here for some reason ; command

# an alias for `true' (lazy programming)
while : ; do command ; done

Je suppose que ce que je recherche vraiment, c’est l’application historique qu’elle aurait pu avoir.

306
amphetamachine

Historiquement, les commandes Bourne n'avaient pas true et false comme commandes intégrées. true était plutôt simplement associé à :, et false à quelque chose comme let 0.

: est légèrement meilleur que true en ce qui concerne la portabilité vers les anciennes coquilles dérivées de Bourne. Comme exemple simple, envisagez de ne pas avoir l'opérateur de pipeline ! ni l'opérateur de liste || (comme c'était le cas pour certains anciens shells Bourne). Cela laisse la clause else de l'instruction if comme le seul moyen de créer des branches en fonction du statut de sortie:

if command; then :; else ...; fi

Puisque if nécessite une clause then non vide et que les commentaires ne sont pas considérés comme non vides, : sert de non-op.

De nos jours (c'est-à-dire: dans un contexte moderne), vous pouvez généralement utiliser : ou true. Les deux sont spécifiés par POSIX, et certains trouvent true plus facile à lire. Cependant, il y a une différence intéressante: : est un soi-disant POSIX spécial intégré , alors que true est un Intégré régulier .

  • Des composants spéciaux spéciaux doivent être intégrés à Shell; Les fonctions intégrées standard ne sont que "typiquement" intégrées, mais cela n'est pas strictement garanti. Il ne devrait généralement pas y avoir de programme régulier nommé : avec la fonction de true dans PATH de la plupart des systèmes.

  • La différence la plus cruciale est sans doute que, avec les éléments intégrés spéciaux, toute variable définie par l'élément intégré, même dans l'environnement au cours de l'évaluation d'une commande simple, persiste une fois la commande terminée, comme le montre ksh93:

    $ unset x; ( x=hi :; echo "$x" )
    hi
    $ ( x=hi true; echo "$x" )
    
    $
    

    Notez que Zsh ignore cette exigence, tout comme GNU Bash, sauf lorsqu'il fonctionne en mode de compatibilité POSIX, mais tous les autres shells majeurs "dérivés de POSIX sh" respectent cela, notamment dash, ksh93 et ​​mksh.

  • Une autre différence est que les programmes intégrés standard doivent être compatibles avec exec - démontrée ici à l'aide de Bash:

    $ ( exec : )
    -bash: exec: :: not found
    $ ( exec true )
    $
    
  • POSIX note aussi explicitement que : peut être plus rapide que true, bien qu'il s'agisse bien entendu d'un détail spécifique à l'implémentation.

379
earl

Je l'utilise pour activer/désactiver facilement les commandes variables:

#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
    vecho=":"     # no "verbose echo"
else
    vecho=echo    # enable "verbose echo"
fi

$vecho "Verbose echo is ON"

Ainsi

$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON

Cela fait un script propre. Cela ne peut pas être fait avec '#'.

Aussi,

: >afile

est l’un des moyens les plus simples de garantir l’existence d’un fichier mais sa longueur est égale à 0.

58
Kevin Little

Une application utile pour: est si vous souhaitez uniquement utiliser les extensions de paramètres pour leurs effets secondaires plutôt que de transmettre leur résultat à une commande. Dans ce cas, vous utilisez le PE comme argument de: ou false selon que vous voulez un état de sortie égal à 0 ou 1. Par exemple, : "${var:=$1}". Puisque : est intégré, il devrait être assez rapide.

50
ormaaj

: peut également être utilisé pour le commentaire de bloc (similaire à/* */en langage C). Par exemple, si vous souhaitez ignorer un bloc de code dans votre script, procédez comme suit:

: << 'SKIP'

your code block here

SKIP
41
zagpoint

Si vous souhaitez tronquer un fichier à zéro octet, utile pour effacer les journaux, essayez ceci:

:> file.log
30
Ahi Tuna

Cela ressemble à pass en Python.

Une utilisation serait de remplacer une fonction jusqu'à ce qu'elle soit écrite:

future_function () { :; }
28
Dennis Williamson

Deux autres utilisations non mentionnées dans les autres réponses:

Enregistrement

Prenons cet exemple de script:

set -x
: Logging message here
example_command

La première ligne, set -x, permet au shell d’imprimer la commande avant de l’exécuter. C'est une construction assez utile. L'inconvénient est que le type habituel d'instruction echo Log message imprime maintenant le message deux fois. La méthode du côlon contourne cela. Notez que vous devrez toujours échapper des caractères spéciaux, comme vous le feriez pour echo.

Titres d'emplois Cron

Je l'ai vu utilisé dans des tâches cron, comme ceci:

45 10 * * * : Backup for database ; /opt/backup.sh

Il s'agit d'un travail cron qui exécute le script /opt/backup.sh tous les jours à 10h45. L’avantage de cette technique est qu’elle permet d’obtenir de meilleurs sujets d’email lorsque le /opt/backup.sh imprime une sortie.

26
Flimm

Vous pouvez l'utiliser en conjonction avec des backticks (``) pour exécuter une commande sans afficher sa sortie, comme ceci:

: `some_command`

Bien sûr, vous pouvez simplement faire some_command > /dev/null, mais la version de :- est un peu plus courte.

Cela étant dit, je ne recommanderais pas de le faire car cela dérouterait les gens. Cela me vient à l’esprit comme un cas d’utilisation possible.

21
sepp2k

C'est aussi utile pour les programmes polyglottes:

#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }

Ceci est maintenant à la fois un script shell exécutable et un programme JavaScript: ce qui signifie que ./filename.js, sh filename.js et node filename.js fonctionnent tous.

(Certainement un peu étrange utilisation, mais néanmoins efficace.)


Quelques explications, à la demande:

  • Les scripts shell sont évalués ligne par ligne. et la commande exec, lorsqu'elle est exécutée, met fin au shell et remplace son traitement avec la commande résultante. Cela signifie que pour le shell, le programme ressemble à ceci:

    #!/usr/bin/env sh
    ':' //; exec "$(command -v node)" "$0" "$@"
    
  • Tant qu'aucun développement de paramètre ou alias ne se produit dans Word, n'importe lequel Word dans un script shell peut être entouré de guillemets sans en changer le sens; cela signifie que ':' est équivalent à : (nous l'avons uniquement mis entre guillemets pour obtenir la sémantique JavaScript décrite ci-dessous).

  • ... et comme décrit ci-dessus, la première commande de la première ligne est un no-op (elle se traduit par : //, ou si vous préférez citer les mots, ':' '//'. Notez que le // n'a pas de signification particulière ici, comme c'est le cas en JavaScript, c'est juste un mot sans signification qui est jeté.)

  • Enfin, la deuxième commande sur la première ligne (après le point-virgule) est la vraie substance du programme: c’est l’appel exec qui remplace = le shell-script étant appelé, avec un nœud Processus .js appelé pour évaluer le reste du script.

  • Pendant ce temps, la première ligne, en JavaScript, analyse comme un littéral chaîne (':'), puis un commentaire, qui est supprimé. ainsi, en JavaScript, le programme ressemble à ceci:

    ':'
    ~function(){ ... }
    

    Étant donné que le littéral chaîne est sur une ligne à part entière, il s'agit d'une instruction no-op et est donc retiré du programme; cela signifie que toute la ligne est supprimée, laissant niquement votre code de programme (dans cet exemple, le corps function(){ ... }.)

13
ELLIOTTCABLE

Fonctions auto-documentées

Vous pouvez également utiliser : pour incorporer la documentation dans une fonction.

Supposons que vous avez un script de bibliothèque mylib.sh, fournissant une variété de fonctions. Vous pouvez soit source la bibliothèque (. mylib.sh) et appeler les fonctions directement après (lib_function1 arg1 arg2), soit éviter d'encombrer votre espace de noms et invoquer la bibliothèque avec un argument de fonction (mylib.sh lib_function1 arg1 arg2).

Ne seriez-vous pas gentil si vous pouviez aussi taper mylib.sh --help et obtenir une liste des fonctions disponibles et leur utilisation, sans avoir à gérer manuellement la liste de fonctions dans le texte d'aide?

 #!/bin/bash 
 
 # toutes les fonctions "publiques" doivent commencer par ce préfixe 
 LIB_PREFIX = 'lib _' 
 
 # Fonctions "publiques" de la bibliothèque 
 lib_function1 () {
: Cette fonction effectue quelque chose de compliqué avec deux arguments. 
: 
: Paramètres: 
: 'arg1 - premier argument ($ 1)' 
: 'arg2 - deuxième argument' 
: 
: Résultat: 
: "c'est compliqué" 
 
 # Le code de la fonction actuelle commence ici 
} 
 
 lib_function2 () {
: La documentation de la fonction 
 
 # code de fonction ici 
} 
 
 # fonction d'aide 
 - help () {
 echo MyLib v0.0.1 
 echo 
 echo Utilisation: mylib.sh [nom_fonction [args]] 
 echo 
 echo Fonctions disponibles: 
 declare -f | sed -n -e '/ ^' $ LIB_PREFIX '/,/^} $/{/\(^' $ LIB_PREFIX '\)\|\(^ [\ t] *: \)/{
 s/^\('$ LIB_PREFIX'. * \) ()/\ n ===\1 === /; s/^ [\ t] *: \? ['\' ""] \?// ; s/['\' '"] \?; \? $ //; p}}' .____.]} 
 
 # code principal 
 si [" $ {BASH_SOURCE [0]} "=" $ {0} "]; alors 
 # le script a été exécuté au lieu de générer 
 # invoquer la fonction demandée ou afficher l'aide 
 si ["$ (type -t -" $ 1 "2>/dev/null) "= fonction]; puis 
 "$ @" 
 sinon 
 --help 
 fi 
 fi 

Quelques commentaires sur le code:

  1. Toutes les fonctions "publiques" ont le même préfixe. Seuls ceux-ci sont destinés à être invoqués par l'utilisateur et à être répertoriés dans le texte d'aide.
  2. La fonction d'auto-documentation s'appuie sur le point précédent et utilise declare -f pour énumérer toutes les fonctions disponibles, puis les filtre à l'aide de sed pour n'afficher que les fonctions avec le préfixe approprié.
  3. Il est judicieux de mettre la documentation entre guillemets afin d'éviter toute expansion et suppression non désirée. Vous devrez également faire attention lorsque vous utilisez des apostrophes/citations dans le texte.
  4. Vous pouvez écrire du code pour internaliser le préfixe de la bibliothèque, c’est-à-dire que l’utilisateur n’a qu’à taper mylib.sh function1 et il est traduit en interne en lib_function1. Ceci est un exercice laissé au lecteur.
  5. La fonction d'aide s'appelle "--help". Il s’agit d’une approche pratique (c’est-à-dire paresseuse) qui utilise le mécanisme d’appel de la bibliothèque pour afficher l’aide elle-même, sans devoir coder une vérification supplémentaire pour $1. En même temps, cela encombrera votre espace de noms si vous sourcez la bibliothèque. Si vous n'aimez pas cela, vous pouvez changer le nom en quelque chose comme lib_help ou bien vérifier les arguments de --help dans le code principal et appeler la fonction d'aide manuellement.
11
Sir Athos

J'ai vu cette utilisation dans un script et j'ai pensé que c'était un bon substitut pour invoquer le nom de base dans un script.

oldIFS=$IFS  
IFS=/  
for basetool in $0 ; do : ; done  
IFS=$oldIFS  

... ceci remplace le code: basetool=$(basename $0)

4
Griff Derryberry