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


26

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

echo "hello world"
echo ${1}

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

./instance solfish

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

hello world
solfish

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

echo $# 

Это говорит "0". Зачем? Я не понимаю, что $#значит.

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


10
Почему ты бежишь $#? Чего ты хочешь достичь? Откуда вы взяли эту команду? Это вообще не актуально.
Pilot6

7
@ Pilot6 в этом случае он спрашивает о значении переменной, это не конкретный случай, так зачем оператору предоставлять эту информацию?
ADDB

21
@solfish Пилот пытается понять, что вы ожидали. Вы сказали, что бежали, echo $#и он вернулся, 0что нормально. Вы были удивлены этим, но вы не объясняете, что вы ожидали или почему вы были удивлены. Так что это поможет нам дать вам лучший ответ, если вы объясните, что вы ожидаете. То, что вы думали, что echo $#будет делать. Если бы вы бежали ./instance solfishи ./instanceсодержались echo $#, то печатали бы 1и нет 0.
тердон


1
Java также не использует argc.
JakeRobb

Ответы:


66

$#это специальная переменная в 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.


1
Я думаю, что этот ответ вводит в заблуждение. $ # - максимальный индекс массива аргументов. Если вы дадите один аргумент, он будет доступен в $ 0, а $ # будет иметь значение 0. Если вы дадите два аргумента, они будут в $ 0 и $ 1, а $ # будет иметь значение 1.
JakeRobb

1
Более того, когда вы используете bash -c, поведение отличается от того, что вы запускаете исполняемый скрипт оболочки, потому что в последнем случае аргумент с индексом 0 является командой оболочки, используемой для его вызова. Таким образом, я думаю, что способ исправить этот ответ состоит в том, чтобы изменить его так, чтобы он выполнял сценарии в виде файлов, а не использовал их bash -c, поскольку именно так это делал проситель.
JakeRobb

1
@JakeRobb Нет. Для скрипта $0будет сам скрипт; аргументы будут $1, $2, $3.... bash -cповедение отличается, так как оно предназначено для неинтерактивного использования, и аргументы, следующие за командой, начинаются с $0, я ясно говорил об этом, я верю.
Heemayl

5
@JakeRobb Вы меня не поняли. Если вы дадите один аргумент, он будет доступен в $ 0, а $ # будет иметь значение 0 - это совершенно неверно.
Heemayl

2
Если вы помещаете законченную программу, которая смотрит на ее аргументы bash -c, вы должны передать bashкакое-то значимое имя в качестве наполнителя для 0-го аргумента. bash -c 'echo "$@"' foo barтолько печатает bar, потому "$@"что не включает $0, как всегда. bash -c«не составит $ 0 один из аргументов», она просто позволяет установить «$ 0» так же , как при запуске программы с помощью к execveсистемному вызову или любой оболочки пути перекрывая 0 - й ARG. $0никогда не следует считать "одним из аргументов".
Питер Кордес

17

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

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

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

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

./instance par1 par2

echo $#помещается в сценарий выхода 2.


6
Для примера OP: Если вы добавляете строку echo $#в свой скрипт и затем запускаетесь снова, ./instance solfishвы должны получить дополнительную строку вывода, 1поскольку вы
указали

14

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

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

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

Для суммирования $#отчетов количество параметров передается в скрипт. В вашем случае вы не передали никаких параметров и сообщаете результат 0.


Другое #использование в Bash

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

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

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

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

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

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

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

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

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

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


$# -ne 1? Почему нет $# -le 0или эквивалент?
Патрик

@PatrickTrentin Потому что я не хочу, чтобы они передавали два или более параметров. Как сказал капитан в «Красном октябре»: «Всего один».
WinEunuuchs2Unix

Затем: «требуется ровно один аргумент ...» в сообщении об ошибке
Патрик

@PatrickTrentin В сообщении об ошибке объясняется, как используется скрипт, с примером использования одного аргумента. Кроме того, скрипт резервного копирования вызывается cron.daily, который настраивается только один раз кем-то, разбирающимся в технологиях: askubuntu.com/questions/917562/…
WinEunuuchs2Unix

Насколько это актуально, я не знаю. Во всяком случае, ура.
Патрик

10

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

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

Этот скрипт всегда печатает 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

1
Престижность за образовательный ответ, который вышел за рамки того, о чем спрашивали, но ответил на учебные намерения оригинального спрашивающего и других, подобных мне!
Рикардо
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.