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.
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.
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.
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.
:
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
Si vous souhaitez tronquer un fichier à zéro octet, utile pour effacer les journaux, essayez ceci:
:> file.log
Cela ressemble à pass
en Python.
Une utilisation serait de remplacer une fonction jusqu'à ce qu'elle soit écrite:
future_function () { :; }
Deux autres utilisations non mentionnées dans les autres réponses:
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
.
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.
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.
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(){ ... }
.)
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:
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é.mylib.sh function1
et il est traduit en interne en lib_function1
. Ceci est un exercice laissé au lecteur.$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.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)