Tips for golfing in Bash



What general tips do you have for golfing in Bash? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Bash (e.g. "remove comments" is not an answer). Please post one tip per answer.


Posted 2013-11-15T09:14:11.900

Reputation: 17 865



Undocumented, but works in every version I've run into for legacy sh backwards compatibility:

for loops allow you to use { } instead of do done. E.g. replace:

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


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

Digital Trauma

Posted 2013-11-15T09:14:11.900

Reputation: 64 644

what shell is the is sh and what shell allows this for syntax? it is expressly allowed in zsh. – mikeserv – 2015-12-23T19:55:40.463

@mikeserv Bash. I remember reading somewhere that this syntax was allowed in some old sh and that Bash also allows it because of that, though sadly I don't have a citation. – Digital Trauma – 2015-12-23T19:57:47.840

ahh... csh, probably - that's how they worked in that shell. – mikeserv – 2015-12-23T20:02:47.397

by the way, in ksh93 the above thing could be :;{1..10}, and in bash: printf %s\\n {1..10} – mikeserv – 2015-12-23T20:08:17.793

1for((;i++<10)){ echo $i;} is shorter than for i in {1..10};{ echo $i;} – Evan Krall – 2017-01-19T04:58:08.560

for((;${#r}>i++;)){ echo ${r:i-1:1};} iterates over each character of a string $r – roblogic – 2019-03-23T12:27:59.727


For arithmetic expansion use $[…] instead of $((…)):

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

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

In arithmetic expansions don't use $:

bash-4.1$ a=1 b=2 c=3

bash-4.1$ echo $[$a+$b*$c]

bash-4.1$ echo $[a+b*c]

Arithmetic expansion is performed on indexed array subscripts, so don't use $ neither there:

bash-4.1$ a=(1 2 3) b=2 c=3

bash-4.1$ echo ${a[$c-$b]}

bash-4.1$ echo ${a[c-b]}

In arithmetic expansions don't use ${…}:

bash-4.1$ a=(1 2 3)

bash-4.1$ echo $[${a[0]}+${a[1]}*${a[2]}]

bash-4.1$ echo $[a[0]+a[1]*a[2]]


Posted 2013-11-15T09:14:11.900

Reputation: 17 865

Replacing while((i--)), which works, with while[i--] or while $[i--] did not work for me. GNU bash, version 4.3.46(1) – Glenn Randers-Pehrson – 2016-11-06T22:54:48.693

1Correct, @GlennRanders-Pehrson. That is not supposed to work. – manatwork – 2016-11-07T09:08:47.167

y=\bc<<<"($x*2.2)%10/1"`` ... example of using bc for non-integer calculations... note the /1 at the end truncates the resulting decimal to an int. – roblogic – 2019-03-23T12:39:41.493

s=$((i%2>0?s+x:s+y)) ... example of using ternary operator in bash arithmetic. It's shorter than if..then..else or [ ] && || – roblogic – 2019-03-23T12:43:24.913

$[ ] is obsolete but still works in some cases. Also, you can increment a counter with just ((i++)) – roblogic – 2019-03-23T12:47:17.267

is $[] documented anywhere? i don't see it in man bash but it works for me. is it for backwards compatibility? – Jonah – 2019-04-03T15:52:22.553

@Jonah, bash 4.4's man page definitely contains it: At least on Ubuntu.

– manatwork – 2019-04-03T16:17:15.817

1@manatwork Thanks. They must have removed it. I'm on GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0) and it's not in mine. – Jonah – 2019-04-03T16:40:52.467

Thank you for the information @Jonah. Now I start to appreciate my Ubuntu's inability to upgrade… – manatwork – 2019-04-04T08:31:10.830


The normal, lengthy and boring way to define a function is

f(){ CODE;}

As this guy found out, you absolutely need the space before CODE and the semicolon after it.

This is a little trick I've learned from @DigitalTrauma:


That is two characters shorter and it works just as well, provided that you don't need to carry over any changes in variables' values after the function returns (the parentheses run the body in a subshell).

As @jimmy23013 points out in the comments, even the parentheses may be unnecessary.

The Bash Reference Manual shows that functions can be defined as follows:

name () compound-command [ redirections ]


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

A compound command can be:

  • a Looping Construct: until, while or for
  • a Conditional Construct: if, case, ((...)) or [[...]]
  • Grouped Commands: (...) or {...}

That means all of the following are valid:

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

And I've been using curly brackets like a sucker...


Posted 2013-11-15T09:14:11.900

Reputation: 196 637

1its especiallly useful w/ for since it defaults to positionals:f()for x do : $x; done;set -x *;PS4=$* f "$@" or something. – mikeserv – 2015-12-23T20:21:26.520

2Note that you can also use f()while ... f()if ... and other compound commands. – jimmy23013 – 2014-11-07T06:13:35.833

This one surprised me because I thought f()CODE was legal. It turns out that f()echo hi is legal in pdksh and zsh, but not in bash. – kernigh – 2014-11-07T15:50:27.450


: is a command that does nothing, its exit status always succeeds, so it can be used instead of true.


Posted 2013-11-15T09:14:11.900


Using a subshell and piping it would use about the same number of bytes, but piping it would be more practical. – ckjbgames – 2017-01-24T16:05:19.443

5Except when you do :(){:|:} – enedil – 2017-07-17T12:50:49.820


More tips

  1. Abuse the ternary operator, ((test)) && cmd1 || cmd2 or [ test ] && cmd1 || cmd2, as much as possible.

    Examples (length counts always exclude the top line):

    if [ $t == "hi" ];then
    elif [ $t == "bye" ];then
    if [ $t == "sup" ];then

    By using ternary operators only, this can easily be shortened to:

    [ $t == "hi" ]&&{
    }||[ $t == "bye" ]&&{
    [ $t == "sup" ]&&cmd6

    As nyuszika7h pointed out in the comments, this specific example could be shortened even further using case:

    case $t in "hi")cmd1;cmd2;;"bye")cmd3;cmd4;;*)cmd5;[ $t == "sup" ]&&cmd6;esac
  2. Also, prefer parentheses to braces as much as possible. Since parentheses are a metacharacter, and not a word, they never require spaces in any context. This also means run as many commands in a subshell as possible, because curly braces (i.e. { and }) are reserved words, not meta-characters, and thus have to have whitespace on both sides to parse correctly, but meta-characters don't. I assume that you know by now that subshells don't affect the parent environment, so assuming that all the example commands can safely be run in a subshell (which isn't typical in any case), you can shorten the above code to this:

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

    Also, if you can't, using parentheses can still minify it some. One thing to keep in mind is that it only works for integers, which renders it useless for the purposes of this example (but it is much better than using -eq for integers).

  3. One more thing, avoid quotes where possible. Using that above advice, you can further minify it. Example:

    [ $t == hi ]&&(cmd1;cmd2)||[ $t == bye ]&&(cmd3;cmd4)||(cmd5;[ $t == sup ]&&cmd6)
  4. In testing conditions, prefer single brackets to double brackets as much as possible with a few exceptions. It drops two characters for free, but it isn't as robust in some cases (it's a Bash extension - see below for an example). Also, use the single equals argument rather than the double. It is a free character to drop.

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

    Note this caveat, especially in checking for null output or an undefined variable:

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

    In all technicality, this specific example would be best with case ... in:

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

So, the moral of this post is this:

  1. Abuse the boolean operators as much as possible, and always use them instead of if/if-else/etc. constructs.
  2. Use parentheses as much as possible and run as many segments as possible in subshells because parentheses are meta-characters and not reserved words.
  3. Avoid quotes as much as physically possible.
  4. Check out case ... in, since it may save quite a few bytes, particularly in string matching.

P.S.: Here's a list of meta-characters recognized in Bash regardless of context (and can separate words):

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

EDIT: As manatwork pointed out, the double parenthesis test only works for integers. Also, indirectly, I found that you need to have whitespace surrounding the == operator. Corrected my post above.

I also was too lazy to recalculate the length of each segment, so I simply removed them. It should be easy enough to find a string length calculator online if necessary.

Isiah Meadows

Posted 2013-11-15T09:14:11.900

Reputation: 1 546

[[ $f ]] is equivalent to [[ "$f" ]]. A [[ context doesn't do word-splitting or pathname expansion, and other expansions don't happen on the result of variable/parameter expansion. So [[ $f ]] is a good bit shorter than any of the safe alternatives, like [ -n "$f" ]. – Peter Cordes – 2015-09-24T06:55:49.810

1The question does ask for one tip per answer... – Toby Speight – 2016-08-22T11:21:59.997


Sorry to say, but you have some serious errors there. [ $t=="hi" ] will always evaluate to 0, as it is parsed as [ -n "STRING" ]. (($t=="hi")) will always evaluate to 0 as long as $t has non-numerical value, as strings are forced into integers in arithmetic evaluations. Some test cases:

– manatwork – 2014-02-15T13:30:52.403

@manatwork Thanks for the catch. I'll update accordingly. – Isiah Meadows – 2014-02-17T03:55:02.620

Using a case would be shorter here. Also, you don't need a space before }, but you do after {. – nyuszika7h – 2014-06-23T15:33:15.203

2Why would = be less robust than ==? = is mandated by POSIX, == isn't. – Dennis – 2014-09-05T04:28:36.727

The latter is a Bash extension, but the nonstandard [[ condition ]] is more powerful than [ condition ] but more verbose. I should fix the misinformation, though. – Isiah Meadows – 2014-09-05T05:26:28.303


Instead of grep -E, grep -F, grep -r, use egrep, fgrep, rgrep, saving two chars. The shorter ones are deprecated but work fine.

(You did ask for one tip per answer!)


Posted 2013-11-15T09:14:11.900


Wouldn't it save 3 including the space? – ckjbgames – 2017-01-24T16:04:50.500

1Too bad there's no Pgrep for grep -P. Although I see how it could be easily confused with pgrep, which is used to look up processes. – nyuszika7h – 2014-04-21T19:21:50.193

1@nyuszika7h I personally use grep -o a lot – None – 2014-04-22T06:55:40.780


Element 0 of an array may be accessed with the variable name only, a five byte saving over explicitly specifying an index of 0:

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

Digital Trauma

Posted 2013-11-15T09:14:11.900

Reputation: 64 644


If you need to pass the content of a variable to STDIN of the next process in a pipeline, it is common to echo the variable into a pipeline. But you can achieve the same thing with a <<< bash here string:

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

Digital Trauma

Posted 2013-11-15T09:14:11.900

Reputation: 64 644

2Since we're golfing, s=code\ golf, echo $s| and <<<$s (keeping in mind that the latter two work only because there are no repeated spaces, etc.). – Dennis – 2014-11-07T03:29:46.960


One-line for loops

An arithmetic expression concatenated with a range expansion will be evaluated for each item in the range. For example the following:

: $[expression]{0..9}

will evaluate expression 10 times.

This is often significantly shorter than the equivalent for loop:

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

If you don't mind command not found errors, you can remove the inital :. For iterations larger than 10, you can also use character ranges, for example {A..z} will iterate 58 times.

As a practical example, the following both produce the first 50 triangular numbers, each on their own line:

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


Posted 2013-11-15T09:14:11.900

Reputation: 30 891

you can also iterate backwards: for((;0<i--;)){ f;} – roblogic – 2019-03-23T13:21:17.663


Avoid $( ...command... ), there is an alternative which saves one char and does the same thing:

` ...command... `


Posted 2013-11-15T09:14:11.900



@undergroundmonorail: you never need backticks. Anything they can do, $() can do if you quote things properly. (unless you need your command to survive something that munges $ but not backticks). There are some subtle differences in quoting things inside them. explains some differences. Unless you're golfing, never use backticks.

– Peter Cordes – 2015-09-24T07:09:47.477

@PeterCordes I'm sure there was a way but everything I tried at the time didn't work. Even if backticks weren't the best solution, I was glad I knew about them because it was the only solution I had. ¯\(ツ) – undergroundmonorail – 2015-09-24T07:43:59.977

@DigitalTrauma Sample of nesting: echo \bc <<<"\`date +%s\`-12"`` ... (It's hard to post sample containing backtick in comment, there! ;) – F. Hauri – 2017-01-24T17:22:46.283

9Sometimes $( ) is needed if you have nested command substitutions; otherwise you'd have to escape the inner \`` – Digital Trauma – 2014-04-13T04:18:10.257

1These technically do different things, I've had to use the backticks instead of $() when I wanted to run the substitution on my machine instead of the scp target machine, for example. In most cases they're identical. – undergroundmonorail – 2014-04-21T19:31:35.647


A shorter syntax for infinite loops (which can be escaped with break or exit statements) is

for((;;)){ code;}

This is shorter than while true; and while :;.

If you don't need break (with exit as the only way to escape), you can use a recursive function instead.

f(){ code;f;};f

If you do need break, but you don't need exit and you don't need to carry over any variable modification outside the loop, you can use a recursive function with parentheses around the body, which run the function body in a subshell.



Posted 2013-11-15T09:14:11.900



Use if to group commands

Compared to this tip which removes the if at all, this should only work better in some very rare cases, such as when you need the return values from the if.

If you have a command group which ends with a if, like these:

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

You can wrap the commands before if in the condition instead:

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

Or if your function ends with a if:

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

You can remove the braces:

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


Posted 2013-11-15T09:14:11.900

Reputation: 34 042

1You could use the ternary operator [test] && $if_true || $else in these functions and save some bytes. – ckjbgames – 2017-01-24T15:09:42.893

Also, you don't need spaces around && and || – roblogic – 2019-03-23T08:18:58.927


Use arithmetic (( ... )) for conditions

You could replace:

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


    echo Do something with i greater than 5

(Note: There is no space after if)

or even

    echo Do something with i greater than 5

... or if only one command

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

Further: Hide variable setting in arithmetic construct:

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

or same

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

... where if i > 5, then c = 1 (not 0;)

F. Hauri

Posted 2013-11-15T09:14:11.900

Reputation: 2 654

You could save 2 bytes by using [ ] instead of (()) . – ckjbgames – 2017-01-24T15:06:07.477

@ckjbgames Are you sure of that!? Wich [tag:bash] version are you using? – F. Hauri – 2017-01-24T15:33:37.630

@FHauri I guess it would be about the same in terms of bytes. – ckjbgames – 2017-01-24T15:56:50.603

@ckjbgames With [ ] you need the dollar sign for variable. I don't see how you could do same with same or smaller length by using [ ]. – F. Hauri – 2017-01-24T16:55:11.360

Bonus Tip: if the first line of a for loop begins with ((...)), no newline or space is necessary. E.g. for((;10>n++;)){((n%3))&&echo $n;} Try it online!

– primo – 2018-12-08T09:41:39.910

@primo I already used this at ip2int IPv4 integer challenge. Read my last purpose, with 68 chars, using mapfile!

– F. Hauri – 2018-12-09T13:12:43.087

@F.Hauri I was surprised that it works ({ followed by anything else seems to require whitespace), and hadn't seen this mentioned anywhere, so I thought it would be a good addition to your tip. – primo – 2018-12-10T15:06:43.743


Loop over arguments

As noted in Bash “for” loop without a “in foo bar…” part, the in "$@;" in for x in "$@;" is redundant.

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

For example, if we want to square all numbers given positional arguments to a Bash script or a function, we can do this.

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

Try it online!


Posted 2013-11-15T09:14:11.900

Reputation: 196 637


Alternative to cat

Say you are trying to read a file and use it in something else. What you might do is:

echo foo `cat bar`

If the contents of bar was foobar, this would print foo foobar.

However, there is an alternative if you are using this method, which saves 3 bytes:

echo foo `<bar`


Posted 2013-11-15T09:14:11.900

Reputation: 15 025

1Is there a reason why <bar by itself does not work but placing it in backticks does? – user41805 – 2018-10-19T18:05:48.797

@Cowsquack Yes. The < puts a file to a command, but in this case it puts it into standard output due to a quirk. The backticks evaluate this together. – Okx – 2018-10-19T19:44:24.537

Is there a shorter way to read from standard input other than \cat``? – Joel – 2019-10-03T21:21:26.277


Alternatives to head

line is three bytes shorter than head -1, but is being deprecated.

sed q is two bytes shorter than head -1.

sed 9q is one byte shorter than head -9.


Posted 2013-11-15T09:14:11.900

Reputation: 55 648


Although doomed, we can still use for a while line from util-linux package to read a single line.

– manatwork – 2017-01-28T12:48:15.060


tr -cd is shorter than grep -o

For example, if you need to count spaces, grep -o <char> (print only the matched) gives 10 bytes while tr -cd <char> (delete complement of <char>) gives 9.

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


Note that they both give slightly different outputs. grep -o returns line separated results while tr -cd gives them all on the same line, so tr might not always be favourable.


Posted 2013-11-15T09:14:11.900

Reputation: 16 320


Shorten file names

In a recent challenge I was trying to read the file /sys/class/power_supply/BAT1/capacity, however this can be shortened to /*/*/*/*/capac*y as no other file exists with that format.

For example, if you had a directory foo/ containing the files foo, bar, foobar, barfoo and you wanted to reference the file foo/barfoo, you can use foo/barf* to save a byte.

The * represents "anything", and is equivalent to the regex .*.


Posted 2013-11-15T09:14:11.900

Reputation: 15 025


Use [ instead of [[ and test when possible


[ -n $x ]

Use = instead of == for comparison


[ $x = y ]

Note that you must have spaces around the equals sign or else it won't work. Same applies to == based on my tests.


Posted 2013-11-15T09:14:11.900

Reputation: 1 624

1General rule: [...] == /bin/test, but [[...]] != /bin/test and one should never prefer [...] over [[...]] outside of codegolf – cat – 2015-12-26T22:23:46.510


The [ vs. [[ may depend on the amount of required quotes:

– manatwork – 2014-04-22T05:56:29.997

@manatwork That's a good point. – nyuszika7h – 2014-04-22T19:02:27.553


Use tail recursion to make loops shorter:

These are equivalent in behavior (though probably not in memory/PID usage):

while :;do body; done
body;exec $0

And these are roughly equivalent:

while condition; do body; done
body;condition&&exec $0

(technically the last three will always execute the body at least once)

Using $0 requires your script to be in a file, not pasted into the bash prompt.

Eventually your stack might overflow, but you save some bytes.

Evan Krall

Posted 2013-11-15T09:14:11.900

Reputation: 251


Sometimes it is shorter to use the expr builtin for displaying the result of a simple arithmetic expression instead of the usual echo $[ ]. For example:

expr $1 % 2

is one byte shorter than:

echo $[$1%2]

Digital Trauma

Posted 2013-11-15T09:14:11.900

Reputation: 64 644


Use a pipe to the : command instead of /dev/null. The : built-in will eat all its input.


Posted 2013-11-15T09:14:11.900


Yes, it's a SIGPIPE problem: tee >(:) < <(seq 1 10) will work, but tee /dev/stderr | : won't. Even a() { :;};tee /dev/stderr < <(seq 1 10)| a don't print anything. – F. Hauri – 2017-01-24T17:20:01.127

@user16402 - you should have a fccing name to my purview... anyway, the : intrinsic eats not at all... if you supposit input to colon you might flood a pipe to in out error... but you can float a redirect by a colon, or drop a process with it... :| while i>&$(($??!$?:${#?})) command shit; do [ -s testitsoutput ]; done or however that pseudo suggestion applies... also, are you aware youre nearly so ghosty as me? ... avoid at all costs the < <(psycho shit i can alias to crazy math eat your world; okay? anyway, ksh93 has a separate but equal composite char placement) – mikeserv – 2019-09-10T02:04:31.330

2No, it will crash the program with SIGPIPE in most cases. echo a|tee /dev/stderr|: will not print anything. – jimmy23013 – 2014-11-07T06:27:44.547

There is a race: echo a|tee /dev/stderr|: did print a on my computer, but elsewhere SIGPIPE might kill tee first. It might depend on version of tee. – kernigh – 2014-11-07T15:56:20.020


split has another (deprecated, but nobody cares) syntax for splitting input into sections of N lines each: instead of split -lN you can use split -N e.g. split -9.


Posted 2013-11-15T09:14:11.900



Expand away the tests

Essentially, the shell is a kind of macro language, or at least a hybrid or some kind. Every command-line can be basically broken into two parts: the parsing/input part and the expansion/output part.

The first part is what most people focus on because it's the most simple: you see what you get. The second part is what many avoid ever even trying to understand very well and is why people say things like eval is evil and always quote your expansions - people want the result of the first part to equal the first. That's ok - but it leads to unnecessarily long code branches and tons of extraneous testing.

Expansions are self-testing. The ${param[[:]#%+-=?]word} forms are more than enough to validate the contents of a parameter, are nestable, and are all based around evaluating for NUL - which is what most people expect of tests anyway. + can be especially handy in loops:

r()while IFS= read -r r&&"${r:+set}" -- "$@" "${r:=$*}";do :;done 2>&-

printf %s\\n some lines\ of input here '' some more|{ r;echo "$r"; }

somexlines ofxinputxhere

...while read pulls in not blank lines "${r:+set}" expands to "set" and the positionals get $r appended. But when a blank line is read, $r is empty and "${r:+set}" expands to "" - which is an invalid command. But because the command-line is expanded before the "" null command is searched, "${r:=$*}" takes the values of all of the positionals concatenated on the first byte in $IFS as well. r() could be called again in |{ compound command ;} w/ a different value for $IFS to get the next input paragraph as well, since it is illegal for a shell's read to buffer beyond the next \newline in input.


Posted 2013-11-15T09:14:11.900

Reputation: 181


Quotes can be omitted when printing strings.

echo "example"
echo example

Output in SM-T335 LTE, Android 5.1.1:

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


Posted 2013-11-15T09:14:11.900



When assigning noncontinuous array items, you can still skip the successive indices of continuous chunks:

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)

The result is the same:

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

According to man bash:

Arrays are assigned to using compound assignments of the form name=(value1 ... valuen), where each value is of the form [subscript]=string. Indexed array assignments do not require anything but string. When assigning to indexed arrays, if the optional brackets and subscript are supplied, that index is assigned to; otherwise the index of the element assigned is the last index assigned to by the statement plus one.


Posted 2013-11-15T09:14:11.900

Reputation: 17 865

Helpful to add: uninitialised elements will expand to 0 in arithmetic expansions and "" in other expansions. – Digital Trauma – 2017-06-29T15:47:24.180


Print the first word in a string

If the string is in the variable a and doesn't contain escape and format characters (\ and %), use this:

printf $a

But it would be longer than the following code if it is needed to save the result into a variable instead of printing:



Posted 2013-11-15T09:14:11.900

Reputation: 34 042


Use pwd instead of echo to generate a line of output

Need to put a line on stdout but don't care about the contents, and want to restrict your answer to shell builtins? pwd is a byte shorter than echo.

Glenn Randers-Pehrson

Posted 2013-11-15T09:14:11.900

Reputation: 1 877


Doing 2 embed loop with 1 for instruction:

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

Posted 2013-11-15T09:14:11.900

Reputation: 2 654


In a case statement, it is valid to omit the last ';;' - which saves 2 bytes:


case $I in

Bastian Bittorf

Posted 2013-11-15T09:14:11.900

Reputation: 101


Assign and Print quoted strings

If you want to assign a quoted string to a variable, and then print the value of that variable, then the usual way to do that would be:

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

If a was previously unset, this may be shortened to:

echo ${a=Programming Puzzles & Code Golf}

If a was previously set, then this should be used instead:

echo ${a+Programming Puzzles & Code Golf}

Note this is only useful if the string requires quotes (e.g. contains whitespace). Without quotes, a=123;echo $a is just as short.

Digital Trauma

Posted 2013-11-15T09:14:11.900

Reputation: 64 644

${a+foo} doesn't set a. – GammaFunction – 2019-10-20T06:28:22.177


This work under simple shell

cd `mktemp -d` &&>'c'a't'
ln -s /proc/loadavg /proc/uptime .

Then now

echo $procs
0.30 0.08 0.03 1/612 31671 322787.60 1259967.99

Nota: Of course, >cat could be written >$(echo -e \\0143\\0141t)

Care, from there, you could encouter some issues due to Locales!

Goto last demo using LC_ALL=C

Same way:

mv cat grep
mv loadavg Mem
ln -s /proc/meminfo Zdatas
rm uptime

echo $mems 
MemTotal: 16386788 kB MemFree: 9816320 kB MemAvailable: 12144892 kB

or worst...

mv grep sed
mv Mem s+\\\(Mem\\\|Swap\\\).\*\:++p\;d


echo $memsw 
16386788 kB 9834080 kB 12163596 kB 0 kB 20971516 kB 20971516 kB

So simple is this!

declare -p procs mems memsw
declare -- procs="0.14 0.12 0.05 1/612 32078
324221.56 1265616.26"
declare -- mems="MemTotal:       16386788 kB
MemFree:         9832212 kB
MemAvailable:   12161688 kB"
declare -- memsw="       16386788 kB
         9834080 kB
   12163596 kB
            0 kB
      20971516 kB
       20971516 kB"

Last test using LANG=C

(or not)

cd `mktemp -d` &&>'c'a't'
ln -s /proc/meminfo zdatas
cp c* y+-+-+\;s+\\\(Mem\\\|Swap\\\).\*\:\ \*++p\;d
rename 'y/act/esd/' ???

may render something like

378908 kB
31940 kB
217924 kB
0 kB
102396 kB
102396 kB


cd `mktemp -d`
ln -s +++\;s+\\\(Mem\\\|Swap\\\).\*\:\ \*++p\;d /proc/me*o ed .
rename 's/^/chr(113+(20>length?1.3*length:8))/e' *

F. Hauri

Posted 2013-11-15T09:14:11.900

Reputation: 2 654

or printf -vv %b \\014{3,1} t&&>$v under [tag:bash] – F. Hauri – 2019-02-02T14:09:56.847


Changing behaviour of += by setting integer flag

Try this many times:

unset i
for a in {1..10} ;do
    ((RANDOM%9)) || declare -i i
echo $i

or in one line

unset i;i=1;for a in {1..10};do i+=1;((RANDOM%9))||declare -i i;done;echo $i

This could answer something between 20 to 11111111111...

F. Hauri

Posted 2013-11-15T09:14:11.900

Reputation: 2 654


It is almost always better to receive input as arguments to a script or function instead of on stdin. Examples:

Input: a string:

# == stdin ==
read a;echo $a
echo `<&0`
# == arguments ==
echo "$1"       # if $1 can have globbing characters/newlines/tabs
echo $1         # if $1 does not have any problematic characters

Input: a list of strings:

# == stdin ==
while read s; do echo $s; done          # one-time use, split on newlines
read -a a;for s in ${a[@]};{echo $s;}   # read one line from stdin; splits on spaces
mapfile a;for s in "${a[@]}";{echo $s;} # read to EOF, split on newlines
# == arguments ==
for s;{ echo $s;}

The exception: a list of strings and other arguments:

read i;while read s; do echo ${s[$i]}; done   # both on stdin
i=$1;shift;for s;{ echo ${s[$i]};}            # both as arguments
read i;for s;{ echo ${s[$i]};}                # list as arguments, other as stdin

Additionally, read will mangle backslashes without -r, and will stop on newlines without -d '', making it even more expensive in worst case scenarios.


Posted 2013-11-15T09:14:11.900

Reputation: 2 838


To print a range of characters (shorter than echo {a..z}|tr -d ' '):

printf "%s" {a..z}

Print range in reverse order (shorter than printf "%s" {a..z}|rev):

printf "%s" {z..a}


Posted 2013-11-15T09:14:11.900

Reputation: 101