Bash'te, bir dizenin bir değerle başlayıp başlamadığını nasıl kontrol edebilirim?


745

Bir dize "düğüm" ile başlar örneğin kontrol etmek istiyorum "düğüm". Gibi bir şey

if [ $HOST == user* ]
  then
  echo yes
fi

Nasıl doğru şekilde yapabilirim?


Ayrıca HOST "kullanıcı1" veya "düğüm" ile başlayıp başlamadığını kontrol etmek için ifadeleri birleştirmek gerekir

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

> > > -bash: [: too many arguments

Nasıl doğru şekilde yapabilirim?


7
İfadeleri birleştirmek için fazla cazip olmayın. Daha iyi hata mesajları verebilir ve betiğinizin hata ayıklamasını kolaylaştırabilirsiniz. Ayrıca bash özelliklerinden kaçınırım. Anahtar gitmenin yoludur.
hendry

Yanıtlar:


1073

Gelişmiş Bash Komut Dosyası Kılavuzu'ndaki bu snippet şunları söylüyor:

# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.

[[ $a == z* ]]   # True if $a starts with a "z" (wildcard matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

Yani neredeyse doğru yapmıştınız; tek parantez yerine çift parantez gerekiyordu .


İkinci sorunuzla ilgili olarak, bu şekilde yazabilirsiniz:

HOST=user1
if  [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes1
fi

HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes2
fi

Hangisi yankılanacak

yes1
yes2

Bash'in ifsözdiziminin (IMO) alışması zor.


12
Normal ifade için [[$ a = ~ ^ z. *]] Demek istiyor musunuz?
JStrahl

3
Yani [[ $a == z* ]]ve arasında fonksiyonel bir fark var [[ $a == "z*" ]]mı? Başka bir deyişle: farklı çalışıyorlar mı? Ve "$ a z * 'ya eşittir" derken özellikle ne demek istiyorsun?
Niels Bom

5
";" Deyimi ayırıcısına ihtiyacınız yok "sonra" kendi satırına koyarsanız
Yaza

6
Sadece bütünlük için: Bir dizginin ENDS olup olmadığını kontrol etmek için ...:[[ $a == *com ]]
lepe

4
ABS talihsiz bir referans seçimidir - W3Schools of bash, eski içerik ve kötü uygulama örnekleri ile doludur; freenode #bash kanalı, en azından 2008'den beri kullanımından vazgeçmeye çalışıyor . BashFAQ # 31'de yeniden imza atma şansı var mı? (Ben de Bash-Hacker'ların wiki'sini önerirdim, ama şimdi biraz aşağı düştü).
Charles Duffy

207

Bash'in (v3 +) yeni bir sürümünü kullanıyorsanız, Bash normal ifade karşılaştırma operatörünü öneririm, =~örneğin,

if [[ "$HOST" =~ ^user.* ]]; then
    echo "yes"
fi

this or thatBir normal ifadeyle eşleşmek |için, örneğin,

if [[ "$HOST" =~ ^user.*|^host1 ]]; then
    echo "yes"
fi

Not - bu 'uygun' normal ifade sözdizimidir.

  • user*araçlar useve sıfır veya-daha fazla yinelemesi r, bu nedenle useve userrrreşleşir.
  • user.*araçlar userve herhangi bir karakterin sıfır veya-daha çok geçmez bu yüzden user1, userXeşleşir.
  • ^user.*user.*$ HOST başındaki kalıpla eşleşmek anlamına gelir .

Normal ifade sözdizimine aşina değilseniz, bu kaynağa başvurmayı deneyin .


Teşekkürler Brabster! Orijinal yazıda if cluase içinde ifadelerin nasıl birleştirileceği hakkında yeni bir soru ekledim.
Tim

2
Kabul edilen cevabın düzenli ifadenin sözdizimi hakkında hiçbir şey söylememesi üzücü.
CarlosRos

20
FYI, Bash =~operatörü yalnızca sağ taraf UNQUOTED olduğunda düzenli ifade eşleşmesi yapar. Sağ tarafı alıntı yaparsanız "Desenin herhangi bir kısmı, bir dize olarak eşleşmeye zorlamak için alıntılanabilir." (1.) düzenli ifadeleri her zaman tırnaksız olarak sağa koyduğunuzdan emin olun ve (2.) düzenli ifadenizi bir değişkende saklarsanız, parametre genişletme yaparken sağ tarafı alıntı ETMEDİĞİNİZDEN emin olun.
Trevor Boyd Smith

145

shBash uzantılarını kullanmak yerine her zaman POSIX ile çalışmaya çalışıyorum , çünkü komut dosyalarının en önemli noktalarından biri taşınabilirliktir ( programları bağlamaktan başka , onları değiştirmekten başka).

İçinde sh"is-prefix" koşulunu kontrol etmenin kolay bir yolu vardır.

case $HOST in node*)
    # Your code here
esac

Ne kadar eski, gizemli ve acımasız sh olduğu göz önüne alındığında (ve Bash tedavi değildir: Daha karmaşık, daha az tutarlı ve daha az taşınabilir), çok güzel bir işlevsel yönü belirtmek isterim: Gibi bazı sözdizimi öğeleri caseyerleşik olsa da sonuçta ortaya çıkan yapılar diğer işlerden farklı değildir. Aynı şekilde bestelenebilirler:

if case $HOST in node*) true;; *) false;; esac; then
    # Your code here
fi

Veya daha da kısa

if case $HOST in node*) ;; *) false;; esac; then
    # Your code here
fi

Ya da daha kısa (sadece !bir dil öğesi olarak sunmak - ama şimdi bu kötü bir stil)

if ! case $HOST in node*) false;; esac; then
    # Your code here
fi

Açık olmak istiyorsanız, kendi dil öğenizi oluşturun:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }

Bu aslında pek hoş değil mi?

if beginswith node "$HOST"; then
    # Your code here
fi

Ve shtemel olarak sadece işler ve dize listeleri (ve içeriden işlerin oluşturulduğu dahili işlemler) olduğundan, şimdi bazı hafif fonksiyonel programlama bile yapabiliriz:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }

all() {
    test=$1; shift
    for i in "$@"; do
        $test "$i" || return
    done
}

all "beginswith x" x xy xyz ; checkresult  # Prints TRUE
all "beginswith x" x xy abc ; checkresult  # Prints FALSE

Bu zarif. shCiddi bir şey için kullanmayı savunacağımdan değil - gerçek dünya gereksinimlerini çok hızlı bir şekilde kırıyor (lambda yok, bu yüzden dizeleri kullanmalıyız. Ancak dizelerle iç içe işlev çağrıları mümkün değil, borular mümkün değil, vb.)


12
+1 Sadece bu taşınabilir değil, aynı zamanda okunabilir, deyimsel ve zariftir (kabuk betiği için). Aynı zamanda doğal olarak çoklu kalıplara da uzanır; case $HOST in user01 | node* ) ...
üçlü

Bu tür kod biçimlendirme için bir ad var mı? if case $HOST in node*) true;; *) false;; esac; then Burada ve orada gördüm, gözüme göre biraz sıkılmış görünüyor.
Niels Bom

@NielsBom Tam biçimlendirme ile ne demek bilmiyorum, ama benim açımdan bu kabuk kodu çok fazla oldu composable . Becaues casekomutları komuttur , içeri girebilirler if ... then.
Jo So

Neden bile kompozit olduğunu görmüyorum, bunun için yeterli kabuk komut dosyası anlamıyorum :-) Benim sorum bu kod eşleşmemiş parantez ve çift noktalı virgül kullanır. Daha önce gördüğüm kabuk komut dosyası gibi bir şey görünmüyor, ancak bas komut dosyasını sh komut dosyasından daha fazla görmeye alışkınım, böylece olabilir.
Niels Bom

Not: olması gerektiği beginswith() { case "$2" in "$1"*) true;; *) false;; esac; }, aksi takdirde $1bir hazır bulunur *ya ?da yanlış cevap verebilir.
LLFourn

80

Dizenin yalnızca kontrol etmek istediğiniz bölümünü seçebilirsiniz:

if [ "${HOST:0:4}" = user ]

Takip sorunuz için bir VEYA kullanabilirsiniz :

if [[ "$HOST" == user1 || "$HOST" == node* ]]

8
Doublequote${HOST:0:4}
Jo So

@Jo So: Sebebi nedir?
Peter Mortensen

@PeterMortensen, deneyinHOST='a b'; if [ ${HOST:0:4} = user ] ; then echo YES ; fi
Jo So

60

Zaten gönderilen diğer yöntemleri tercih ederim, ancak bazı insanlar kullanmayı sever:

case "$HOST" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac

Düzenle:

Alternatiflerinizi yukarıdaki vaka beyanına ekledim

Düzenlediğiniz sürümde çok fazla parantez var. Şöyle görünmelidir:

if [[ $HOST == user1 || $HOST == node* ]];

Teşekkürler Dennis! Orijinal yazıda if cluase içinde ifadelerin nasıl birleştirileceği hakkında yeni bir soru ekledim.
Tim

11
"bazı insanlar ..." gibi: bu sürümler ve kabuklar arasında daha taşınabilir.
carlosayam

Vaka ifadeleriyle, kelime bölünmesi olmadığından tırnakları değişkenin etrafında bırakabilirsiniz. Anlamsız ve tutarsız olduğunu biliyorum ama yerel olarak daha görsel olarak cazip hale getirmek için tırnak işaretlerini orada bırakmak istiyorum.
Jo So

Ve benim durumumda, daha önce alıntıları bırakmak zorunda kaldım): "/ *") işe yaramadı, / *) işe yaramadı. (/, Yani, mutlak yollarla başlayan dizeleri arıyorum)
Josiah Yoder

36

Burada cevapların çoğunu oldukça doğru bulsam da, birçoğunda gereksiz Bashizmler var. POSIX parametre genişletmesi size ihtiyacınız olan her şeyi sağlar:

[ "${host#user}" != "${host}" ]

ve

[ "${host#node}" != "${host}" ]

${var#expr}En küçük önek eşleşmesini şeritler exprgelen ${var}ve iadeler söyledi. Bu nedenle, eğer ${host}yok değildir ile başlar user( node), ${host#user}( ${host#node}) ile aynıdır ${host}.

exprfnmatch()joker karakterlere izin verir , böylece ${host#node??}arkadaşlar da çalışır.


2
Bashizmin [[ $host == user* ]]gerekli olabileceğini savunuyorum , çünkü daha okunabilir [ "${host#user}" != "${host}" ]. Komut dosyasının yürütüldüğü ortamı (en son sürümlerini hedefleyin) kontrol ettiğiniz gibi bash, ilk tercih edilir.
x-yuri

2
@ x-yuri Açıkçası, bunu bir has_prefix()fonksiyona toplayıp bir daha asla bakmam.
Dhke

30

Yana #Bash bir anlamı vardır, ben şu çözümü lazım.

Ayrıca boşlukların üstesinden gelmek için dizeleri "" ile paketlemekten daha çok hoşlanıyorum.

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "Skip comment line"
fi

Tam da ihtiyacım olan buydu. Teşekkürler!
Ionică Bizău

teşekkürler, ben de ile başlayan bir dize nasıl maç merak ediyordum blah:, bu cevap gibi görünüyor!
Anentropik

12

Mark Rushakoff'un en yüksek rütbe cevabına biraz daha sözdizimi detayı eklemek.

İfade

$HOST == node*

Ayrıca şu şekilde de yazılabilir

$HOST == "node"*

Etkisi aynı. Joker karakterin alıntılanan metnin dışında olduğundan emin olun. Joker karakter tırnak işaretleri içindeyse , kelimenin tam anlamıyla yorumlanır (yani joker karakter olarak değil).


8

@ OP, her iki sorunuz için case / esac kullanabilirsiniz:

string="node001"
case "$string" in
  node*) echo "found";;
  * ) echo "no node";;
esac

İkinci soru

case "$HOST" in
 node*) echo "ok";;
 user) echo "ok";;
esac

case "$HOST" in
 node*|user) echo "ok";;
esac

Veya Bash 4.0

case "$HOST" in
 user) ;&
 node*) echo "ok";;
esac

Not ;&Bash> = 4'te kullanılabilir
bir sonraki duyuruya kadar durdurulmuş.

6
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

Çünkü her şeyden çalışmaz [, [[ve testaynı nonrecursive dilbilgisi tanır. Bkz. KOŞULLU ANLATIMLARBash man sayfanızdaki .

Bir kenara, SUSv3 diyor ki

KornShell'den türetilen koşullu komut (çift köşeli ayraç [[]] ) erken bir teklifte kabuk komut dili açıklamasından kaldırıldı. Gerçek sorunun test komutunun ( [ ) yanlış kullanımı olduğuna itiraz edildi ve kabuğun içine konulması sorunu düzeltmenin yanlış yoludur. Bunun yerine, uygun belgeler ve yeni bir kabuk ayrılmış sözcük ( ! ) Yeterlidir.

Çoklu test gerektiren testler işlemlerinin bireysel çağırmaları kullanılarak kabuk düzeyinde yapılabilir Test komuta ve kabuk Mantıksal bölümler yerine hataya eğilimli kullanılarak -o bayrağını testi .

Bu şekilde yazmanız gerekir, ancak test bunu desteklemez:

if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi

testi string eşitliği için = kullanır ve daha önemlisi kalıp eşleşmeyi desteklemez.

case/ esacdesen eşleşmesi için iyi bir desteğe sahiptir:

case $HOST in
user1|node*) echo yes ;;
esac

Bash'a bağlı olmaması da ek bir yararı vardır ve sözdizimi taşınabilirdir. Gönderen Single Unix Specification , Shell Command Language :

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac

1
[ve testBash yerleşiklerinin yanı sıra harici programlar. Deneyin type -a [.
sonraki duyuruya kadar duraklatıldı.

"Bileşik" ile ilgili sorunları açıkladığınız için çok teşekkürler, @just birisi - tam olarak böyle bir şey arıyordu! Şerefe! PS notu (OP ile ilgisi yoktur) if [ -z $aa -or -z $bb ]:; ... " bash: [: -veya: ikili operatör beklenir ” verir; ancak if [ -z "$aa" -o -z "$bb" ] ; ...geçer.
sdaau

2

grep

Performansı unutmak, bu POSIX ve caseçözümlerden daha güzel görünüyor :

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi

Açıklama:


2

@ Markrushakoff'un cevabı, çağrılabilir bir işlev haline getirmek için tweaked:

function yesNo {
  # Prompts user with $1, returns true if response starts with y or Y or is empty string
  read -e -p "
$1 [Y/n] " YN

  [[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}

Şöyle kullanın:

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] Y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] yes
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n]
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] ddddd
false

Belirtilen bir varsayılan değer sağlayan daha karmaşık bir sürüm:

function toLowerCase {
  echo "$1" | tr '[:upper:]' '[:lower:]'
}

function yesNo {
  # $1: user prompt
  # $2: default value (assumed to be Y if not specified)
  # Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string

  local DEFAULT=yes
  if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
  if [[ "$DEFAULT" == y* ]]; then
    local PROMPT="[Y/n]"
  else
    local PROMPT="[y/N]"
  fi
  read -e -p "
$1 $PROMPT " YN

  YN="$( toLowerCase "$YN" )"
  { [ "$YN" == "" ] && [[ "$PROMPT" = *Y* ]]; } || [[ "$YN" = y* ]]
}

Şöyle kullanın:

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N]
false

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N] y
true

$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

-5

Yapabileceğiniz başka bir şey cat, yankılanıp boru yaptığınız şeydirinline cut -c 1-1

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.