Bash'da bir “git” ifadesi var mı?


205

Bash'de "git" ifadesi var mı? Kötü uygulama olarak kabul edildiğini biliyorum, ama özellikle "git" la ihtiyacım var.


4
Hayır, gotobash'da yok (en azından command not foundbenim için söylüyor). Neden? Muhtemelen bunu yapmanın daha iyi bir yolu vardır.
Niklas

153
Sebepleri olabilir. Bu soruyu buldum çünkü bir gotoifadenin, çeşitli ilgisiz görevlerin tamamlanması için bir saat beklemeden büyük bir komut dosyasının hata ayıklaması için çok fazla kod atlamasını istiyorum . gotoÜretim kodunda kesinlikle bir kullanmazdım , ancak kodumda hata ayıklamak için hayatımı son derece kolaylaştıracak ve kaldırmaya geldiğinde fark etmek daha kolay olurdu.
Karl Nicoll

30
@delnan Ama hiç gitmemek bazı şeyleri daha karmaşık hale getirebilir. Gerçekten de kullanım örnekleri vardır.
glglgl

71
Bu efsane efsanesinden bıktım! Gitmeyle ilgili yanlış bir şey yok! Sonunda yazdığınız her şey gitmeye başlar. Montajcıda sadece goto var. Goto'yu daha yüksek programlama dillerinde kullanmanın iyi bir nedeni, örneğin iç içe ilmeklerden temiz ve okunabilir bir şekilde atlamaktır.
Alexander Czutro

25
"Gitmekten kaçın" harika bir kuraldır. Herhangi bir kural gibi, üç aşamada öğrenilmelidir. İlk olarak: ikinci doğa olana kadar kuralı takip edin. İkincisi, kuralın nedenlerini anlamayı öğrenin. Üçüncüsü, kuralı nasıl takip edeceğinizi ve kuralın nedenlerini kapsamlı bir şekilde anlayarak, kurala ilişkin iyi istisnaları öğrenin. Yukarıdaki adımları atlamaktan kaçının; bu "goto" kullanmak gibidir. ;-)
Jeff Learman

Yanıtlar:


75

Hayır yok; bkz §3.2.4 "Bileşik Komutları" in Manuel Bash Referans kontrol yapıları hakkında bilgi almak için yapmak vardır. Özellikle, bir söz dikkat breakve continuegibi esnek olarak hangilerinin goto, ancak bazı dillerde daha Bash daha esnektir ve istediğini elde etmenize yardımcı olabilir. (İstediğin her neyse.)


10
"Bash'te bazı dillerden daha esnek" konusunu genişletebilir misiniz?
user239558

21
@ user239558: Bazı diller yalnızca en içteki döngüde breakveya continuebu döngüden izin verirken Bash, kaç döngü düzeyinin atlayacağını belirtmenize izin verir. (Ve rastgele döngüler yapmanıza breakveya continuebunlardan çıkmanıza izin veren diller bile , çoğu statik olarak ifade edilmesini gerektirir - örneğin, break foo;etiketli döngüden kopar foo- Bash'de dinamik olarak ifade edilir - örneğin, döngülerden break "$foo"kopar $foo. )
ruakh

İşlevleri gerekli özelliklerle tanımlamanızı ve kodun diğer bölümlerinden kalıtsalmanızı öneririm.
blmayer

@ruakh Aslında Bash'ın break LABEL_NAME;iğrenç hali yerine birçok dil izin veriyor break INTEGER;.
Sapphire_Brick

1
@Sapphire_Brick: Evet, yorumda yanıtladığınızdan bahsettim.
ruakh

124

Hata ayıklama için büyük bir komut dosyasının bir bölümünü atlamak için kullanıyorsanız (Karl Nicoll'un yorumuna bakın), false iyi bir seçenek olabilirse ("false" öğesinin her zaman kullanılabilir olup olmadığından emin değilim, benim için / bin / false dizininde) :

# ... Code I want to run here ...

if false; then

# ... Code I want to skip here ...

fi

# ... I want to resume here ...

Zorluk, hata ayıklama kodunuzu çıkarmanın zamanı geldiğinde ortaya çıkar. "İf false" yapısı oldukça basit ve akılda kalıcıdır, ancak eşleşen fi'yi nasıl bulursunuz? Editörünüz girintiyi engellemenize izin veriyorsa, atlanan bloğu girintileyebilirsiniz (işiniz bittiğinde geri koymak istersiniz). Ya da fi hattına bir yorum, ancak hatırlayacağınız bir şey olmalı, ki bu çok programcıya bağımlı olacaktır.


6
Evet falseher zaman kullanılabilir. Ancak yürütmek istemediğiniz bir kod bloğunuz varsa, bunu yorumlamanız yeterlidir. Veya silin (ve daha sonra kurtarmanız gerekirse kaynak kontrol sisteminize bakın).
Keith Thompson

1
Kod bloğu her seferinde bir satırını sıkıcı bir şekilde yorumlamak için çok uzunsa, bu hilelere bakın. stackoverflow.com/questions/947897/… Ancak, bunlar da bir metin düzenleyicinin başından sonuna kadar eşleşmesine yardımcı olmaz, bu yüzden çok fazla bir gelişme değildir.
Camille Goudeseune

4
"if false" genellikle kodu yorumlamaktan çok daha iyidir, çünkü ekteki kodun yasal kod olmaya devam etmesini sağlar. Kodu yorumlamak için en iyi bahane, gerçekten silinmesi gerektiğinde, ancak hatırlanması gereken bir şey var - bu sadece bir yorum ve artık "kod" değil.
Jeff Learman

1
'#İf false;' yorumunu kullanıyorum. Bu şekilde sadece bunu arayabilir ve hata ayıklama kaldırma bölümünün hem başlangıç ​​hem de bitişini bulabilirim.
Jon

Bu sorunun cevabı OP'nin sorusuna hiçbir şekilde cevap vermediği için kaldırılmamalıdır.
Viktor Joras

54

Gerçekten de bazı hata ayıklama veya gösterim ihtiyaçları için yararlı olabilir.

Bob Copeland çözümünün http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:

#!/bin/bash
# include this boilerplate
function jumpto
{
    label=$1
    cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | grep -v ':$')
    eval "$cmd"
    exit
}

start=${1:-"start"}

jumpto $start

start:
# your script goes here...
x=100
jumpto foo

mid:
x=101
echo "This is not printed!"

foo:
x=${x:-10}
echo x is $x

sonuç:

$ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101

36
"Bash'i montaj dili gibi gösterme arayışım tamamlanmaya yaklaşıyor." - Vay. Vay canına.
Brian Agnew

5
değiştireceğim tek şey etiketlerin : start:sözdizimi hataları olmaması için başlamasını sağlamaktır .
Alexej Magura

5
Bunu yapsaydınız daha iyi olurdu: cmd = $ (sed -n "/ # $ label: / {: a; n; p; ba};" $ 0 | grep -v ': $') şu şekilde başlayan etiketlerle: # başlangıç: => bu komut dosyası hatalarını önler
access_granted

2
Hm, gerçekten büyük olasılıkla benim hatam. Düzenleme yayınım var. Teşekkür ederim.
Hubbitus

2
set -xneler olduğunu anlamaya yardımcı olur
John Lin

30

Bir casegotoyu simüle etmek için bash'ta kullanabilirsiniz:

#!/bin/bash

case bar in
  foo)
    echo foo
    ;&

  bar)
    echo bar
    ;&

  *)
    echo star
    ;;
esac

üretir:

bar
star

4
Bunun gerekli olduğunu unutmayın bash v4.0+. Bununla birlikte, genel bir amaç gotodeğil, caseaçıklama için düşme seçeneğidir .
mklement0

3
Bence bu cevap olmalı. belirli bir talimat, bir komut dosyası yürütme sürdürmek desteklemek için gitmek için gerçek bir ihtiyaç var. bu, her şekilde semantik, goto ve semantik ve sözdizimsel şekerler sevimli olmakla birlikte kesinlikle gerekli değildir. harika bir çözüm, IMO.
nathan g

1
@nathang, bu olsun cevap davanız OP hakkında sorulan genel davanın alt kümesiyle örgü olur bağlıdır. Ne yazık ki, soru genel durumu soruyor ve bu cevabı doğru olamayacak kadar dar yapıyor. (Bu sorunun bu nedenle çok geniş olarak kapatılıp kapatılmayacağı farklı bir tartışmadır).
Charles Duffy

1
goto seçmekten daha fazlasıdır. goto özelliği, bazı koşullara göre yerlere atlamak, akış gibi bir döngü bile oluşturmak anlamına gelir ...
Sergio Abreu

19

Bir bash betiğini test ediyorsanız / hata ayıklama yapıyorsanız ve kodun bir veya daha fazla bölümünü geçip atlamak istiyorsanız, işte bunu daha sonra bulmanın ve kaldırmanın çok kolay olduğu çok basit bir yol var (yöntemlerin çoğunun aksine) Yukarıda tarif edilen).

#!/bin/bash

echo "Run this"

cat >/dev/null <<GOTO_1

echo "Don't run this"

GOTO_1

echo "Also run this"

cat >/dev/null <<GOTO_2

echo "Don't run this either"

GOTO_2

echo "Yet more code I want to run"

Betiğinizi normale döndürmek için, ile satırları silin GOTO.

Ayrıca, gototakma ad olarak bir komut ekleyerek bu çözümü önizleyebiliriz :

#!/bin/bash

shopt -s expand_aliases
alias goto="cat >/dev/null <<"

goto GOTO_1

echo "Don't run this"

GOTO_1

echo "Run this"

goto GOTO_2

echo "Don't run this either"

GOTO_2

echo "All done"

Takma adlar genellikle bash komut dosyalarında çalışmaz, bu yüzden bunu shoptdüzeltmek için komuta ihtiyacımız vardır.

goto'Nizi etkinleştirmek / devre dışı bırakmak istiyorsanız , biraz daha fazlasına ihtiyacımız var:

#!/bin/bash

shopt -s expand_aliases
if [ -n "$DEBUG" ] ; then
  alias goto="cat >/dev/null <<"
else
  alias goto=":"
fi

goto '#GOTO_1'

echo "Don't run this"

#GOTO1

echo "Run this"

goto '#GOTO_2'

echo "Don't run this either"

#GOTO_2

echo "All done"

Sonra export DEBUG=TRUEkomut dosyasını çalıştırmadan önce yapabilirsiniz .

Etiketler yorumdur, bu nedenle goto'lerimizi devre dışı bırakırsak sözdizimi hatalarına neden olmaz ( goto' :'no-op olarak ayarlayarak ), ancak bu, bunları gotoifadelerimizde alıntılamamız gerektiği anlamına gelir .

Her türlü gotoçözümü kullanırken, geçmişte atladığınız kodun daha sonra güveneceğiniz herhangi bir değişken ayarlamamasına dikkat etmeniz gerekir - bu tanımları komut dosyanızın üstüne veya hemen üstüne taşımanız gerekebilir senin içinde gototablolar.


Goto'ların iyiliği / kötülüğüne girmeden, Laurence'ın çözümü sadece iyidir. Oyumu sen aldın.
Mogens TrasherDK

1
Bu daha çok bir atlama if(false)gibi, daha mantıklı görünüyor (benim için).
vesperto

2
Git "etiketini" Burada-doc o da araçlara aktaran değil bazı zor kurtarır değerlendirdi genişletilmiş / böcek (bulmak tırnaksız, bu tabi olacaktır: bunların hepsi olabilir, parametre genişletme, komut ikamesi ve aritmetik genişleme yan etkiler). Ayrıca bunu biraz değiştirebilir alias goto=":<<"ve cattamamen dağıtabilirsiniz .
mr.spuratic

evet, vesperto, bir 'if' ifadesi kullanabilirsiniz, ve bunu birçok komut dosyasında yaptım, ancak özellikle dağınık noktalarınızı sık sık değiştirmek istiyorsanız, çok dağınık hale gelir ve kontrol edilmesi ve izlenmesi çok daha zordur.
Laurence Renshaw

12

Diğerleri bash'de doğrudan bir gotoeşdeğer olmadığını (ve işlevler, döngüler ve kopma gibi en yakın alternatifleri sağladığını) açıklamış olsa da, bir döngü artısının breakbelirli bir goto ifadesi türünü nasıl simüle edebileceğini göstermek istiyorum .

Bunu en yararlı bulduğum durum, belirli koşullar karşılanmadığında kod bölümünün başına dönmem gerektiğidir. Aşağıdaki örnekte while döngüsü, ping paketleri bir test IP'sine bırakmayı durdurana kadar sonsuza kadar çalışacaktır.

#!/bin/bash

TestIP="8.8.8.8"

# Loop forever (until break is issued)
while true; do

    # Do a simple test for Internet connectivity
    PacketLoss=$(ping "$TestIP" -c 2 | grep -Eo "[0-9]+% packet loss" | grep -Eo "^[0-9]")

    # Exit the loop if ping is no longer dropping packets
    if [ "$PacketLoss" == 0 ]; then
        echo "Connection restored"
        break
    else
        echo "No connectivity"
    fi
done

7

Bu çözümün aşağıdaki sorunları vardı:

  • İle biten tüm kod satırlarını rasgele kaldırır :
  • Satırdaki label:herhangi bir yere etiket olarak davranır

İşte sabit ( shell-checktemiz) bir sürüm:


#!/bin/bash

# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461
function goto
{
 local label=$1
 cmd=$(sed -En "/^[[:space:]]*#[[:space:]]*$label:[[:space:]]*#/{:a;n;p;ba};" "$0")
 eval "$cmd"
 exit
}

start=${1:-start}
goto "$start"  # GOTO start: by default

#start:#  Comments can occur after labels
echo start
goto end

  # skip: #  Whitespace is allowed
echo this is usually skipped

# end: #
echo end

6

İstenen sonuçları elde etmek için bir yetenek daha var: komut trap. Örneğin temizlik amacıyla kullanılabilir.


4

gotoBash yok .

İşte trapsadece geriye doğru atlayan kirli bir çözüm var :)

#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT

echo foo
goto trap 2> /dev/null
echo bar

Çıktı:

$ ./test.sh 
foo
I am
here now.

Bu şekilde kullanılmamalı, sadece eğitim amaçlı kullanılmalıdır. İşte bunun nedeni:

trapkod akışındaki değişikliği sağlamak için kural dışı durum işleme kullanıyor. Bu durumda trap, komut dosyasının EXIT olmasına neden olan her şeyi yakalar. Komut gotomevcut değildir ve bu nedenle komut dosyasından normalde çıkacak bir hata atar. Bu hata yakalanıyor trapve 2>/dev/nullnormalde görüntülenecek hata mesajını gizliyor.

Goto'nun bu uygulaması kesinlikle güvenilir değildir, çünkü varolmayan herhangi bir komut (veya bu şekilde başka bir hata) aynı tuzak komutunu yürütür. Özellikle, hangi etikete gideceğinizi seçemezsiniz.


Temel olarak gerçek senaryoda herhangi bir goto ifadesine ihtiyacınız yoktur, farklı yerlere rastgele çağrılar sadece kodunuzu anlamayı zorlaştırdığı için gereksizdirler.

Kodunuz birçok kez çağrılırsa, döngü kullanmayı continueve iş akışını ve kullanacak şekilde değiştirmeyi düşünün break.

Kodunuz bunu tekrarlıyorsa, işlevi yazmayı ve istediğiniz kadar çağırmayı düşünün.

Kodunuzun değişken değere dayalı olarak belirli bir bölüme atlaması gerekiyorsa, casedeyimi kullanmayı düşünün .

Uzun kodunuzu daha küçük parçalara ayırabiliyorsanız, ayrı dosyalara taşımayı düşünün ve üst komut dosyasından çağırın.


Bu form ile normal işlev arasındaki farklar nelerdir?
yurenchen

2
@yurenchen - Kod akışındaki değişikliği başarmak için trapkullanmayı düşünün exception handling. Bu durumda tuzak, EXITolmayan komutun çağrılmasıyla tetiklenen komut dosyasına neden olan her şeyi yakalar goto. BTW: Bağımsız değişken goto trapherhangi bir şey olabilir, goto ignoredçünkü gotoEXIT'e neden olan budur ve 2>/dev/nullkomut dosyanızın çıktığını söyleyen hata iletisini gizler.
Jesse Chisholm

2

Bu, Hubbbitus tarafından hazırlanan Judy Schmidt senaryosunun küçük bir düzeltmesidir.

Komut dosyasına kaçan etiketleri koymak makinede sorunlu ve çökmesine neden oldu. Bu, etiketlerden kaçmak için # ekleyerek çözülebilecek kadar kolaydı. Alexej Magura ve access_granted önerileri için teşekkürler.

#!/bin/bash
# include this boilerplate
function goto {  
label=$1
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}

start=${1:-"start"}

goto $start

#start#
echo "start"
goto bing

#boom#
echo boom
goto eof

#bang#
echo bang
goto boom

#bing#
echo bing
goto bang

#eof#
echo "the end mother-hugger..."

Bu kopyala macunu çalışmıyor => hala bir jumpto var.
Alexis Paques

Bu ne anlama geliyor? Neler çalışmıyor? Daha spesifik olabilir misiniz?
thebunnyrules

Tabii ki kodu denedim! Hepsini başarılı bir şekilde göndermeden önce 5 veya 6 farklı koşulda test ettim. Kod sizin için nasıl başarısız oldu, hangi hata mesajı veya kod?
thebunnyrules

Ne olursa olsun adamım. Sana yardım etmek istiyorum ama gerçekten belirsiz ve iletişimsiz oluyorsun ("test ettin mi" sorusuyla hakaretten bahsetmiyorum bile). "Doğru yapıştırmadınız" ne anlama geliyor? "/ $ # Etiketine #: / {: a; n; p; ba};" kısmen farklı olduğunun farkındayım. Yeni etiket biçimi için değiştirdim (bazı durumlarda komut dosyasını çökerten diğer cevaptaki # etiket # vs: etiket). Cevabımla ilgili geçerli bir probleminiz var mı (script çökmesi veya kötü sözdizimi gibi) veya "Nasıl kesip yapıştıracağımı bilmiyorum" diye düşündüğünüz için cevabımı -1 mi yaptınız?
thebunnyrules

1
@thebunnyrules Seninle aynı konuya rastladım ama çözümün için +1 farklı çözdüm !
Fabby

2

Hata ayıklama sırasında kod bloklarını yorumlamak için basit bir arama yapılabilir goto.

GOTO=false
if ${GOTO}; then
    echo "GOTO failed"
    ...
fi # End of GOTO
echo "GOTO done"

Sonuç -> GOTO bitti


1

İşlevleri kullanarak bunu yapmanın bir yolunu buldum.

Say, örneğin, 3 seçeneğiniz var: A, B, ve C. Ave Bbir komutu uygulamak, ancak Csize daha fazla bilgi verir ve özgün istemi yeniden götürür. Bu işlevler kullanılarak yapılabilir.

Satır kaplaması function demoFunctionişlevi ayarladığından demoFunction, işlevin gerçekten çalışması için bu komut dosyasından sonra çağırmanız gerektiğini unutmayın .

GOTOKabuk betiğinizde başka bir yere " " ihtiyaç duyarsanız, birden çok işlev yazıp çağırarak bunu kolayca uyarlayabilirsiniz .

function demoFunction {
        read -n1 -p "Pick a letter to run a command [A, B, or C for more info] " runCommand

        case $runCommand in
            a|A) printf "\n\tpwd being executed...\n" && pwd;;
            b|B) printf "\n\tls being executed...\n" && ls;;
            c|C) printf "\n\toption A runs pwd, option B runs ls\n" && demoFunction;;
        esac
}

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