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