Tipps zum Golfen in Bash


55

Welche allgemeinen Tipps haben Sie zum Golfen in Bash? Ich bin auf der Suche nach Ideen, die sich auf Code-Golf-Probleme im Allgemeinen anwenden lassen, die zumindest etwas spezifisch für Bash sind (z. B. "Kommentare entfernen" ist keine Antwort). Bitte posten Sie einen Tipp pro Antwort.

Antworten:


36

Undokumentiert , funktioniert aber in jeder Version, die ich aus Gründen der älterenshAbwärtskompatibilität kennengelernt habe:

forLoops können Sie { }anstelle von verwenden do done. ZB ersetzen:

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

mit:

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

Welche Shell ist das shund welche Shell erlaubt diese forSyntax? es ist ausdrücklich erlaubt in zsh.
mikeserv

@mikeserv Bash. Ich erinnere mich, dass ich irgendwo gelesen habe, dass diese Syntax in einigen alten shVersionen erlaubt war und dass Bash sie deswegen auch erlaubt, obwohl ich leider kein Zitat habe.
Digital Trauma

ahh ... cshwahrscheinlich - so haben sie in dieser Shell gearbeitet.
mikeserv

ksh93;{1..10}bashprintf %s\\n {1..10}
Übrigens

1
for((;i++<10)){ echo $i;}ist kürzer alsfor i in {1..10};{ echo $i;}
Evan Krall

29

Für die arithmetische Erweiterung verwenden Sie $[…]anstelle von $((…)):

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

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

Verwenden Sie in arithmetischen Erweiterungen nicht $:

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

Die arithmetische Erweiterung wird für indizierte Array-Indizes ausgeführt. Verwenden Sie daher $keine der folgenden Optionen:

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

Verwenden Sie in arithmetischen Erweiterungen nicht ${…}:

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

Ersetzen while((i--)), was funktioniert, mit while[i--]oder while $[i--]hat bei mir nicht geklappt. GNU Bash, Version 4.3.46 (1)
Glenn Randers-Pehrson,

1
Richtig, @ GlennRanders-Pehrson. Das soll nicht funktionieren.
Manatwork

y=`bc<<<"($x*2.2)%10/1"`... Beispiel für die Verwendung bcfür nicht ganzzahlige Berechnungen ... Beachten Sie, dass /1am Ende die resultierende Dezimalstelle auf ein int abgeschnitten wird.
Roblogic

s=$((i%2>0?s+x:s+y))... Beispiel für die Verwendung eines ternären Operators in der Bash-Arithmetik. Es ist kürzer als if..then..elseoder[ ] && ||
Roblogic

1
@manatwork Danke. Sie müssen es entfernt haben. Ich bin dran GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)und es ist nicht in meinem.
Jonah

19

Die normale, langwierige und langweilige Art, eine Funktion zu definieren, ist

f(){ CODE;}

Wie dieser Typ herausgefunden hat, braucht man unbedingt das Leerzeichen davor CODEund das Semikolon danach.

Dies ist ein kleiner Trick, den ich von @DigitalTrauma gelernt habe :

f()(CODE)

Das ist zwei Zeichen kürzer und funktioniert auch, vorausgesetzt, Sie müssen nach der Rückkehr der Funktion keine Änderungen an den Variablenwerten vornehmen ( die Klammern führen den Text in einer Subshell aus ).

Wie @ jimmy23013 in den Kommentaren betont , sind möglicherweise sogar die Klammern unnötig.

Das Bash-Referenzhandbuch zeigt, dass Funktionen wie folgt definiert werden können:

name () compound-command [ redirections ]

oder

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

Ein zusammengesetzter Befehl kann sein:

  • ein Looping Baukonstruktion: until, whileoderfor
  • ein Conditional Baukonstruktion: if, case, ((...))oder[[...]]
  • Gruppierte Befehle: (...)oder{...}

Das heißt, alle folgenden Punkte sind gültig:

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

Und ich habe geschweifte Klammern wie ein Trottel benutzt ...


2
Beachten Sie, dass Sie auch f()while ... f()if ...andere zusammengesetzte Befehle verwenden können.
Jimmy23013

Dieser hat mich überrascht, weil ich dachte, er f()CODEsei legal. Es stellt sich heraus, dass dies f()echo hiin pdksh und zsh legal ist, aber nicht in bash.
Kernigh

1
Es ist besonders nützlich, forda standardmäßig Positionals verwendet werden: f()for x do : $x; done;set -x *;PS4=$* f "$@"oder so.
mikeserv

16

:Ist ein Befehl, der nichts tut, ist sein Exit-Status immer erfolgreich, sodass er anstelle von verwendet werden kann true.


Wenn Sie eine Subshell und Piping verwenden, wird ungefähr die gleiche Anzahl von Bytes verwendet, aber das Piping ist praktischer.
ckjbgames

5
Außer wenn du es tust:(){:|:}
Uhr

14

Mehr Tipps

  1. Missbrauch des ternären Operators ((test)) && cmd1 || cmd2oder [ test ] && cmd1 || cmd2so viel wie möglich.

    Beispiele (Längenangaben schließen immer die oberste Zeile aus):

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

    Wenn nur ternäre Operatoren verwendet werden, kann dies leicht verkürzt werden auf:

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

    Wie nyuszika7h in den Kommentaren ausführte, könnte dieses spezielle Beispiel noch weiter verkürzt werden case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
  2. Ziehen Sie außerdem Klammern möglichst Klammern vor. Da Klammern ein Metazeichen und kein Wort sind, benötigen sie in keinem Kontext Leerzeichen. Dies bedeutet auch, dass so viele Befehle wie möglich in einer Subshell ausgeführt werden, da geschweifte Klammern (dh {und }) reservierte Wörter und keine Metazeichen sind und daher auf beiden Seiten Leerzeichen enthalten müssen, um eine korrekte Analyse durchzuführen, Metazeichen jedoch nicht. Ich gehe davon aus, dass Sie inzwischen wissen, dass sich Subshells nicht auf die übergeordnete Umgebung auswirken. Wenn Sie also davon ausgehen, dass alle Beispielbefehle sicher in einer Subshell ausgeführt werden können (was ohnehin nicht typisch ist), können Sie den obigen Code hierauf verkürzen :

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

    Wenn Sie dies nicht können, kann die Verwendung von Klammern das Problem möglicherweise verringern. Beachten Sie, dass es nur für Ganzzahlen funktioniert, was es für die Zwecke dieses Beispiels unbrauchbar macht (aber es ist viel besser als die Verwendung -eqfür Ganzzahlen).

  3. Eine weitere Sache, vermeiden Sie Anführungszeichen, wo es möglich ist. Mit dem obigen Hinweis können Sie ihn weiter minimieren. Beispiel:

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
  4. Ziehen Sie unter Testbedingungen mit wenigen Ausnahmen einfache Klammern doppelten Klammern so weit wie möglich vor. Es werden kostenlos zwei Zeichen abgelegt, aber in einigen Fällen ist es nicht so robust (es ist eine Bash-Erweiterung - siehe unten für ein Beispiel). Verwenden Sie auch das Argument single equals und nicht double. Es ist ein Freizeichen zum Ablegen.

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

    Beachten Sie diese Einschränkung, insbesondere beim Überprüfen auf Null-Ausgabe oder eine undefinierte Variable:

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

    In der Tat wäre dieses spezielle Beispiel am besten mit case... in:

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

Die Moral dieses Beitrags lautet also:

  1. Missbrauchen Sie die booleschen Operatoren so oft wie möglich und verwenden Sie sie immer anstelle von if/ if-else/ etc. Konstrukte.
  2. Verwenden Sie so viele Klammern wie möglich und führen Sie so viele Segmente wie möglich in Subshells aus, da Klammern Metazeichen und keine reservierten Wörter sind.
  3. Vermeiden Sie Anführungszeichen so oft wie möglich.
  4. Check out case... in, da es einige Bytes einsparen kann, insbesondere beim String-Matching.

PS: Hier ist eine Liste von Metazeichen, die in Bash unabhängig vom Kontext erkannt werden (und Wörter trennen können):

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

BEARBEITEN: Wie manatwork hervorhob, funktioniert der doppelte Klammertest nur für ganze Zahlen. Außerdem habe ich indirekt festgestellt, dass der ==Operator von Leerzeichen umgeben sein muss . Korrigierte meinen Beitrag oben.

Ich war auch zu faul, um die Länge der einzelnen Segmente neu zu berechnen, also habe ich sie einfach entfernt. Es sollte einfach genug sein, bei Bedarf einen Stringlängenrechner online zu finden.


Entschuldigung, aber Sie haben einige schwerwiegende Fehler. [ $t=="hi" ]wird immer mit 0 ausgewertet, da es analysiert wird als [ -n "STRING" ]. (($t=="hi"))wird immer auf 0 ausgewertet, solange $ t keinen numerischen Wert hat, da Zeichenfolgen in arithmetischen Auswertungen zu Ganzzahlen gezwungen werden. Einige Testfälle: pastebin.com/WefDzWbL
Manatwork

@manatwork Danke für den Fang. Ich werde entsprechend aktualisieren.
Isiah Meadows

Die Verwendung von a casewäre hier kürzer. Außerdem brauchen Sie vorher kein Leerzeichen }, aber nachher {.
Nyuszika7h

1
Warum wäre =weniger robust als ==? =wird von POSIX beauftragt, ==nicht wahr?
Dennis

1
Die Frage fragt nach einem Tipp pro Antwort ...
Toby Speight

7

Statt grep -E, grep -F, grep -r, Einsatz egrep, fgrep, rgrep, spart zwei Zeichen. Die kürzeren sind veraltet, funktionieren aber einwandfrei.

(Sie haben pro Antwort um einen Tipp gebeten!)


1
Schade , es gibt keine Pgrepfür grep -P. Obwohl ich sehe, wie es leicht zu verwechseln ist pgrep, die verwendet wird, um Prozesse nachzuschlagen.
nyuszika7h

1
@ nyuszika7h Ich persönlich benutze grep -oviel

Würde es nicht 3 inklusive Platz sparen?
ckjbgames

7

Auf Element 0 eines Arrays kann nur mit dem Variablennamen zugegriffen werden, wobei beim Speichern von fünf Bytes explizit ein Index von 0 angegeben wird:

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

7

Wenn Sie den Inhalt einer Variablen an STDIN des nächsten Prozesses in einer Pipeline übergeben müssen, wird die Variable häufig in eine Pipeline zurückgegeben. Aber Sie können dasselbe mit einem <<< Bash-Here-String erreichen :

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

2
Da wir Golf spielen, s=code\ golf, echo $s|und <<<$s(dass die beiden letzteren Arbeit im Auge behalten nur , weil es keine wiederholte Plätze sind, etc.).
Dennis

6

Vermeiden Sie $( ...command... ), dass es eine Alternative gibt, die ein Zeichen spart und dasselbe tut:

` ...command... `

9
Manchmal $( )ist es erforderlich, wenn Sie verschachtelte Befehlsersetzungen haben. sonst müsstest du dem inneren``
digitalen Trauma

1
Diese Dinge sind technisch gesehen unterschiedlich. Ich musste die Backticks verwenden, anstatt $()die Ersetzung beispielsweise auf meinem Computer anstelle des scpZielcomputers auszuführen . In den meisten Fällen sind sie identisch.
Undergroundmonorail

2
@undergroundmonorail: Sie brauchen nie Backticks. Alles, was sie können, $()können sie tun, wenn Sie die Dinge richtig zitieren. (es sei denn, Sie benötigen Ihr Kommando, um etwas zu überleben, das Mungos, $aber keine Backticks enthält). Es gibt einige subtile Unterschiede darin, Dinge in ihnen zu zitieren. mywiki.wooledge.org/BashFAQ/082 erklärt einige Unterschiede. Verwenden Sie niemals Backticks, es sei denn, Sie spielen Golf.
Peter Cordes

@PeterCordes Ich bin mir sicher, dass es einen Weg gab, aber alles, was ich damals ausprobierte, funktionierte nicht. Auch wenn Backticks nicht die beste Lösung waren, war ich froh, dass ich davon wusste, denn es war die einzige Lösung, die ich hatte. ¯ \ _ (ツ) _ / ¯
undergroundmonorail

@DigitalTrauma Beispiel für Verschachtelung: echo `bc <<<"\`date +%s\`-12"`... (Es ist schwierig, ein Beispiel mit einem Backtick im Kommentar zu veröffentlichen!)
F. Hauri

6

Dient ifzum Gruppieren von Befehlen

Im Vergleich zu diesem Tipp, mit dem das ifüberhaupt entfernt wird, sollte dies nur in sehr seltenen Fällen besser funktionieren, z if.

Wenn Sie eine Befehlsgruppe haben, die mit a endet if, wie folgt:

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

Sie können die Befehle ifstattdessen vorher in die Bedingung einschließen:

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

Oder wenn Ihre Funktion mit einem endet if:

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

Sie können die Klammern entfernen:

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

1
Sie könnten den ternären Operator [test] && $if_true || $elsein diesen Funktionen verwenden und einige Bytes sparen.
ckjbgames

Außerdem brauchen Sie keine Leerzeichen um &&und||
roblogic

6

Verwenden Sie Arithmetik (( ... ))für Bedingungen

Sie könnten ersetzen:

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

durch

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

(Hinweis: Nachher ist kein Leerzeichen mehr vorhanden. if)

oder auch

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

... oder wenn nur ein Befehl

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

Weiter: Variableneinstellung im Rechenkonstrukt ausblenden:

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

oder gleich

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

... wo, wenn i> 5 ist, dann ist c = 1 (nicht 0;)


Sie können 2 Bytes einsparen, indem Sie [ ]anstelle von verwenden (()).
ckjbgames

@ckjbgames Bist du dir da sicher? Welche Bash- Version verwendest du?
F. Hauri

@FHauri Ich denke, es wäre ungefähr das gleiche in Bezug auf Bytes.
ckjbgames

@ckjbgames Mit [ ]brauchst du das Dollarzeichen für Variable. Ich verstehe nicht, wie Sie dasselbe mit derselben oder einer geringeren Länge erreichen können, wenn Sie verwenden [ ].
F. Hauri

Bonus-Tipp: Wenn die erste Zeile einer for-Schleife mit beginnt ((...)), ist weder eine neue Zeile noch ein Leerzeichen erforderlich. ZB for((;10>n++;)){((n%3))&&echo $n;} online ausprobieren!
Primo

6

Eine kürzere Syntax für Endlosschleifen (die mit breakoder exitAnweisungen maskiert werden können) ist

for((;;)){ code;}

Dies ist kürzer als while true;und while :;.

Wenn Sie nicht brauchen break( exitals einzige Möglichkeit, um zu entkommen), können Sie stattdessen eine rekursive Funktion verwenden.

f(){ code;f;};f

Wenn Sie break benötigen, aber kein exit benötigen und keine Variablenänderung außerhalb der Schleife vornehmen müssen, können Sie eine rekursive Funktion mit runden Klammern um den Körper verwenden , die den Funktionskörper in einer Subshell ausführt.

f()(code;f);f

6

Einzeilige forSchleifen

Ein arithmetischer Ausdruck, der mit einer Bereichserweiterung verknüpft ist, wird für jedes Element im Bereich ausgewertet. Zum Beispiel das Folgende:

: $[expression]{0..9}

wird expression10 mal ausgewertet .

Dies ist häufig erheblich kürzer als die entsprechende forSchleife:

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

Wenn es Ihnen nichts ausmacht, Fehler zu befehlen, die nicht gefunden wurden, können Sie das Initial entfernen :. Bei Iterationen über 10 können Sie auch Zeichenbereiche verwenden, die beispielsweise {A..z}58-mal wiederholt werden.

Als praktisches Beispiel ergeben beide die ersten 50 Dreieckszahlen in einer eigenen Zeile:

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

Sie können auch rückwärts iterieren:for((;0<i--;)){ f;}
Roblogic

5

Argumente durchlaufen

Wie in bemerkte , ohne „in foo bar ...“ Schlag „für“ Schleifenteil , das in "$@;"in for x in "$@;"redundant ist.

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

Wenn wir beispielsweise alle Zahlen, denen Positionsargumente gegeben wurden, mit einem Bash-Skript oder einer Funktion quadrieren möchten, können wir dies tun.

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

Probieren Sie es online!


5

Alternative zur Katze

Angenommen, Sie versuchen, eine Datei zu lesen und in etwas anderem zu verwenden. Was Sie tun könnten, ist:

echo foo `cat bar`

Wenn der Inhalt von barwar foobar, würde dies drucken foo foobar.

Es gibt jedoch eine Alternative, wenn Sie diese Methode verwenden, die 3 Bytes einspart:

echo foo `<bar`

1
Gibt es einen Grund, warum <barallein nicht funktioniert, aber das Platzieren in Backticks?
Kritixi Lithos

@Cowsquack Ja. Das <versetzt eine Datei in einen Befehl, aber in diesem Fall wird sie aufgrund einer Eigenart in die Standardausgabe gestellt. Die Backticks bewerten dies gemeinsam.
Okx

Gibt es einen kürzeren Weg, um von der Standardeingabe zu lesen als `cat`?
Joel

4

Verwenden Sie [anstelle von [[und testwenn möglich

Beispiel:

[ -n $x ]


Verwenden Sie =statt ==zum Vergleich

Beispiel:

[ $x = y ]

Beachten Sie, dass Sie Leerzeichen um das Gleichheitszeichen haben müssen, sonst funktioniert es nicht. Gleiches gilt ==aufgrund meiner Tests.


3
Die [vs. [[kann über die Höhe der erforderlichen Zitate abhängen: pastebin.com/UPAGWbDQ
Manatwork

@manatwork Das ist ein guter Punkt.
nyuszika7h

1
Allgemeine Regel: [... ]== /bin/test, aber [[... ]]! = /bin/testUnd man sollte niemals [... ]vor [[... ]]außerhalb von Codegolf
Katze

4

Alternativen zu head

lineist drei Bytes kürzer als head -1, aber wird als veraltet .

sed qist zwei Bytes kürzer als head -1.

sed 9qist ein Byte kürzer als head -9.


1
Obwohl zum Scheitern verurteilt , können wir noch eine Zeit lang verwenden linevon util-linux - Paket eine einzige Zeile zu lesen.
Manatwork

4

tr -cd ist kürzer als grep -o

Wenn Sie zum Beispiel Leerzeichen zählen müssen, gibt grep -o <char>(nur das Übereinstimmende drucken) 10 Bytes aus, während tr -cd <char>(Komplement löschen von <char>) 9 ergibt.

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

( Quelle )

Beachten Sie, dass beide leicht unterschiedliche Ausgaben liefern. grep -oGibt zeilengetrennte Ergebnisse zurück, während tr -cdsich alle in derselben Zeile befinden. Dies ist trmöglicherweise nicht immer günstig.


4

Dateinamen kürzen

Bei einer kürzlich durchgeführten Herausforderung habe ich versucht, die Datei zu lesen. /sys/class/power_supply/BAT1/capacityDies kann jedoch auf verkürzt werden, /*/*/*/*/capac*yda keine andere Datei mit diesem Format vorhanden ist.

Wenn Sie beispielsweise ein Verzeichnis foo/mit den Dateien hatten foo, bar, foobar, barfoound auf die Datei verweisen möchten foo/barfoo, können Sie foo/barf*ein Byte speichern.

Das steht *für "irgendetwas" und entspricht dem regulären Ausdruck .*.


3

Verwenden Sie :anstelle von eine Pipe zum Befehl /dev/null. Das :eingebaute System frisst alle Eingaben.


2
Nein, in den meisten Fällen stürzt das Programm mit SIGPIPE ab. echo a|tee /dev/stderr|:druckt nichts.
Jimmy23013

Es gibt ein Rennen: echo a|tee /dev/stderr|:Ich habe ein auf meinem Computer gedruckt, aber anderswo könnte Sigpipe zuerst Tee töten. Es kann von der Version von abhängen tee.
Kernigh

Ja, es ist ein SIGPIPE-Problem: tee >(:) < <(seq 1 10)Wird funktionieren, aber tee /dev/stderr | :nicht. Auch a() { :;};tee /dev/stderr < <(seq 1 10)| anichts drucken.
F. Hauri

@ user16402 - du solltest einen fccing-Namen in meinem Zuständigkeitsbereich haben ... auf jeden Fall isst das :Intrinsic überhaupt nicht ... wenn du eine Eingabe in den Doppelpunkt unterstellst, könntest du eine Pipe zu einem In-Out-Fehler fluten ... aber du kannst eine Umleitung schweben lassen durch einen Doppelpunkt, oder lassen Sie einen Prozess damit fallen ... oder wie auch immer dieser Pseudovorschlag zutrifft ... auch, sind Sie sich bewusst, dass Sie fast so gespenstisch sind wie ich? ... vermeiden Sie auf jeden :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; done< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
Fall

3

splithat eine andere (veraltete, aber niemanden interessierende) Syntax zum Aufteilen von Eingaben in Neinzelne Zeilenabschnitte: Anstelle von können split -lNSie split -Nz split -9.


3

Erweitern Sie die Tests

Im Wesentlichen ist die Shell eine Art Makrosprache oder zumindest eine hybride oder irgendeine Art von Sprache. Jede Befehlszeile kann grundsätzlich in zwei Teile unterteilt werden: den Parsing- / Eingabeteil und den Expansions- / Ausgabeteil.

Der erste Teil ist das, worauf sich die meisten Leute konzentrieren, weil es am einfachsten ist: Sie sehen, was Sie bekommen. Der zweite Teil ist das, was viele vermeiden, überhaupt zu versuchen, sehr gut zu verstehen, und ist der Grund, warum Leute sagen, dass Dinge evalböse sind und immer Ihre Erweiterungen zitieren - die Leute wollen, dass das Ergebnis des ersten Teils gleich dem ersten ist. Das ist in Ordnung - aber es führt zu unnötig langen Code-Verzweigungen und einer Menge unnötiger Tests.

Erweiterungen sind Selbsttests . Die ${param[[:]#%+-=?]word}Formulare sind mehr als genug, um den Inhalt eines Parameters zu validieren, sind verschachtelbar und basieren auf der Auswertung für NUL - was die meisten Leute sowieso von Tests erwarten. +kann in Schleifen besonders praktisch sein:

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

... während readnicht leere Zeilen "${r:+set}"eingezogen werden, werden "set"die Positionsangaben $rangehängt. Aber wenn eine leere Zeile ist read, $rist sie leer und "${r:+set}"erweitert sich zu ""- was ein ungültiger Befehl ist. Da die Befehlszeile jedoch erweitert wird, bevor der ""Nullbefehl durchsucht wird, werden auch "${r:=$*}"die Werte aller im ersten Byte verketteten Positionsangaben übernommen $IFS. r()könnte im |{zusammengesetzten Befehl ;}mit einem anderen Wert erneut aufgerufen werden $IFS, um auch den nächsten Eingabeabsatz abzurufen , da readdas \nPuffern einer Shell über die nächste ewline in der Eingabe hinaus unzulässig ist.


3

Verwenden Sie die Schwanzrekursion, um Schleifen zu verkürzen:

Diese sind im Verhalten gleichwertig (obwohl wahrscheinlich nicht im Speicher / PID-Gebrauch):

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

Und das sind ungefähr gleichbedeutend:

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

(Technisch gesehen führen die letzten drei den Körper immer mindestens einmal aus)

Für die Verwendung $0muss sich Ihr Skript in einer Datei befinden, die nicht in die Bash-Eingabeaufforderung eingefügt wird.

Möglicherweise läuft Ihr Stapel über, aber Sie sparen einige Bytes.


3

Manchmal ist es kürzer, den expreingebauten Ausdruck anstelle des üblichen Ausdrucks für die Anzeige des Ergebnisses eines einfachen arithmetischen Ausdrucks zu verwenden echo $[ ]. Zum Beispiel:

expr $1 % 2

ist ein Byte kürzer als:

echo $[$1%2]

2

Verwenden Sie pwdanstelle von echo, um eine Ausgabezeile zu generieren

Möchten Sie eine Zeile in stdout einfügen, sich aber nicht um den Inhalt kümmern und Ihre Antwort auf Shell-Builtins beschränken? pwdist ein Byte kürzer als echo.


2

Beim Drucken von Zeichenfolgen können Anführungszeichen weggelassen werden.

echo "example"
echo example

Ausgabe in SM-T335 LTE, Android 5.1.1:

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

2

Wenn Sie nicht zusammenhängende Array-Elemente zuweisen, können Sie die aufeinanderfolgenden Indizes von zusammenhängenden Blöcken dennoch überspringen:

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)

Das Ergebnis ist dasselbe:

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")

Nach man bash:

Arrays werden unter Verwendung von Verbindung Zuordnungen des Formular zugeordnet namen = (Wert 1 ... Wert n ), wobei jeder Wert von der Form [ Index ] = Zeichenfolge . Indizierte Array-Zuweisungen erfordern nur Zeichenfolgen . Wenn bei der Zuweisung zu indizierten Arrays die optionalen Klammern und der Index angegeben werden, wird dieser Index zugewiesen. Andernfalls ist der Index des zugewiesenen Elements der letzte Index, dem die Anweisung plus eins zugewiesen hat.


Hilfreich: Nicht initialisierte Elemente werden in arithmetischen Erweiterungen auf "" 0 und in anderen Erweiterungen auf "" 0 erweitert.
Digital Trauma

2

Gibt das erste Wort in einer Zeichenfolge aus

Wenn sich die Zeichenfolge in der Variablen befindet aund keine Escape- und Formatierungszeichen ( \und %) enthält, verwenden Sie Folgendes:

printf $a

Es wäre jedoch länger als der folgende Code, wenn das Ergebnis in einer Variablen gespeichert und nicht gedruckt werden müsste:

x=($a)
$x

1

2 embed loop mit 1 forAnweisung machen:

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

In Anführungszeichen gesetzte Zeichenfolgen zuweisen und drucken

Wenn Sie einer Variablen eine Zeichenfolge in Anführungszeichen zuweisen und dann den Wert dieser Variablen ausgeben möchten, lautet die übliche Vorgehensweise wie folgt:

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

Wenn azuvor nicht festgelegt, kann dies verkürzt werden auf:

echo ${a=Programming Puzzles & Code Golf}

Wenn azuvor festgelegt, sollte dies stattdessen verwendet werden:

echo ${a+Programming Puzzles & Code Golf}

Beachten Sie, dass dies nur sinnvoll ist, wenn die Zeichenfolge Anführungszeichen erfordert (z. B. Leerzeichen). Ohne Anführungszeichen a=123;echo $aist das genauso kurz.


${a+foo}setzt nicht a.
GammaFunction
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.