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.
55
sh
et quel shell permet cettefor
syntaxe? il est expressément autorisé à entrerzsh
.sh
et que Bash l’ autorisait aussi à cause de cela, même si malheureusement je n’ai pas de citation.csh
probablement - c'est comme ça qu'ils ont travaillé dans cette coquille.ksh93
ce qui précède pourrait être;{1..10}
bash
printf %s\\n {1..10}
for((;i++<10)){ echo $i;}
est plus court quefor i in {1..10};{ echo $i;}
Pour une expansion arithmétique, utilisez à la
$[…]
place de$((…))
:Dans les développements arithmétiques, n'utilisez pas
$
:Le développement arithmétique est effectué sur des indices de tableau indexés, ne l'utilisez donc
$
pas:Dans les développements arithmétiques, n'utilisez pas
${…}
:la source
while((i--))
, qui fonctionne, avecwhile[i--]
ouwhile $[i--]
n'a pas fonctionné pour moi. GNU bash, version 4.3.46 (1)y=`bc<<<"($x*2.2)%10/1"`
... exemple d'utilisationbc
pour des calculs non entiers ... notez/1
qu'à la fin, le nombre décimal résultant est tronqué en un entier .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 queif..then..else
ou[ ] && ||
GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)
et ce n'est pas dans le mien.La manière normale, longue et ennuyeuse de définir une fonction est
Comme ce gars l'a découvert, vous avez absolument besoin d'espace avant
CODE
et du point-virgule après.C'est un petit truc que j'ai appris de @DigitalTrauma :
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:
Une commande composée peut être:
until
,while
oufor
if
,case
,((...))
ou[[...]]
(...)
ou{...}
Cela signifie que tous les éléments suivants sont valides:
Et j'ai utilisé des accolades comme une ventouse ...
la source
f()while ...
f()if ...
et d'autres commandes composées.f()CODE
c'était légal. Il s'avère quef()echo hi
c'est légal dans pdksh et zsh, mais pas dans bash.for
depuis il par défaut aux positions:f()for x do : $x; done;set -x *;PS4=$* f "$@"
ou quelque chose.:
est une commande qui ne fait rien, son état de sortie est toujours réussi, il peut donc être utilisé à la place detrue
.la source
:(){:|:}
Plus de conseils
Abuser de l'opérateur ternaire,
((test)) && cmd1 || cmd2
ou[ test ] && cmd1 || cmd2
autant que possible.Exemples (les nombres de longueur excluent toujours la ligne du haut):
En utilisant uniquement des opérateurs ternaires, cela peut facilement être réduit à:
Comme nyuszika7h l'a souligné dans les commentaires, cet exemple spécifique pourrait être encore raccourci en utilisant
case
: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. :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
-eq
pour des entiers).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:
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.
Notez cette mise en garde, en particulier lors de la vérification d'une sortie nulle ou d'une variable non définie:
En toute technicité, cet exemple spécifique conviendrait mieux avec
case
...in
:Donc, la morale de ce post est la suivante:
if
/if-else
/ etc. construit.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):
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.
la source
[ $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/WefDzWbLcase
serait plus court ici. En outre, vous n'avez pas besoin d'espace avant}
, mais après{
.=
moins robuste que==
?=
est mandaté par POSIX,==
non.Au lieu de
grep -E
,grep -F
,grep -r
, utilisationegrep
,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
Pgrep
pourgrep -P
. Bien que je vois comment on pourrait facilement le confondrepgrep
, qui est utilisé pour rechercher des processus.grep -o
beaucoupL'é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:
la source
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 :la source
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.).Evitez
$( ...command... )
, il existe une alternative qui enregistre un caractère et fait la même chose:la source
$( )
nécessaire si vous avez des substitutions de commandes imbriquées; sinon vous auriez à échapper à l'intérieur``
$()
lorsque je voulais exécuter le remplacement sur ma machine plutôt que sur lascp
machine cible. Dans la plupart des cas, ils sont identiques.$()
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.echo `bc <<<"\`date +%s\`-12"`
... (Il est difficile d'afficher un commentaire contenant un prélèvement en arrière, ici!;)Utiliser
if
pour grouper des commandesPar rapport à cette astuce qui supprime le
if
tout, cela ne devrait fonctionner mieux que dans de très rares cas, comme lorsque vous avez besoin des valeurs de retour deif
.Si vous avez un groupe de commandes qui se termine par un
if
, comme celui-ci:Vous pouvez envelopper les commandes auparavant
if
dans la condition suivante:Ou si votre fonction se termine par un
if
:Vous pouvez enlever les accolades:
la source
[test] && $if_true || $else
dans ces fonctions et sauvegarder des octets.&&
et||
Utiliser l'arithmétique
(( ... ))
pour les conditionsVous pourriez remplacer:
par
(Remarque: il n'y a pas d'espace après
if
)ou même
... ou si une seule commande
En outre: Masquer le paramètre de variable dans la construction arithmétique:
ou même
... où si i> 5, alors c = 1 (pas 0;)
la source
[ ]
place de(())
.[ ]
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[ ]
.((...))
, aucune nouvelle ligne ni aucun espace ne sont nécessaires. Par exemple,for((;10>n++;)){((n%3))&&echo $n;}
essayez-le en ligne!Une syntaxe plus courte pour les boucles infinies (qui peuvent être échappées avec
break
ouexit
statement) estC'est plus court que
while true;
etwhile :;
.Si vous n'avez pas besoin
break
(avecexit
comme seul moyen de vous échapper), vous pouvez utiliser une fonction récursive à la place.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.
la source
for
Boucles d'une ligneUne expression arithmétique concaténée avec une extension de plage sera évaluée pour chaque élément de la plage. Par exemple:
évaluera
expression
10 fois.C'est souvent beaucoup plus court que la
for
boucle équivalente :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:
la source
for((;0<i--;)){ f;}
Boucle sur les arguments
Comme indiqué dans Bash « pour » boucle sans « dans le bar foo ... » partie , l'
in "$@;"
enfor x in "$@;"
est redondant.De
help for
: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.
Essayez-le en ligne!
la source
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:
Si le contenu de
bar
étaitfoobar
, cela imprimeraitfoo foobar
.Cependant, il existe une alternative si vous utilisez cette méthode, qui économise 3 octets:
la source
<bar
par lui-même, ne fonctionne pas, mais que le placer dans les backticks fonctionne?<
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.`cat`
?Utilisez
[
au lieu de[[
ettest
quand cela est possibleExemple:
Utilisez
=
au lieu de==
pour la comparaisonExemple:
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.la source
[
vs[[
peut dépendre de la quantité de devis requis: pastebin.com/UPAGWbDQ[
...]
==/bin/test
, mais[[
...]]
! =/bin/test
Et il ne faut jamais préférer[
...]
plus[[
... en]]
dehors de codegolfAlternatives à
head
line
est trois octets plus court quehead -1
, mais est obsolète .sed q
est deux octets plus court quehead -1
.sed 9q
est un octet plus court quehead -9
.la source
line
du paquet util-linux pour lire une seule ligne.tr -cd
est plus court quegrep -o
Par exemple, si vous devez compter les espaces,
grep -o <char>
(imprimer uniquement les éléments correspondants) donne 10 octets tandis quetr -cd <char>
(supprimer le complément de<char>
) en donne 9.( source )
Notez que les deux donnent des résultats légèrement différents.
grep -o
renvoie les résultats séparés par une ligne tout en lestr -cd
donnant tous sur la même ligne, ce quitr
peut ne pas être toujours favorable.la source
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*y
car aucun autre fichier n’existe avec ce format.Par exemple, si vous avez un répertoire
foo/
contenant les fichiersfoo, bar, foobar, barfoo
et que vous souhaitez référencer le fichierfoo/barfoo
, vous pouvez utiliserfoo/barf*
pour enregistrer un octet.Le
*
représente "n'importe quoi" et est équivalent à la regex.*
.la source
Utilisez un tuyau pour la
:
commande au lieu de/dev/null
. L':
intégré va manger toute son entrée.la source
echo a|tee /dev/stderr|:
n'imprimera rien.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 detee
.tee >(:) < <(seq 1 10)
cela fonctionnera maistee /dev/stderr | :
ne fonctionnera pas. Mêmea() { :;};tee /dev/stderr < <(seq 1 10)| a
n'imprime rien.:
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 ]; done
ou 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)
split
a une autre syntaxe (obsolète, mais personne ne s'en soucie) pour diviser les entrées en sections deN
lignes:split -lN
vous pouvez utilisersplit -N
par exemple, par exemplesplit -9
.la source
É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
eval
mal 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:... tandis que
read
les lignes non vierges se"${r:+set}"
développent"set"
et que les positions sont$r
ajoutées. Mais quand une ligne vide estread
,$r
est 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$IFS
obtenir le prochain paragraphe d'entrée également, car il est illégal pour un shell deread
tamponner au-delà de la ligne suivante\n
en entrée.la source
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):
Et ceux-ci sont à peu près équivalents:
(techniquement, les trois derniers exécuteront toujours le corps au moins une fois)
L'utilisation
$0
né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.
la source
Parfois, il est plus court d’utiliser l’
expr
intégré pour afficher le résultat d’une expression arithmétique simple au lieu de l’habituelecho $[ ]
. Par exemple:est un octet plus court que:
la source
Utiliser
pwd
au lieu deecho
pour générer une ligne de sortieVous 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?
pwd
est un octet plus court queecho
.la source
Les citations peuvent être omises lors de l'impression de chaînes.
Sortie dans SM-T335 LTE, Android 5.1.1:
la source
Lorsque vous affectez des éléments de tableau non continus, vous pouvez toujours ignorer les index successifs des fragments continus:
Le résultat est le même:
Selon
man bash
:la source
Imprimer le premier mot d'une chaîne
Si la chaîne est dans la variable
a
et ne contient pas de caractères d'échappement et de format (\
et%
), utilisez ceci: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:
la source
Faire 2 boucle d'intégration avec 1
for
instruction:la source
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:
Si
a
précédemment non défini, cela peut être réduit à:Si
a
était précédemment défini, alors ceci devrait être utilisé à la place: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 $a
c'est tout aussi court.la source
${a+foo}
ne prend pasa
.