Consigli per giocare a golf a Bash


55

Quali consigli generali hai per giocare a golf a Bash? Sto cercando idee che possano essere applicate ai problemi del codice golf in generale che siano almeno in qualche modo specifiche per Bash (ad esempio "rimuovere i commenti" non è una risposta). Si prega di inviare un suggerimento per risposta.

Risposte:


36

Non documentato , ma funziona in tutte le versioni che ho incontrato pershla compatibilità con le versioni precedentilegacy:

fori loop ti consentono di utilizzare { }invece di do done. Ad esempio sostituire:

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

con:

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

che shell è she che shell consente questa forsintassi? è espressamente consentito in zsh.
Mikeserv,

@mikeserv Bash. Ricordo di aver letto da qualche parte che questa sintassi era consentita in alcuni vecchi she che Bash lo permetteva anche per questo, anche se purtroppo non ho una citazione.
Trauma digitale il

ahh ... csh, probabilmente - è così che hanno funzionato in quella shell.
Mikeserv,

a proposito, nella ksh93cosa sopra potrebbe essere ;{1..10}bashprintf %s\\n {1..10}
:,

1
for((;i++<10)){ echo $i;}è più corto difor i in {1..10};{ echo $i;}
Evan Krall il

29

Per l'espansione aritmetica utilizzare $[…]invece di $((…)):

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

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

In espansioni aritmetiche non usare $:

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

L'espansione aritmetica viene eseguita su indici di array indicizzati, quindi non usare $né lì:

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

In espansioni aritmetiche non usare ${…}:

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

Sostituzione while((i--)), che funziona, con while[i--]o while $[i--]non ha funzionato per me. GNU bash, versione 4.3.46 (1)
Glenn Randers-Pehrson,

1
Corretto, @ GlennRanders-Pehrson. Non dovrebbe funzionare.
arte

y=`bc<<<"($x*2.2)%10/1"`... esempio di utilizzo bcper calcoli non interi ... notare che /1alla fine tronca il decimale risultante in un int.
roblogic

s=$((i%2>0?s+x:s+y))... esempio di utilizzo dell'operatore ternario in aritmetica bash. È più breve di if..then..elseo[ ] && ||
roblogic

1
@manatwork Grazie. Devono averlo rimosso. Sono acceso GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)e non è nel mio.
Giona

19

Il modo normale, lungo e noioso per definire una funzione è

f(){ CODE;}

Come ha scoperto questo ragazzo , hai assolutamente bisogno dello spazio prima CODEe del punto e virgola dopo.

Questo è un piccolo trucco che ho imparato da @DigitalTrauma :

f()(CODE)

Questo è più corto di due caratteri e funziona altrettanto bene, a condizione che non sia necessario riportare eventuali modifiche ai valori delle variabili dopo il ritorno della funzione ( le parentesi eseguono il corpo in una subshell ).

Come sottolinea @ jimmy23013 nei commenti, anche le parentesi potrebbero non essere necessarie.

Il Manuale di riferimento di Bash mostra che le funzioni possono essere definite come segue:

name () compound-command [ redirections ]

o

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

Un comando composto può essere:

  • un costrutto iterativo: until, whileofor
  • Un costrutto condizionale: if, case, ((...))o[[...]]
  • Comandi raggruppati: (...)o{...}

Ciò significa che sono validi tutti i seguenti:

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

E ho usato le parentesi graffe come una ventosa ...


2
Nota che puoi anche usare f()while ... f()if ...e altri comandi composti.
jimmy23013,

Questo mi ha sorpreso perché pensavo f()CODEfosse legale. Si scopre che f()echo hiè legale in pdksh e zsh, ma non in bash.
kernigh,

1
è particolarmente utile w / fordato che per impostazione predefinita è posizionali: f()for x do : $x; done;set -x *;PS4=$* f "$@"o qualcosa del genere.
Mikeserv,

16

:è un comando che non fa nulla, il suo stato di uscita ha sempre successo, quindi può essere usato al posto di true.


L'uso di una subshell e il piping userebbero circa lo stesso numero di byte, ma il piping sarebbe più pratico.
ckjbgames

5
Tranne quando lo fai:(){:|:}
enedil

14

Più suggerimenti

  1. Abusare dell'operatore ternario ((test)) && cmd1 || cmd2o [ test ] && cmd1 || cmd2, per quanto possibile.

    Esempi (i conteggi di lunghezza escludono sempre la riga superiore):

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

    Utilizzando solo operatori ternari, questo può essere facilmente abbreviato in:

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

    Come sottolineato da nyuszika7h nei commenti, questo esempio specifico potrebbe essere ulteriormente abbreviato utilizzando case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
  2. Inoltre, preferisci tra parentesi le parentesi il più possibile. Poiché le parentesi sono un metacarattere e non una parola, non richiedono mai spazi in nessun contesto. Questo significa anche eseguire il maggior numero possibile di comandi in una subshell, perché le parentesi graffe (ie {e }) sono parole riservate, non meta-caratteri, e quindi devono avere spazi bianchi su entrambi i lati per analizzare correttamente, ma i meta-personaggi no. Presumo che tu sappia ormai che i subshells non influiscono sull'ambiente padre, quindi supponendo che tutti i comandi di esempio possano essere eseguiti in modo sicuro in una subshell (che non è tipico in ogni caso), puoi abbreviare il codice sopra a questo :

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

    Inoltre, se non puoi, usare le parentesi può comunque minimizzarlo. Una cosa da tenere a mente è che funziona solo per numeri interi, il che lo rende inutile ai fini di questo esempio (ma è molto meglio che usare -eqper numeri interi).

  3. Ancora una cosa, evitare citazioni ove possibile. Utilizzando questo consiglio, puoi ulteriormente minimizzarlo. Esempio:

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
  4. In condizioni di test, preferisci parentesi singole a doppie parentesi il più possibile con poche eccezioni. Rilascia due caratteri gratis, ma in alcuni casi non è così robusto (è un'estensione di Bash - vedi sotto per un esempio). Inoltre, usa l'argomento single uguale anziché il doppio. È un personaggio gratuito da abbandonare.

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

    Nota questo avvertimento, specialmente nel controllo dell'output nullo o di una variabile non definita:

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

    In tutta tecnicità, questo esempio specifico sarebbe meglio con case... in:

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

Quindi, la morale di questo post è questa:

  1. Abusare il più possibile degli operatori booleani e usarli sempre al posto di if/ if-else/ etc. costrutti.
  2. Usa le parentesi il più possibile ed esegui il maggior numero possibile di segmenti nelle subshells perché le parentesi sono meta-caratteri e non parole riservate.
  3. Evita le citazioni il più fisicamente possibile.
  4. Dai un'occhiata case... in, dal momento che potrebbe risparmiare parecchi byte, in particolare nella corrispondenza delle stringhe.

PS: ecco un elenco di meta-personaggi riconosciuti in Bash indipendentemente dal contesto (e possono separare le parole):

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

EDIT: Come ha sottolineato il manatwork, il test della doppia parentesi funziona solo per numeri interi. Inoltre, indirettamente, ho scoperto che è necessario disporre di spazi bianchi attorno ==all'operatore. Corretto il mio post sopra.

Ero anche troppo pigro per ricalcolare la lunghezza di ogni segmento, quindi li ho semplicemente rimossi. Dovrebbe essere abbastanza facile trovare una calcolatrice della lunghezza delle stringhe online se necessario.


Mi dispiace dirlo, ma ci sono alcuni errori gravi lì. [ $t=="hi" ]valuterà sempre a 0, poiché viene analizzato come [ -n "STRING" ]. (($t=="hi"))valuterà sempre a 0 fintanto che $ t ha un valore non numerico, poiché le stringhe vengono forzate in numeri interi nelle valutazioni aritmetiche. Alcuni casi di test: pastebin.com/WefDzWbL
manatwork

@manatwork Grazie per la cattura. Aggiornerò di conseguenza.
Isiah Meadows,

L'uso di a casesarebbe più breve qui. Inoltre, non è necessario uno spazio prima }, ma dopo {.
nyuszika7h,

1
Perché sarebbe =meno robusto di ==? =è richiesto da POSIX, ==non lo è.
Dennis,

1
La domanda richiede un suggerimento per risposta ...
Toby Speight,

7

Invece di grep -E, grep -F, grep -r, uso egrep, fgrep, rgrep, risparmiando due caratteri. Quelli più corti sono deprecati ma funzionano bene.

(Hai chiesto un suggerimento per risposta!)


1
Peccato che non ci sia Pgrepper grep -P. Anche se vedo come potrebbe essere facilmente confuso con pgrep, che viene utilizzato per cercare i processi.
nyuszika7h

1
@ nyuszika7h Personalmente uso grep -omolto

Non risparmierebbe 3 compreso lo spazio?
ckjbgames

7

È possibile accedere all'elemento 0 di un array solo con il nome della variabile, un salvataggio di cinque byte che specifica esplicitamente un indice di 0:

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

7

Se è necessario passare il contenuto di una variabile a STDIN del processo successivo in una pipeline, è comune eseguire l'eco della variabile in una pipeline. Ma puoi ottenere la stessa cosa con una <<< stringa bash qui :

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

2
Dal momento che siamo il golf, s=code\ golf, echo $s|e <<<$s(tenendo presente che gli ultimi due lavori solo perché ci sono spazi non ripetuti, ecc).
Dennis,

6

Evita $( ...command... ), c'è un'alternativa che salva un carattere e fa la stessa cosa:

` ...command... `

9
A volte $( )è necessario se si hanno sostituzioni di comandi nidificate; altrimenti dovresti fuggire dall'interno``
Digital Trauma

1
Tecnicamente fanno cose diverse, ho dovuto usare i backtick invece di $()quando volevo eseguire la sostituzione sul mio computer anziché sul computer di scpdestinazione, per esempio. Nella maggior parte dei casi sono identici.
undergroundmonorail,

2
@undergroundmonorail: non hai mai bisogno di backtick. Tutto ciò che possono fare, $()può fare se citi le cose correttamente. (a meno che tu non abbia bisogno del tuo comando per sopravvivere a qualcosa che muta $ma non contraccolpi). Ci sono alcune sottili differenze nel citare cose al loro interno. mywiki.wooledge.org/BashFAQ/082 spiega alcune differenze. A meno che tu non stia giocando a golf, non usare mai le levette.
Peter Cordes,

@PeterCordes Sono sicuro che ci era un modo, ma tutto quello che ho provato al momento non ha funzionato. Anche se i backtick non erano la soluzione migliore, ero contento di conoscerli perché era l'unica soluzione che avevo. ¯ \ _ (ツ) _ / ¯
undergroundmonorail

@DigitalTrauma Esempio di annidamento: echo `bc <<<"\`date +%s\`-12"`... (È difficile pubblicare un campione contenente backtick in un commento, lì!;)
F. Hauri

6

Utilizzare ifper raggruppare i comandi

Rispetto a questo suggerimento che rimuove del iftutto, questo dovrebbe funzionare meglio solo in alcuni casi molto rari, come quando hai bisogno dei valori di ritorno da if.

Se hai un gruppo di comandi che termina con un if, come questi:

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

Puoi invece avvolgere i comandi prima ifnella condizione:

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

O se la tua funzione termina con un if:

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

È possibile rimuovere le parentesi graffe:

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

1
È possibile utilizzare l'operatore ternario [test] && $if_true || $elsein queste funzioni e salvare alcuni byte.
ckjbgames,

Inoltre, non hai bisogno di spazi intorno &&e||
roblogic

6

Usa l'aritmetica (( ... ))per le condizioni

È possibile sostituire:

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

di

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

(Nota: dopo non c'è spazio if)

o anche

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

... o se solo un comando

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

Inoltre: nascondi l'impostazione della variabile nel costrutto aritmetico:

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

o lo stesso

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

... dove se i> 5, quindi c = 1 (non 0;)


È possibile salvare 2 byte utilizzando [ ]invece di (()).
ckjbgames,

@ckjbgames Ne sei sicuro !? Quale versione bash stai usando?
F. Hauri,

@FHauri Immagino che sarebbe quasi lo stesso in termini di byte.
ckjbgames,

@ckjbgames Con [ ]te hai bisogno del simbolo del dollaro per la variabile. Non vedo come si possa fare lo stesso con una lunghezza uguale o inferiore usando [ ].
F. Hauri,

Suggerimento bonus: se inizia la prima riga di un ciclo for ((...)), non è necessaria alcuna nuova riga o spazio. Ad esempio, for((;10>n++;)){((n%3))&&echo $n;} provalo online!
primo

6

È una sintassi più breve per loop infiniti (che possono essere salvati con breako exitistruzioni)

for((;;)){ code;}

Questo è più breve di while true;e while :;.

Se non ti serve break(con exitl'unico modo per scappare), puoi invece utilizzare una funzione ricorsiva.

f(){ code;f;};f

Se è necessario interrompere, ma non è necessario uscire e non è necessario trasferire alcuna modifica variabile all'esterno del ciclo, è possibile utilizzare una funzione ricorsiva con parentesi attorno al corpo , che eseguono il corpo della funzione in una subshell.

f()(code;f);f

6

forAnelli a una riga

Un'espressione aritmetica concatenata con un'espansione dell'intervallo verrà valutata per ciascun elemento dell'intervallo. Ad esempio quanto segue:

: $[expression]{0..9}

valuterà expression10 volte.

Questo è spesso significativamente più breve del forloop equivalente :

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

Se non ti dispiace comandare errori non trovati, puoi rimuovere l'iniziale :. Per iterazioni superiori a 10, puoi anche utilizzare intervalli di caratteri, ad esempio {A..z}ripeterà 58 volte.

Come esempio pratico, entrambi i seguenti producono i primi 50 numeri triangolari, ciascuno sulla propria riga:

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

puoi anche for((;0<i--;)){ f;}
scorrere

5

Ripassa gli argomenti

Come notato nel loop "for" di Bash senza una parte "in foo bar ..." , in "$@;"in for x in "$@;"è ridondante.

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

Ad esempio, se vogliamo quadrare tutti i numeri dati argomenti posizionali a uno script Bash o una funzione, possiamo farlo.

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

Provalo online!


5

Alternativa al gatto

Supponi che stai provando a leggere un file e utilizzarlo in qualcos'altro. Quello che potresti fare è:

echo foo `cat bar`

Se il contenuto di barfosse foobar, questo verrebbe stampato foo foobar.

Tuttavia, esiste un'alternativa se si utilizza questo metodo, che consente di risparmiare 3 byte:

echo foo `<bar`

1
C'è un motivo per cui di <barper sé non funziona ma inserendolo in backtick funziona?
Kritixi Lithos,

@Cowsquack Sì. La <mette un file a un comando, ma in questo caso si mette in output standard a causa di un capriccio. I backtick lo valutano insieme.
Okx,

Esiste un modo più breve di leggere dallo standard input diverso da `cat`?
Joel

4

Utilizzare al [posto di [[e testquando possibile

Esempio:

[ -n $x ]


Utilizzare =invece che ==per il confronto

Esempio:

[ $x = y ]

Nota che devi avere spazi attorno al segno uguale altrimenti non funzionerà. Lo stesso vale per i ==miei test.


3
Il [vs. [[può dipendere dalla quantità di preventivi richiesti: pastebin.com/UPAGWbDQ
manatwork

@manatwork Questo è un buon punto.
nyuszika7h

1
Regola generale: [... ]== /bin/test, ma [[... ]]! = /bin/testE non si dovrebbe mai preferire [... ]oltre [[... ]]al di fuori di codegolf
cat,

4

Alternative a head

lineè tre byte più breve di head -1, ma è deprecato .

sed qè due byte più breve di head -1.

sed 9qè un byte più breve di head -9.


1
Sebbene condannato , possiamo ancora usare per un po ' linedal pacchetto util-linux per leggere una sola riga.
arte

4

tr -cd è più corto di grep -o

Ad esempio, se devi contare gli spazi, grep -o <char>(stampa solo la corrispondenza) restituisce 10 byte mentre tr -cd <char>(elimina complemento di <char>) restituisce 9.

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

( fonte )

Si noti che entrambi forniscono output leggermente diversi. grep -orestituisce risultati separati da riga mentre tr -cdli dà tutti sulla stessa riga, quindi trpotrebbe non essere sempre favorevole.


4

Abbreviare i nomi dei file

In una recente sfida stavo cercando di leggere il file /sys/class/power_supply/BAT1/capacity, tuttavia questo può essere abbreviato in /*/*/*/*/capac*yquanto non esiste nessun altro file con quel formato.

Ad esempio, se si dispone di una directory foo/contenente i file foo, bar, foobar, barfooe si desidera fare riferimento al file foo/barfoo, è possibile utilizzare foo/barf*per salvare un byte.

La *rappresenta "qualsiasi cosa", ed è equivalente al regex .*.


3

Utilizzare una pipe per il :comando anziché /dev/null. Il :built-in mangerà tutti i suoi input.


2
No, nella maggior parte dei casi si bloccherà il programma con SIGPIPE. echo a|tee /dev/stderr|:non stamperà nulla.
jimmy23013,

C'è una gara: echo a|tee /dev/stderr|:ho stampato un sul mio computer, ma altrove SIGPIPE potrebbe uccidere prima il tee. Potrebbe dipendere dalla versione di tee.
kernigh,

Sì, è un problema SIGPIPE: tee >(:) < <(seq 1 10)funzionerà, ma tee /dev/stderr | :non lo farà. Anche a() { :;};tee /dev/stderr < <(seq 1 10)| anon stampare nulla.
F. Hauri,

@utente16402 - dovresti avere un nome fccing nel mio ambito ... in ogni caso, l' :intrinseco non mangia affatto ... se supponi l'input al colon potresti inondare un pipe in in out errore ... ma puoi far galleggiare un reindirizzamento dai due punti, o trascina un processo con esso ... :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; doneo comunque che si applichi anche lo pseudo suggerimento ... sei consapevole che sei quasi così fantasma come me? ... evitare a tutti i costi il< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
mikeserv il

3

splitha un'altra sintassi (deprecata, ma a nessuno importa) per dividere l'input in sezioni di Nlinee ciascuna: invece di split -lNte puoi usare split -Nad es split -9.


3

Espandi i test

In sostanza, la shell è un tipo di linguaggio macro, o almeno un ibrido o un qualche tipo. Ogni riga di comando può essere sostanzialmente suddivisa in due parti: la parte di analisi / input e la parte di espansione / output.

La prima parte è ciò su cui la maggior parte delle persone si concentra perché è la più semplice: vedi cosa ottieni. La seconda parte è ciò che molti evitano persino di provare a capire molto bene ed è il motivo per cui le persone dicono che cose del genere evalsono cattive e citano sempre le tue espansioni : le persone vogliono che il risultato della prima parte sia uguale alla prima. Va bene, ma porta a rami di codice inutilmente lunghi e tonnellate di test estranei.

Le espansioni sono auto-test . I ${param[[:]#%+-=?]word}moduli sono più che sufficienti per convalidare il contenuto di un parametro, sono annidabili e sono tutti basati sulla valutazione di NUL , che è comunque ciò che la maggior parte delle persone si aspetta dai test. +può essere particolarmente utile nei loop:

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

... mentre readle linee di "${r:+set}"trazione non vuote si espandono "set"e i posizionali vengono $raggiunti. Ma quando una riga vuota è read, $rè vuota e si "${r:+set}"espande in ""- che è un comando non valido. Ma poiché la linea di comando viene espansa prima il ""comando vuoto viene ricercato, "${r:=$*}"assume i valori di tutte le positionals concatenate sul primo byte $IFSpure. r()potrebbe essere richiamato di nuovo nel |{comando composto ;}con un valore diverso per $IFSottenere anche il prossimo paragrafo di input, dal momento che è illegale che una shell esegua il readbuffer oltre la successiva \newline in input.


3

Usa la ricorsione della coda per ridurre gli anelli:

Questi sono equivalenti nel comportamento (sebbene probabilmente non nell'uso della memoria / PID):

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

E questi sono approssimativamente equivalenti:

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

(tecnicamente gli ultimi tre eseguiranno sempre il corpo almeno una volta)

L'utilizzo $0richiede che lo script sia in un file, non incollato nel prompt di bash.

Alla fine lo stack potrebbe traboccare, ma si risparmiano alcuni byte.


3

A volte è più breve usare l' exprintegrato per visualizzare il risultato di una semplice espressione aritmetica invece del solito echo $[ ]. Per esempio:

expr $1 % 2

è un byte più breve di:

echo $[$1%2]

2

Utilizzare pwdinvece di echogenerare una linea di output

Hai bisogno di mettere una riga su stdout ma non ti interessa il contenuto e vuoi limitare la tua risposta ai builtin della shell? pwdè un byte più corto di echo.


2

Le virgolette possono essere omesse durante la stampa di stringhe.

echo "example"
echo example

Uscita in SM-T335 LTE, Android 5.1.1:

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

2

Quando si assegnano elementi di array non continui, è comunque possibile saltare gli indici successivi di blocchi continui:

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)

Il risultato è lo stesso:

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

Secondo man bash:

Le matrici sono assegnate all'uso delle assegnazioni composte del nome del modulo = (valore 1 ... valore n ), in cui ciascun valore è del formato [ indice ] = stringa . Le assegnazioni di array indicizzate non richiedono altro che stringa . Quando si assegna a matrici indicizzate, se vengono fornite le parentesi e il pedice facoltativi, a tale indice viene assegnato; altrimenti l'indice dell'elemento assegnato è l'ultimo indice assegnato dall'istruzione più uno.


Utile da aggiungere: gli elementi non inizializzati si espandono a 0 nelle espansioni aritmetiche e "" in altre espansioni.
Digital Trauma,

2

Stampa la prima parola in una stringa

Se la stringa si trova nella variabile ae non contiene caratteri di escape e formattazione ( \e %), utilizzare questo:

printf $a

Ma sarebbe più lungo del seguente codice se è necessario per salvare il risultato in una variabile invece di stampare:

x=($a)
$x

1

Esecuzione di 2 loop di incorporamento con 1 foristruzione:

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

Assegna e stampa stringhe tra virgolette

Se si desidera assegnare una stringa tra virgolette a una variabile e quindi stampare il valore di quella variabile, il modo normale per farlo sarebbe:

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

Se aprecedentemente non impostato, questo può essere abbreviato in:

echo ${a=Programming Puzzles & Code Golf}

Se aprecedentemente impostato, utilizzare invece questo:

echo ${a+Programming Puzzles & Code Golf}

Nota che è utile solo se la stringa richiede virgolette (ad es. Contiene spazi bianchi). Senza virgolette, a=123;echo $aè altrettanto breve.


${a+foo}non imposta a.
GammaFunction
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.