Bagaimana menjadikan argumen sebagai opsional dalam bash?

13

Dalam fungsi di bawah ini dengan 9 argumen:

SUM() { 
    echo "The sum is $(($1+$2+$3+$4+$5+$6+$7+$8+$9))"
}

Saya ingin membuat argumen kedua ke yang berikutnya (3..9) menjadi argumen opsional .

Ketika saya memanggil fungsi dengan 2 argumen, saya mendapatkan kesalahan:

SUM 3 8
bash: 3+8+++++++: syntax error: operand expected (error token is "+")

Catatan BOLD : argumen pertama dan argumen kedua adalah argumen paksa dan bukan opsional untuk fungsi. Saya hanya ingin argumen kedua ke yang berikutnya adalah opsional dan ketika saya memanggil fungsi kurang dari 2 argumen fungsi harus mengembalikan hasil.

αғsнιη
sumber

Jawaban:

22

Jika Anda tidak akan memberikan argumen dengan spasi:

sum() {  
[[ -n $2 ]] && echo $(( $(tr ' ' '+' <<<"$@") ))
}

Efek:

$ sum 1 2 3
6

Penjelasan:

  1. <<<"some string"umpan hanya "some string"sebagai input. Anggap saja sebagai singkatan echo "some string" |. Ini disebut String Di Sini .
  2. "$@"meluas ke semua parameter posisi, dipisahkan oleh spasi. Ini setara dengan "$1 $2 ...".
  3. Oleh karena itu, tr ' ' '+' <<<"$@"output "$1+$2+$3...", yang dievaluasi oleh luar $(( )).
  4. [[ -n $2 ]]menguji apakah parameter kedua tidak kosong. Anda bisa menggantinya [[ -n $2 ]] &&dengan [[ -z $2 ]] ||.

Cara lain:

sum() {
[[ -n $2 ]] && (IFS=+; echo $(( $* )))
}

Penjelasan:

  1. $*hanya seperti $@, kecuali bahwa parameter tidak dipisahkan oleh spasi, tetapi oleh karakter pertama Pemisah Bidang Internal ( IFS) . Dengan IFS=+, itu diperluas menjadi "$ 1 + $ 2 + ...". Lihat Apa perbedaan antara $ * dan $ @?
  2. Kami memasang IFSsubkulit (perhatikan tanda kurung di sekitarnya) sehingga shell utama tidak terpengaruh. IFSsecara default: \t\n(spasi, tab, baris baru). Ini adalah alternatif untuk menggunakan localvariabel.

Sekarang untuk menjawab pertanyaan Anda:

Anda dapat menggunakan nilai default untuk variabel atau parameter apa pun. Antara:

SUM() { 
 echo "The sum is $(($1+$2+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))" || false
}

Atau:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}
muru
sumber
6
Bagus! Saya tahu komentar tidak dimaksudkan untuk pujian dan terima kasih gratis, tetapi solusi ini hanya ... jahat! :-)
zwets
17

Lihatlah shiftoperatornya. Ini akan menggeser argumen 2 dan seterusnya ke posisi 1 dan seterusnya, membuang argumen 1.

sum () {
    local total=0;
    while [ $# -gt 0 ]; do
        total=$(($total + $1))
        shift
    done
    echo $total
}
zwets
sumber
4

Anda bisa menggunakan definisi rekursif yang berakhir ketika sumdipanggil tanpa argumen. Kami menggunakan fakta yang testtanpa argumen dievaluasi false.

sum () {
    test $1 && echo $(( $1 + $(shift; sum $@) )) || echo 0
}
zwets
sumber
3

Coba ini:

SUM () {
 [ $# -lt "2" ] && return 1
 for par in $@; do
   local sum=`expr $sum + $par`
 done
 echo $sum
 return 0
}

SUM 3 4 5
SUM 3 4 5 1 1 1 1 2 3 4 5

Ini akan menghasilkan 12 dan 30.

$@merujuk ke parameter, $#mengembalikan jumlah parameter, dalam hal ini 3 atau 11.

Diuji di linux redhat 4

Lety
sumber
2

Anda bisa menggunakan loop kecil:

sum(){
    t=0;
    for i in "$@"; do t=$((t + i )); done
    echo $t;
}

Secara pribadi, saya hanya menggunakan perlatau awksebagai gantinya:

sum(){
 echo "$@" | perl -lane '$s+=$_ for @F; print $s'
}

atau

sum(){
 echo "$@" | awk '{for(i=1; i<=NF; i++){k+=$i} print k}'
}
terdon
sumber
2

Gunakan 0 sebagai nilai default untuk $ 1 hingga $ 9:

SUM() { 
    echo "The sum is $((${1:-0}+${2:-0}+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))"
}

Dari man bash:

${parameter:-word}
    Use Default Values. If parameter is unset or null, the expansion
    of word is substituted. Otherwise, the value of parameter is
    substituted.

Contoh:

$ SUM

Jumlahnya adalah 0

$ SUM 1 2 

Jumlahnya adalah 3

$ SUM 1 1 1 1 1 1 1 1 1 

Jumlahnya adalah 9


Output yang sama dengan awk:

SUM() {
  echo -e ${@/%/\\n} | awk '{s+=$1} END {print "The sum is " s}'
}
Cyrus
sumber
1

Ini juga solusi saya sendiri, saya mencobanya dan menemukan:

SUM() { 
    echo "The sum is $(($1+$2+$[$3]+$[$4]+$[$5]+$[$6]+$[$7]+$[$8]+$[$9]))"
 }

$ SUM 4 6 5
The sum is 15

Tapi jawaban @ muru bagus.

αғsнιη
sumber
+1: Penggunaan dua ekspansi aritmatika yang menarik untuk mengevaluasi parameter kosong menjadi nol.
muru
1
@muru terima kasih, tetapi dalam hal ini jawaban saya, kami tidak menggunakan lebih dari 9 argumen dan kami harus menggunakan kelompok argumen untuk melewati lebih dari 9. terima kasih atas jawaban Anda yang sempurna.
αғsнιη