Apa arti $ # dalam bash?

26

Saya memiliki skrip dalam file bernama instance:

echo "hello world"
echo ${1}

Dan ketika saya menjalankan skrip ini menggunakan:

./instance solfish

Saya mendapatkan hasil ini:

hello world
solfish

Tapi ketika saya lari:

echo $# 

Dikatakan "0". Mengapa? Saya tidak mengerti apa $#artinya.

Tolong jelaskan itu.

solfish
sumber
10
Kenapa kamu lari $#? Apa yang ingin Anda capai? Di mana Anda mendapatkan perintah ini. Itu tidak relevan sama sekali.
Pilot6
7
@ Pilot6 dalam hal ini ia meminta arti dari suatu variabel, ini bukan kasus spesifik jadi mengapa op perlu memberikan informasi itu?
ADDB
21
@solfish Pilot sedang mencoba memahami apa yang Anda harapkan. Anda bilang Anda lari echo $#dan kembali 0yang normal. Anda terkejut dengan ini, tetapi Anda tidak menjelaskan apa yang Anda harapkan atau mengapa Anda terkejut. Jadi itu akan membantu kami memberikan jawaban yang lebih baik jika Anda menjelaskan apa yang Anda harapkan. Apa yang Anda pikir echo $#akan dilakukan. Jika Anda berlari ./instance solfishdan ./instancememuat echo $#, itu akan mencetak 1dan tidak 0.
terdon
1
Java juga tidak menggunakan argc.
JakeRobb

Jawaban:

66

$#adalah variabel khusus dalam bash, yang memperluas ke jumlah argumen (parameter posisi) yaitu $1, $2 ...diteruskan ke skrip yang bersangkutan atau shell dalam kasus argumen langsung diteruskan ke shell misalnya di bash -c '...' .....

Ini mirip dengan argcdi C.


Mungkin ini akan memperjelas:

$ bash -c 'echo $#'
0

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

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

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

Perhatikan bahwa, bash -cambil argumen setelah perintah yang mengikutinya mulai dari 0 ( $0; secara teknis, itu hanya bashcara untuk membiarkan Anda menetapkan $0, bukan argumen sebenarnya), jadi _digunakan di sini hanya sebagai pengganti; argumen aktual adalah x( $1), y( $2), dan z( $3).


Demikian pula, dalam skrip Anda (dengan asumsi script.sh) jika Anda memiliki:

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

Lalu ketika Anda melakukannya:

./script.sh foo bar

skrip akan menampilkan 2; juga,

./script.sh foo

akan menampilkan 1.

heemayl
sumber
1
Saya pikir jawaban ini menyesatkan. $ # adalah indeks maksimum dari array argumen. Jika Anda memberikan satu argumen, itu akan tersedia pada $ 0, dan $ # akan memiliki nilai 0. Jika Anda memberikan dua argumen, mereka akan berada dalam $ 0 dan $ 1, dan $ # akan memiliki nilai 1.
JakeRobb
1
Selain itu, ketika Anda menggunakan bash -c, perilaku berbeda dari jika Anda menjalankan skrip shell yang dapat dieksekusi, karena dalam kasus terakhir argumen dengan indeks 0 adalah perintah shell yang digunakan untuk menjalankannya. Karena itu, saya pikir cara untuk memperbaiki jawaban ini adalah dengan mengubahnya untuk mengeksekusi skrip sebagai file daripada menggunakan bash -c, karena itulah bagaimana penanya melakukannya.
JakeRobb
1
@JakeRobb No. Untuk skrip, $0akan menjadi skrip itu sendiri; argumen akan menjadi $1, $2, $3.... bash -cperilaku berbeda karena ini dimaksudkan untuk penggunaan non-interaktif dan argumen mengikuti perintah akan mulai dari $0, saya telah menyebutkan ini dengan jelas saya percaya.
heemayl
5
@JakeRobb Anda salah dengar. Jika Anda memberikan satu argumen, itu akan tersedia pada $ 0, dan $ # akan memiliki nilai 0 - ini jelas salah.
heemayl
2
Jika Anda memasukkan program lengkap yang melihatnya sebagai argumen bash -c, Anda harus memberikan bashbeberapa nama yang bermakna sebagai pengisi untuk arg ke-0. bash -c 'echo "$@"' foo barhanya cetak bar, karena "$@"tidak termasuk $0, sama seperti biasanya. bash -ctidak "membuat $ 0 salah satu args", itu hanya memungkinkan Anda untuk set "$ 0" dengan cara yang sama Anda ketika menjalankan program dengan para execvesystem call atau cara cangkang menimpa arg 0. $0tidak boleh dianggap "salah satu argumen".
Peter Cordes
17

echo $# menampilkan jumlah parameter posisi skrip Anda.

Anda tidak memilikinya, sehingga menghasilkan 0.

echo $# berguna di dalam skrip, bukan sebagai perintah terpisah.

Jika Anda menjalankan skrip dengan beberapa parameter seperti

./instance par1 par2

yang echo $#ditempatkan ke dalam skrip akan menampilkan 2.

Pilot6
sumber
6
Sebagai contoh OP: Jika Anda menambahkan baris echo $#ke skrip Anda dan kemudian jalankan lagi ./instance solfishAnda harus mendapatkan jalur output tambahan 1karena Anda memberikan 1 parameter
derHugo
14

$#biasanya digunakan dalam skrip bash untuk memastikan parameter dilewatkan. Umumnya Anda memeriksa parameter di awal skrip Anda.

Misalnya, inilah cuplikan skrip yang saya kerjakan hari ini:

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

Untuk meringkas $#laporan, jumlah parameter diteruskan ke skrip. Dalam kasus Anda, Anda tidak melewati parameter dan hasil yang dilaporkan adalah 0.


#Kegunaan lain di Bash

Ini #sering digunakan dalam bash untuk menghitung jumlah kemunculan atau panjang variabel.

Untuk menemukan panjang string:

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

pengembalian: 11

Untuk menemukan jumlah elemen array:

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

pengembalian: 3

Untuk menemukan panjang elemen array pertama:

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

mengembalikan: 1(Panjang A, 0 adalah elemen pertama karena array menggunakan indeks / subskripsi berbasis nol).

WinEunuuchs2Unix
sumber
$# -ne 1? Kenapa tidak $# -le 0atau setara?
Patrick Trentin
@ PatrickTrentin Karena saya tidak ingin mereka melewati dua atau lebih parameter. Seperti kata Kapten dalam "Red October": "Just One".
WinEunuuchs2Unix
Kemudian: "tepat satu argumen yang diperlukan ..." dalam pesan kesalahan
Patrick Trentin
@ PatrickTrentin Pesan galat menjelaskan bagaimana skrip digunakan dengan contoh penggunaan satu argumen. Selanjutnya skrip cadangan dipanggil oleh cron.daily yang hanya dikonfigurasikan sekali oleh seseorang yang ahli teknologi: askubuntu.com/questions/917562/…
WinEunuuchs2Unix
Bagaimana itu relevan saya tidak akan tahu. Bagaimanapun, tepuk tangan.
Patrick Trentin
10

$# adalah jumlah argumen, tetapi ingat itu akan berbeda dalam suatu fungsi.

$#adalah jumlah parameter posisi yang diteruskan ke fungsi skrip, shell, atau shell . Ini karena, saat fungsi shell sedang berjalan, parameter posisi sementara diganti dengan argumen ke fungsi . Ini memungkinkan fungsi menerima dan menggunakan parameter posisinya sendiri.

Skrip ini selalu dicetak 3, terlepas dari berapa banyak argumen yang diteruskan ke skrip itu sendiri, karena "$#"dalam fungsinya fmemperluas ke jumlah argumen yang diteruskan ke fungsi:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Ini penting karena ini berarti kode seperti ini tidak berfungsi seperti yang Anda harapkan, jika Anda tidak terbiasa dengan cara parameter posisi bekerja dalam fungsi shell:

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

Dalam check_args, $#memperluas ke jumlah argumen yang diteruskan ke fungsi itu sendiri, yang dalam skrip itu selalu 0.

Jika Anda ingin fungsi seperti itu dalam fungsi shell, Anda harus menulis sesuatu seperti ini sebagai gantinya:

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

Ini bekerja karena $#diperluas di luar fungsi dan dilewatkan ke fungsi sebagai salah satu yang parameter posisi. Di dalam fungsi, $1memperluas ke parameter posisi pertama yang dilewatkan ke fungsi shell, bukan ke skrip bagian itu.

Dengan demikian, seperti $#, parameter khusus $1, $2, dll, serta $@dan $*, juga berkaitan dengan argumen dilewatkan ke fungsi, ketika mereka diperluas dalam fungsi. Namun, $0tidak tidak berubah nama fungsi, itulah sebabnya saya masih bisa menggunakannya untuk menghasilkan pesan kesalahan kualitas.

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

Demikian pula, jika Anda mendefinisikan satu fungsi di dalam yang lain, Anda bekerja dengan parameter posisi yang diteruskan ke fungsi terdalam di mana ekspansi dilakukan:

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

Saya menelepon skrip ini nesteddan (setelah menjalankan chmod +x nested) saya menjalankannya:

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

Ya saya tahu. "1 argumen" adalah bug pluralisasi.

Parameter posisi juga dapat diubah.

Jika Anda menulis skrip, parameter posisi di luar fungsi akan menjadi argumen baris perintah yang diteruskan ke skrip kecuali Anda telah mengubahnya .

Salah satu cara umum untuk mengubahnya adalah dengan shiftbuiltin, yang menggeser setiap parameter posisi ke kiri oleh satu, menjatuhkan yang pertama dan menurun $#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".

Mereka juga dapat diubah dengan setbuiltin:

#!/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
Eliah Kagan
sumber
1
Kudos untuk jawaban edukatif yang melampaui apa yang diminta tetapi menanggapi niat belajar dari penanya asli dan orang lain seperti saya!
Ricardo