¿Qué significa $ # en bash?

19

Tengo un script en un archivo llamado instance:

echo "hello world"
echo ${1}

Y cuando ejecuto este script usando:

./instance solfish

Obtengo esta salida:

hello world
solfish

Pero cuando corro:

echo $# 

Dice "0". ¿Por qué? No entiendo lo que significa $# .

Por favor, explícalo.

    
pregunta solfish 25.07.2017 - 14:26

4 respuestas

58

$# es una variable especial en bash , que se expande al número de argumentos (parámetros posicionales), es decir, $1, $2 ... pasado al script en cuestión o al shell en caso de que el argumento se transmita directamente al shell, por ejemplo. en codigo%.

Esto es similar a bash -c '...' .... en C.

Quizás esto lo aclare:

$ bash -c 'echo $#'
0

$ bash -c 'echo $#' _ x
1

$ bash -c 'echo $#' _ x y
2

$ bash -c 'echo $#' _ x y z
3

Tenga en cuenta que, argc toma el argumento después del comando que comienza a partir de 0 ( bash -c ; técnicamente, es solo la manera de $0 de permitirle configurar bash , no es un argumento realmente), por lo que $0 se usa aquí solo como un marcador de posición; los argumentos reales son _ ( x ), $1 ( y ) y $2 ( z ).

De manera similar, en su script (asumiendo $3 ) si tiene:

#!/usr/bin/env bash
echo "$#"

Entonces cuando lo hagas:

./script.sh foo bar

el script generará 2; del mismo modo,

./script.sh foo

dará salida a 1.

    
respondido por el heemayl 25.07.2017 - 14:39
16

echo $# genera el número de parámetros posicionales de su script.

No tienes ninguno, por lo que da un resultado de 0.

echo $# es útil dentro de la secuencia de comandos, no como un comando separado.

Si ejecuta un script con algunos parámetros como

./instance par1 par2

el echo $# colocado en el script generará 2.

    
respondido por el Pilot6 25.07.2017 - 14:29
12

$# se usa normalmente en los scripts de bash para garantizar que se pasa un parámetro. En general, comprueba si hay un parámetro al principio de su script.

Por ejemplo, aquí hay un fragmento de un script en el que estaba trabajando hoy:

if [[ $# -ne 1 ]]; then
    echo 'One argument required for file name, e.g. "Backup-2017-07-25"'
    echo '.tar will automatically be added as a file extension'
    exit 1
fi

Para resumir $# informa el número de parámetros pasados a un script. En su caso, no pasó ningún parámetro y el resultado informado es 0 .

Otros # usan en Bash

El # se usa a menudo en bash para contar el número de ocurrencias o la longitud de una variable.

Para encontrar la longitud de una cadena:

myvar="some string"; echo ${#myvar}

devuelve: 11

Para encontrar el número de elementos de matriz:

myArr=(A B C); echo ${#myArr[@]}

devuelve: 3

Para encontrar la longitud del primer elemento de la matriz:

myArr=(A B C); echo ${#myArr[0]}

devuelve: 1 (la longitud de A , 0 es el primer elemento, ya que las matrices utilizan índices / subíndices basados en cero).

    
respondido por el WinEunuuchs2Unix 25.07.2017 - 14:44
9

$# es el número de argumentos, pero recuerda que será diferente en una función.

$# es el número de parámetros posicionales pasados al script, shell, o función de shell . Esto se debe a que, mientras se ejecuta una función de shell, los parámetros posicionales se reemplazan temporalmente con los argumentos a la función . Esto permite que las funciones acepten y utilicen sus propios parámetros posicionales.

Esta secuencia de comandos siempre imprime 3 , independientemente de cuántos argumentos se pasaron a la secuencia de comandos misma, porque "$#" en la función f se expande a la cantidad de argumentos pasados a la función:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Esto es importante porque significa que el código como este no funciona como cabría esperar, si no está familiarizado con el funcionamiento de los parámetros posicionales en las funciones de shell:

#!/bin/sh

check_args() { # doesn't work!
    if [ "$#" -ne 2 ]; then
        printf '%s: error: need 2 arguments, got %d\n' "$0" "$#" >&2
        exit 1
    fi
}

# Maybe check some other things...
check_args
# Do other stuff...

En check_args , $# se expande a la cantidad de argumentos pasados a la función en sí, que en ese script siempre es 0.

Si quieres dicha funcionalidad en una función de shell, deberías escribir algo como esto en su lugar:

#!/bin/sh

check_args() { # works -- the caller must pass the number of arguments received
    if [ "$1" -ne 2 ]; then
        printf '%s: error: need 2 arguments, got %d\n' "$0" "$1" >&2
        exit 1
    fi
}

# Maybe check some other things...
check_args "$#"

Esto funciona porque $# se expande fuera de la función y se pasa a la función como uno de sus parámetros posicionales. Dentro de la función, $1 se expande al primer parámetro posicional que se pasó a la función de shell, en lugar de a la secuencia de comandos de la que forma parte.

Por lo tanto, como $# , los parámetros especiales $1 , $2 , etc., así como $@ y $* , también pertenecen a los argumentos pasados a una función, cuando se expanden en la función. Sin embargo, $0 no no cambia el nombre de la función, por lo que aún pude usarla para producir un mensaje de error de calidad.

$ ./check-args-demo a b c
./check-args-demo: error: need 2 arguments, got 3

De manera similar, si define una función dentro de otra, está trabajando con los parámetros posicionales pasados a la función más interna en la que se realiza la expansión:

#!/bin/sh

outer() {
    inner() {
        printf 'inner() got %d arguments\n' "$#"
    }

    printf 'outer() got %d arguments\n' "$#"
    inner x y z
}

printf 'script got %d arguments\n' "$#"
outer p q

Llamé a este script nested y (después de ejecutar chmod +x nested ) lo ejecuté:

$ ./nested a
script got 1 arguments
outer() got 2 arguments
inner() got 3 arguments

Sí, lo sé. "1 argumentos" es un error de pluralización.

Los parámetros posicionales también se pueden cambiar.

Si está escribiendo un script, los parámetros posicionales fuera de una función serán los argumentos de la línea de comandos que se pasarán al script a menos que los haya cambiado .

Una forma común de cambiarlos es con shift incorporado, que desplaza cada parámetro posicional a la izquierda en uno, eliminando el primero y disminuyendo $# en 1:

#!/bin/sh

while [ "$#"  -ne 0 ]; do
    printf '%d argument(s) remaining.\nGot "%s".\n\n' "$#" "$1"
    shift
done
$ ./do-shift foo bar baz      # I named the script do-shift.
3 argument(s) remaining.
Got "foo".

2 argument(s) remaining.
Got "bar".

1 argument(s) remaining.
Got "baz".

También se pueden cambiar con el set builtin:

#!/bin/sh

printf '%d args: %s\n' "$#" "$*"
set foo bar baz
printf '%d args: %s\n' "$#" "$*"
$ ./set-args a b c d e      # I named the script set-args.
5 args: a b c d e
3 args: foo bar baz
    
respondido por el Eliah Kagan 25.07.2017 - 19:17

Lea otras preguntas en las etiquetas