Parameter Substitution (Bash): verschil tussen versies
(41 tussenliggende versies door dezelfde gebruiker niet weergegeven) | |||
Regel 1: | Regel 1: | ||
− | ''Parameter substitution'', ''parameter expansion'' | + | ''Parameter substitution'', ''parameter expansion'' or ''shell parameter expansion'' means that in a Bash expression a ''parameter'' or ''variable'' is replaced by the contents of that variable, possibly in combination with an operation. |
− | Parameter expansion | + | Parameter expansion is indicated by <code>$</code>, often in combination with curly braces: <code>${}</code>, or even more complete: <code>${parameter}</code>. |
+ | |||
+ | Judging by [https://tldp.org/LDP/abs/html/string-manipulation.html], all cases are covered in this article. | ||
+ | |||
+ | → This list is not complete. Check https://stackoverflow.com/questions/5406858/difference-between-unset-and-empty-variables-in-bash#5406887 for some more amazing things. | ||
+ | |||
+ | For trim, rather see [[Trim (Bash)]]. | ||
== ${} == | == ${} == | ||
− | + | Something as simple as displaying the value of a variable, is already an example of parameter substitution: | |
<pre> | <pre> | ||
Regel 14: | Regel 20: | ||
</pre> | </pre> | ||
− | + | Adding braces might make the concept of ''expansion'' even clearer. In particular, that <code>$</code> is the operator. It's not part of the variable's name: The part inside the curly braces doesn't get a $ sign: | |
<pre> | <pre> | ||
Regel 23: | Regel 29: | ||
</pre> | </pre> | ||
− | == | + | == No nesting == |
You can't nest <code>${}</code> expressions: The syntax expander doesn't know how to handle them [https://stackoverflow.com/questions/917260/can-var-parameter-expansion-expressions-be-nested-in-bash]. Also see below at ''Find & replace''. | You can't nest <code>${}</code> expressions: The syntax expander doesn't know how to handle them [https://stackoverflow.com/questions/917260/can-var-parameter-expansion-expressions-be-nested-in-bash]. Also see below at ''Find & replace''. | ||
Regel 35: | Regel 41: | ||
</pre> | </pre> | ||
− | == ${parameter:-word} == | + | == Null substitution - ${parameter:-word} == |
− | + | Use this syntax to replace <code>parameter</code> with ''literal'' <code>word</code> if the former is empty. Something like <code>ifnull(a, b)</code> in SQL: | |
<pre> | <pre> | ||
− | # | + | # The parameter has a value: |
$ i1="Hello, world!" | $ i1="Hello, world!" | ||
Regel 76: | Regel 82: | ||
</pre> | </pre> | ||
− | == ${parameter/pattern/string} | + | == Substring replacement - ${parameter/pattern/string} == |
− | + | You can use parameter expansion for ''find-&-replace'': | |
<pre> | <pre> | ||
Regel 87: | Regel 93: | ||
</pre> | </pre> | ||
− | + | Only the first ''occurence'' of ''pattern'' will be replaced: | |
− | |||
− | |||
<pre> | <pre> | ||
Regel 98: | Regel 102: | ||
</pre> | </pre> | ||
− | == | + | == Substring replacement - ${parameter//pattern/string} == |
− | + | Use a double <code>/</code> after ''parameter'', to replace ''all'' occurences of ''pattern''. | |
<pre> | <pre> | ||
Regel 131: | Regel 135: | ||
* https://stackoverflow.com/questions/8960677/string-replacement-in-bash-bad-substitution-error | * https://stackoverflow.com/questions/8960677/string-replacement-in-bash-bad-substitution-error | ||
− | == | + | == String pattern filtering - ${parameter/pattern} == |
− | + | * This is a variant of two chapters before. It uses a pattern which is a bit similar (but not really!) to regex | |
+ | * As ''string'' is empty, the pattern will be deleted from the input: | ||
+ | |||
+ | <pre> | ||
+ | $ i="hoi123hallo" | ||
+ | $ echo ${i/[()a-zA-Z]} | ||
+ | |||
+ | oi123hallo | ||
+ | </pre> | ||
+ | |||
+ | == String pattern filtering - ${parameter//pattern} == | ||
+ | |||
+ | Use a double <code>/</code> between ''parameter'' and ''pattern'', to filter out all occurences of the pattern - Now you can really filter out stuff: | ||
+ | |||
+ | <pre> | ||
+ | i="hoi123hallo"; echo ${i/[()a-zA-Z]} # 123 | ||
+ | i="howarexxdxxy 12 (diameter)"; echo ">>"${i//[()a-zA-Z]}"<<" # >> 12 << - Spaces get preserved | ||
+ | i="howarexxdxxy 12 (diameter)"; echo ">>"${i//[()a-zA-Z ]}"<<" # >>12<< - Spaces get also filtered out | ||
+ | </pre> | ||
− | == Convert | + | == Convert case == |
[https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html]: | [https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html]: | ||
Regel 149: | Regel 171: | ||
$ echo ${name^^} | $ echo ${name^^} | ||
VIVEK | VIVEK | ||
+ | </pre> | ||
+ | |||
+ | == String length - ${#string} == | ||
+ | |||
+ | This syntax is not only for arrays, but also for scalars: | ||
+ | |||
+ | <pre> | ||
+ | i="hoihallo" | ||
+ | echo ${#i} | ||
+ | |||
+ | 8 | ||
+ | </pre> | ||
+ | |||
+ | == Substring index extraction - ${string:index:length} == | ||
+ | |||
+ | <pre> | ||
+ | i="1234567890" | ||
+ | echo ${i:3} # Select from index 3 (base 0 → 4th character) | ||
+ | echo ${i:3:5} # Select 5 characters | ||
+ | |||
+ | 4567890 | ||
+ | 45678 | ||
+ | </pre> | ||
+ | |||
+ | == Trim leading characters - ${string#substring} == | ||
+ | |||
+ | <code>${string#substring}</code> deletes the shortest match of <code>substring</code> (as a pattern expression) from the front of <code>$string</code> | ||
+ | |||
+ | === Examples === | ||
+ | |||
+ | <pre> | ||
+ | i="1234567890" | ||
+ | |||
+ | echo $i | ||
+ | echo ${i#12} # First 2 characters deleted | ||
+ | echo ${i#34} # Nothing deleted: String doesn't start with "34" | ||
+ | echo ${i#*34} # First 4 characters deleted | ||
+ | echo ${i#34*} # Nothing deleted: String doesn't start with "34" | ||
+ | </pre> | ||
+ | |||
+ | The wildcard can also be used ''inside'' the substring: | ||
+ | |||
+ | <pre> | ||
+ | i="12345-12345" | ||
+ | |||
+ | echo $i # 12345-12345 | ||
+ | echo ${i#14} # 12345-12345 | ||
+ | echo ${i#1*4} # 5-12345 | ||
+ | echo ${i#25} # 12345-12345 | ||
+ | echo ${i#*25} # 12345-12345 | ||
+ | echo ${i#*2*5} # -12345 | ||
+ | |||
+ | echo ${i#*2*} # 2345-12345 - Trailing "*" has no meaning | ||
+ | </pre> | ||
+ | |||
+ | This pattern matching syntax is called ''Pattern Matching'' [https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html], [https://tldp.org/LDP/abs/html/string-manipulation.html]. It's not the same as ''regular expressions''. At times, I call it ''bracket expressions'', which I still find quite a good name | ||
+ | |||
+ | <pre> | ||
+ | # Bracket expressions | ||
+ | ######################################## | ||
+ | # | ||
+ | i="12345-12345" | ||
+ | |||
+ | echo $i # 12345-12345 | ||
+ | echo ${i#[14]} # 2345-12345 - "1" matches and is deleted | ||
+ | echo ${i#[14]2} # 345-12345 - "12" matches | ||
+ | echo ${i#[1234]} # 2345-12345 - Only 1 character is picked | ||
+ | echo ${i#[234]} # 12345-12345 - Nothing happens | ||
+ | echo ${i#[1234]*} # 2345-12345 | ||
+ | echo ${i#*[1234]} # 2345-12345 - Smallest interval is chosen | ||
+ | echo ${i#*[4321]} # 2345-12345 - Smallest interval is chosen | ||
+ | echo ${i#1*[34]} # 45-12345 - Smallest interval is chosen | ||
+ | echo ${i#1*[43]} # 45-12345 - Smallest interval is chosen | ||
+ | echo ${i#*[34]} # 45-12345 | ||
+ | echo ${i#*[43]} # 45-12345 | ||
+ | echo ${i#*[34]5} # -12345 - Surprise. Maybe *45 is smallest interval? | ||
+ | echo ${i#*[43]5} # -12345 - Surprise. *45 is selected as interval | ||
+ | echo ${i#*[34]2} # Nothing deleted | ||
+ | echo ${i#*[43]2} # Nothing deleted | ||
+ | </pre> | ||
+ | |||
+ | Here's some more regex stuff. It's a bit of a mixed bag: | ||
+ | |||
+ | <pre> | ||
+ | # Some more regex stuff | ||
+ | ######################################## | ||
+ | # | ||
+ | i="12345-12345" | ||
+ | |||
+ | echo $i # 12345-12345 | ||
+ | echo ${i#[0-9]} # 2345-12345 | ||
+ | echo ${i#[0-9]{2}} # 12345-12345} | ||
+ | echo ${i#[0-9]+} # 2345-12345 | ||
+ | echo ${i#.....} # 12345-12345 | ||
+ | echo ${i#[0-9][0-9]} # 345-12345 | ||
+ | </pre> | ||
+ | |||
+ | == Trim leading characters - ${string##substring} == | ||
+ | |||
+ | As before, except that the pattern matching is done as inclusive (or ''greedy'') as possible. | ||
+ | |||
+ | * This command seems intended to remove substrings that consists of different characters | ||
+ | * You can use it to remove multiple instances of the same character, but you need a (slight) trick for this. | ||
+ | |||
+ | === Intro === | ||
+ | |||
+ | <pre> | ||
+ | i="12345-12345" | ||
+ | |||
+ | echo $i # 12345-12345 | ||
+ | echo ${i##14} # 12345-12345 | ||
+ | echo ${i##1*4} # 5 - Only the last character left | ||
+ | |||
+ | echo ${i##25} # 12345-12345 | ||
+ | echo ${i##*25} # 12345-12345 | ||
+ | echo ${i##*2*5} # Evertything removed | ||
+ | |||
+ | echo ${i##*5} # Everything removed | ||
+ | echo ${i##*4} # 5 - Last character left | ||
+ | </pre> | ||
+ | |||
+ | === Remove multiple identical characters === | ||
+ | |||
+ | <pre> | ||
+ | i="111-2345" | ||
+ | echo ${i#1} # 11-2345 | ||
+ | echo ${i#11} # 1-2345 | ||
+ | echo ${i#111} # -2345 | ||
+ | echo ${i##1} # 11-2345 # Now what you unexpected? | ||
+ | echo ${i##1*1} # -2345 # This is how to do it! | ||
+ | </pre> | ||
+ | |||
+ | === Remove multiple spaces === | ||
+ | |||
+ | Quite suprisingly, it works just for spaces as for regular characters, but ''displaying'' might be tricky and it's problematic when there are also ''trailing'' spaces | ||
+ | |||
+ | <pre> | ||
+ | # 3 leading spaces | ||
+ | # 8 characters in total | ||
+ | # | ||
+ | i=" -2345" | ||
+ | |||
+ | # Output: > -2345< - 8 | ||
+ | # Whitespace gets stripped from output! | ||
+ | # Length is still 8, but you don't see more than one space | ||
+ | # | ||
+ | echo ">"$i"< - ${#i}" | ||
+ | |||
+ | # This goes all fine: | ||
+ | # | ||
+ | echo ">${i}< - ${#i}" # > -2345< - 8 | ||
+ | j=${i# }; echo ">${j}< - ${#j}" # > -2345< - 7 | ||
+ | j=${i# }; echo ">${j}< - ${#j}" # > -2345< - 6 | ||
+ | j=${i# }; echo ">${j}< - ${#j}" # >-2345< - 5 | ||
+ | j=${i## }; echo ">${j}< - ${#j}" # > -2345< - 7 | ||
+ | j=${i## * }; echo ">${j}< - ${#j}" # >-2345< - 5 | ||
+ | </pre> | ||
+ | |||
+ | Problem when there are also ''trailing'' spaces - Everything now gets removed: | ||
+ | |||
+ | <pre> | ||
+ | # Remove leading spaces when there are also trailing spaces | ||
+ | ######################################## | ||
+ | # | ||
+ | # This doesn't work: The whole string now gets removed | ||
+ | # | ||
+ | i=" abcde "; echo ">>>${i}<<< - ${#i}" | ||
+ | i=${i## * }; echo ">>>${i}<<< - ${#i}" | ||
+ | </pre> | ||
+ | |||
+ | == Substring removal - ${string%substring} == | ||
+ | |||
+ | As <code>${string#substring}</code> but now starting at the end of the string. Like SQL's <code>substring_index</code> with a negative index: | ||
+ | |||
+ | <pre> | ||
+ | i="1234567890" | ||
+ | |||
+ | echo $i | ||
+ | echo ${i%90} # Last 2 characters deleted | ||
+ | echo ${i%89} # Nothing happened: String doesn't end at "89" | ||
+ | echo ${i%89*} # Last 3 characters deleted | ||
+ | echo ${i%6*9*} # Last 5 characters deleted | ||
+ | </pre> | ||
+ | |||
+ | == Substring removal - ${string%%substring} == | ||
+ | |||
+ | As before, but with a pattern match that is as broadly as possible: | ||
+ | |||
+ | === Intro === | ||
+ | |||
+ | <pre> | ||
+ | i="12345-12345" | ||
+ | |||
+ | echo $i # 12345-12345 | ||
+ | echo ${i%%45} # 12345-123 | ||
+ | echo ${i%%4*5} # 123 | ||
+ | echo ${i%%34} # 12345-12345 | ||
+ | echo ${i%%34*} # 12 - Only first two characters remain | ||
+ | echo ${i%%3*4*} # 12 - Only first two characters remain | ||
+ | </pre> | ||
+ | |||
+ | === Removing spaces === | ||
+ | |||
+ | See an earlier chapter about trimming multiple leading spaces for details: | ||
+ | |||
+ | <pre> | ||
+ | i="123 " # 3 characters, 5 spaces | ||
+ | |||
+ | echo ">"$i"< - ${#i}" | ||
+ | echo ">${i}< - ${#i}" | ||
+ | j=${i% }; echo ">${j}< - ${#j}" | ||
+ | j=${i% }; echo ">${j}< - ${#j}" | ||
+ | j=${i% }; echo ">${j}< - ${#j}" | ||
+ | j=${i%% }; echo ">${j}< - ${#j}" | ||
+ | j=${i% * }; echo ">${j}< - ${#j}" | ||
+ | j=${i%% * }; echo ">${j}< - ${#j}" # The only correct version | ||
+ | </pre> | ||
+ | |||
+ | === Removing tabs === | ||
+ | |||
+ | The trick: <code>\t</code> is not the same as an actual tab: →<code> </code>← | ||
+ | |||
+ | With <code>\t</code>: | ||
+ | |||
+ | <pre> | ||
+ | i="\t\t\t123" | ||
+ | echo ">${i}< - ${#i}" # Output: \t\t\t123< -9 | ||
+ | echo ">"${i}"< - ${#i}" # Output: 123< -9 | ||
+ | j=${i##'\t'*'\t'}; echo ">${j}< - ${#j}" | ||
+ | Etc. | ||
+ | </pre> | ||
+ | |||
+ | Anyhow, this works: | ||
+ | |||
+ | <pre> | ||
+ | j=${i## * }; # Use actual tabs | ||
</pre> | </pre> | ||
Regel 167: | Regel 425: | ||
|} | |} | ||
− | == | + | == See also == |
* [[Command substitution (Bash)]] | * [[Command substitution (Bash)]] | ||
+ | * [[Regular expressions (Bash)]] | ||
+ | * [[String comparison (Bash)]] | ||
* [[Substring extraction (Bash)]] | * [[Substring extraction (Bash)]] | ||
+ | * [[Trim (Bash)]] | ||
− | == | + | == Sources == |
* https://tldp.org/LDP/abs/html/parameter-substitution.html | * https://tldp.org/LDP/abs/html/parameter-substitution.html | ||
* https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html | * https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html | ||
* https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html | * https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html | ||
+ | |||
+ | '''Pattern Matching c.q. Bracket Matching''' | ||
+ | |||
+ | * https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html | ||
+ | * https://tldp.org/LDP/abs/html/string-manipulation.html |
Huidige versie van 17 nov 2023 om 13:15
Parameter substitution, parameter expansion or shell parameter expansion means that in a Bash expression a parameter or variable is replaced by the contents of that variable, possibly in combination with an operation.
Parameter expansion is indicated by $
, often in combination with curly braces: ${}
, or even more complete: ${parameter}
.
Judging by [1], all cases are covered in this article.
→ This list is not complete. Check https://stackoverflow.com/questions/5406858/difference-between-unset-and-empty-variables-in-bash#5406887 for some more amazing things.
For trim, rather see Trim (Bash).
${}
Something as simple as displaying the value of a variable, is already an example of parameter substitution:
$ a=12 $ echo $a 12
Adding braces might make the concept of expansion even clearer. In particular, that $
is the operator. It's not part of the variable's name: The part inside the curly braces doesn't get a $ sign:
$ a=12 $ echo ${a} 12
No nesting
You can't nest ${}
expressions: The syntax expander doesn't know how to handle them [2]. Also see below at Find & replace.
Illustration:
$ echo ${${HELLO}WORLD} -bash: ${${HELLO}WORLD}: bad substitution
Null substitution - ${parameter:-word}
Use this syntax to replace parameter
with literal word
if the former is empty. Something like ifnull(a, b)
in SQL:
# The parameter has a value: $ i1="Hello, world!" $ echo ${i1:-De parameter is leeg!} Hello, world!
# De parameter bestaat niet - De literal wordt weergegeven $ i1="Hello, world!" $ echo ${i3:-De parameter is leeg!} De parameter is leeg!
Wil je in plaats van een literal een parameter meegeven? Geen probleem: Daar gaat dit artikel over:
$ i1="Hello, world!" $ i2="Vervanging" $ echo ${i3:-${i2}} Vervanging
Als de parameter wel bestaat, maar leeg is:
$ i1= $ echo ${i1:-Leeg} Leeg
Substring replacement - ${parameter/pattern/string}
You can use parameter expansion for find-&-replace:
$ i1="Hello, world!" $ echo ${i1/world/moon} Hello, moon!
Only the first occurence of pattern will be replaced:
$ i1="Hello, world1, world2 & world3!" $ echo ${i1/world/moon} Hello, moon1, world2 & world3!
Substring replacement - ${parameter//pattern/string}
Use a double /
after parameter, to replace all occurences of pattern.
$ i1="Hello, world1, world2 & world3!" $ echo ${i1//world/moon} Hello, moon1, moon2 & moon3!
Meer:
The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. The match is performed according to the rules described below (see Pattern Matching). If pattern begins with ‘/’, all matches of pattern are replaced with string. Normally only the first match is replaced. If pattern begins with ‘#’, it must match at the beginning of the expanded value of parameter. If pattern begins with ‘%’, it must match at the end of the expanded value of parameter. If string is null, matches of pattern are deleted and the / following pattern may be omitted. If the nocasematch shell option (see the description of shopt in The Shopt Builtin) is enabled, the match is performed without regard to the case of alphabetic characters. If parameter is ‘@’ or ‘*’, the substitution operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with ‘@’ or ‘*’, the substitution operation is applied to each member of the array in turn, and the expansion is the resultant list.
Some inspiration:
- https://stackoverflow.com/questions/15116427/bash-string-replacement-gives-me-bad-substitution
- https://stackoverflow.com/questions/8960677/string-replacement-in-bash-bad-substitution-error
String pattern filtering - ${parameter/pattern}
- This is a variant of two chapters before. It uses a pattern which is a bit similar (but not really!) to regex
- As string is empty, the pattern will be deleted from the input:
$ i="hoi123hallo" $ echo ${i/[()a-zA-Z]} oi123hallo
String pattern filtering - ${parameter//pattern}
Use a double /
between parameter and pattern, to filter out all occurences of the pattern - Now you can really filter out stuff:
i="hoi123hallo"; echo ${i/[()a-zA-Z]} # 123 i="howarexxdxxy 12 (diameter)"; echo ">>"${i//[()a-zA-Z]}"<<" # >> 12 << - Spaces get preserved i="howarexxdxxy 12 (diameter)"; echo ">>"${i//[()a-zA-Z ]}"<<" # >>12<< - Spaces get also filtered out
Convert case
[3]:
$ name="vivek" $ echo $name vivek $ echo ${name^} Vivek $ echo ${name^^} VIVEK
String length - ${#string}
This syntax is not only for arrays, but also for scalars:
i="hoihallo" echo ${#i} 8
Substring index extraction - ${string:index:length}
i="1234567890" echo ${i:3} # Select from index 3 (base 0 → 4th character) echo ${i:3:5} # Select 5 characters 4567890 45678
Trim leading characters - ${string#substring}
${string#substring}
deletes the shortest match of substring
(as a pattern expression) from the front of $string
Examples
i="1234567890" echo $i echo ${i#12} # First 2 characters deleted echo ${i#34} # Nothing deleted: String doesn't start with "34" echo ${i#*34} # First 4 characters deleted echo ${i#34*} # Nothing deleted: String doesn't start with "34"
The wildcard can also be used inside the substring:
i="12345-12345" echo $i # 12345-12345 echo ${i#14} # 12345-12345 echo ${i#1*4} # 5-12345 echo ${i#25} # 12345-12345 echo ${i#*25} # 12345-12345 echo ${i#*2*5} # -12345 echo ${i#*2*} # 2345-12345 - Trailing "*" has no meaning
This pattern matching syntax is called Pattern Matching [4], [5]. It's not the same as regular expressions. At times, I call it bracket expressions, which I still find quite a good name
# Bracket expressions ######################################## # i="12345-12345" echo $i # 12345-12345 echo ${i#[14]} # 2345-12345 - "1" matches and is deleted echo ${i#[14]2} # 345-12345 - "12" matches echo ${i#[1234]} # 2345-12345 - Only 1 character is picked echo ${i#[234]} # 12345-12345 - Nothing happens echo ${i#[1234]*} # 2345-12345 echo ${i#*[1234]} # 2345-12345 - Smallest interval is chosen echo ${i#*[4321]} # 2345-12345 - Smallest interval is chosen echo ${i#1*[34]} # 45-12345 - Smallest interval is chosen echo ${i#1*[43]} # 45-12345 - Smallest interval is chosen echo ${i#*[34]} # 45-12345 echo ${i#*[43]} # 45-12345 echo ${i#*[34]5} # -12345 - Surprise. Maybe *45 is smallest interval? echo ${i#*[43]5} # -12345 - Surprise. *45 is selected as interval echo ${i#*[34]2} # Nothing deleted echo ${i#*[43]2} # Nothing deleted
Here's some more regex stuff. It's a bit of a mixed bag:
# Some more regex stuff ######################################## # i="12345-12345" echo $i # 12345-12345 echo ${i#[0-9]} # 2345-12345 echo ${i#[0-9]{2}} # 12345-12345} echo ${i#[0-9]+} # 2345-12345 echo ${i#.....} # 12345-12345 echo ${i#[0-9][0-9]} # 345-12345
Trim leading characters - ${string##substring}
As before, except that the pattern matching is done as inclusive (or greedy) as possible.
- This command seems intended to remove substrings that consists of different characters
- You can use it to remove multiple instances of the same character, but you need a (slight) trick for this.
Intro
i="12345-12345" echo $i # 12345-12345 echo ${i##14} # 12345-12345 echo ${i##1*4} # 5 - Only the last character left echo ${i##25} # 12345-12345 echo ${i##*25} # 12345-12345 echo ${i##*2*5} # Evertything removed echo ${i##*5} # Everything removed echo ${i##*4} # 5 - Last character left
Remove multiple identical characters
i="111-2345" echo ${i#1} # 11-2345 echo ${i#11} # 1-2345 echo ${i#111} # -2345 echo ${i##1} # 11-2345 # Now what you unexpected? echo ${i##1*1} # -2345 # This is how to do it!
Remove multiple spaces
Quite suprisingly, it works just for spaces as for regular characters, but displaying might be tricky and it's problematic when there are also trailing spaces
# 3 leading spaces # 8 characters in total # i=" -2345" # Output: > -2345< - 8 # Whitespace gets stripped from output! # Length is still 8, but you don't see more than one space # echo ">"$i"< - ${#i}" # This goes all fine: # echo ">${i}< - ${#i}" # > -2345< - 8 j=${i# }; echo ">${j}< - ${#j}" # > -2345< - 7 j=${i# }; echo ">${j}< - ${#j}" # > -2345< - 6 j=${i# }; echo ">${j}< - ${#j}" # >-2345< - 5 j=${i## }; echo ">${j}< - ${#j}" # > -2345< - 7 j=${i## * }; echo ">${j}< - ${#j}" # >-2345< - 5
Problem when there are also trailing spaces - Everything now gets removed:
# Remove leading spaces when there are also trailing spaces ######################################## # # This doesn't work: The whole string now gets removed # i=" abcde "; echo ">>>${i}<<< - ${#i}" i=${i## * }; echo ">>>${i}<<< - ${#i}"
Substring removal - ${string%substring}
As ${string#substring}
but now starting at the end of the string. Like SQL's substring_index
with a negative index:
i="1234567890" echo $i echo ${i%90} # Last 2 characters deleted echo ${i%89} # Nothing happened: String doesn't end at "89" echo ${i%89*} # Last 3 characters deleted echo ${i%6*9*} # Last 5 characters deleted
Substring removal - ${string%%substring}
As before, but with a pattern match that is as broadly as possible:
Intro
i="12345-12345" echo $i # 12345-12345 echo ${i%%45} # 12345-123 echo ${i%%4*5} # 123 echo ${i%%34} # 12345-12345 echo ${i%%34*} # 12 - Only first two characters remain echo ${i%%3*4*} # 12 - Only first two characters remain
Removing spaces
See an earlier chapter about trimming multiple leading spaces for details:
i="123 " # 3 characters, 5 spaces echo ">"$i"< - ${#i}" echo ">${i}< - ${#i}" j=${i% }; echo ">${j}< - ${#j}" j=${i% }; echo ">${j}< - ${#j}" j=${i% }; echo ">${j}< - ${#j}" j=${i%% }; echo ">${j}< - ${#j}" j=${i% * }; echo ">${j}< - ${#j}" j=${i%% * }; echo ">${j}< - ${#j}" # The only correct version
Removing tabs
The trick: \t
is not the same as an actual tab: →
←
With \t
:
i="\t\t\t123" echo ">${i}< - ${#i}" # Output: \t\t\t123< -9 echo ">"${i}"< - ${#i}" # Output: 123< -9 j=${i##'\t'*'\t'}; echo ">${j}< - ${#j}" Etc.
Anyhow, this works:
j=${i## * }; # Use actual tabs
Casus: Extra line breaks product_cat (aug. 2021)
WordPress product_cat-taxon-beschrijvingen worden via WP-CLI en Bash bijgewerkt. Ik gebruik parameter substitution for find-&-replace, met code zoals dit:
i1=$(wp wc product_cat --user=4 get 18869 --field="description") wp wc product_cat --user=4 update 18869 --description="${i1/Alle widgets voor/All widgets for}"
Er wordt echter een karakter 0a
(line feed) toegevoegd aan het begin van de string. Hoe kan dat? Hoe verhinder ik dat?
See also
- Command substitution (Bash)
- Regular expressions (Bash)
- String comparison (Bash)
- Substring extraction (Bash)
- Trim (Bash)
Sources
- https://tldp.org/LDP/abs/html/parameter-substitution.html
- https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
- https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html
Pattern Matching c.q. Bracket Matching