Skip to content

Instantly share code, notes, and snippets.

@magnetikonline
Last active March 16, 2024 02:00
Show Gist options
  • Save magnetikonline/90d6fe30fc247ef110a1 to your computer and use it in GitHub Desktop.
Save magnetikonline/90d6fe30fc247ef110a1 to your computer and use it in GitHub Desktop.
Bash string manipulation cheatsheet.

Bash string manipulation cheatsheet

Assignment
Assign value to variable if variable is not already set, value is returned.

Combine with a : no-op to discard/ignore return value.
${variable="value"}
: ${variable="value"}
Removal
Delete shortest match of needle from front of haystack ${haystack#needle}
Delete longest match of needle from front of haystack ${haystack##needle}
Delete shortest match of needle from back of haystack ${haystack%needle}
Delete longest match of needle from back of haystack ${haystack%%needle}
Replacement
Replace first match of needle with replacement from haystack ${haystack/needle/replacement}
Replace all matches of needle with replacement from haystack ${haystack//needle/replacement}
If needle matches front of haystack replace with replacement ${haystack/#needle/replacement}
If needle matches back of haystack replace with replacement ${haystack/%needle/replacement}
Substitution
If variable not set, return value, else variable ${variable-value}
If variable not set or empty, return value, else variable ${variable:-value}
If variable set, return value, else null string ${variable+value}
If variable set and not empty, return value, else null string ${variable:+value}
Extraction
Extract length characters from variable starting at position ${variable:position:length}
Return string length of variable ${#variable}
Escaping
Single quotes inside a single quoted string echo 'Don'\''t break my escape!'
Indirection
Return value of variable name held in indirect, else value indirect="apple"
apple="fruit"
${!indirect-value}

Reference

@chb0github
Copy link

I'd like to propose a few additions

${variable##*.} -> returns everything after the last . in a string. Handy for getting file extensions

variable="/Users/cbongiorno/rds.json"
echo ${variable##*.}
json

Remove the last character from a string:

echo ${variable%%?}
/Users/cbongiorno/rds.jso

Remove the first character from a string:

echo ${variable##?}
Users/cbongiorno/rds.json

@magnetikonline
Copy link
Author

Nice one @chb0github 👍

The last/first char drops can be extended by padding out the ? count too - works nicely.

@vsoch
Copy link

vsoch commented Apr 12, 2019

Maybe you can make this into a repo so others can PR with said additions?

@BobDodds
Copy link

Regex for these string manipulation functions seems to have its own unique standard, not regular or extended like sed and [[ ]]. Where is that cheatsheet?

@magnetikonline
Copy link
Author

@BobDodds these methods/functions don't support Regular Expressions. They are effectively glob like patterns, like * and . - sadly not a full regexp implement/engine.

Examples are here: http://tldp.org/LDP/abs/html/string-manipulation.html

@BobDodds
Copy link

BobDodds commented Sep 2, 2019

@magnetikonline Thank you yes I found where it is explained that if we have shellopt extglob on, then we have a regex-like power but not a BASH_REMATCH or sed extended regex and \1 \2 etc to use in the right hand of ${string/x(y)x/z\1z/}.

Example. and extglob works like this:

      ?(pattern-list)
             Matches zero or one occurrence of the given patterns
      *(pattern-list)
             Matches zero or more occurrences of the given patterns
      +(pattern-list)
             Matches one or more occurrences of the given patterns
      @(pattern-list)
             Matches one of the given patterns
      !(pattern-list)
             Matches anything except one of the given patterns

"extglob implements a subset of ksh extended globs. ksh93 actually has a printf operator to convert between patterns and (AT&T) REs (printf '%P\n' '\[[0-9]\]' gives \[([0-9])\]) – Stéphane Chazelas Aug 28 '13 at 10:03
Hmm, it seems that *[0-9] works in other regex queries (without round brackets). – macieksk Jan 22 at 22:27"

@BobDodds
Copy link

BobDodds commented Sep 2, 2019

One more thing that we can do...

string=abcdeff ; echo ${string/#a/} ${string/%f/} ${string/%f*/}
bcdeff abcdef abcde

...so weird the "#" working as expected as synonym for '^' in extended regex, but so weirdly the "%" coming before the "f" to glob the same as '$' in sed -E extended regex

@magnetikonline
Copy link
Author

Thanks for that @BobDodds - going over that voodoo, you can excuse me if I decide to avoid that going forward 😄 - my brain can only handle/hold the workings of Regexp!

@BobDodds
Copy link

BobDodds commented Sep 3, 2019

# is for hopscotch at the ground level = beginning.

% is for mickey mouse ears on top = end.

Mickey Mouse club, cult, voodoo: ${string/%f*/} like % is a weird internal func %(){THE END;} which is mickey_mouse_ears() {garbage;}

I may need to meditate on that with my legs crossed like the optic nerve makes an X like #, behind the two eyeballs like % with a nose in the middle. That's not helping. I should just know Y.

@sean9999
Copy link

sean9999 commented Dec 9, 2019

Thank you @BobDodds, for shining more confusion on this already confusing topic

@dkhode
Copy link

dkhode commented Jan 17, 2020


String manipulation:
I have a variable called AWS_REGION, it's value get changed every time I deploy my application to different AWS regions.
for example:
AWS_REGION=eu-west-1 // here the value is not fixed.

export SHORT_REGION="${AWS_REGION/us-east-1/ue1}"
export SHORT_REGION="${AWS_REGION/us-east-2/ue2}"
export SHORT_REGION="${AWS_REGION/eu-west-1/ew1}"
export SHORT_REGION="${AWS_REGION/ap-northest-1/apne-1}"

this works, but it's kinda repetitive.

you guys have any suggestions using sed, awk etc...,

@BobDodds
Copy link

BobDodds commented Jan 19, 2020 via email

@gazzat5
Copy link

gazzat5 commented Apr 10, 2021

Can i ask that string concatenation be included?

For example:

VAR1="Hello,"
VAR2=" World"
VAR3="$VAR1$VAR2"

and for literal strings and variable:

VAR1="Hello, "
VAR2="${VAR1}World"

From the guide here:https://linuxize.com/post/bash-concatenate-strings/

Thanks!

@nightskyguy
Copy link

nightskyguy commented Jul 29, 2023

BASH Lookup ${! and $[!NAME*} can be powerful, too. You can use it to make indirect references or calculate

#!/bin/bash

VAR1="abc"
VAR2="def"
REF="VAR2"
def="1234"

$ echo "  \${!VAR*} = '${!VAR*}'"
  ${!VAR*} = 'VAR1 VAR2'
# Lists all variables that start with VAR

$ echo -e "  \${!REF} = '${!REF}'"
  ${!REF} = 'def'
# Because REF is 'VAR2' and the VALUE of VAR2 is 'def'.

$ echo "  \${!VAR1:-NOT FOUND} = '${!VAR1:-NOT FOUND} '"
  ${!VAR1:-NOT FOUND} = 'NOT FOUND '
# because VAR1 is abc which is not defined. However...

$ CALCREF="VAR${1:-2}"; echo -e "  \${CALCREF} = '${CALCREF}':\n  \${!CALCREF:-missing} = '${!CALCREF:-missing}'"
  ${CALCREF} = 'VAR2':
  ${!CALCREF:-missing} = 'def'
# Use the parameter $1 to lookup VAR${1}. If $1 has no value, use '2' so return the value of VAR2. 
# Or if VAR2 not found, return 'missing'
# NOTE that "${!VAR${INDEX}}" will not work - you get `bad substitution`. You have to create the reference variable 
# in one statement and the lookup in the next.
 
$ echo "  \${!VAR2:-NOT FOUND} = '${!VAR2:-NOT FOUND}'"
  ${!VAR2:-NOT FOUND} = '1234'
# because VAR2 = "def" and the value of def is 1234!

Lastly you can do some nesting

$ MYVALUE="${!AREF:-${DEFAULTVAL:-nothing}}";   echo "  \${MYVALUE} = '${MYVALUE}'"
  ${MYVALUE} = 'nothing'

The above means look up AREF (which does not exist. So instead it looks up DEFAULTVAL which also doesn't exist and thus returns nothing.
If AREF contained a valid reference, or DEFAULTVAL had a value those would have been return.

And here is an important point: by always using :- in every variable reference you can safely run a bash script with the '-u' option. -u is useful for finding uses of unassigned variables. In my experience that often happens due to misspellings which can be a booger to find.

@magnetikonline
Copy link
Author

BASH Lookup ${! and $[!NAME*} can be powerful, too. You can use it to make indirect references

thanks @nightskyguy - I've actually been needing this for a script recently. Nicely timed. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment