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ź.
55
sh
powłoka i jaka powłoka pozwala na tęfor
składnię? jest to wyraźnie dozwolone wzsh
.sh
i że Bash na to pozwala również z tego powodu, choć niestety nie mam cytatu.csh
prawdopodobnie - tak działali w tej skorupie.ksh93
powyższym przypadku może być;{1..10}
bash
printf %s\\n {1..10}
for((;i++<10)){ echo $i;}
jest krótszy niżfor i in {1..10};{ echo $i;}
Do rozszerzania arytmetycznego użyj
$[…]
zamiast$((…))
:W rozszerzeniach arytmetycznych nie używaj
$
:Rozbudowa arytmetyczna jest wykonywana na indeksach tablic indeksowanych, więc nie używaj
$
żadnego z nich:W rozszerzeniach arytmetycznych nie używaj
${…}
:źródło
while((i--))
, które działa,while[i--]
lubwhile $[i--]
nie działało dla mnie. GNU bash, wersja 4.3.46 (1)y=`bc<<<"($x*2.2)%10/1"`
... przykład użyciabc
do obliczeń niecałkowitych ... zwróć uwagę, że/1
na końcu skraca wynikowy ułamek dziesiętny do liczby całkowitej .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..else
lub[ ] && ||
GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)
i nie ma go.Normalnym, długim i nudnym sposobem definiowania funkcji jest
Jak się dowiedział ten facet , absolutnie potrzebujesz miejsca przed,
CODE
a po nim średnika.Oto mała sztuczka, której nauczyłem się od @DigitalTrauma :
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:
Polecenie związek może być:
until
,while
lubfor
if
,case
,((...))
lub[[...]]
(...)
lub{...}
Oznacza to, że wszystkie poniższe informacje są prawidłowe:
I używałem nawiasów klamrowych jak przyssawki ...
źródło
f()while ...
f()if ...
innych poleceń złożonych.f()CODE
to legalne. Okazuje się, żef()echo hi
jest to legalne w pdksh i zsh, ale nie w bash.for
ponieważ domyślnie ustawia się na pozycję:f()for x do : $x; done;set -x *;PS4=$* f "$@"
lub coś.:
Jest to polecenie, że nic nie robi, jego status wyjścia zawsze udaje, dzięki czemu może być stosowany zamiasttrue
.źródło
:(){:|:}
Więcej porad
Nadużywaj operatora trójskładnikowego
((test)) && cmd1 || cmd2
lub[ test ] && cmd1 || cmd2
, w miarę możliwości.Przykłady (liczenie długości zawsze wyklucza górną linię):
Używając tylko operatorów trójskładnikowych, można to łatwo skrócić do:
Jak zauważył nyuszika7h w komentarzach, ten konkretny przykład można jeszcze bardziej skrócić, używając
case
: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 :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
-eq
przypadku liczb całkowitych).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:
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.
Zwróć uwagę na to zastrzeżenie, szczególnie podczas sprawdzania wartości zerowej lub niezdefiniowanej zmiennej:
Pod względem technicznym ten konkretny przykład byłby najlepszy z
case
...in
:Morał tego postu jest następujący:
if
/if-else
/ etc. konstruuje.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):
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.
źródło
[ $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/WefDzWbLcase
byłoby krótsze tutaj. Nie potrzebujesz też wcześniej miejsca}
, ale robisz to później{
.=
być mniej wytrzymały niż==
?=
jest wymagane przez POSIX,==
nie jest.Zamiast
grep -E
,grep -F
,grep -r
, użytkowanieegrep
,fgrep
,rgrep
, oszczędzając dwa znaki. Krótsze są przestarzałe, ale działają dobrze.(Poprosiłeś o jedną wskazówkę na odpowiedź!)
źródło
Pgrep
po cogrep -P
. Chociaż widzę, jak można to łatwo pomylićpgrep
, co służy do wyszukiwania procesów.grep -o
dużoDostę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:
źródło
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 :źródło
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.).Unikaj
$( ...command... )
, istnieje alternatywa, która oszczędza jeden znak i robi to samo:źródło
$( )
jest to potrzebne, jeśli zagnieżdżono podstawienia poleceń; inaczej musiałbyś uciec od wewnętrznej``
$()
kiedy chciałem uruchomić podstawienie na moim komputerze zamiast nascp
komputerze docelowym. W większości przypadków są identyczne.$()
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.echo `bc <<<"\`date +%s\`-12"`
... (Trudno jest opublikować próbkę zawierającą backstick w komentarzu, tamSłuży
if
do grupowania poleceńW porównaniu z tą wskazówką, która
if
w ogóle usuwa , powinna działać lepiej tylko w bardzo rzadkich przypadkach, na przykład gdy potrzebujesz wartości zwracanych zif
.Jeśli masz grupę poleceń, która kończy się na
if
, takie jak:Zamiast tego możesz zawinąć polecenia wcześniej
if
w warunek:Lub jeśli twoja funkcja kończy się na
if
:Możesz usunąć szelki:
źródło
[test] && $if_true || $else
tych funkcjach możesz użyć operatora trójskładnikowego i zaoszczędzić trochę bajtów.&&
i||
Użyj arytmetyki
(( ... ))
dla warunkówMożesz wymienić:
przez
(Uwaga: nie ma spacji po
if
)lub nawet
... lub jeśli tylko jedno polecenie
Ponadto: Ukryj ustawienie zmiennej w konstrukcji arytmetycznej:
lub to samo
... gdzie jeśli i> 5, to c = 1 (nie 0;)
źródło
[ ]
zamiast(())
.[ ]
potrzebujesz znaku dolara dla zmiennej. Nie rozumiem, jak można to zrobić przy użyciu tej samej lub mniejszej długości[ ]
.((...))
, nowa linia lub spacja nie są potrzebne. Np.for((;10>n++;)){((n%3))&&echo $n;}
Wypróbuj online!Krótsza składnia dla nieskończonych pętli (do których można uciec
break
lubexit
instrukcji) toTo jest krótsze niż
while true;
iwhile :;
.Jeśli nie potrzebujesz
break
(exit
jako jedyny sposób na ucieczkę), możesz zamiast tego użyć funkcji rekurencyjnej.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.
źródło
Jedna linia
for
pętleWyrażenie arytmetyczne połączone z rozszerzeniem zakresu zostanie ocenione dla każdego elementu w zakresie. Na przykład:
oceni
expression
10 razy.Jest to często znacznie krótsze niż równoważna
for
pętla: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:
źródło
for((;0<i--;)){ f;}
Pętla nad argumentami
Jak zauważono w pętli Bash „for” bez części „in foo bar…” ,
in "$@;"
infor x in "$@;"
jest redundantne.Od
help for
:Na przykład, jeśli chcemy wyrównać wszystkie liczby z podanymi argumentami pozycyjnymi do skryptu Bash lub funkcji, możemy to zrobić.
Wypróbuj online!
źródło
Alternatywa dla kota
Załóżmy, że próbujesz odczytać plik i użyć go w innym celu. Co możesz zrobić to:
Gdyby zawartość
bar
byłafoobar
, wydrukowałoby sięfoo foobar
.Istnieje jednak alternatywa, jeśli używasz tej metody, która oszczędza 3 bajty:
źródło
<bar
którego sam w sobie nie działa, ale umieszcza go w backticksie?<
Umieszcza plik do polecenia, ale w tym przypadku to stawia go na standardowe wyjście powodu dziwactwo. Backticks oceniają to razem.`cat`
?Użyj
[
zamiast[[
itest
jeśli to możliwePrzykład:
Użyj
=
zamiast==
do porównaniaPrzykład:
Pamiętaj, że musisz mieć spacje wokół znaku równości, bo inaczej to nie zadziała. To samo dotyczy
==
moich testów.źródło
[
Vs.[[
może zależeć od ilości wymaganych cytatów: pastebin.com/UPAGWbDQ[
...]
==/bin/test
, ale[[
...]]
! =/bin/test
I nigdy nie należy preferować[
...]
ponad[[
...]]
poza codegolfAlternatywy dla
head
line
jest trzy bajty krótszy niżhead -1
, ale jest przestarzały .sed q
jest dwa bajty krótszy niżhead -1
.sed 9q
jest o jeden bajt krótszy niżhead -9
.źródło
line
z pakietu util-linux , aby przeczytać jedną linię.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, atr -cd <char>
(usuń uzupełnienie<char>
) daje 9.( źródło )
Zauważ, że oba dają nieco inne wyniki.
grep -o
zwraca wyniki oddzielone od linii, a jednocześnietr -cd
podaje je wszystkie w tej samej linii, więctr
nie zawsze może być korzystneźródło
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*y
ponieważ żaden inny plik nie istnieje w tym formacie.Na przykład, jeśli masz katalog
foo/
zawierający plikifoo, bar, foobar, barfoo
i chcesz odwołać się do plikufoo/barfoo
, możesz użyćfoo/barf*
do zapisania bajtu.*
Oznacza „nic”, a jest odpowiednikiem regex.*
.źródło
Użyj potoku do
:
polecenia zamiast/dev/null
.:
Wbudowaną zje wszystkie swoje wejście.źródło
echo a|tee /dev/stderr|:
nic nie wydrukuje.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 wersjitee
.tee >(:) < <(seq 1 10)
zadziała, aletee /dev/stderr | :
nie zadziała. Naweta() { :;};tee /dev/stderr < <(seq 1 10)| a
nic nie drukuj.:
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 ]; done
lub 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)
split
ma inną (przestarzałą, ale nikogo to nie obchodzi) składnię do dzielenia danych wejściowych na sekcjeN
linii: zamiast tegosplit -lN
możesz użyćsplit -N
npsplit -9
.źródło
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
eval
są 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:... podczas gdy
read
ciągnie się niepuste linie,"${r:+set}"
rozszerza się"set"
i pozycjonuje się$r
. Ale gdy pusty wiersz jestread
,$r
jest 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$IFS
również.r()
może zostać wywołany ponownie w|{
poleceniu złożonym;}
z inną wartością,$IFS
aby uzyskać następny akapit wejściowy, ponieważ nielegalne jest, aby powłokaread
buforowała poza kolejną\n
ewline na wejściu.źródło
Użyj rekurencji ogona, aby skrócić pętle:
Są równoważne w zachowaniu (choć prawdopodobnie nie w użyciu pamięci / PID):
A te są mniej więcej równoważne:
(technicznie ostatnie trzy zawsze wykonują ciało przynajmniej raz)
Używanie
$0
wymaga, 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.
źródło
Czasami krótsze jest użycie
expr
wbudowanego do wyświetlenia wyniku prostego wyrażenia arytmetycznego zamiast zwykłegoecho $[ ]
. Na przykład:jest o jeden bajt krótszy niż:
źródło
Użyj
pwd
zamiast,echo
aby 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?
pwd
jest bajtem krótszym niżecho
.źródło
Cytaty można pominąć podczas drukowania ciągów.
Dane wyjściowe w SM-T335 LTE, Android 5.1.1:
źródło
Przypisując nieciągłe elementy tablicy, nadal możesz pomijać kolejne wskaźniki ciągłych porcji:
Wynik jest taki sam:
Według
man bash
:źródło
Wydrukuj pierwsze słowo w ciągu
Jeśli ciąg znajduje się w zmiennej
a
i nie zawiera znaków zmiany znaczenia ani formatowania (\
i%
), użyj tego:Byłby jednak dłuższy niż poniższy kod, gdyby konieczne było zapisanie wyniku w zmiennej zamiast drukowania:
źródło
Wykonanie 2 pętli osadzania z 1
for
instrukcją:źródło
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:
Jeśli
a
był wcześniej rozbrojony, można go skrócić do:Jeśli
a
został wcześniej ustawiony, należy go użyć zamiast tego:Uwaga: jest to przydatne tylko wtedy, gdy ciąg wymaga cudzysłowów (np. Zawiera spacje). Bez cytatów
a=123;echo $a
jest równie krótki.źródło
${a+foo}
nie ustawia sięa
.