Что означает $# в bash?

Что означает $# в bash?

У меня есть скрипт в файле с именем instance:

echo "hello world"
echo ${1}

И когда я запускаю этот скрипт с помощью:

./instance solfish

Я получаю такой вывод:

hello world
solfish

Но когда я бегу:

echo $# 

Там написано "0". Почему? Я не понимаю, что это $#значит.

Пожалуйста, объясните это.

решение1

$#— это специальная переменная в bash, которая расширяется до количества аргументов (позиционных параметров), т.е. $1, $2 ...переданных рассматриваемому скрипту или оболочке в случае, если аргумент напрямую передан оболочке, например, в bash -c '...' .....

Это похоже на argcC.


Возможно, это прояснит ситуацию:

$ bash -c 'echo $#'
0

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

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

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

Обратите внимание, что bash -cпринимает аргумент после следующей за ним команды, начиная с 0 ( $0; технически, это просто bashспособ , позволяющий вам установить $0, а не аргумент на самом деле), поэтому _здесь используется просто как заполнитель; фактическими аргументами являются x( $1), y( $2) и z( $3).


Аналогично, в вашем скрипте (предполагается script.sh), если у вас есть:

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

Затем, когда вы это сделаете:

./script.sh foo bar

скрипт выведет 2; аналогично,

./script.sh foo

выведет 1.

решение2

echo $#выводит количество позиционных параметров вашего скрипта.

У вас их нет, поэтому выводится 0.

echo $#полезен внутри скрипта, а не как отдельная команда.

Если вы запустите скрипт с некоторыми параметрами, например

./instance par1 par2

помещенный echo $#в скрипт, выведет 2.

решение3

$#обычно используется в скриптах bash для обеспечения передачи параметра. Обычно вы проверяете параметр в начале скрипта.

Например, вот фрагмент сценария, над которым я работал сегодня:

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

Чтобы суммировать $#отчеты, количество параметров, переданных скрипту. В вашем случае вы не передали ни одного параметра, и отчетный результат равен 0.


Другие #применения в Bash

Часто используется #в bash для подсчета количества вхождений или длины переменной.

Чтобы найти длину строки:

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

возвращает:11

Чтобы найти количество элементов массива:

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

возвращает:3

Чтобы найти длину первого элемента массива:

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

возвращает: 1(Длина A, 0 — первый элемент, так как в массивах используются индексы/индексы, начинающиеся с нуля).

решение4

$#— это количество аргументов, но помните, что в функции оно будет другим.

$#количество позиционных параметров, переданных скрипту, оболочке,или функция оболочки. Это происходит потому, что во время работы функции оболочкипозиционные параметры временно заменяются аргументами функции. Это позволяет функциям принимать и использовать свои собственные позиционные параметры.

Этот скрипт всегда печатает 3, независимо от того, сколько аргументов было передано самому скрипту, поскольку "$#"в функции fрасширяется до количества аргументов, переданных функции:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Это важно, поскольку это означает, что подобный код не будет работать так, как вы могли бы ожидать, если вы не знакомы с тем, как работают позиционные параметры в функциях оболочки:

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

В check_argsрасширяется $#до количества аргументов, переданных самой функции, которое в этом скрипте всегда равно 0.

Если вам нужна такая функциональность в функции оболочки, вам придется написать что-то вроде этого:

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

Это работает, потому что $#расширеноснаружифункция и передана функции как один изегопозиционные параметры. Внутри функции $1расширяется до первого позиционного параметра, который был передан в функцию оболочки, а не до скрипта, частью которого он является.

Таким образом, как и $#, специальные параметры $1, $2и т.д., а также $@и $*, также относятся к аргументам, переданным в функцию, когда они раскрываются в функции. Однако $0,нетизменил название функции, поэтому мне все равно удалось использовать ее для создания качественного сообщения об ошибке.

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

Аналогично, если вы определяете одну функцию внутри другой, вы работаете с позиционными параметрами, переданными самой внутренней функции, в которой выполняется расширение:

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

Я вызвал этот скрипт nestedи (после запуска chmod +x nested) запустил его:

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

Да, я знаю. «1 аргумент» — это ошибка образования множественного числа.

Позиционные параметры также могут быть изменены.

Если вы пишете скрипт, позиционные параметры вне функции будут аргументами командной строки, переданными скрипту.если вы их не изменили.

Один из распространенных способов их изменения — использование shiftвстроенной функции, которая сдвигает каждый позиционный параметр влево на единицу, отбрасывая первый и уменьшая $#на 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".

Их также можно изменить с помощью setвстроенной команды:

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

Связанный контент