$ # หมายถึงอะไรในการทุบตี?


26

ฉันมีสคริปต์ในไฟล์ชื่ออินสแตนซ์:

echo "hello world"
echo ${1}

และเมื่อฉันเรียกใช้สคริปต์นี้โดยใช้:

./instance solfish

ฉันได้ผลลัพธ์นี้:

hello world
solfish

แต่เมื่อฉันวิ่ง:

echo $# 

มันบอกว่า "0" ทำไม? ฉันไม่เข้าใจความ$#หมาย

กรุณาอธิบาย


10
ทำไมคุณเรียกใช้$#? คุณต้องการทำอะไรให้สำเร็จ คุณได้รับคำสั่งนี้จากที่ไหน มันไม่เกี่ยวข้องเลย
Pilot6

7
@ Pilot6 ในกรณีนี้เขาถามถึงความหมายของตัวแปรนี่ไม่ใช่กรณีเฉพาะดังนั้นเหตุใด op จึงต้องการให้ข้อมูลนั้น
ADDB

21
@solfish Pilot พยายามที่จะเข้าใจสิ่งที่คุณคาดหวัง คุณบอกว่าคุณวิ่งecho $#และมันกลับมา0ซึ่งเป็นเรื่องปกติ คุณประหลาดใจกับสิ่งนี้ แต่คุณไม่ได้อธิบายสิ่งที่คุณคาดหวังหรือทำไมคุณถึงประหลาดใจ ดังนั้นมันจะช่วยให้เราได้คำตอบที่ดีกว่าถ้าคุณอธิบายสิ่งที่คุณคาดหวัง สิ่งที่คุณคิดว่าecho $#จะทำ ถ้าคุณขับรถ./instance solfishและ./instanceมีecho $#ที่จะพิมพ์และไม่ได้1 0
terdon


1
Java ยังไม่ได้ใช้ argc
JakeRobb

คำตอบ:


66

$#เป็นตัวแปรที่พิเศษในการ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


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ชื่อที่มีความหมายว่าฟิลเลอร์สำหรับ arg ที่ 0 bash -c 'echo "$@"' foo barพิมพ์เพียงbarเพราะ"$@"ไม่รวม$0เหมือนกันเสมอ bash -cไม่ได้ "ทำให้ $ 0 เป็นหนึ่งใน args" แต่อนุญาตให้คุณตั้งค่า "$ 0" แบบเดียวกับที่คุณใช้เมื่อรันโปรแกรมด้วยการexecveเรียกของระบบหรือวิธีเชลล์ใด ๆ $0ไม่ควรถูกพิจารณาว่าเป็น "หนึ่งใน args"
Peter Cordes

17

echo $# ส่งออกจำนวนพารามิเตอร์ตำแหน่งของสคริปต์ของคุณ

คุณไม่มีดังนั้นมันจะส่งออก 0

echo $# มีประโยชน์ภายในสคริปต์ไม่ใช่คำสั่งแยกต่างหาก

หากคุณเรียกใช้สคริปต์ด้วยพารามิเตอร์บางอย่างเช่น

./instance par1 par2

ที่echo $#วางไว้ในสคริปต์จะส่งออก 2


6
สำหรับตัวอย่างของ OP: หากคุณเพิ่มบรรทัดecho $#ลงในสคริปต์ของคุณและเรียกใช้อีกครั้ง./instance solfishคุณควรได้รับบรรทัดเอาต์พุตเพิ่มเติม1เนื่องจากคุณได้ระบุพารามิเตอร์ 1 ตัว
derHugo

14

$#โดยทั่วไปจะใช้ในสคริปต์ทุบตีเพื่อให้แน่ใจว่าพารามิเตอร์จะถูกส่งผ่าน โดยทั่วไปคุณตรวจสอบพารามิเตอร์ในตอนต้นของสคริปต์ของคุณ

ตัวอย่างเช่นนี่เป็นตัวอย่างของสคริปต์ที่ฉันกำลังทำอยู่ในวันนี้:

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(ความยาวเท่ากับA0 คือองค์ประกอบแรกที่อาร์เรย์ใช้ดัชนี / ตัวห้อยที่อ้างอิงศูนย์)


$# -ne 1? ทำไมไม่$# -le 0หรือเทียบเท่า
Patrick Trentin

@PatrickTrentin เพราะฉันไม่ต้องการให้พารามิเตอร์สองตัวขึ้นไป อย่างที่กัปตันพูดใน "Red October": "Just One"
WinEunuuchs2Unix

จากนั้น: "จำเป็นต้องมีอาร์กิวเมนต์หนึ่งข้อ ... " ในข้อความแสดงข้อผิดพลาด
Patrick Trentin

@PatrickTrentin ข้อความแสดงข้อผิดพลาดจะอธิบายวิธีการใช้สคริปต์พร้อมกับตัวอย่างของการใช้อาร์กิวเมนต์หนึ่งรายการ นอกจากนี้สคริปต์สำรองข้อมูลถูกเรียกโดย cron.daily ซึ่งมีการกำหนดค่าเพียงครั้งเดียวโดยคนที่มีความชำนาญด้านเทคโนโลยี: askubuntu.com/questions/917562/…
WinEunuuchs2Unix

ฉันจะไม่ทราบว่าเกี่ยวข้องกันอย่างไร อย่างไรก็ตามไชโย
Patrick Trentin

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".

พวกเขาสามารถเปลี่ยนแปลงได้ด้วย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

1
ความรุ่งโรจน์สำหรับคำตอบที่ให้ความรู้นอกเหนือไปจากสิ่งที่ถูกถาม แต่ตอบสนองต่อความตั้งใจในการเรียนรู้ของผู้ถามดั้งเดิมและคนอื่น ๆ เช่นฉัน!
Ricardo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.