¿Cuál es el caso de uso de noop [:] en bash?


124

Busqué noop en bash (:), pero no pude encontrar ninguna buena información. ¿Cuál es el propósito exacto o el caso de uso de este operador?

Intenté seguir y funciona así para mí:

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

Por favor avíseme, cualquier caso de uso de este operador en tiempo real o en cualquier lugar donde sea obligatorio utilizarlo.



Tenga en cuenta que el :archivo integrado existe en bourne shell y ksh, así como en bash.
ghoti


6
¿Cómo no es esto una pregunta real? Creo que es una muy buena pregunta. Incluso le doy un buen uso, pero no puedo publicar una respuesta.
Steven Lu

Respuestas:


176

Está ahí más por razones históricas. El colon incorporado :es exactamente equivalente a true. Es tradicional usarlo truecuando el valor de retorno es importante, por ejemplo, en un ciclo infinito:

while true; do
  echo 'Going on forever'
done

Es tradicional usarlo :cuando la sintaxis del shell requiere un comando pero no tiene nada que hacer.

while keep_waiting; do
  : # busy-wait
done

El :incorporado se remonta al shell de Thompson , estaba presente en Unix v6 . :era un indicador de etiqueta para la gotodeclaración de la cáscara de Thompson . La etiqueta puede ser cualquier texto, por lo que se :duplica como indicador de comentario (si no hay goto comment, entonces : commentes efectivamente un comentario). El caparazón de Bourne no tenía gotopero se mantuvo :.

Un modismo común que se usa :es : ${var=VALUE}, que se establece varen VALUEsi estaba desarmado y no hace nada si varya se configuró. Esta construcción solo existe en forma de sustitución de variable, y esta sustitución de variable debe ser parte de un comando de alguna manera: un comando sin operación sirve muy bien.

Consulte también ¿ Para qué sirve el sistema de colon integrado? .


11
Buen resumen. Además, el shell Bourne original no se usó #para comentarios (o #!/bin/shshebangs); el :comando introdujo comentarios, y ay del programador ingenuo que hizo una bonita caja de estrellas en sus comentarios: : ****(o, peor aún, : * * *).
Jonathan Leffler

3
@JonathanLeffler: Es una vergüenza, pero no lo entiendo. ¿Qué pasaría con : * * *?
DevSolar

7
Como :es un comando, el shell aún tiene que procesar sus argumentos antes de que pueda descubrir que los :ignora. En general, solo está haciendo que el shell haga un trabajo adicional al expandirse *a una lista de archivos en el directorio actual; en realidad, no afectará el funcionamiento del script.
chepner

Supongo que su secuencia de comandos podría fallar si se ejecuta en un directorio con muchos archivos, lo que hace que la expansión global supere el límite de longitud del comando. Quizás solo si lo tienes puesto set -e. De todos modos, espero que todos podamos aceptar usar #:)
Jack O'Connor

2
A veces lo uso while : ; do ... ; donesi quiero un bucle infinito rápido y sucio. (Por lo general, hay un sleepbucle y escribo Ctrl-C para eliminarlo)
Keith Thompson

21

Lo uso para declaraciones if cuando comento todo el código. Por ejemplo, tienes una prueba:

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

pero desea comentar temporalmente todo lo que contiene:

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

Lo que hace que bash dé un error de sintaxis:

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

Bash no puede tener bloques vacíos (WTF). Entonces agregas un no-op:

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

o puede usar el no-op para comentar las líneas:

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

12

Si lo usa, set- eentonces || :es una excelente manera de no salir del script si ocurre una falla (lo hace explícitamente aprobado).


1
Encontré esto útil para ciertas aplicaciones de llamada de scripts de shell. Por ejemplo, Automator en Mac se cerrará en caso de error y no le dirá por qué. Pero usar || :y luego manejar el error usted mismo con un exit 0le permitirá mostrar todos los stdout y stderr en la ventana de la aplicación durante la depuración. Considere { foo ... 2>&1; } || :qué es mucho más simple que configurar trampas más complejas y si-entonces.
Beejor

7

Lo usaría :para proporcionar un comando que tiene éxito pero no hace nada. En este ejemplo, el comando "verbosidad" está desactivado de forma predeterminada, configurándolo en :. La opción 'v' lo enciende.

#!/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  

estrictamente hablando, asignar un valor a una variable es "hacer algo", de ahí que no arroje un error en su declaración de caso.
qodeninja

@qodeninja: Nadie afirmó que la asignación de la variable "no hiciera nada"; el caso es que el $(verbosity $i)último (y condicionalmente) no hace nada.
Lightness Races in Orbit

6

Ignorar aliasargumentos

Algunas veces querrás tener un alias que no acepte ningún argumento. Puedes hacerlo usando ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

no el votante negativo, pero esta respuesta no demuestra cómo :funciona, desafortunadamente. El ejemplo que proporcionó parece redundante.
qodeninja

2
La pregunta no es cómo funciona, sino cuál es el caso de uso . Es cierto que mi respuesta es un poco similar a stackoverflow.com/a/37170755/6320039 . Pero realmente no es el mismo caso de uso. Me alegraría mejorar mi respuesta, pero realmente no sé cómo aquí ..
Ulysse BN

1
Este es un poco raro, un caso de esquina, por así decirlo, pero aún así es bastante interesante y podría ser un truco inteligente para tener en el bolsillo trasero.
Mike S

2
El mismo se puede conseguir con#
Zoey Hewll

2
@ZoeyHewll, no del todo. Como los alias se sustituyen textualmente antes de cualquier tipo de ejecución, usar un alias para #haría que todo lo que venga sintácticamente después en la misma línea sea ignorado. Esto es realmente diferente (considere my_alias arg ; other_commando, a la inversa, my_alias arg1 {BACKSLASH}{NEWLINE} arg2), y probablemente no sea lo que desea, ya que puede causar errores de sintaxis (adivine qué hará while my_alias ; do true ; doneo while true ; do my_alias ; donehará).
Maëlan

4

Un uso es como comentarios de varias líneas o para comentar parte de su código con fines de prueba usándolo junto con un archivo here.

: << 'EOF'

This part of the script is a commented out

EOF

No olvides usar comillas EOFpara que el código interno no sea evaluado, como $(foo). También podría ser digno de usar un nombre terminador intuitiva como NOTES, SCRATCHPADo TODO.


¡Buen truco! Especialmente para notas de borrador y código que requiere docenas de "#" cuando están empaquetados. Un efecto secundario es que, a diferencia de las líneas de comentarios, algunos resaltadores de sintaxis ignorarán los documentos heredados, coloreándolos como código normal (o al menos texto plano). Lo cual puede ser excelente para examinar el código comentado o para evitar los párrafos en un color de comentario de neón. La única salvedad es que estos bloques deben indicarse como comentarios en el código compartido, ya que la sintaxis es única y podría causar confusión. Por otra parte, estamos hablando de un caparazón, donde el potencial de confusión es sistémico.
Beejor

3

Dos míos.

Insertar comentarios de POD

Una aplicación bastante original de :es para incrustar comentarios de POD en scripts bash , de modo que las páginas de manual se puedan generar rápidamente. Por supuesto, uno eventualmente volvería a escribir todo el script en Perl ;-)

Enlace de función en tiempo de ejecución

Se trata de una especie de patrón de código para vincular funciones en tiempo de ejecución. Fi, tenga una función de depuración para hacer algo solo si se establece una determinada bandera:

#!/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'

Usted obtiene:

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

2

A veces, las cláusulas de no operación pueden hacer que su código sea más legible.

Eso puede ser una cuestión de opinión, pero aquí hay un ejemplo. Supongamos que ha creado una función que funciona tomando dos rutas de Unix. Calcula la 'ruta de cambio' necesaria para cambiar de una ruta a otra. Usted coloca una restricción en su función de que las rutas deben comenzar con '/' O ambas no deben comenzar.

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

Algunos desarrolladores querrán eliminar el no-op, pero eso significaría negar el condicional:

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

Ahora, en mi opinión, no está tan claro en la cláusula if las condiciones en las que querría omitir la función. Para eliminar el no-op y hacerlo claramente, querrá mover la cláusula if fuera de la función:

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

Eso se ve mejor, pero muchas veces no podemos hacer esto; queremos que la verificación se realice dentro de la función.

Entonces, ¿con qué frecuencia sucede esto? No muy seguido. Quizás una o dos veces al año. Ocurre con bastante frecuencia, por lo que debes estar al tanto. No evito usarlo cuando creo que mejora la legibilidad de mi código (independientemente del idioma).


3
Si está respondiendo una pregunta sobre el propósito de :, debe usar :, no true, en la respuesta. Dicho esto, la manera más fácil para negar la condición aquí es utilizar un solo [[ ... ]]comando y como prefijo !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner

1
Ah, muy bien, y debería rechazar mi respuesta. Hmmm ... todavía estoy pensando en ejemplos en los que tuve que usar una cláusula de no operación. Esto es lo mejor que se me ocurrió.
Bitdiot

Realmente solo se conserva para compatibilidad con versiones anteriores, aunque : ${...=...}podría decirse que tiene un aspecto menos incómodo que true ${...=...}. Algunos también pueden preferir while :; dohacerlo while true; dopor ser conciso.
chepner

2

Algo relacionado con esta respuesta , encuentro que esta no-operación es bastante conveniente para hackear scripts políglotas . Por ejemplo, aquí hay un comentario válido tanto para bash como para vimscript:

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

Claro, es posible que lo hayamos usado trueigual de bien, pero al :ser un signo de puntuación y no una palabra irrelevante en inglés, queda claro que es un símbolo de sintaxis.


En cuanto a por qué alguien haría algo tan complicado como escribir una secuencia de comandos políglota (además de ser genial): resulta útil en situaciones en las que normalmente escribiríamos varios archivos de secuencia de comandos en varios idiomas diferentes, con el archivo Xhaciendo referencia a archivo Y.

En tal situación, la combinación de ambos scripts en un solo archivo políglota evita cualquier trabajo Xpara determinar la ruta a Y(es simplemente "$0"). Más importante aún, hace que sea más conveniente moverse o distribuir el programa.

  • Un ejemplo común. Existe un problema bien conocido y de larga data con shebangs: la mayoría de los sistemas (incluidos Linux y Cygwin) permiten que solo se pase un argumento al intérprete. El siguiente shebang:

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

    disparará el siguiente comando:

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

    y no el previsto:

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

    Por lo tanto, terminaría escribiendo un script de envoltura, como:

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

    Aquí es donde entra en escena la poliglosia.

  • Un ejemplo más específico. Una vez escribí un script bash que, entre otras cosas, invocaba a Vim. Necesitaba darle a Vim una configuración adicional, lo que podría hacerse con la opción --cmd "arbitrary vimscript command here". Sin embargo, esa configuración era sustancial, por lo que incluirla en una cadena habría sido terrible (si es que alguna vez fue posible). Por lo tanto, una mejor solución fue escribirlo en extenso en algún archivo de configuración, luego hacer que Vim lea ese archivo con -S "/path/to/file". Por lo tanto, terminé con un archivo bash / vimscript políglota.


1

También he usado en él scripts para definir variables predeterminadas.


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

0

suponga que tiene un comando que desea encadenar al éxito de otro:

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

pero ahora desea ejecutar los comandos de forma condicional y desea mostrar los comandos que se ejecutarían (ejecución en seco):

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

por lo que si configura DEBUG y NOEXEC, el segundo comando nunca aparece. esto se debe a que el primer comando nunca se ejecuta (porque NOEXEC no está vacío) pero la evaluación de ese hecho lo deja con un retorno de 1, lo que significa que el comando subordinado nunca se ejecuta (pero lo desea porque es un ensayo). así que para solucionar esto, puede restablecer el valor de salida que queda en la pila con un noop:

[ -z "$NOEXEC" ] && $cmd || :
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.