Quel est le cas d'utilisation de noop [:] dans bash?


124

J'ai cherché noop dans bash (:), mais je n'ai trouvé aucune bonne information. Quel est le but exact ou le cas d'utilisation de cet opérateur?

J'ai essayé de suivre et ça marche comme ça pour moi:

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

Merci de me faire part de tout cas d'utilisation de cet opérateur en temps réel ou de tout endroit où il est obligatoire de l'utiliser.



Notez que le :intégré existe dans bourne shell et ksh ainsi que bash.
ghoti


6
Comment n'est-ce pas une vraie question? Je pense que c'est une très bonne question. J'en ai même un bon usage, mais je ne peux pas poster de réponse.
Steven Lu

Réponses:


176

C'est là plus pour des raisons historiques. Le colon intégré :est exactement équivalent à true. Il est traditionnel à utiliser truelorsque la valeur de retour est importante, par exemple dans une boucle infinie:

while true; do
  echo 'Going on forever'
done

Il est traditionnel à utiliser :lorsque la syntaxe du shell nécessite une commande mais que vous n'avez rien à faire.

while keep_waiting; do
  : # busy-wait
done

L' :intégré remonte au shell Thompson , il était présent dans Unix v6 . :était un indicateur d'étiquette pour la gotodéclaration de l'obus Thompson . L'étiquette peut être n'importe quel texte, donc :doublé comme indicateur de commentaire (s'il n'y en a pas goto comment, il : comments'agit en fait d'un commentaire). Le Bourne Shell n'avait pas gotomais conservé :.

Un idiome commun que les usages :est : ${var=VALUE}, qui définit varà VALUEs'il était hors service et ne fait rien si vardéjà défini. Cette construction n'existe que sous la forme d'une substitution de variable, et cette substitution de variable doit faire partie d'une commande d'une manière ou d'une autre: une commande no-op sert bien.

Voir aussi À quoi sert le colon intégré? .


11
Bon résumé. De plus, le shell Bourne original n'a pas été utilisé #pour les commentaires (ou les #!/bin/shshebangs); la :commande a introduit des commentaires, et malheur au programmeur naïf qui a fait une belle boîte d'étoiles dans leurs commentaires: : ****(ou, pire, : * * *).
Jonathan Leffler

3
@JonathanLeffler: Honte à moi, mais je ne comprends pas. Qu'arriverait-il : * * *?
DevSolar

7
Comme il :s'agit d'une commande, le shell doit encore traiter ses arguments avant de pouvoir découvrir qu'il les :ignore. La plupart du temps, vous faites simplement faire un travail supplémentaire au shell en développant *une liste de fichiers dans le répertoire courant; cela n'affectera pas réellement le fonctionnement du script.
chepner

Je suppose que cela pourrait faire échouer votre script si vous exécutez dans un répertoire avec beaucoup de fichiers, ce qui fait que l'extension glob dépasse la limite de longueur de commande? Peut-être seulement si vous avez set -esur. Quoi qu'il en soit, j'espère que nous pourrons tous accepter d'utiliser #:)
Jack O'Connor

2
J'utilise parfois while : ; do ... ; donesi je veux une boucle infinie rapide et sale. (Habituellement, il y a un sleepdans la boucle, et je tape Ctrl-C pour le tuer.)
Keith Thompson

21

Je l'utilise pour les instructions if lorsque je commente tout le code. Par exemple, vous avez un test:

if [ "$foo" != "1" ]
then
    echo Success
fi

mais vous souhaitez temporairement commenter tout ce qu'il contient:

if [ "$foo" != "1" ]
then
    #echo Success
fi

Ce qui fait que bash donne une erreur de syntaxe:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash ne peut pas avoir de blocs vides (WTF). Donc, vous ajoutez un no-op:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

ou vous pouvez utiliser le no-op pour commenter les lignes:

if [ "$foo" != "1" ]
then
    : echo Success
fi

12

Si vous utilisez set- ethen, || :c'est un excellent moyen de ne pas quitter le script en cas d'échec (cela le fait explicitement passer).


1
J'ai trouvé cela utile pour certaines applications appelant des scripts shell. Par exemple, Automator sur Mac se fermera en cas d'erreur et ne vous dira pas pourquoi. Mais en utilisant || :plus tard la gestion de l'erreur vous-même avec un exit 0, vous pourrez afficher tous les stdout et stderr dans la fenêtre de l'application pendant le débogage. Considérez { foo ... 2>&1; } || :ce qui est beaucoup plus simple que de configurer des pièges et des if-thens plus complexes.
Beejor

7

Vous utiliseriez :pour fournir une commande qui réussit mais ne fait rien. Dans cet exemple, la commande "verbosité" est désactivée par défaut, en la définissant sur :. L'option 'v' l'allume.

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  

à proprement parler, attribuer une valeur à une variable signifie «faire quelque chose», d'où la raison pour laquelle cela ne renvoie pas d'erreur dans votre instruction case.
qodeninja

@qodeninja: Personne n'a prétendu que l'affectation de variable "n'a rien fait"; le fait est que ce $(verbosity $i)dernier (et conditionnellement) ne fait rien.
Courses de légèreté en orbite

6

Ignorer les aliasarguments

Parfois, vous voulez avoir un alias qui n'accepte aucun argument. Vous pouvez le faire en utilisant ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

pas le downvoter, mais cette réponse ne montre pas comment :fonctionne, malheureusement. L'exemple que vous avez fourni semble redondant.
qodeninja

2
La question n'est pas de savoir comment cela fonctionne, mais quel est le cas d'utilisation . C'est vrai que ma réponse est un peu similaire à stackoverflow.com/a/37170755/6320039 . Mais ce n'est vraiment pas le même cas d'utilisation. Je serais heureux d'améliorer ma réponse mais je ne sais vraiment pas comment ici ..
Ulysse BN

1
C'est un peu génial - un cas d'angle, si vous voulez - mais toujours assez intéressant et pourrait être un truc intelligent à avoir dans sa poche arrière.
Mike S

2
La même chose peut être réalisée avec#
Zoey Hewll

2
@ZoeyHewll, pas tout à fait. Comme les alias sont substitués textuellement avant tout type d'exécution, l'utilisation d'un alias pour #ferait ignorer tout ce qui vient syntaxiquement après sur la même ligne. Ceci est vraiment différent (considérez my_alias arg ; other_commandou, au contraire, my_alias arg1 {BACKSLASH}{NEWLINE} arg2), et ce n'est probablement pas ce que vous voulez car cela peut provoquer des erreurs de syntaxe (devinez ce que fera while my_alias ; do true ; doneou while true ; do my_alias ; donefera).
Maëlan

4

Une utilisation est en tant que commentaires multilignes, ou pour commenter une partie de votre code à des fins de test en l'utilisant conjointement avec un fichier here.

: << 'EOF'

This part of the script is a commented out

EOF

N'oubliez pas d'utiliser des guillemets EOFpour que tout code à l'intérieur ne soit pas évalué, comme $(foo). Il pourrait également être intéressant d' utiliser un nom de terminaison intuitive comme NOTES, SCRATCHPADou TODO.


Cool truc! Surtout pour les notes à gratter et le code nécessitant des dizaines de "#" lorsqu'ils sont emballés. Un effet secondaire est que contrairement aux lignes de commentaires, certains surligneurs de syntaxe ignoreront les heredocs, les colorant comme du code normal (ou du moins du texte brut). Ce qui peut être idéal pour examiner le code commenté ou pour sauver vos yeux des paragraphes dans une couleur de commentaire néon. La seule mise en garde est que ces blocs doivent être notés comme étant des commentaires dans du code partagé, car la syntaxe est unique et pourrait prêter à confusion. Là encore, nous parlons de shell, où le potentiel de confusion est systémique.
Beejor

3

Deux des miens.

Incorporer les commentaires du POD

Une application assez géniale de :consiste à incorporer des commentaires POD dans des scripts bash , afin que les pages de manuel puissent être générées rapidement. Bien sûr, on réécrirait éventuellement tout le script en Perl ;-)

Liaison de fonction d'exécution

Il s'agit d'une sorte de modèle de code pour les fonctions de liaison au moment de l'exécution. Fi, ayez une fonction de débogage pour faire quelque chose uniquement si un certain drapeau est défini:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

Vous obtenez:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar

2

Parfois, les clauses no-op peuvent rendre votre code plus lisible.

Cela peut être une question d'opinion, mais voici un exemple. Supposons que vous ayez créé une fonction qui fonctionne en prenant deux chemins Unix. Il calcule le «chemin de changement» nécessaire pour passer d'un chemin à un autre. Vous placez une restriction sur votre fonction selon laquelle les chemins doivent tous deux commencer par un «/» OU les deux ne doivent pas.

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

Certains développeurs voudront supprimer le no-op, mais cela signifierait annuler le conditionnel:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

Maintenant, à mon avis, il n'est pas si clair d'après la clause if les conditions dans lesquelles vous voudriez sauter la fonction. Pour éliminer le no-op et le faire clairement, vous voudrez déplacer la clause if hors de la fonction:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

Cela semble mieux, mais souvent nous ne pouvons pas faire cela; nous voulons que la vérification soit effectuée à l'intérieur de la fonction.

Alors, à quelle fréquence cela se produit-il? Pas très souvent. Peut-être une ou deux fois par an. Cela arrive assez souvent pour que vous en soyez conscient. Je n'hésite pas à l'utiliser quand je pense que cela améliore la lisibilité de mon code (quelle que soit la langue).


3
Si vous répondez à une question sur le but de :, vous devez utiliser :, non true, dans la réponse. Cela dit, la meilleure façon de nier l'ici conditionnelle est d'utiliser une [[ ... ]]commande et le préfixe avec !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner

1
Ah, très gentil, et je devrais voter ma réponse. Hmmm ... Je pense encore à des exemples où j'ai dû utiliser une clause de non-intervention. C'est le meilleur que j'ai trouvé.
Bitdiot

Il est simplement conservé pour la compatibilité avec les versions antérieures, bien qu'il : ${...=...}soit sans doute moins gênant que true ${...=...}. Certains pourraient préférer while :; doà while true; dodes laconisme aussi bien.
chepner

2

Un peu lié à cette réponse , je trouve ce no-op plutôt pratique pour pirater des scripts polyglottes . Par exemple, voici un commentaire valide à la fois pour bash et pour vimscript:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

Bien sûr, nous avons peut-être truetout aussi bien utilisé , mais :être un signe de ponctuation et non un mot anglais hors de propos indique clairement qu'il s'agit d'un jeton de syntaxe.


Quant à savoir pourquoi quelqu'un ferait une chose aussi délicate que d'écrire un script polyglotte (en plus d'être cool): cela s'avère utile dans les situations où nous écririons normalement plusieurs fichiers de script dans plusieurs langues différentes, le fichier Xfaisant référence au fichier Y.

Dans une telle situation, combiner les deux scripts dans un seul fichier polyglotte évite tout travail Xpour déterminer le chemin vers Y(c'est simplement "$0"). Plus important encore, il est plus pratique de se déplacer ou de distribuer le programme.

  • Un exemple courant. Il y a un problème bien connu et de longue date avec les shebangs: la plupart des systèmes (y compris Linux et Cygwin) n'autorisent qu'un seul argument à passer à l'interpréteur. Le shebang suivant:

    #!/usr/bin/env interpreter --load-libA --load-libB

    lancera la commande suivante:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"

    et non pas l'intention:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Ainsi, vous finiriez par écrire un script wrapper, tel que:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    C'est là que la polyglossie entre en scène.

  • Un exemple plus précis. J'ai écrit un jour un script bash qui, entre autres, invoquait Vim. J'avais besoin de donner une configuration supplémentaire à Vim, ce qui pourrait être fait avec l'option --cmd "arbitrary vimscript command here". Cependant, cette configuration était substantielle, de sorte que l'incruster dans une chaîne aurait été terrible (si jamais possible). Par conséquent, une meilleure solution était de l'écrire en extenso dans un fichier de configuration, puis de faire lire ce fichier à Vim avec -S "/path/to/file". Par conséquent, je me suis retrouvé avec un fichier bash / vimscript polyglotte.


1

J'ai également utilisé des scripts pour définir des variables par défaut.


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}

0

supposons que vous ayez une commande que vous souhaitez enchaîner au succès d'une autre:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

mais maintenant vous voulez exécuter les commandes de manière conditionnelle et vous voulez afficher les commandes qui seraient exécutées (à sec):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

donc si vous définissez DEBUG et NOEXEC, la deuxième commande ne s'affiche jamais. c'est parce que la première commande ne s'exécute jamais (parce que NOEXEC n'est pas vide) mais l'évaluation de ce fait vous laisse avec un retour de 1, ce qui signifie que la commande subordonnée ne s'exécute jamais (mais vous le voulez parce que c'est une exécution à sec). pour résoudre ce problème, vous pouvez réinitialiser la valeur de sortie laissée sur la pile avec un noop:

[ -z "$NOEXEC" ] && $cmd || :
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.