I was reviewing some of my old code and came across this syntax:
extractDir="${downloadFileName%.*}-tmp"
The only information I found searching refers to a list of commands, but this is just one variable. What does this curly-brace syntax mean in bash?
In this context, it is a parameter substitution.
The ${variable%.*} notation means take the value of $variable, strip off the pattern .* from the tail of the value — mnemonic: percenT has a 't' at the Tail — and give the result. (By contrast, ${variable#xyz} means remove xyz from the head of the variable's value — mnemonic: a Hash has an 'h' at the Head.)
Given:
downloadFileName=abc.tar.gz
evaluating extractDir="${downloadFileName%.*}-tmp" yields the equivalent of:
extractDir="abc.tar-tmp"
The alternative notation with the double %:
extractDir="${downloadFileName%%.*}-tmp"
would yield the equivalent of:
extractDir="abc-tmp"
The %% means remove the longest possible tail; correspondingly, ## means remove the longest matching head.
It indicates that parameter expansion will occur.
It is used when expanding an environment variable adjacent to some text that is not the variable, so the shell does not include all of it in the variable name.
Related
If you append ^ to a variable, Bash capitalises the first letter of its contents. (Similarly, , sends it to lowercase and doubling-up either of these applies the transformation to the whole string, rather than just the first letter.)
foo="hello world"
echo ${foo^} # Hello world
You can also do ${variable:position:length} to extract a substring:
echo ${foo:0:1} # h
So far, I haven't found a way to combine these without, obviously, creating a temporary variable. Is there a form where I can get just the capitalised first letter out of an arbitrary string?
It does not change the basic limitation you are seeing in terms of not being able to "chain" expansions, but you can assign the result of an expansion to the same variable and do away with the temporary variable.
For instance:
A=text
A="${A^}"
A="${A//x/s}"
echo "$A"
echoes "Test".
No. Parameter expansion operators do not compose, so if you want more than one side effect, you need a temporary variable (which can include overwriting the original value as shown by #fred) or an external tool to process the result of the expansion (as shown by #anubhava).
Your other alternative is to use a different shell that does support more complicated operations, like zsh:
% foo="hello world"
% % print ${(U)${foo:0:1}}
H
You can use tr with substring:
tr [[:lower:]] [[:upper:]] <<< "${foo:0:1}"
H
I've come across this curious bash expression:
somestring=4.5.6
echo ${somestring%rc*}
For all that I can tell it just prints 4.5.6. So why would anybody use it?
I found it in this script (look for pkgver), so I hope I didn't miss any context which is necessary for this to work.
Source:
${string%substring} deletes shortest match of $substring from back of
$string.
The intention is to echo the numerical version only, without the rc* suffix for strings like:
somestring=4.5.6rc1
somestring=4.5.6rc23_whatever
UPDATE:
The better choice is to echo ${somestring%%rc*}.
Otherwise, the following might happen:
somestring=4.5.6rc1_rc2
echo ${somestring%rc*}
4.5.6rc1_
whereas:
echo ${somestring%%rc*}
4.5.6
It removes rc and any following characters (the *) from somestring. See, e.g., this answer.
This is known as parameter expansion.
From Bash manual:
${parameter%word}
${parameter%%word}
The word is expanded to produce a pattern just as in filename expansion. If the pattern matches a trailing portion of the expanded
value of parameter, then the result of the expansion is the value of
parameter with the shortest matching pattern (the ‘%’ case) or the
longest matching pattern (the ‘%%’ case) deleted. If parameter is ‘#’
or ‘’, the pattern removal 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
pattern removal operation is applied to each member of the array in
turn, and the expansion is the resultant list.
Simply "${somestring%rc*}" is the string left by cutting rc* (* means anything after rc) from $somestring from right i.e rc* is matched in the string from right and then deleted and the resultant is the remaining string.
I am reading a bash script that takes two arguments in input but I can't figure out what does
${2%.*}
does exactly, can someone explain me what does the curly braces, 2, %, "." and * refers two?
Thanks
$2 is the second argument passed to the program. That is, if your script was run with
myscript foo.txt bar.jpg
$2 would have the value bar.jpg.
The % operator removes a suffix from the value that matches the following pattern. .* matches a period (.) followed by zero or more characters. Put together, your expression removes a single extension from the value. Using the above example,
$ echo ${2%.*}
bar
P.S. Perhaps worth noting that % will remove the shortest match for the following pattern: So if $2 was for example bar.jpg.xz, then ${2%.*} would be bar.jpg. (Conversely, the %% operator will remove the longest match for the pattern, so ${2%%.*} would be bar in both examples.)
In Linux /etc/init.d/functions script I found the following parameter expansions that I don't quite understand:
${p//[0-9]/} replace all instances of any number to/by what?
${1##[-+]} This seems to remove all the longest left instances of minuses and pluses?
${LSB:-} This seems to say that if LSB is not set then set nothing? in other words do nothing?
These are instances of bash Shell Parameter Expansion;
see http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
Note: ksh and zsh support the expansions in your question, too (I'm unclear on the full extent of the overlap in functionality), whereas sh (POSIX-features-only shells), does NOT support the string-replacement expansion, ${p//[0-9]/}.
${p//[0-9]/}
Removes all digits: replaces all (//) instances of digits ([0-9]) with an empty string - i.e., it removes all digits (what comes after the last / is the replacement string, which is empty in this case).
${1##[-+]}
Strips a single leading - or +, if present: Technically, this removes the longest prefix (##) composed of a single - or + character from parameter $1. Given that the search pattern matches just a single character, there is no need to use ## for the longest prefix here, and # - for the shortest prefix - would do.
${LSB:-}
A no-op designed to prevent the script from breaking when run with the -u (nounset) shell attribute: Technically, this expansion means: In case variable $LSB is either not set or empty, it is to be replaced with the string following :-, which, in this case, is also empty.
While this may seem pointless at first glance, it has its purpose, as Sigi points out:
"
The ${LSB:-} construct makes perfect sense if the shell is invoked with the -u option (or set -u is used), and the variable $LSB might actually be unset. You then avoid the shell bailing out if you reference $LSB as ${LSB:-} instead. Since it's good practice to use set -u in complex scripts, this move comes in handy quite often.
"
${p//[0-9]/} # removes digits from anywhere in `$p`
${1##[-+]} # removes + or - from start in $1
${LSB:-} # not really doing anything
I have a portion of a bash script that is getting a filename without extension, but I'm trying to understand what is really going on here. What are the "%%"'s for? Can someone elaborate on what bash is doing behind the scenes? How can this technique be used on a general basis?
#!/bin/bash
for src in *.tif
do
txt=${src%%.*}
tesseract ${src} ${txt}
done
It gets rid of the filename extension (here: .tif), sample:
$ for A in test.py test.sh test.xml test.xsl; do echo "$A: ${A%%.*}"; done
test.py: test
test.sh: test
test.xml: test
test.xsl: test
from bash manual:
${parameter%%word}
The word is expanded to produce a pattern just as in pathname expansion. If the
pattern matches a trailing portion of the expanded value of parameter, then the
result of the expansion is the expanded value of parameter with the shortest
matching pattern (the ``%'' case) or the longest matching pattern (the ``%%''
case) deleted. If parameter is # or *, the pattern removal 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 pattern removal
operation is applied to each member of the array in turn, and the expansion is
the resultant list.
Here's output from the bash man page
${parameter%%word}
The word is expanded to produce a pattern just as in pathname
expansion. If the pattern matches a trailing portion of the
expanded value of parameter, then the result of the expansion is
the expanded value of parameter with the shortest matching pat-
tern (the ``%'' case) or the longest matching pattern (the
``%%'' case) deleted. If parameter is # or *, the pattern
removal 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 pattern removal
operation is applied to each member of the array in turn, and
the expansion is the resultant list.
Apparently bash has several "Parameter Expansion" tools which include:
Simply substituting the value...
${parameter}
Expanding to a sub-string...
${parameter:offset}
${parameter:offset:length}
Substitute the length of the parameters value...
${#parameter}
Expanding upon a match at the beginning of the parameter...
${parameter#word}
${parameter##word}
Expanding upon a match at the end of the parameter...
${parameter%word}
${parameter%%word}
Expands the parameter to find and replace a string...
${parameter/pattern/string}
These are my interpretation of the parts I think I understand from this section of the man pages. Let me know if I missed something important.
Check out "Parameter Expansion" in the bash man pages. That syntax expands the $src variable deleting stuff that matches the .* pattern from it.
It's a string removal operation in the format: ${str%%substr}
Where str is the string you are operating on, and substr is the pattern to match. It looks for the longest match of substr in str, and removes everything from that point on.