$ # Bashta ne anlama geliyor?


24

Örnek isimli bir dosyada bir betiğim var:

echo "hello world"
echo ${1}

Ve bu betiği kullanarak çalıştırdığımda:

./instance solfish

Bu çıktıyı alıyorum:

hello world
solfish

Ama kaçtığımda:

echo $# 

"0" yazıyor. Neden? Ne anlama $#geldiğini anlamıyorum .

Lütfen açıkla.


9
Neden çalıştırabilirim $#? Ne elde etmek istiyorsun? Bu komutu nereden aldın? Hiç alakalı değil.
Pilot6,

7
@ Pilot6 bu durumda bir değişkenin anlamını sorar, bu durum özel değildir, bu yüzden neden op bu bilgiyi vermek zorunda olsun istedi?
ADDB

21
@solfish Pilot ne beklediğini anlamaya çalışıyor. Koştuğunu echo $#ve 0normal olanın geri döndüğünü söyledin . Buna şaşırdın, ama ne beklediğini ya da neden şaşırdığını açıklamıyorsun. Beklediğiniz şeyi açıklarsanız, size daha iyi bir cevap vermemize yardımcı olacaktır. Ne echo $#yapabileceğini düşündün . Eğer koştuysan ./instance solfishve ./instanceiçerdiysen echo $#, yazdırabilir 1ve yazdırmaz 0.
terdon


1
Java ayrıca argc kullanmaz.
JakeRobb

Yanıtlar:


65

$#özel bir değişkendir bashargüman (konumsal parametreler) sayısı kadar genişler, yani $1, $2 ..., söz konusu komut dosyası veya doğrudan kabuk, örneğin iletilen değişken durumunda kabuk geçirilir bash -c '...' .....

Bu, argcC'ye benzer .


Belki de bu açıkça ortaya çıkarır:

$ bash -c 'echo $#'
0

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

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

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

Unutmayın, bash -c0'dan başlayan komuttan sonra argüman alır ( $0; teknik olarak, sadece bir argümanı değil, bashayarlamanıza izin verme şeklidir $0), bu yüzden _burada sadece bir yer tutucu olarak kullanılır; gerçek argümanlar x( $1), y( $2) ve z( $3) 'dir.


Benzer şekilde, script.sheğer varsa betiğinizde (varsayarak ):

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

Sonra ne zaman:

./script.sh foo bar

komut dosyası 2 çıkacak; aynı şekilde,

./script.sh foo

1 çıkacak.


1
Bence bu cevap yanıltıcıdır. $ #, bağımsız değişken dizisinin maksimum dizinidir. Onlar değeri 1. olacak yılında $ 0 $ ve 1 $ ve # olacağım, bir argüman verirseniz, o $ 0 mevcut olacak ve iki argüman verirseniz $ # 0 değerine sahip olacaktır
JakeRobb

1
Ayrıca, kullandığınızda bash -cdavranış, çalıştırılabilir bir kabuk betiğini çalıştırmanızdan farklıdır, çünkü ikinci durumda dizin 0 ile argüman onu çağırmak için kullanılan kabuk komutudur. Dolayısıyla, bu cevabı düzeltmenin yolunun komut dosyası kullanmak yerine komut dosyası olarak değiştirmesini sağlamak olduğunu düşünüyorum, çünkü soru bash -csahibi bu şekilde yapıyordu.
JakeRobb

1
@JakeRobb Hayır. Bir komut dosyası $0için komut dosyasının kendisi olur; Argümanlar olurdu $1, $2, $3.... bash -cdavranış etkileşimli olmayan kullanım içindir ve komutu izleyen argümanlar başlayacağı için farklıdır, $0açık bir şekilde bahsetmiştim.
heemayl

5
@JakeRobb Beni karıştırdın. Bir argüman verirseniz, bu $ 0'dan itibaren kullanılabilir olacak ve $ #, 0 değerine sahip olacak - bu yanlış.
heemayl

2
Arguları inceleyen eksiksiz bir program koyarsanız , 0'ın argümanının dolgusu olarak anlamlı bir isim bash -cvermelisiniz bash. bash -c 'echo "$@"' foo baryalnızca yazdırır bar, çünkü her zaman olduğu gibi "$@"içermez $0. bash -c"$ 0 args biri haline" değil, bu sadece size "$ 0" tan, bir program çalıştırarak aynı şekilde ayarlamanızı sağlar sistem çağrısı veya 0 arg geçersiz kılma herhangi kabuk yolu. asla "yollardan biri" olarak değerlendirilmemelidir. execve$0
Peter Cordes

17

echo $# betiğinizin konumsal parametrelerinin sayısını gösterir.

Hiç yok, bu yüzden 0 çıktısı var.

echo $# ayrı bir komut olarak değil betiğin içinde kullanışlıdır.

Bir betiği gibi bazı parametrelerle çalıştırıyorsanız

./instance par1 par2

echo $#komut çıkış 2 olacak yerleştirilir.


6
OP örneği için: Eğer echo $#betiğinize bir satır eklerseniz ve tekrar çalıştırıyorsanız, 1 parametresi sağladığınızdan ./instance solfishek bir çıkış satırı 1
almalısınız

13

$#tipik olarak bir parametrenin geçirilmesini sağlamak için bas komut dosyalarında kullanılır. Genellikle betiğinizin başında bir parametre olup olmadığını kontrol edersiniz.

Örneğin, bugün üzerinde üzerinde çalıştığım bir betiğin parçası:

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

Özetlemek gerekirse $#, bir komut dosyasına iletilen parametrelerin sayısı. Senin durumunda hiçbir parametre geçmedin ve raporlanan sonuç 0.


#Bash diğer kullanım alanları

#Genellikle geçiş sayısının, ya da bir değişken uzunluk saymak için Bash kullanılır.

Bir dizenin uzunluğunu bulmak için:

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

döner: 11

Dizi elemanlarının sayısını bulmak için:

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

döner: 3

İlk dizi elemanının uzunluğunu bulmak için:

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

Döndürür: 1( ADiziler sıfır temelli dizinler / aboneler kullandıklarından, 0 uzunluğu ilk öğedir).


$# -ne 1? Neden olmasın $# -le 0ya da eşdeğeri?
Patrick Trentin

@PatrickTrentin Çünkü iki veya daha fazla parametre geçmelerini istemiyorum. Kaptan "Kırmızı Ekim" dediği gibi: "Sadece Bir".
WinEunuuchs2Unix

Sonra: "tam olarak bir argüman gerekli ..." hata mesajında
Patrick Trentin

@PatrickTrentin Hata mesajı, betiğin tek bir argüman kullanımı örneği ile nasıl kullanıldığını açıklar. Ayrıca yedekleme betiği, yalnızca bir kez teknoloji meraklısı bir kişi tarafından yapılandırılan cron.daily tarafından çağrılır: askubuntu.com/questions/917562/…
WinEunuuchs2Unix

Bunun ne kadar alakalı olduğunu bilemem. Neyse, şerefe.
Patrick Trentin

10

$# bağımsız değişkenlerin sayısıdır, ancak bir işlevde farklı olacağını unutmayın.

$#komut dosyası, kabuk veya kabuk işlevine iletilen konumsal parametrelerin sayısıdır . Bunun nedeni, bir kabuk işlevi çalışırken, konum parametrelerinin geçici olarak işlevin argümanları ile değiştirilmiş olmasıdır . Bu, fonksiyonların kendi konum parametrelerini kabul etmesini ve kullanmasını sağlar.

Bu komut dosyası 3, komut dosyasında kaç tane argüman kullanıldığına bakılmaksızın her zaman yazdırılır , çünkü "$#"işlevde işleve fverilen argüman sayısına genişler:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Bu önemlidir, çünkü konum parametrelerinin kabuk işlevlerinde nasıl çalıştığını bilmiyorsanız, böyle bir kodun beklediğiniz gibi çalışmadığı anlamına gelir:

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

Içinde check_args, $#bu betiğin içinde her zaman 0 olan fonksiyonun kendisine iletilen argümanların sayısına genişler.

Bir kabuk işlevinde bu tür bir işlevsellik istiyorsanız, bunun yerine böyle bir şey yazmanız gerekir:

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

Bunun nedeni çalışır $#genişletilir dışında fonksiyonu ve biri olarak işleve iletilen onun konumsal parametreler. İşlev içinde, $1parçası olduğu komut dosyası yerine kabuk işlevine geçirilen ilk konumsal parametreye genişler.

Böylece gibi $#, özel parametreler $1, $2vs., hem de $@ve $*, aynı zamanda fonksiyon genişletilmiş bir işleve geçirilen bağımsız değişkenler, aittir. Ancak, $0yok değil hala kaliteli bir hata mesajı üretmek için kullanmak başardı yüzden fonksiyonun ismine değiştirin.

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

Benzer şekilde, bir işlevi diğerinin içinde tanımlarsanız, genişlemenin gerçekleştirildiği en içteki işleve iletilen konumsal parametrelerle çalışıyorsunuz:

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

Bu betiği aradım nestedve (çalıştırdıktan sonra chmod +x nested) koştum:

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

Evet biliyorum. "1 argüman" çoğullaştırma hatası.

Konumsal parametreler de değiştirilebilir.

Bir komut dosyası yazıyorsanız, bir işlevin dışındaki konumsal parametreler siz değiştirmediğiniz sürece komut dosyasına iletilen komut satırı argümanları olacaktır .

Bunları değiştirmenin yaygın bir yolu shift, her bir konumsal parametreyi bir sola kaydırıp, ilkini düşürerek $#ve 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".

setYerleşik ile de değiştirilebilirler :

#!/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
Sorulanın ötesine geçen ancak asıl askerin ve benim gibi diğerlerinin öğrenme niyetlerine cevap veren eğitici cevap için Kudos!
Ricardo,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.