What ${ (%):-%n} means? - shell

I don't know what "${ (%):-%n}" means.
${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh
The output of echo ${(%):-%n} is my username.
output:

The (%) is a parameter expansion flag, specifically the one that causes prompt escape sequences in the value of the parameter to be expanded. For example,
% x="%n"
% echo $x
%n
% echo ${(%)x}
pi
The :- operator with no parameter name causes the following text to be treated as the value of the expansion:
% echo ${:-%n}
%n
Put them together, and you get an expression that expands to your current username. The string %n is the result of the parameter expansion, which also undergoes prompt expansion.
% echo ${(%):-%n}
pi

Related

How do bash variable types work and how to work around automatic interpretation?

I am trying to set up a variable that contains a string representation of a value with leading zeroes. I know I can printf to terminal the value, and I can pass the string output of printf to a variable. It seems however that assigning the value string to a new variable reinterprets the value and if I then print it, the value has lost its leading zeroes.
How do we work with variables in bash scripts to avoid implicit type inferences and ultimately how do I get to the solution I'm looking for. FYI I'm looking to concatenate a large fixed length string numeric, something like a part number, and build it from smaller prepared strings.
Update:
Turns out exactly how variables are assigned changes their interpretation in some way, see below:
Example:
#!/bin/bash
a=3
b=4
aStr=$(printf %03d $a)
bStr=$(printf %03d $b)
echo $aStr$bStr
output
$ ./test.sh
003004
$
Alternate form:
#!/bin/bash
((a = 3))
((b = 4))
((aStr = $(printf %03d $a)))
((bStr = $(printf %03d $b)))
echo $aStr$bStr
output
$ ./test.sh
34
$
How do bash variable types
There are no variable types. All variables are strings (type).. Variables store a value (a string), but also variables have some additional magic attributes associated with them.
There are Bash arrays, but I think it's an attribute that a variable is an array. Still, in any case, every array element holds a string. There is a "numeric" variable declare -i var, but it's attribute of the variable - in memory, the variable is still a string, only when setting it Bash checks if the string (still a string!) to be set is a number.
assigning the value string to a new variable reinterprets the value
Bash does not "interpret" the value on assignment.
How do we work with variables in bash scripts to avoid implicit type inferences
There are no "type inferences". The type of variable does not change - it holds a string.
The value of the variable undergoes different expansions and conversions depending on the context where it is used. For example $(...) removes trailing newlines. Most notably unquoted variable expansions undergo word splitting and filename expansion.
Example:
Posting your code to shellcheck results in:
Line 2:
a = 3
^-- SC2283 (error): Remove spaces around = to assign (or use [ ] to compare, or quote '=' if literal).
Line 3:
b = 4
^-- SC2283 (error): Remove spaces around = to assign (or use [ ] to compare, or quote '=' if literal).
Line 4:
aStr = $(printf %03d $a)
^-- SC2283 (error): Remove spaces around = to assign (or use [ ] to compare, or quote '=' if literal).
^-- SC2046 (warning): Quote this to prevent word splitting.
^-- SC2154 (warning): a is referenced but not assigned.
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
Did you mean: (apply this, apply all SC2086)
aStr = $(printf %03d "$a")
Line 5:
bStr = $(printf %03d $b)
^-- SC2283 (error): Remove spaces around = to assign (or use [ ] to compare, or quote '=' if literal).
^-- SC2046 (warning): Quote this to prevent word splitting.
^-- SC2154 (warning): b is referenced but not assigned.
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
Did you mean: (apply this, apply all SC2086)
bStr = $(printf %03d "$b")
Line 7:
echo $aStr$bStr
^-- SC2154 (warning): aStr is referenced but not assigned.
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
^-- SC2154 (warning): bStr is referenced but not assigned.
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
Did you mean: (apply this, apply all SC2086)
echo "$aStr""$bStr"
Shellcheck tells you what is wrong. After fixing the problems:
#!/bin/bash
a=3
b=4
aStr=$(printf %03d "$a")
bStr=$(printf %03d "$b")
echo "$aStr$bStr"
Which upon execution outputs your expected output:
003004
By doing ((aStr = $(printf %03d $a))), you are destroying again the careful formatting done by printf. You would see the same effect if you do a
(( x = 005 ))
echo $x
which outputs 5.
Actually the zeroes inserted by printf could do harm to your number, as you see by the following example:
(( x = 015 ))
echo $x
which outputs 13, because the ((....)) interprets the leading zero as an indication for octal numbers.
Hence, if you have a string representing a formatted (pretty-printed) number, don't use this string in numeric context anymore.

printing value from variable returning the existing paths using '*' unix

how could I print a variable that has '*', I just want to print value and is taking it as "all"
example of what I want:
var=/home/us*r/
echo ${var}
Result:
/home/us*r/
example of what is doing
var=/home/us*r/
echo ${var}
Result:
/home/user/
/home/usar/
/home/usir/
any idea of how to print just the value of variable?
Put the variable expansion in double quotes:
echo "$var"
Curly braces are optional and they help if you want to interpolate a variable expansion in a string where it's surrounded by a character that's valid in a variable name, e.g.: echo "${var}_$var2", but it's double quotes that supress pathname expansion and field splitting.
See the Word Expansion section of dash(1) for more details.

bash - brace expansion not expanding?

On Kubuntu 15.10
echo $BASH_VERSION
4.3.42(1)-release
I try
reps=1
threads={1}{10..60..10}
for((r=0;r<$reps;r++))
do
tCount=0
for t in $threads
do
echo "t=$t, tCount=${tCount}"
#do something funny with it
((tCount++))
done
done
and it produces a single line
t={1}{10..60..10}, tCount=0
How do I get this to work?
edit
I expect
t=1, tCount=0
t=10, tCount=1
t=20, tCount=2
t=30, tCount=3
t=40, tCount=4
t=50, tCount=5
t=60, tCount=6
update
note that threads=({1}{10..60..10})
and then for t in ${threads[#]}
will prefix the 10..60..10 range with the string {1}
(i.e. {1}10,{1}20,..,{1}60 )
The {1} expression is just a string, since it doesn't conform to the brace expansion syntax:
A sequence expression takes the form {X..Y[..INCR]}, where X and Y are either integers or single characters, and INCR, an optional increment, is an integer.
The brace expansion is performed before variable expansion, so you can't expand braces by just referring to a variable:
The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion.
Whether write the brace expansion directly, or use eval (usually not recommended).
Example:
tCount=0
for t in {1,{10..60..10}}; do
echo "t=$t tCount=$tCount"
(( tCount++ ))
done

Replace " " with "\ " in a file path string with variable expansion

I know there is a better way to do this.
What is the better way?
How do you do a string replace on a string variable in bash?
For Example: (using php because that's what I know)
$path = "path/to/directory/foo bar";
$path = str_replace(" ", "\ ", "$path");
echo $path;
returns:
path/to/directory/foo\ bar
To perform the specific replacement in bash:
path='path/to/directory/foo bar'
echo "${path// /\\ }"
Don't use prefix $ when assigning to variables in bash.
No spaces are allowed around the =.
Note that path is assigned with single quotes, whereas the string replacement occurs in double quotes - this distinction is important: bash does NOT interpret single-quoted strings, whereas you can refer to variables (and do other things) in double-quoted strings; (also, not quoting a variable reference at all has other ramifications, often undesired - in general, double-quote your variable references)
Explanation of string replacement "${path// /\\ }":
In order to perform value substitution on a variable, you start with enclosing the variable name in {...}
// specifies that ALL occurrences of the following search pattern are to be replaced (use / to replace the first occurrence only).
/ separates the search pattern, (a single space), from the replacement string, \\ .
The replacement string, \ , must be represented as \\ , because \ has special meaning as an escape char. and must therefore itself be escaped for literal use.
The above is an instance of what bash (somewhat cryptically) calls shell parameter expansion and also parameter expansion and [parameter and] variable expansion. There are many more flavors, such as for extracting a substring, providing a default value, stripping a prefix or suffix, ... - see the BashGuide page on the topic or the manual.
As for what types of expressions are supported in the search and replacement strings:
The search expression is a globbing pattern of the same type used in filename expansion (e.g, *.txt); for instance, v='dear me'; echo "${v/m*/you}" yields 'dear you'. Note that the longest match will be used.
Additionally, the first character of the pattern has special meaning in this context:
/, as we've seen above, causes all matching occurrences of the pattern to be replaced - by default, only the first one is replaced.
# causes the rest of the pattern to only match at the beginning of the input variable
% only matches at the end
The replacement expression is a string that is subject to shell expansions; while there is no support for backreferences, the fact that the string is expanded allows you to have the replacement string reference other variables, contain commands, with $(...), ...; e.g.:
v='sweet home'; echo "${v/home/$HOME}" yields, for instance, 'sweet /home/jdoe'.
v='It is now %T'; echo "${v/\%T/$(date +%T)}" yields, for instance, It is now 10:05:17.
o1=1 o2=3 v="$o1 + $o2 equals result"; echo "${v/result/$(( $o1 + $o2 ))}" yields '1 + 3 equals 4' (I think)
There are many more features and subtleties - refer to the link above.
How about sed? Is that what you're looking for?
#!/bin/bash
path="path/to/directory/foo bar"
new_path=$(echo "$path" | sed 's/ /\\ /g')
echo "New Path: '$new_path"
But as #n0rd pointed out in his comment, is probably better just quoting the path when you want to use it; something like...
path="path/to/directory/foo bar"
echo "test" > "$path"

confusing statement in shell script

while sizes=`sizes $pgid`
do
set -- $sizes
sample=$((${#/#/+}))
let peak="sample > peak ? sample : peak"
sleep 0.1
done
i am confused about the below statement:
sample=$((${#/#/+}))
could anybody explain this?
The '${#/#/+}' part is a regular expression expansion:
${parameter/pattern/string}
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. 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 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.
So, it looks like it replaces the empty string at the start of each value in the argument list '$#' with a '+'. It's key merit is that it prefixes each argument in one fell swoop; otherwise, it is similar to "+$var".
The '$(( ... )) part is an arithmetic expression. It performs arithmetic on the expression between the parentheses. So, in context, it adds up the values in the argument list, assuming they are all numeric. Given the expansion, it might yield:
set -- 2 3 5 7 11
sample=$((${#/#/+}))
sample1=$((+2 +3 +5 +7 +11))
echo $sample = $sample1
and hence '28 = 28'.
Let's take the line from the inside out.
${#/#/+}
This is a parameter expansion, which expands the $# parameter (which in this case, will be an array of all of the items in $sizes), and then does a pattern match on each item, replacing each matched sequence with +. The # in the pattern matches the beginning of each item in the input; it doesn't actually consume anything, so the replacement by + will just add a + before each item. You can see this in action with a simple test function:
$ function tst() { echo ${#/#/+}; }
$ tst 1 2 3
+1 +2 +3
The result of this is then substituted into $(( )), which performs arithmetic expansion, evaluating the expression within it. The end result is that the variable $sample is set to the sum of all of the numbers in $sizes.
It's an arithmetic expansion of a string replacement.
$(( )) is arithmetic expansion - eg echo $((1 + 2)).
${var/x/y} is a string replacement; in this case, replace the first # in a line with +. $# is a variable that in this case contains $sizes; this will replace the string and then looks like it will add the values in it.
${var/old/new} expands $var, changing any "old" to "new".
${var/#old/new} insists that the match start at the start of the value
${var/#/new} substitutes at the start of every variable
${#/#/new} (and $#) applies to each parameter
$(( 1 + 3 )) replaces with the arithmetic result.
$(( ${#/#/+/ ))
Expands $#, the arguments from set -- $sizes, prepends a "+" to each parameter and runs the result through an arithmetic evaluation. It looks like it is adding all values on each line.

Resources