¿Qué significa $ # en bash?


26

Tengo un script en un archivo llamado instancia:

echo "hello world"
echo ${1}

Y cuando ejecuto este script usando:

./instance solfish

Me sale esta salida:

hello world
solfish

Pero cuando corro:

echo $# 

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

Por favor explícalo.


10
¿Por qué corres $#? ¿Qué quieres lograr? ¿De dónde sacaste este comando? No es relevante en absoluto.
Pilot6

77
@ Pilot6 en este caso pregunta por el significado de una variable, este no es un caso específico, entonces, ¿por qué el operador necesitaría dar esa información?
ADDB

21
@solfish Pilot está tratando de entender lo que estabas esperando. Dijiste que corriste echo $#y volvió, lo 0cual es normal. Te sorprendió esto, pero no explicas qué estabas esperando o por qué te sorprendiste. Por lo tanto, nos ayudaría a darle una mejor respuesta si explicara lo que esperaba. Lo que pensaste que echo $#haría. Si corrieras ./instance solfishy ./instancecontuvieras echo $#, se imprimiría 1y no 0.
terdon


1
Java tampoco usa argc.
JakeRobb

Respuestas:


66

$#es una variable especial en bash, que se expande a la cantidad de argumentos (parámetros posicionales), es decir, $1, $2 ...pasados ​​al script en cuestión o al shell en caso de que el argumento pase directamente al shell, por ejemplo, in bash -c '...' .....

Esto es similar a argcen 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, bash -ctoma el argumento después del comando que lo sigue a partir de 0 ( $0; técnicamente, es solo bashla forma de dejarlo establecer $0, no un argumento realmente), por lo que _se usa aquí solo como marcador de posición; Los argumentos reales son x( $1), y( $2) y z( $3).


Del mismo modo, en su secuencia de comandos (suponiendo script.sh) si tiene:

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

Entonces cuando lo haces:

./script.sh foo bar

el script generará 2; igualmente,

./script.sh foo

dará salida a 1.


1
Creo que esta respuesta es engañosa. $ # es el índice máximo de la matriz de argumentos. Si da un argumento, estará disponible en $ 0, y $ # tendrá el valor 0. Si da dos argumentos, estarán en $ 0 y $ 1, y $ # tendrá el valor 1.
JakeRobb

1
Además, cuando usa bash -c, el comportamiento es diferente que si ejecuta un script de shell ejecutable, porque en el último caso el argumento con el índice 0 es el comando de shell utilizado para invocarlo. Como tal, creo que la forma de arreglar esta respuesta es cambiarla para ejecutar scripts como archivos en lugar de usarlos bash -c, ya que así lo hacía el autor de la pregunta.
JakeRobb

1
@JakeRobb No. Para un guión, $0sería el guión mismo; argumentos serían $1, $2, $3.... bash -cel comportamiento es diferente, ya que está destinado a un uso no interactivo y los argumentos que siguen al comando comenzarían $0, lo he mencionado claramente, creo.
heemayl

55
@JakeRobb Me has escuchado mal. Si da un argumento, estará disponible en $ 0, y $ # tendrá el valor 0 , esto es simplemente incorrecto.
heemayl

2
Si coloca un programa completo que lo examina bash -c, debe pasar bashun nombre significativo como relleno para el 0º argumento. bash -c 'echo "$@"' foo barsolo imprime bar, porque "$@"no incluye $0, igual que siempre. bash -cno "convierte a $ 0 en uno de los argumentos", simplemente le permite establecer "$ 0" de la misma manera que cuando ejecuta un programa con la execvellamada al sistema o cualquier forma de shell de anular el 0º argumento. $0nunca debe considerarse "uno de los argumentos".
Peter Cordes

17

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

No tiene ninguno, por lo que genera 0.

echo $# es útil dentro del script, 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.


66
Para el ejemplo del OP: si agrega la línea echo $#a su script y luego se ejecuta nuevamente ./instance solfish, debería obtener una línea de salida adicional 1ya que proporcionó 1 parámetro
derHugo el

14

$#normalmente se usa en scripts bash para garantizar que se pasa un parámetro. En general, verifica si hay un parámetro al comienzo 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 $#informes, 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 #usos en Bash

A #menudo se usa 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}

devoluciones: 11

Para encontrar el número de elementos de la matriz:

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

devoluciones: 3

Para encontrar la longitud del primer elemento de matriz:

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

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


$# -ne 1? ¿Por qué no $# -le 0o equivalente?
Patrick Trentin

@PatrickTrentin Porque no quiero que pasen dos o más parámetros. Como dijo el Capitán en "Octubre Rojo": "Solo uno".
WinEunuuchs2Unix

Luego: "se requiere exactamente un argumento ..." en el mensaje de error
Patrick Trentin

@PatrickTrentin El mensaje de error explica cómo se usa el script con un ejemplo del uso de un argumento. Además, cron.daily llama al script de copia de seguridad, que solo es configurado una vez por alguien experto en tecnología: askubuntu.com/questions/917562/…
WinEunuuchs2Unix

Cómo eso es relevante no lo sabría. De todos modos, saludos.
Patrick Trentin

10

$# es el número de argumentos, pero recuerde 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 de la función . Esto permite que las funciones acepten y usen sus propios parámetros posicionales.

Este script siempre se imprime 3, independientemente de cuántos argumentos se pasaron al script en sí, porque "$#"en la función se fexpande a la cantidad de argumentos pasados ​​a la función:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Esto es importante porque significa que un 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 al número de argumentos pasados ​​a la función en sí, que en ese script siempre es 0.

Si desea dicha funcionalidad en una función de shell, tendría que 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, se $1expande al primer parámetro posicional que se pasó a la función de shell, en lugar de al script del que forma parte.

Por lo tanto, al igual que $#, 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, $0no no cambiar el nombre de la función, que es por lo que todavía era capaz de utilizarlo para producir un mensaje de error de la calidad.

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

Del mismo modo, 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 nestedy (después de ejecutarlo 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 pasados ​​al script a menos que los haya cambiado .

Una forma común de cambiarlos es con el shiftincorporado, que desplaza cada parámetro posicional hacia la izquierda en uno, bajando 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 setincorporado:

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

1
¡Felicitaciones por la respuesta educativa que fue más allá de lo que se preguntó pero que respondió a las intenciones de aprendizaje del autor de la pregunta original y de otros como yo!
Ricardo
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.