Що означає $ # в баші?


26

У мене є сценарій у файлі з ім'ям екземпляра:

echo "hello world"
echo ${1}

І коли я запускаю цей сценарій, використовуючи:

./instance solfish

Я отримую цей вихід:

hello world
solfish

Але коли я біжу:

echo $# 

На ньому написано "0". Чому? Я не розумію, що $#означає.

Будь ласка, поясніть це.


10
Чому ти біжиш $#? Чого ти хочеш досягти? Звідки ви взяли цю команду. Це зовсім не актуально.
Пілот6,

7
@ Pilot6 в цьому випадку він запитує значення змінної, це не конкретно для конкретного випадку, то чому б оператору потрібно було надати цю інформацію?
ADDB

21
@solfish Pilot намагається зрозуміти, чого ви очікували. Ви сказали, що ви побігли, 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
Для прикладу ОП: Якщо ви додасте рядок echo $#до свого сценарію і знову запустите, ./instance solfishвам слід отримати додатковий рядок виводу, 1оскільки ви надали 1 параметр
derHugo

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

The #Часто використовується в Баш , щоб підрахувати кількість входжень або довжину змінної.

Щоб знайти довжину рядка:

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

повертає: 11

Щоб знайти кількість елементів масиву:

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

повертає: 3

Щоб знайти довжину першого елемента масиву:

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

повертає: 1(Довжина A0, перший елемент, оскільки масиви використовують нульові індекси / підписки).


$# -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.