Conseils pour jouer au golf à Bash

55

Quels conseils généraux avez-vous pour jouer au golf à Bash? Je recherche des idées pouvant être appliquées aux problèmes de code de golf en général, qui sont au moins quelque peu spécifiques à Bash (par exemple, "supprimer les commentaires" n'est pas une réponse). Merci de poster un pourboire par réponse.

homme au travail
la source

Réponses:

36

Non documenté , mais fonctionne dans toutes les versions que j'ai rencontrées pourshune compatibilité ascendantehéritée:

forles boucles vous permettent d'utiliser { }au lieu de do done. Par exemple remplacer:

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

avec:

for i in {1..10};{ echo $i;}
Trauma numérique
la source
quel shell est le shet quel shell permet cette forsyntaxe? il est expressément autorisé à entrer zsh.
mikeserv
@mikeserv Bash. Je me souviens d’avoir lu quelque part que cette syntaxe était permise dans certains vieux shet que Bash l’ autorisait aussi à cause de cela, même si malheureusement je n’ai pas de citation.
Digital Trauma
ahh ... cshprobablement - c'est comme ça qu'ils ont travaillé dans cette coquille.
mikeserv
à propos, dans ksh93ce qui précède pourrait être ;{1..10}bashprintf %s\\n {1..10}
:,
1
for((;i++<10)){ echo $i;}est plus court quefor i in {1..10};{ echo $i;}
Evan Krall
29

Pour une expansion arithmétique, utilisez à la $[…]place de $((…)):

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

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

Dans les développements arithmétiques, n'utilisez pas $:

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

Le développement arithmétique est effectué sur des indices de tableau indexés, ne l'utilisez donc $pas:

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

Dans les développements arithmétiques, n'utilisez pas ${…}:

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
homme au travail
la source
Remplacement while((i--)), qui fonctionne, avec while[i--]ou while $[i--]n'a pas fonctionné pour moi. GNU bash, version 4.3.46 (1)
Glenn Randers-Pehrson
1
Correct, @ GlennRanders-Pehrson. Cela n'est pas censé fonctionner.
Manatwork
y=`bc<<<"($x*2.2)%10/1"`... exemple d'utilisation bcpour des calculs non entiers ... notez /1qu'à la fin, le nombre décimal résultant est tronqué en un entier .
roblogic
s=$((i%2>0?s+x:s+y))... exemple d'utilisation d'un opérateur ternaire dans l'arithmétique de bash. C'est plus court que if..then..elseou[ ] && ||
roblogic
1
@manatwork Merci. Ils doivent l'avoir enlevé. Je suis sur GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)et ce n'est pas dans le mien.
Jonah
19

La manière normale, longue et ennuyeuse de définir une fonction est

f(){ CODE;}

Comme ce gars l'a découvert, vous avez absolument besoin d'espace avant CODEet du point-virgule après.

C'est un petit truc que j'ai appris de @DigitalTrauma :

f()(CODE)

Cela fait deux caractères plus courts et cela fonctionne aussi bien, à condition que vous n'ayez pas besoin de reporter les modifications des valeurs des variables après le retour de la fonction ( les parenthèses exécutent le corps dans un sous-shell ).

Comme @ jimmy23013 l' indique dans les commentaires, même les parenthèses peuvent être inutiles.

Le manuel de référence de Bash montre que les fonctions peuvent être définies comme suit:

name () compound-command [ redirections ]

ou

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

Une commande composée peut être:

  • Construct une boucle: until, whileoufor
  • un produit d' assemblage conditionnel: if, case, ((...))ou[[...]]
  • Commandes groupées: (...)ou{...}

Cela signifie que tous les éléments suivants sont valides:

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

Et j'ai utilisé des accolades comme une ventouse ...

Dennis
la source
2
Notez que vous pouvez également utiliser f()while ... f()if ...et d'autres commandes composées.
Jimmy23013
Celui-ci m'a surpris parce que je pensais que f()CODEc'était légal. Il s'avère que f()echo hic'est légal dans pdksh et zsh, mais pas dans bash.
Kernigh
1
son surtout utile w / fordepuis il par défaut aux positions: f()for x do : $x; done;set -x *;PS4=$* f "$@"ou quelque chose.
mikeserv
16

:est une commande qui ne fait rien, son état de sortie est toujours réussi, il peut donc être utilisé à la place de true.


la source
L'utilisation d'un sous-shell et de la tuyauterie utiliserait à peu près le même nombre d'octets, mais la tuyauterie serait plus pratique.
ckjbgames
5
Sauf quand tu le fais:(){:|:}
enedil
14

Plus de conseils

  1. Abuser de l'opérateur ternaire, ((test)) && cmd1 || cmd2ou [ test ] && cmd1 || cmd2autant que possible.

    Exemples (les nombres de longueur excluent toujours la ligne du haut):

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

    En utilisant uniquement des opérateurs ternaires, cela peut facilement être réduit à:

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

    Comme nyuszika7h l'a souligné dans les commentaires, cet exemple spécifique pourrait être encore raccourci en utilisant case:

    t="$something"
    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
  2. Aussi, préférez les parenthèses aux accolades autant que possible. Puisque les parenthèses sont un métacaractère et non un mot, elles ne nécessitent jamais d'espaces, quel que soit le contexte. Cela signifie également que vous exécuterez autant de commandes que possible dans un sous-shell, car les accolades (ie {et }) sont des mots réservés, et non des méta-caractères, et doivent donc comporter des espaces des deux côtés pour être analysées correctement, contrairement aux méta-caractères. Je suppose que vous savez maintenant que les sous-shell n’affectent pas l’environnement parent. Par conséquent, en supposant que toutes les commandes données en exemple puissent être exécutées en toute sécurité dans un sous-shell (ce qui n’est en général pas le cas), vous pouvez raccourcir le code ci-dessus en ceci. :

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

    En outre, si vous ne pouvez pas, utiliser des parenthèses peut encore en réduire certaines. Une chose à garder à l'esprit est que cela ne fonctionne que pour les entiers, ce qui le rend inutile pour les besoins de cet exemple (mais c'est beaucoup mieux que d'utiliser -eqpour des entiers).

  3. Une dernière chose, évitez les guillemets dans la mesure du possible. En utilisant les conseils ci-dessus, vous pouvez les réduire davantage. Exemple:

    t=$something
    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
  4. Dans les conditions de test, préférez autant que possible les supports simples aux supports doubles, à quelques exceptions près. Il laisse tomber deux caractères gratuitement, mais dans certains cas, il n’est pas aussi robuste (c’est une extension Bash - voir ci-dessous pour un exemple). En outre, utilisez l'argument single equals plutôt que le double. C'est un personnage gratuit à laisser tomber.

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

    Notez cette mise en garde, en particulier lors de la vérification d'une sortie nulle ou d'une variable non définie:

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

    En toute technicité, cet exemple spécifique conviendrait mieux avec case... in:

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

Donc, la morale de ce post est la suivante:

  1. Abusez autant que possible des opérateurs booléens et utilisez-les toujours à la place de if/ if-else/ etc. construit.
  2. Utilisez autant que possible des parenthèses et exécutez autant de segments que possible dans les sous-shell, car les parenthèses sont des méta-caractères et non des mots réservés.
  3. Évitez les citations autant que physiquement possible.
  4. Check out case... in, car cela peut économiser pas mal d'octets, en particulier dans la correspondance de chaînes.

PS: Voici une liste de méta-caractères reconnus dans Bash indépendamment du contexte (et peuvent séparer des mots):

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

EDIT: Comme l’a souligné Manatwork, le test de double parenthèse ne fonctionne que pour les nombres entiers. Aussi, indirectement, j’ai trouvé qu’il fallait que les espaces soient entourés par l’ ==opérateur. Corrigé mon post ci-dessus.

J'étais aussi trop paresseux pour recalculer la longueur de chaque segment, alors je les ai simplement supprimés. Il devrait être assez facile de trouver une calculatrice de longueur de chaîne en ligne si nécessaire.

Isiah Meadows
la source
Désolé de le dire, mais vous avez quelques erreurs graves. [ $t=="hi" ]sera toujours évalué à 0, car il est analysé comme [ -n "STRING" ]. (($t=="hi"))sera toujours évalué à 0 tant que $ t a une valeur non numérique, car les chaînes sont forcées en entiers dans les évaluations arithmétiques. Quelques cas de test: pastebin.com/WefDzWbL
manatwork
@manatwork Merci pour la capture. Je mettrai à jour en conséquence.
Isiah Meadows
Utiliser un caseserait plus court ici. En outre, vous n'avez pas besoin d'espace avant }, mais après {.
Nyuszika7h
1
Pourquoi serait =moins robuste que ==? =est mandaté par POSIX, ==non.
Dennis
1
La question demande un pourboire par réponse ...
Toby Speight
7

Au lieu de grep -E, grep -F, grep -r, utilisation egrep, fgrep, rgrep, sauvant deux caractères. Les plus courtes sont déconseillées mais fonctionnent bien.

(Vous avez demandé un pourboire par réponse!)


la source
1
Dommage qu'il n'y ait pas Pgreppour grep -P. Bien que je vois comment on pourrait facilement le confondre pgrep, qui est utilisé pour rechercher des processus.
nyuszika7h
1
@ nyuszika7h J'utilise personnellement grep -obeaucoup
Ne serait-il pas économiser 3, y compris l'espace?
ckjbgames
7

L'élément 0 d'un tableau est accessible avec le nom de variable uniquement, une sauvegarde sur cinq octets spécifiant explicitement un index de 0:

$ a=(code golf)
$ echo ${a[0]}
code
$ echo $a
code
$ 
Trauma numérique
la source
7

Si vous devez transmettre le contenu d'une variable à STDIN du processus suivant dans un pipeline, il est courant de renvoyer la variable dans un pipeline. Mais vous pouvez obtenir la même chose avec une <<< chaîne bash ici :

$ s="code golf"
$ echo "$s"|cut -b4-6
e g
$ cut -b4-6<<<"$s"
e g
$ 
Trauma numérique
la source
2
Puisque nous jouer au golf, s=code\ golf, echo $s|et <<<$s( en gardant à l' esprit que les deux derniers travail seulement parce qu'il ya des espaces non répétés, etc.).
Dennis
6

Evitez $( ...command... ), il existe une alternative qui enregistre un caractère et fait la même chose:

` ...command... `

la source
9
Parfois $( )nécessaire si vous avez des substitutions de commandes imbriquées; sinon vous auriez à échapper à l'intérieur``
Digital Trauma
1
Du point de vue technique, ils ont des tâches différentes. J'ai dû utiliser les backtick au lieu de, par exemple, $()lorsque je voulais exécuter le remplacement sur ma machine plutôt que sur la scpmachine cible. Dans la plupart des cas, ils sont identiques.
undergroundmonorail
2
@groundgroundorail: vous n'avez jamais besoin de backticks. Tout ce qu'ils peuvent faire, $()peut faire si vous citez des choses correctement. (sauf si vous avez besoin de votre commande pour survivre à quelque chose qui bat, $mais pas des backticks). Il y a quelques différences subtiles dans la citation de choses à l'intérieur. mywiki.wooledge.org/BashFAQ/082 explique certaines différences. Sauf si vous jouez au golf, n'utilisez jamais de backtick.
Peter Cordes
@PeterCordes Je suis sûr qu'il y avait un moyen mais tout ce que j'ai essayé à l'époque ne fonctionnait pas. Même si les backticks n'étaient pas la meilleure solution, j'étais heureuse de les connaître parce que c'était la seule solution que j'avais. ¯ \ _ (ツ) _ / ¯
undergroundmonorail
@DigitalTrauma Exemple de nidification: echo `bc <<<"\`date +%s\`-12"`... (Il est difficile d'afficher un commentaire contenant un prélèvement en arrière, ici!;)
F. Hauri
6

Utiliser ifpour grouper des commandes

Par rapport à cette astuce qui supprime le iftout, cela ne devrait fonctionner mieux que dans de très rares cas, comme lorsque vous avez besoin des valeurs de retour de if.

Si vous avez un groupe de commandes qui se termine par un if, comme celui-ci:

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

Vous pouvez envelopper les commandes auparavant ifdans la condition suivante:

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

Ou si votre fonction se termine par un if:

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

Vous pouvez enlever les accolades:

f()if a;b;then c;else d;fi
jimmy23013
la source
1
Vous pouvez utiliser l'opérateur ternaire [test] && $if_true || $elsedans ces fonctions et sauvegarder des octets.
ckjbgames
En outre, vous n'avez pas besoin d'espaces autour &&et||
roblogic
6

Utiliser l'arithmétique (( ... ))pour les conditions

Vous pourriez remplacer:

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

par

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

(Remarque: il n'y a pas d'espace après if)

ou même

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

... ou si une seule commande

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

En outre: Masquer le paramètre de variable dans la construction arithmétique:

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

ou même

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

... où si i> 5, alors c = 1 (pas 0;)

F. Hauri
la source
Vous pouvez économiser 2 octets en utilisant à la [ ]place de (()).
ckjbgames
@ckjbgames Êtes-vous sûr de ça!? Quelle version bash utilisez-vous?
F. Hauri
@ FHauri Je suppose que ce serait à peu près la même chose en termes d'octets.
ckjbgames
@ckjbgames Avec [ ]vous avez besoin du signe dollar pour variable. Je ne vois pas comment vous pourriez faire la même chose avec une longueur identique ou inférieure [ ].
F. Hauri
Conseil bonus: si la première ligne d'une boucle for commence par ((...)), aucune nouvelle ligne ni aucun espace ne sont nécessaires. Par exemple, for((;10>n++;)){((n%3))&&echo $n;} essayez-le en ligne!
primo
6

Une syntaxe plus courte pour les boucles infinies (qui peuvent être échappées avec breakou exitstatement) est

for((;;)){ code;}

C'est plus court que while true;et while :;.

Si vous n'avez pas besoin break(avec exitcomme seul moyen de vous échapper), vous pouvez utiliser une fonction récursive à la place.

f(){ code;f;};f

Si vous avez besoin d'une pause, mais que vous n'avez pas besoin de quitter ni de modifier une variable en dehors de la boucle, vous pouvez utiliser une fonction récursive avec des parenthèses autour du corps , qui exécute le corps de la fonction dans un sous-shell.

f()(code;f);f
Gilles, arrête de faire le mal
la source
6

forBoucles d'une ligne

Une expression arithmétique concaténée avec une extension de plage sera évaluée pour chaque élément de la plage. Par exemple:

: $[expression]{0..9}

évaluera expression10 fois.

C'est souvent beaucoup plus court que la forboucle équivalente :

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

Si cela ne vous dérange pas que la commande ne trouve pas d’erreur, vous pouvez supprimer l’inital :. Pour les itérations supérieures à 10, vous pouvez également utiliser des plages de caractères, par exemple une {A..z}itération 58 fois.

Comme exemple pratique, les deux suivants produisent les 50 premiers nombres triangulaires, chacun sur leur propre ligne:

for((;50>d++;)){ echo $[n+=d];} # 31 bytes
printf %d\\n $[n+=++d]{A..r}    # 28 bytes
primo
la source
vous pouvez également parcourir à l'envers:for((;0<i--;)){ f;}
roblogic
5

Boucle sur les arguments

Comme indiqué dans Bash « pour » boucle sans « dans le bar foo ... » partie , l' in "$@;"en for x in "$@;"est redondant.

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

Par exemple, si nous voulons mettre tous les nombres en carré avec des arguments de position dans un script Bash ou une fonction, nous pouvons le faire.

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

Essayez-le en ligne!

Dennis
la source
5

Alternative au chat

Supposons que vous essayez de lire un fichier et que vous l'utilisez dans quelque chose d'autre. Ce que tu pourrais faire c'est:

echo foo `cat bar`

Si le contenu de barétait foobar, cela imprimerait foo foobar.

Cependant, il existe une alternative si vous utilisez cette méthode, qui économise 3 octets:

echo foo `<bar`
Okx
la source
1
Y a-t-il une raison pour laquelle, <barpar lui-même, ne fonctionne pas, mais que le placer dans les backticks fonctionne?
Kritixi Lithos
@Cowsquack Oui. Le <met un fichier dans une commande, mais dans ce cas, il le place dans la sortie standard à cause d'un caprice. Les backticks évaluent cela ensemble.
Okx
Existe-t-il un moyen plus court de lire à partir d’une entrée standard autre que `cat`?
Joël
4

Utilisez [au lieu de [[et testquand cela est possible

Exemple:

[ -n $x ]


Utilisez =au lieu de ==pour la comparaison

Exemple:

[ $x = y ]

Notez que vous devez avoir des espaces autour du signe égal sinon cela ne fonctionnera pas. La même chose s'applique à ==basé sur mes tests.

nyuszika7h
la source
3
Le [vs [[peut dépendre de la quantité de devis requis: pastebin.com/UPAGWbDQ
manatwork
@manatwork C'est un bon point.
nyuszika7h
1
Règle générale: [... ]== /bin/test, mais [[... ]]! = /bin/testEt il ne faut jamais préférer [... ]plus [[... en ]]dehors de codegolf
cat
4

Alternatives à head

lineest trois octets plus court que head -1, mais est obsolète .

sed qest deux octets plus court que head -1.

sed 9qest un octet plus court que head -9.

Lynn
la source
1
Bien que condamné , nous pouvons toujours utiliser pendant un certain temps linedu paquet util-linux pour lire une seule ligne.
Manatwork
4

tr -cd est plus court que grep -o

Par exemple, si vous devez compter les espaces, grep -o <char>(imprimer uniquement les éléments correspondants) donne 10 octets tandis que tr -cd <char>(supprimer le complément de <char>) en donne 9.

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

( source )

Notez que les deux donnent des résultats légèrement différents. grep -orenvoie les résultats séparés par une ligne tout en les tr -cddonnant tous sur la même ligne, ce qui trpeut ne pas être toujours favorable.

Kritixi Lithos
la source
4

Raccourcir les noms de fichiers

Dans un défi récent, j’essayais de lire le fichier /sys/class/power_supply/BAT1/capacity. Cependant, il est possible de le réduire /*/*/*/*/capac*ycar aucun autre fichier n’existe avec ce format.

Par exemple, si vous avez un répertoire foo/contenant les fichiers foo, bar, foobar, barfooet que vous souhaitez référencer le fichier foo/barfoo, vous pouvez utiliser foo/barf*pour enregistrer un octet.

Le *représente "n'importe quoi" et est équivalent à la regex .*.

Okx
la source
3

Utilisez un tuyau pour la :commande au lieu de /dev/null. L' :intégré va manger toute son entrée.


la source
2
Non, le programme avec SIGPIPE plantera dans la plupart des cas. echo a|tee /dev/stderr|:n'imprimera rien.
Jimmy23013
Il y a une course: echo a|tee /dev/stderr|:j'ai imprimé un sur mon ordinateur, mais ailleurs, SIGPIPE pourrait tuer le tee en premier. Cela peut dépendre de la version de tee.
Kernigh
Oui, c’est un problème SIGPIPE: tee >(:) < <(seq 1 10)cela fonctionnera mais tee /dev/stderr | :ne fonctionnera pas. Même a() { :;};tee /dev/stderr < <(seq 1 10)| an'imprime rien.
F. Hauri
@ user16402 - vous devriez avoir un nom fccing dans mon champ ... de toute façon, l' :intrinsèque ne mange pas du tout ... si vous supposez une entrée dans le côlon, vous risquez d'inonder une erreur d'erreur ... mais vous pouvez créer une redirection par deux points, ou laisser tomber un processus avec elle ... :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; doneou quelle que soit la pseudo suggestion proposée ... aussi, savez-vous que vous êtes presque aussi fantomatique que moi? ... éviter à tout prix le< <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement)
mikeserv
3

splita une autre syntaxe (obsolète, mais personne ne s'en soucie) pour diviser les entrées en sections de Nlignes: split -lNvous pouvez utiliser split -Npar exemple, par exemple split -9.


la source
3

Élargir les tests

En gros, le shell est une sorte de langage macro, ou au moins un hybride ou autre. Chaque ligne de commande peut être divisée en deux parties: la partie analyse / entrée et la partie expansion / sortie.

La première partie est ce sur quoi la plupart des gens se concentrent car c'est la plus simple: vous voyez ce que vous obtenez. La deuxième partie est ce que beaucoup évitent même d’essayer même de bien comprendre et c’est la raison pour laquelle les gens disent que le evalmal est et cite toujours vos développements - les gens veulent que le résultat de la première partie soit égal à celui de la première. Ce n'est pas grave, mais cela conduit à des branches de code inutilement longues et à des tonnes de tests superflus.

Les extensions sont des tests automatiques . Les ${param[[:]#%+-=?]word}formulaires sont plus que suffisants pour valider le contenu d'un paramètre, ils peuvent être imbriqués et sont tous basés sur l'évaluation de NUL - ce que la plupart des gens attendent de toute façon des tests. +peut être particulièrement utile dans les boucles:

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

... tandis que readles lignes non vierges se "${r:+set}"développent "set"et que les positions sont $rajoutées. Mais quand une ligne vide est read, $rest vide et "${r:+set}"s'agrandit ""- ce qui est une commande invalide. Mais comme la ligne de commande est développée avant la ""recherche de la commande null, "${r:=$*}"prend également les valeurs de toutes les positions concaténées sur le premier octet $IFS. r()pourrait être appelé à nouveau dans la |{commande composée ;}avec une valeur différente pour $IFSobtenir le prochain paragraphe d'entrée également, car il est illégal pour un shell de readtamponner au-delà de la ligne suivante \nen entrée.

Mikeserv
la source
3

Utilisez la récursion de la queue pour raccourcir les boucles:

Ce sont des comportements équivalents (mais probablement pas en mémoire / utilisation du PID):

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

Et ceux-ci sont à peu près équivalents:

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

(techniquement, les trois derniers exécuteront toujours le corps au moins une fois)

L'utilisation $0nécessite que votre script soit dans un fichier et non collé dans l'invite bash.

Éventuellement, votre pile peut déborder, mais vous enregistrez des octets.

Evan Krall
la source
3

Parfois, il est plus court d’utiliser l’ exprintégré pour afficher le résultat d’une expression arithmétique simple au lieu de l’habituel echo $[ ]. Par exemple:

expr $1 % 2

est un octet plus court que:

echo $[$1%2]
Trauma numérique
la source
2

Utiliser pwdau lieu de echopour générer une ligne de sortie

Vous avez besoin de mettre une ligne sur stdout mais ne vous souciez pas du contenu et souhaitez limiter votre réponse aux commandes intégrées au shell? pwdest un octet plus court que echo.

Glenn Randers-Pehrson
la source
2

Les citations peuvent être omises lors de l'impression de chaînes.

echo "example"
echo example

Sortie dans SM-T335 LTE, Android 5.1.1:

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

la source
2

Lorsque vous affectez des éléments de tableau non continus, vous pouvez toujours ignorer les index successifs des fragments continus:

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)

Le résultat est le même:

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

Selon man bash:

Les tableaux sont affectés à l'aide d'assignations composées de la forme nom = (valeur 1 ... valeur n ), où chaque valeur est de la forme [ indice ] = chaîne . Les assignations de tableaux indexés ne nécessitent rien d’autre que des chaînes . Lors de l'affectation à des tableaux indexés, si les crochets et les indices facultatifs sont fournis, cet index est affecté à; sinon, l'index de l'élément affecté est le dernier index attribué à l'instruction plus un.

homme au travail
la source
Utile pour ajouter: les éléments non initialisés seront étendus à 0 dans les développements arithmétiques et "" dans les autres développements.
Trauma numérique
2

Imprimer le premier mot d'une chaîne

Si la chaîne est dans la variable aet ne contient pas de caractères d'échappement et de format ( \et %), utilisez ceci:

printf $a

Mais il serait plus long que le code suivant s'il était nécessaire de sauvegarder le résultat dans une variable au lieu d'imprimer:

x=($a)
$x
jimmy23013
la source
1

Faire 2 boucle d'intégration avec 1 forinstruction:

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
F. Hauri
la source
1

Assigner et imprimer des chaînes entre guillemets

Si vous souhaitez affecter une chaîne entre guillemets à une variable, puis en imprimer la valeur, la méthode habituelle pour le faire est la suivante:

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

Si aprécédemment non défini, cela peut être réduit à:

echo ${a=Programming Puzzles & Code Golf}

Si aétait précédemment défini, alors ceci devrait être utilisé à la place:

echo ${a+Programming Puzzles & Code Golf}

Notez que cela n’est utile que si la chaîne nécessite des guillemets (contient par exemple des espaces). Sans guillemets, a=123;echo $ac'est tout aussi court.

Trauma numérique
la source
${a+foo}ne prend pas a.
GammaFunction