'1 [$ 1 = “1”]' dalı neden $ 1 1 olmasa bile her zaman seçilir?


10

Bunun gibi 'teleport.sh' adlı bir kabuk komut dosyası var:

if [ $1="1" ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ $1="2" ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ $1="3" ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

Çalıştırdığımda:

sh teleport.sh 2 testfile

Bu testfile, ~/lab/Sundizeye 1 veya '1' iletmediğim için beni çok şaşırtan dizine taşınır .

Burada yanlış olan ne?


1
Laboratuvar , Güneş , Ay , Dünya ve ışınlanma için +1 . Ancak , genişletmeleri ( , ve hatta [ tercih edilmesi gereken]) her zaman iki katına çıkarmalısınız. Orada olmayan bazı kenar durumlardır var alıntı ama her zaman zarar vermez yapıyor. $var$(cmd)`cmd`$(cmd)
nyuszika7h

@ nyuszika7h, çift tırnak "$ var" ve "$ cmd" anlamına gelmemelidir? Yukarıda bahsettiğiniz yuvarlak köşeli parantezin avantajı nedir?
Zen

$(cmd)bir komut ikamesi (çoğunlukla), aynı `cmd`. Bkz. Mywiki.wooledge.org/CommandSubstitution ve mywiki.wooledge.org/BashFAQ/082
nyuszika7h

Yanıtlar:


19

Boşluk kullanmak sorununuzu çözer.

if [ "$1" = 1 ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ "$1" = 2 ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ "$1" = 3 ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

Bu daha temiz olmasına rağmen:

#!/bin/bash

action=$1
shift
files=("$@")
case $action in  
  1) mv -- "${files[@]}" ~/lab/Sun     ;;
  2) mv -- "${files[@]}" ~/lab/Moon    ;;
  3) mv -- "${files[@]}" ~/lab/Earth   ;;
esac

3
Evet, boşluklar gerekli, ama orijinal $1="1"sözdiziminin neden "işe yaradığını" merak ediyorum (gerçek bir sonuç üretiyor). [/ testBuiltin bu ifadeyi gerçekte nasıl yorumlar?
echristopherson

5
@echristopherson Boşluk olmadan, testyalnızca bir argüman görür ("l-değeri"). Diğer argümanlar olmadan ("operatör" ve "r-değeri"), test edilecek hiçbir şey yoktur, bu yüzden testsadece "tamam, evet, bana bir şey verdin ve bu tamamen doğru olmalı" diyor.
piskopos

2
$1="1"olduğu $1ile birleştirilmiş=1
jdh8

vaka kullanırken, koşullardan hiçbiri karşılanmadığında yürütülecek bir eylem nasıl ayarlanır?
Zen

11

İlk bariz şey [, testveya [[:

if [ "$1" = 1 ];

Bash'deyken, [[ ]]sözcük bölme ve yol adı genişletme gibi koşullu ifade için gereksiz şeyler yapmadığı için kullanılması önerilir. Çift tırnak işaretleri arasında alıntı yapmak da gerekli değildir. Daha okunabilir bir operatör ==de kullanılabilir.

if [[ $1 == 1 ]];

Eklenen not: İkinci işlenen da değişkenler içeriyorsa o gibi tanınabilir karakterler içeriyorsa desen eşleştirme tabi olabilir gibi alıntı gereklidir *, ?, [], .. Eğer globbing genişletilmiş veya desen eşleştirme ile etkindir vb shopt -s extglob, diğer formları gibi @(), !()vb desen olarak da kabul edilecektir. Bkz. Örüntü Eşleme .

Gibi operatörler ile <ve >hala bir kez ikinci argüman alıntı değil farklı sonuçlara neden bir hata ile karşılaşmış gibi gerekli olabilir.

İlk işlenen için hiçbir şey geçerli değildir.

Bu daha basit varyasyonu da düşünün:

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac

Veya yoğunlaştırılmış:

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac

"${@:2}"2ofset olan bir alt dize genişletme veya dizi üyesi genişletme biçimidir . Bu, genişlemeyi ikinci değerden başlatır. Bununla kullanmamız gerekmeyebilir shift.

Eklenen , tire ( ) ile başlayan dosya adlarının geçersiz seçenekler olarak tanınmasını --önler .mv-


her durumda kırılmamalısın?
Archemar

2
@Archemar: hayır, düşme yok (diğer birçok dilin aksine).
Mat

2
@Mat, (ksh, bash, yalnızca zsh) ;&yerine kullanırsanız düşme olur ;;. Ama sonra breakhala düşmeyi engellemez break, sadece döngülerden kurtulmaktır.
Stéphane Chazelas

Bu if, [komuta değil, argümana .
Stéphane Chazelas

1
Düzeltme: çift tırnak sadece sol tarafta gerekli değildir! [[ $foo == $bar ]]desen eşleşmesi gerçekleştirir, ancak gerçekleştirmez [[ $foo == "$bar" ]].
nyuszika7h

7

Bu neden oluyor sorusunu yanıtlamak için bu davranış [aka testedilir POSIX'deki belgelenmiştir :

Aşağıdaki listede $ 1, $ 2, $ 3 ve $ 4 test etmek için sunulan argümanları temsil etmektedir:

[...]

1 argüman:

$ 1 boş değilse true (0) 'dan çıkın; aksi takdirde false'tan çıkın.

Ona 2=1null olmayan ve bu nedenle testbaşarılı bir şekilde çıkan 1 argümanını geçiyorsunuz.

Diğer mesajlar (ve şöyle shellcheck ) Eğer eşitlik karşılaştırmak istiyorsa, bunun yerine 3 argümanları geçmek zorunda, işaret 2, =ve 1.


3
Bir anlamda bu sadece soruyu cevaplayan cevaptır (OP'nin gerçekleştirmek istediği şeyi yapan bir veya daha fazla tarif vermek yerine) ve kabuk, yaptığı şeyleri neden yaptığını bilen yeterince gizemli olabilir. .
dmckee --- eski moderatör yavru kedi

1

Sadece taşınabilir ama aynı zamanda daha temiz bir alternatif tavsiye etmek istiyorum . Bash evrensel değildir (ve evrensel olana ihtiyacınız yoksa, neden bir kabuk senaryosu yazıyorsunuz?)

#! /bin/sh
action="$1"
shift
case "$action" in
    1) dest=Sun   ;;
    2) dest=Moon  ;;
    3) dest=Earth ;;
    *) echo "Unrecognized action code '$action' (must be 1, 2, or 3)" >&2; exit 1 ;;
esac
mv -- "$@" ~/lab/"$dest"

(Bilgiçler için Not: evet, etrafa tırnak biliyorum $actionüzerinde case "$action" ingereksizdir çizgi, ama o gelecek okuyucular unutmamak gerek kalmaz, zaten orada onları koymak en iyisidir hissediyorum.)


1
"Bash evrensel değil (ve evrensel ihtiyacınız yoksa, neden bir kabuk betiği yazıyorsunuz?)" - Bu, bash komut dosyasının kullanım durumları olmadığı anlamına geliyor.
Ruslan

@Ruslan Evet, bu benim görüşüm. /bin/shGerekirse taşınabilir komut dosyaları yazın ; aksi takdirde, kabuktan daha az korkunç bir betik dilinde yazın. Temel Perl tercümanı aslında eski tescilli ortamlarda ve kesilmiş gömülü ortamlarda bulunma olasılığı Bash'e göre daha fazladır.
zwol
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.