Bash betiğini farklı parametrelerle yinelemeli olarak çağırmak


1

Verilen dizindeki ve alt dizinlerdeki tüm dosyaları sayan bir bash betiği yazmaya çalışıyorum, bu yüzden şunu yazdım:

#!/bin/bash

var=0
if ["$#" == "0"]
    directory="$(echo pwd)"
  then
    directory=$1
fi

echo $directory;
for x in `ls -l $directory | grep "^-" | tr -s ' ' | cut -d ' ' -f 9`;
do
var=$((var+1))
done

for x in `ls -l $directory | grep "^d" | tr -s ' ' | cut -d ' ' -f 9`;
do
output = "$($0 $x)"
done

var=$((var+output))
echo $var

Ama böyle bir şey alıyorum:

./lala2
./lala2: line 4: [0: command not found

test
./lala2: line 4: [1: command not found
./lala2: line 4: [1: command not found

Neden? Değişkenler küresel midir?

Yanıtlar:


3

Değiştir:

if ["$#" == "0"]

ile:

if [ "$#" = "0" ]

Bazı yorumlar:

  1. Kabukta boşluklar önemlidir. Kabuğun test komutunu tanımasını istiyorsanız, [etrafında boşluklar olması gerekir [.

    Boşluklar, kabuk anahtar kelimeler için önemli değildir , ancak [aksine [[, bir komuttur, anahtar kelime değildir.

  2. Hata mesajının anlamı [0: command not foundolduğunu "$#"dize böylece 0'a değerlendirildi ["$#"oldu [0ve bash adıyla hiçbir komutu bulabiliriz [0.

  3. ==Bash'te string eşitliği için kullanmak mümkündür . Ancak, taşınabilir değil. POSIX, doğru eşitlik operatörü içinde [...]olduğunu =. Komut dosyanızı kasten veya kasıtsız olarak bir POSIX kabuğu altında çalıştırırsanız, bu çok önemlidir dash.

Diğer sorunlar

  1. Bir kez daha, boşluklar önemlidir. Kabuk bu komutu gördüğünde:

    output = "$($0 $x)"

    denilen bir komutu çalıştırmaya çalışacak outputve birincisi =ve diğeri sonucu olan iki argüman sunmaya çalışacaktır "$($0 $x)". Sonuç olarak, Gordon Davisson'un işaret ettiği gibi, bu satırın yerine:

    output="$($0 $x)"

    Etrafında boşluk olmadığı =için kabuk bunu değişken atama olarak kabul edecektir.

  2. Betik çıktısını ayrıştırmaya çalışır lsve bu güvenilmezdir . Bu durumda, alt dizin adlarından herhangi birinin beyaz boşluk içermesi hatalara yol açacaktır.

  3. Rici'nin işaret ettiği gibi, komut directory="$(echo pwd)"muhtemelen beklediğiniz şeyi yapmaz. Gözlemek:

    $ directory="$(echo pwd)"
    $ echo "$directory"
    pwd

    Bu komut dizeyi pwddeğişkene atar directory. Muhtemelen, şu andaki çalışma dizinini istersiniz, böyle:

    directory=$(pwd)

    Ancak, bundan daha basittir. Unix'te mevcut çalışma dizini daima .:

    directory=.
  4. Bu if-then ifadesinin birinin umabileceğini yapmamasının başka nedenleri de vardır:

    if ["$#" == "0"]
        directory="$(echo pwd)"
      then
        directory=$1
    fi

    Muhtemelen thenifadeyi taşımak ve bir elseifade eklemek istersiniz :

    if [ "$#" = "0" ]
    then
        directory=.
    else
        directory="$1"
    fi

Alternatif komut dosyası

Amaç, bir dizindeki ve altındaki tüm dizinlerdeki normal dosya sayısını saymaksa, deneyin:

echo "$(find "$directory" -type f -printf "1\n" | wc -l)"

Bu yaklaşım, dosya veya dizin adları boşluk veya diğer zor karakterler içeriyorsa bile güvenlidir.


Ayrıca, bir test (veya [) komutunda boşluklar gerekliyken , ödevlerde yasaktır. Bu yüzden output = "$($0 $x)"değiştirilmelidir output="$($0 $x)". (Ayrıca, boşlukların komutların yanı sıra anahtar kelimeler için de önemli olduğunu söyleyebilirim: [["$#"daha iyi çalışmayacak ["$#").
Gordon Davisson

@GordonDavisson Evet. Mükemmel nokta Cevaba yapılan ödevlerin tartışmasını ekledim.
John1024,

1
directory="$(echo pwd)"Ayrıca neredeyse kesinlikle yanlıştır. ( directory=pwd
Amaçlanmadığı

@rici İyi gözlem. Ben echo pwdde cevaba bir tartışma ekledim .
John1024

2
İlginçtir ki, 4. puanınız% 100 doğru olsa da, yanlış kod amaçlanan etkiye sahip olacaktır. directory=$(echo pwd) her zaman başarılı olur, bu nedenle thenfıkra her zaman yürütülecektir; Eğer $1atanmamışsa, o zaman bu atanacak directory=ve daha sonra bunun yerine ls -l $directorygenişleyecektir , eğer değişken genişleme doğru bir şekilde alıntı yapılmış olsaydı ne elde edersiniz. Bence buna "hata komedisi" denir. ls -l ls -l ''
rici,
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.