
У меня есть скрипт в файле с именем instance:
echo "hello world"
echo ${1}
И когда я запускаю этот скрипт с помощью:
./instance solfish
Я получаю такой вывод:
hello world
solfish
Но когда я бегу:
echo $#
Там написано "0". Почему? Я не понимаю, что это $#
значит.
Пожалуйста, объясните это.
решение1
$#
— это специальная переменная в bash
, которая расширяется до количества аргументов (позиционных параметров), т.е. $1, $2 ...
переданных рассматриваемому скрипту или оболочке в случае, если аргумент напрямую передан оболочке, например, в bash -c '...' ....
.
Это похоже на argc
C.
Возможно, это прояснит ситуацию:
$ 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