Wskazówki dotyczące gry w golfa w Bash


55

Jakie masz ogólne wskazówki na temat gry w golfa w Bash? Szukam pomysłów, które można by zastosować do problemów z golfem w kodzie, które są przynajmniej nieco specyficzne dla Basha (np. „Usuń komentarze” nie jest odpowiedzią). Proszę zamieścić jedną wskazówkę na odpowiedź.

Odpowiedzi:


36

Nieudokumentowane , ale działa w każdej wersji, na którą się natknąłem, w celu zachowaniashzgodnościze starszymiwersjami:

forPętle pozwalają używać { }zamiast do done. Np. Zamień:

for i in {1..10};do echo $i; done

z:

for i in {1..10};{ echo $i;}

co to jest shpowłoka i jaka powłoka pozwala na tę forskładnię? jest to wyraźnie dozwolone w zsh.
mikeserv

@mikeserv Bash. Pamiętam, że czytałem gdzieś, że ta składnia była dozwolona w jakimś starym shi że Bash na to pozwala również z tego powodu, choć niestety nie mam cytatu.
Cyfrowy uraz

ahh ... cshprawdopodobnie - tak działali w tej skorupie.
mikeserv

tak przy okazji, w ksh93powyższym przypadku może być ;{1..10}bashprintf %s\\n {1..10}
:,

1
for((;i++<10)){ echo $i;}jest krótszy niżfor i in {1..10};{ echo $i;}
Evan Krall

29

Do rozszerzania arytmetycznego użyj $[…]zamiast $((…)):

bash-4.1$ echo $((1+2*3))
7

bash-4.1$ echo $[1+2*3]
7

W rozszerzeniach arytmetycznych nie używaj $:

bash-4.1$ a=1 b=2 c=3

bash-4.1$ echo $[$a+$b*$c]
7

bash-4.1$ echo $[a+b*c]
7

Rozbudowa arytmetyczna jest wykonywana na indeksach tablic indeksowanych, więc nie używaj $żadnego z nich:

bash-4.1$ a=(1 2 3) b=2 c=3

bash-4.1$ echo ${a[$c-$b]}
2

bash-4.1$ echo ${a[c-b]}
2

W rozszerzeniach arytmetycznych nie używaj ${…}:

bash-4.1$ a=(1 2 3)

bash-4.1$ echo $[${a[0]}+${a[1]}*${a[2]}]
7

bash-4.1$ echo $[a[0]+a[1]*a[2]]
7

Zastąpienie while((i--)), które działa, while[i--]lub while $[i--]nie działało dla mnie. GNU bash, wersja 4.3.46 (1)
Glenn Randers-Pehrson

1
Prawidłowo, @ GlennRanders-Pehrson. To nie powinno działać.
manatwork

y=`bc<<<"($x*2.2)%10/1"`... przykład użycia bcdo obliczeń niecałkowitych ... zwróć uwagę, że /1na końcu skraca wynikowy ułamek dziesiętny do liczby całkowitej .
roblogic

s=$((i%2>0?s+x:s+y))... przykład użycia operatora trójskładnikowego w arytmetyce bash. Jest krótszy niż if..then..elselub[ ] && ||
roblogic

1
@manatwork Thanks. Musieli to usunąć. Jestem włączony GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)i nie ma go.
Jonasz

19

Normalnym, długim i nudnym sposobem definiowania funkcji jest

f(){ CODE;}

Jak się dowiedział ten facet , absolutnie potrzebujesz miejsca przed, CODEa po nim średnika.

Oto mała sztuczka, której nauczyłem się od @DigitalTrauma :

f()(CODE)

To dwa znaki krótsze i działa równie dobrze, pod warunkiem, że nie musisz przenosić żadnych zmian wartości zmiennych po powrocie funkcji ( nawiasy uruchamiają treść w podpowłoce ).

Jak @ jimmy23013 wskazuje w komentarzach, nawet nawiasy mogą być niepotrzebne.

Instrukcja Bash referencyjny pokazuje, że funkcje można zdefiniować następująco:

name () compound-command [ redirections ]

lub

function name [()] compound-command [ redirections ]

Polecenie związek może być:

  • Konstrukt zapętlanie: until, whilelubfor
  • Warunkowe Construct: if, case, ((...))lub[[...]]
  • Zgrupowane polecenia: (...)lub{...}

Oznacza to, że wszystkie poniższe informacje są prawidłowe:

$ f()if $1;then $2;fi
$ f()($1&&$2)
$ f()(($1))                # This one lets you assign integer values

I używałem nawiasów klamrowych jak przyssawki ...


2
Pamiętaj, że możesz także używać f()while ... f()if ...innych poleceń złożonych.
jimmy23013

Ten mnie zaskoczył, ponieważ myślałem, że f()CODEto legalne. Okazuje się, że f()echo hijest to legalne w pdksh i zsh, ale nie w bash.
kernigh,

1
jest to szczególnie użyteczne w /, forponieważ domyślnie ustawia się na pozycję: f()for x do : $x; done;set -x *;PS4=$* f "$@"lub coś.
mikeserv

16

:Jest to polecenie, że nic nie robi, jego status wyjścia zawsze udaje, dzięki czemu może być stosowany zamiast true.


Korzystanie z podpowłoki i przesyłanie potokowe wymagałoby użycia tej samej liczby bajtów, ale przesyłanie potokowe byłoby bardziej praktyczne.
ckjbgames

5
Z wyjątkiem kiedy to zrobisz:(){:|:}
enedil

14

Więcej porad

  1. Nadużywaj operatora trójskładnikowego ((test)) && cmd1 || cmd2lub [ test ] && cmd1 || cmd2, w miarę możliwości.

    Przykłady (liczenie długości zawsze wyklucza górną linię):

    t="$something"
    if [ $t == "hi" ];then
    cmd1
    cmd2
    elif [ $t == "bye" ];then
    cmd3
    cmd4
    else
    cmd5
    if [ $t == "sup" ];then
    cmd6
    fi
    fi
    

    Używając tylko operatorów trójskładnikowych, można to łatwo skrócić do:

    t="$something"
    [ $t == "hi" ]&&{
    cmd1;cmd2
    }||[ $t == "bye" ]&&{
    cmd3;cmd4
    }||{
    cmd5
    [ $t == "sup" ]&&cmd6
    }
    

    Jak zauważył nyuszika7h w komentarzach, ten konkretny przykład można jeszcze bardziej skrócić, używając case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
    
  2. Preferuj także nawiasy niż nawiasy klamrowe. Ponieważ nawiasy są metaznakiem, a nie słowem, nigdy nie wymagają spacji w żadnym kontekście. Oznacza to również uruchamianie jak największej liczby poleceń w podpowłoce, ponieważ nawiasy klamrowe (tj. {I }) są słowami zastrzeżonymi, a nie metaznakami, a zatem muszą mieć białe znaki po obu stronach, aby poprawnie parsować, ale metaznaki nie. Zakładam, że wiesz już, że podpowłoki nie wpływają na środowisko nadrzędne, więc zakładając, że wszystkie przykładowe polecenia można bezpiecznie uruchamiać w podpowłoce (co w żadnym wypadku nie jest typowe), możesz skrócić powyższy kod do tego :

    t=$something
    [ $t == "hi" ]&&(cmd1;cmd2)||[ $t == "bye" ]&&(cmd3;cmd4)||(cmd5;[ $t == "sup" ]&&cmd6)
    

    Ponadto, jeśli nie możesz, użycie nawiasów nadal może je zminimalizować. Należy pamiętać, że działa tylko w przypadku liczb całkowitych, co czyni go bezużytecznym dla celów tego przykładu (ale jest znacznie lepszy niż w -eqprzypadku liczb całkowitych).

  3. Jeszcze jedno, unikaj cudzysłowów, jeśli to możliwe. Korzystając z powyższej porady, możesz ją jeszcze bardziej zminimalizować. Przykład:

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
    
  4. W warunkach testowych wolisz pojedyncze nawiasy kwadratowe od podwójnych nawiasów tak bardzo, jak to możliwe z kilkoma wyjątkami. Upuszcza dwie postacie za darmo, ale w niektórych przypadkach nie jest tak solidny (jest to rozszerzenie Bash - patrz przykład poniżej). Ponadto użyj argumentu single equals zamiast double. Jest to darmowa postać do upuszczenia.

    [[ $f == b ]]&&: # ... <-- Bad
    [ $f == b ]&&: # ... <-- Better
    [ $f = b ]&&: # ... <-- Best.  word splits and pathname-expands the contents of $f.  Esp. bad if it starts with -
    

    Zwróć uwagę na to zastrzeżenie, szczególnie podczas sprawdzania wartości zerowej lub niezdefiniowanej zmiennej:

    [[ $f ]]&&:    # double quotes aren't needed inside [[, which can save chars
    [ "$f" = '' ]&&: <-- This is significantly longer
    [ -n "$f" ]&&:
    

    Pod względem technicznym ten konkretny przykład byłby najlepszy z case... in:

    t=$something
    case $t in hi)cmd1;cmd2;;bye)cmd3;cmd4;;*)cmd5;[ $t == sup ]&&cmd6;esac
    

Morał tego postu jest następujący:

  1. Nadużywaj operatorów boolowskich w jak największym stopniu i zawsze używaj ich zamiast if/ if-else/ etc. konstruuje.
  2. Używaj nawiasów jak najwięcej i uruchamiaj jak najwięcej segmentów w podpowłokach, ponieważ nawiasy to metaznaki, a nie słowa zastrzeżone.
  3. Unikaj cytowań tak bardzo, jak to fizycznie możliwe.
  4. Sprawdź case... in, ponieważ może zaoszczędzić sporo bajtów, szczególnie przy dopasowywaniu ciągów.

PS: Oto lista metaznaków rozpoznawanych w Bash bez względu na kontekst (i może oddzielić słowa):

&lt; &gt; ( ) ; & | &lt;space&gt; &lt;tab&gt;

EDYCJA: Jak wskazano manatwork, test podwójnego nawiasu działa tylko dla liczb całkowitych. Pośrednio też odkryłem, że musisz mieć spacje wokół ==operatora. Poprawiłem mój post powyżej.

Byłem też zbyt leniwy, aby przeliczyć długość każdego segmentu, więc po prostu je usunąłem. W razie potrzeby znalezienie online kalkulatora długości łańcucha powinno być łatwe.


Przykro mi to mówić, ale masz tam poważne błędy. [ $t=="hi" ]zawsze będzie oceniać na 0, ponieważ jest analizowane jako [ -n "STRING" ]. (($t=="hi"))zawsze będzie oceniać na 0, o ile $ t ma wartość nienumeryczną, ponieważ łańcuchy są wymuszane na liczby całkowite w obliczeniach arytmetycznych. Niektóre przypadki testowe: pastebin.com/WefDzWbL
manatwork

@manatwork Dzięki za połów. Zaktualizuję odpowiednio.
Isiah Meadows

Używanie casebyłoby krótsze tutaj. Nie potrzebujesz też wcześniej miejsca }, ale robisz to później {.
nyuszika7h

1
Dlaczego miałby =być mniej wytrzymały niż ==? =jest wymagane przez POSIX, ==nie jest.
Dennis

1
Pytanie wymaga jednej wskazówki na odpowiedź ...
Toby Speight

7

Zamiast grep -E, grep -F, grep -r, użytkowanie egrep, fgrep, rgrep, oszczędzając dwa znaki. Krótsze są przestarzałe, ale działają dobrze.

(Poprosiłeś o jedną wskazówkę na odpowiedź!)


1
Szkoda, że ​​nie ma Pgreppo co grep -P. Chociaż widzę, jak można to łatwo pomylić pgrep, co służy do wyszukiwania procesów.
nyuszika7h

1
@ nyuszika7h Osobiście grep -odużo

Czy nie oszczędziłoby 3, w tym miejsca?
ckjbgames

7

Dostęp do elementu 0 tablicy można uzyskać tylko przy użyciu nazwy zmiennej, co oznacza pięciobajtową oszczędność w stosunku do jawnego określenia indeksu 0:

$ a=(code golf)
$ echo ${a[0]}
code
$ echo $a
code
$ 

7

Jeśli chcesz przekazać zawartość zmiennej do STDIN następnego procesu w potoku, często echo zmiennej w potoku. Ale możesz osiągnąć to samo za pomocą <<< ciągu tutaj :

$ s="code golf"
$ echo "$s"|cut -b4-6
e g
$ cut -b4-6<<<"$s"
e g
$ 

2
Ponieważ nie jesteśmy w golfa, s=code\ golf, echo $s|i <<<$s(pamiętając, że dwie ostatnie prace tylko dlatego, że nie istnieją żadne powtarzające się spacje, etc.).
Dennis

6

Unikaj $( ...command... ), istnieje alternatywa, która oszczędza jeden znak i robi to samo:

` ...command... `

9
Czasami $( )jest to potrzebne, jeśli zagnieżdżono podstawienia poleceń; inaczej musiałbyś uciec od wewnętrznej``
Digital Trauma

1
Te technicznie robią różne rzeczy, musiałem użyć backsticków zamiast na przykład, $()kiedy chciałem uruchomić podstawienie na moim komputerze zamiast na scpkomputerze docelowym. W większości przypadków są identyczne.
undergroundmonorail

2
@undergroundmonorail: nigdy nie potrzebujesz backticksa. Wszystko, co mogą zrobić, $()może zrobić, jeśli odpowiednio cytujesz. (chyba że potrzebujesz komendy, aby przetrwać coś, co munguje, $ale nie ma odwrotu). Istnieją pewne subtelne różnice w cytowaniu rzeczy w nich. mywiki.wooledge.org/BashFAQ/082 wyjaśnia pewne różnice. O ile nie grasz w golfa, nigdy nie używaj rzutów.
Peter Cordes,

@PeterCordes Jestem pewien, że był sposób, ale wszystko, co wtedy próbowałem, nie zadziałało. Nawet jeśli backtyki nie były najlepszym rozwiązaniem, cieszyłem się, że o nich wiem, ponieważ było to jedyne rozwiązanie, jakie miałem. ¯ \ _ (ツ) _ / ¯
undergroundmonorail

@DigitalTrauma Próbka zagnieżdżenia: echo `bc <<<"\`date +%s\`-12"`... (Trudno jest opublikować próbkę zawierającą backstick w komentarzu, tam
!;

6

Służy ifdo grupowania poleceń

W porównaniu z tą wskazówką, która ifw ogóle usuwa , powinna działać lepiej tylko w bardzo rzadkich przypadkach, na przykład gdy potrzebujesz wartości zwracanych z if.

Jeśli masz grupę poleceń, która kończy się na if, takie jak:

a&&{ b;if c;then d;else e;fi;}
a&&(b;if c;then d;else e;fi)

Zamiast tego możesz zawinąć polecenia wcześniej ifw warunek:

a&&if b;c;then d;else e;fi

Lub jeśli twoja funkcja kończy się na if:

f(){ a;if b;then c;else d;fi;}

Możesz usunąć szelki:

f()if a;b;then c;else d;fi

1
W [test] && $if_true || $elsetych funkcjach możesz użyć operatora trójskładnikowego i zaoszczędzić trochę bajtów.
ckjbgames

Ponadto nie potrzebujesz spacji &&i||
roblogic

6

Użyj arytmetyki (( ... ))dla warunków

Możesz wymienić:

if [ $i -gt 5 ] ; then
    echo Do something with i greater than 5
fi

przez

if((i>5));then
    echo Do something with i greater than 5
fi

(Uwaga: nie ma spacji po if)

lub nawet

((i>5))&&{
    echo Do something with i greater than 5
}

... lub jeśli tylko jedno polecenie

((i>5))&&echo Echo or do something with i greater than 5

Ponadto: Ukryj ustawienie zmiennej w konstrukcji arytmetycznej:

((i>5?c=1:0))&&echo Nothing relevant there...
# ...
((c))&&echo Doing something else if i was greater than 5

lub to samo

((c=i>5?c=0,1:0))&&echo Nothing relevant there...
# ...
((c))&&echo Doing something else if i was greater than 5

... gdzie jeśli i> 5, to c = 1 (nie 0;)


Możesz zapisać 2 bajty, używając [ ]zamiast (()).
ckjbgames

@ckjbgames Czy jesteś tego pewien !? Jakiej wersji bash używasz?
F. Hauri

@FHauri Myślę, że byłoby mniej więcej tak samo pod względem bajtów.
ckjbgames

@ckjbgames Z [ ]potrzebujesz znaku dolara dla zmiennej. Nie rozumiem, jak można to zrobić przy użyciu tej samej lub mniejszej długości [ ].
F. Hauri

Dodatkowa wskazówka: jeśli pierwsza linia pętli for zaczyna się od ((...)), nowa linia lub spacja nie są potrzebne. Np. for((;10>n++;)){((n%3))&&echo $n;} Wypróbuj online!
primo,

6

Krótsza składnia dla nieskończonych pętli (do których można uciec breaklub exitinstrukcji) to

for((;;)){ code;}

To jest krótsze niż while true;i while :;.

Jeśli nie potrzebujesz break( exitjako jedyny sposób na ucieczkę), możesz zamiast tego użyć funkcji rekurencyjnej.

f(){ code;f;};f

Jeśli potrzebujesz przerwy, ale nie potrzebujesz wyjścia i nie musisz przenosić żadnych modyfikacji poza pętlę, możesz użyć funkcji rekurencyjnej z nawiasami wokół ciała , które uruchamiają ciało funkcji w podpowłoce.

f()(code;f);f

6

Jedna linia forpętle

Wyrażenie arytmetyczne połączone z rozszerzeniem zakresu zostanie ocenione dla każdego elementu w zakresie. Na przykład:

: $[expression]{0..9}

oceni expression10 razy.

Jest to często znacznie krótsze niż równoważna forpętla:

for((;10>n++;expression with n)){ :;}
: $[expression with ++n]{0..9}

Jeśli nie masz nic przeciwko błędom nie znaleziono polecenia, możesz usunąć inital :. W przypadku iteracji większych niż 10 możesz również użyć zakresów znaków, na przykład {A..z}iteruje 58 razy.

Jako praktyczny przykład, poniższe oba dają pierwsze 50 liczb trójkątnych, każdy w osobnej linii:

for((;50>d++;)){ echo $[n+=d];} # 31 bytes
printf %d\\n $[n+=++d]{A..r}    # 28 bytes

możesz także iterować wstecz:for((;0<i--;)){ f;}
roblogic

5

Pętla nad argumentami

Jak zauważono w pętli Bash „for” bez części „in foo bar…” , in "$@;"in for x in "$@;"jest redundantne.

Od help for:

for: for NAME [in WORDS ... ] ; do COMMANDS; done
    Execute commands for each member in a list.

    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.

    Exit Status:
    Returns the status of the last command executed.

Na przykład, jeśli chcemy wyrównać wszystkie liczby z podanymi argumentami pozycyjnymi do skryptu Bash lub funkcji, możemy to zrobić.

for n;{ echo $[n*n];}

Wypróbuj online!


5

Alternatywa dla kota

Załóżmy, że próbujesz odczytać plik i użyć go w innym celu. Co możesz zrobić to:

echo foo `cat bar`

Gdyby zawartość barbyła foobar, wydrukowałoby się foo foobar.

Istnieje jednak alternatywa, jeśli używasz tej metody, która oszczędza 3 bajty:

echo foo `<bar`

1
Czy istnieje powód, dla <barktórego sam w sobie nie działa, ale umieszcza go w backticksie?
Kritixi Lithos

@Cowsquack Tak. <Umieszcza plik do polecenia, ale w tym przypadku to stawia go na standardowe wyjście powodu dziwactwo. Backticks oceniają to razem.
Okx,

Czy istnieje krótszy sposób odczytu ze standardowego wejścia innego niż `cat`?
Joel

4

Użyj [zamiast [[i testjeśli to możliwe

Przykład:

[ -n $x ]


Użyj =zamiast ==do porównania

Przykład:

[ $x = y ]

Pamiętaj, że musisz mieć spacje wokół znaku równości, bo inaczej to nie zadziała. To samo dotyczy ==moich testów.


3
[Vs. [[może zależeć od ilości wymaganych cytatów: pastebin.com/UPAGWbDQ
manatwork

@manatwork To dobra uwaga.
nyuszika7h

1
Ogólna zasada: [... ]== /bin/test, ale [[... ]]! = /bin/testI nigdy nie należy preferować [... ]ponad [[... ]]poza codegolf
kot

4

Alternatywy dla head

linejest trzy bajty krótszy niż head -1, ale jest przestarzały .

sed qjest dwa bajty krótszy niż head -1.

sed 9qjest o jeden bajt krótszy niż head -9.


1
Mimo że jesteśmy skazani na porażkę , możemy jeszcze przez jakiś czas korzystać linez pakietu util-linux , aby przeczytać jedną linię.
manatwork

4

tr -cd jest krótszy niż grep -o

Na przykład, jeśli chcesz policzyć spacje, grep -o <char>(wydrukuj tylko dopasowane) daje 10 bajtów, a tr -cd <char>(usuń uzupełnienie <char>) daje 9.

# 16 bytes
grep -o \ |wc -l
# 15 bytes
tr -cd \ |wc -c

( źródło )

Zauważ, że oba dają nieco inne wyniki. grep -ozwraca wyniki oddzielone od linii, a jednocześnie tr -cdpodaje je wszystkie w tej samej linii, więc trnie zawsze może być korzystne


4

Skróć nazwy plików

W ostatnim wyzwaniu próbowałem odczytać plik /sys/class/power_supply/BAT1/capacity, jednak można go skrócić, /*/*/*/*/capac*yponieważ żaden inny plik nie istnieje w tym formacie.

Na przykład, jeśli masz katalog foo/zawierający pliki foo, bar, foobar, barfooi chcesz odwołać się do pliku foo/barfoo, możesz użyć foo/barf*do zapisania bajtu.

*Oznacza „nic”, a jest odpowiednikiem regex .*.


3

Użyj potoku do :polecenia zamiast /dev/null. :Wbudowaną zje wszystkie swoje wejście.


2
Nie, w większości przypadków spowoduje awarię programu za pomocą SIGPIPE. echo a|tee /dev/stderr|:nic nie wydrukuje.
jimmy23013

Jest wyścig: echo a|tee /dev/stderr|:wydrukowałem na moim komputerze, ale gdzie indziej SIGPIPE może najpierw zabić tee. Może to zależeć od wersji tee.
kernigh

Tak, to problem SIGPIPE: tee >(:) < <(seq 1 10)zadziała, ale tee /dev/stderr | :nie zadziała. Nawet a() { :;};tee /dev/stderr < <(seq 1 10)| anic nie drukuj.
F. Hauri

@ user16402 - powinieneś mieć nazwę fccing według mojej wiedzy ... w każdym razie, :wewnętrzne jedzenie wcale nie je ... jeśli podejrzewasz wejście do dwukropka, możesz zalać potok błędem ... ale możesz unosić przekierowanie przez dwukropek, lub upuść z nim proces ... :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; donelub jakkolwiek ta pseudo-sugestia ma zastosowanie ... także, czy wiesz, że jesteś prawie tak upiorny jak ja? ... za wszelką cenę< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
mikeserv

3

splitma inną (przestarzałą, ale nikogo to nie obchodzi) składnię do dzielenia danych wejściowych na sekcje Nlinii: zamiast tego split -lNmożesz użyć split -Nnp split -9.


3

Rozwiń testy

Zasadniczo powłoka jest rodzajem języka makro, a przynajmniej hybrydą lub jakimś innym. Każdy wiersz poleceń można zasadniczo podzielić na dwie części: część parsującą / wejściową i część rozwijającą / wyjściową.

Pierwsza część jest tym, na czym skupia się większość ludzi, ponieważ jest najprostsza: widzisz, co dostajesz. Druga część jest tym, czego wielu nigdy nie próbuje nawet bardzo dobrze zrozumieć i dlatego ludzie mówią, że rzeczy takie evalsą złe i zawsze cytują twoje rozszerzenia - ludzie chcą, aby wynik pierwszej części był równy pierwszej. To w porządku - ale prowadzi to do niepotrzebnie długich gałęzi kodu i mnóstwa zewnętrznych testów.

Rozszerzenia są autotestem . Te ${param[[:]#%+-=?]word}formy są bardziej niż wystarczające, aby potwierdzić zawartość parametru, są przystosowane do gniazdowania, i są oparte wokół oceny dla NUL - czyli to, co większość ludzi oczekuje testów anyway. +może być szczególnie przydatny w pętlach:

r()while IFS= read -r r&&"${r:+set}" -- "$@" "${r:=$*}";do :;done 2>&-

IFS=x
printf %s\\n some lines\ of input here '' some more|{ r;echo "$r"; }

somexlines ofxinputxhere

... podczas gdy readciągnie się niepuste linie, "${r:+set}"rozszerza się "set"i pozycjonuje się $r. Ale gdy pusty wiersz jest read, $rjest pusty i "${r:+set}"rozwija się do ""- co jest niepoprawnym poleceniem. Ale ponieważ wiersza polecenia jest rozwinięty przed"" zerowy komenda jest poszukiwany, "${r:=$*}"przyjmuje wartości wszystkich positionals łączone na pierwszy bajt w $IFSrównież. r()może zostać wywołany ponownie w |{poleceniu złożonym ;}z inną wartością, $IFSaby uzyskać następny akapit wejściowy, ponieważ nielegalne jest, aby powłoka readbuforowała poza kolejną \newline na wejściu.


3

Użyj rekurencji ogona, aby skrócić pętle:

Są równoważne w zachowaniu (choć prawdopodobnie nie w użyciu pamięci / PID):

while :;do body; done
f()(body;f);f
body;exec $0
body;$0

A te są mniej więcej równoważne:

while condition; do body; done
f()(body;condition&&f);f
body;condition&&exec $0
body;condition&&$0

(technicznie ostatnie trzy zawsze wykonują ciało przynajmniej raz)

Używanie $0wymaga, aby skrypt znajdował się w pliku, a nie wklejał w monicie bash.

W końcu twój stos może się przepełnić, ale oszczędzasz trochę bajtów.


3

Czasami krótsze jest użycie exprwbudowanego do wyświetlenia wyniku prostego wyrażenia arytmetycznego zamiast zwykłego echo $[ ]. Na przykład:

expr $1 % 2

jest o jeden bajt krótszy niż:

echo $[$1%2]

2

Użyj pwdzamiast, echoaby wygenerować linię wyjściową

Chcesz umieścić linię na standardowym wyjściu, ale nie przejmuj się zawartością i chcesz ograniczyć swoją odpowiedź do wbudowanych powłok? pwdjest bajtem krótszym niż echo.


2

Cytaty można pominąć podczas drukowania ciągów.

echo "example"
echo example

Dane wyjściowe w SM-T335 LTE, Android 5.1.1:

u0_a177@milletlte:/ $ echo "example"
example
u0_a177@milletlte:/ $ echo example
example

2

Przypisując nieciągłe elementy tablicy, nadal możesz pomijać kolejne wskaźniki ciągłych porcji:

bash-4.4$ a=([1]=1 [2]=2 [3]=3 [21]=1 [22]=2 [23]=3 [31]=1)

bash-4.4$ b=([1]=1 2 3 [21]=1 2 3 [31]=1)

Wynik jest taki sam:

bash-4.4$ declare -p a b
declare -a a=([1]="1" [2]="2" [3]="3" [21]="1" [22]="2" [23]="3" [31]="1")
declare -a b=([1]="1" [2]="2" [3]="3" [21]="1" [22]="2" [23]="3" [31]="1")

Według man bash:

Tablice są przypisane do używania złożonych przypisań nazwy formularza = (wartość 1 ... wartość n ), gdzie każda wartość ma postać [ indeks dolny ] = ciąg . Indeksowane przypisania tablic nie wymagają niczego oprócz łańcucha . Podczas przypisywania do tablic indeksowanych, jeśli podano opcjonalne nawiasy klamrowe i indeks dolny, indeks ten jest przypisywany; w przeciwnym razie indeks przypisanego elementu jest ostatnim indeksem przypisanym przez instrukcję plus jeden.


Pomocne w dodaniu: niezainicjowane elementy rozwiną się do 0 w rozwinięciach arytmetycznych i „” w innych rozszerzeniach.
Cyfrowa trauma

2

Wydrukuj pierwsze słowo w ciągu

Jeśli ciąg znajduje się w zmiennej ai nie zawiera znaków zmiany znaczenia ani formatowania ( \i %), użyj tego:

printf $a

Byłby jednak dłuższy niż poniższy kod, gdyby konieczne było zapisanie wyniku w zmiennej zamiast drukowania:

x=($a)
$x

1

Wykonanie 2 pętli osadzania z 1 forinstrukcją:

for ((l=i=0;l<=99;i=i>98?l++,0:++i)) ;do
    printf "I: %2d, L: %2d\n" $i $l
done |
    tee >(wc) | (head -n4;echo ...;tail -n 5)
I:  0, L:  0
I:  1, L:  0
I:  2, L:  0
I:  3, L:  0
...
I: 96, L: 99
I: 97, L: 99
I: 98, L: 99
I: 99, L: 99
  10000   40000  130000

1

Przypisz i wydrukuj cytowane ciągi

Jeśli chcesz przypisać ciągowi cudzysłowu do zmiennej, a następnie wydrukować wartość tej zmiennej, to zwykle jest to następujący sposób:

a="Programming Puzzles & Code Golf";echo $a

Jeśli abył wcześniej rozbrojony, można go skrócić do:

echo ${a=Programming Puzzles & Code Golf}

Jeśli azostał wcześniej ustawiony, należy go użyć zamiast tego:

echo ${a+Programming Puzzles & Code Golf}

Uwaga: jest to przydatne tylko wtedy, gdy ciąg wymaga cudzysłowów (np. Zawiera spacje). Bez cytatów a=123;echo $ajest równie krótki.


${a+foo}nie ustawia się a.
GammaFunction
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.