What does `${1+/$1}` mean in Bash? - linux

I was reading a Bash script with this line:
FOO=${1+/$1}
What does this line do?

The / makes it look slightly more confusing than it is, but this is just an example of ${foo+bar}, which expands to bar if $foo is set.
In this case, the variable is $1, the first positional parameter passed to the script or function.
example () {
echo "${1+/$1}"
}
example # outputs nothing
example '' # outputs "/"
example foo # outputs "/foo"
There is a table that summarises these parameter expansions in the spec. The rules for ${parameter+word} are:
Set and Not Null: substitute word
Set But Null: substitute word
Unset: substitute null
So to answer your question directly, FOO=${1+/$1} assigns /$1 to FOO is $1 is set, otherwise FOO is set to null (an empty string).

Related

Can I make Lua's string.gsub() start substitution from right to left? [duplicate]

I have a string that contains something like this:
##### abc 'foo'
/path/to/filename:1
##### abc 'bar'
/path/to/filename:1
The string can potentially be very long (say, 50 lines) and doesn't change often.
I would like to fetch the last occurrence of text in between the single-quotes (bar in this example). This is similar to someone else's Python problem (except the answer there doesn't work for me in Lua, as seen far below).
I could parse each line, and put the results into an array, and then just take the last element of the array, but that doesn't seem elegant to me:
local text = [[
##### abc 'foo'
/path/to/filename:1
##### abc 'bar'
/path/to/filename:1
]]
local arr = {}
local pattern = "abc '([^']+)'"
for s in text:gmatch(pattern) do
table.insert(arr, s)
end
print('last:', arr[#arr])
I'm interested in using Lua string patterns to search the string from the end. The pattern I tried below starts from the beginning instead of the end:
local text = [[
##### abc 'foo'
/path/to/filename:1
##### abc 'bar'
/path/to/filename:1
]]
-- FIXME: pattern searches from beginning
local pattern = "abc '([^']+)'.*$"
local s = text:gmatch(pattern)()
assert(s == 'bar', 'expected "bar" but saw "'..s..'"')
print('last:', s)
This yields:
input:12: expected "bar" but saw "foo"
What string pattern specifies the "reverse search" I'm looking for?
You could use
local pattern = ".*abc '([^']+)'"
The .* is greedy so it chews up as much as it can before it matches (in this case, it chews up all the earlier matches and gives you the last).
Or if you really wanted, you could reverse your string and (sort of) your pattern too, but I think it's better to rely on the greedy .* :P
pattern = "'([^']+)' cba"
print(text:reverse():gmatch(pattern)()) -- rab
print(text:reverse():gmatch(pattern)():reverse()) -- bar
Another option would be to use the $ pattern anchor to anchor the pattern at the end of the string. You also don't need to use gmatch here, just match suffices (and saves you the need to call the iterator function returned by gmatch). All in all you get:
text:match"'([^']+)'$"

Escaping front slash inside shell variable value

I am using bash shell script to do certain pattern replacement.
Pattern replacement using sed commands to replace one value to another
sed/s/abc/$var/g
Shell variable var contains path value in shell variable,
e.g var="dir/abc.txt"
With shell variable having this value, it causes issue for sed.
Idea would be to replace path like this
var="dir\/abc.txt"
I am using the following function to do the same. This function iterates character by character through string
and replaces the / with \/
# This function takes a string and scans for forward slash
# If forward slash is found , it will replace with escape char.
# input = "dir/abc.txt
# output ="dir\/abc.txt
handle_escape_chars()
{
RETURN_VAL=""
fileName=$1
for i in $(seq 1 ${#fileName}); do
#echo "${fileName:i-1:1}"
z="${fileName:i-1:1}"
if [ $z == "/" ];
then
z="\/"
fi
RETURN_VAL="${RETURN_VAL}${z}"
done
echo $RETURN_VAL
return
}
Have tried using another alternative, but this did not work
fileName="dir/abc.txt"
printf "%q\n" "$fileName"

Bad substitution for string passed as argument

I'm writing a script that, at one point, needs to compute the length of a string to draw a box around it. I can't use wc -c for that effect because I will use it for non-ASCII characters (sad). I'm also avoiding bashims for maximum compatibility.
I'm using the shell's built-in string length calculator:
string="string"
echo ${#string}
# 6
string="stríñg"
echo ${#string}
# 6
So far so good, but now when I pass it as an argument...
_function () {
string_length=$(${#"$1"})
}
# line 21: ${#"$1"}: bad substitution
Priting $string_length doesn't show anything as well.
What am I doing terribly wrong?
It's
string_length=${#1}
The variable is 1, so $1 or ${1} get's the value of the variable. Then ${#1} get's the length of 1 after expansion.

How do you interpret ${VAR#*:*:*} in Bourne Shell

I am using Bourne Shell. Need to confirm if my understanding of following is correct?
$ echo $SHELL
/bin/bash
$ VAR="NJ:NY:PA" <-- declare an array with semicolon as separator?
$ echo ${VAR#*} <-- show entire array without separator?
NJ:NY:PA
$ echo ${VAR#*:*} <-- show array after first separator?
NY:PA
$ echo ${VAR#*:*:*} <-- show string after two separator
PA
${var#pattern} is a parameter expansion that expands to the value of $var with the shortest possible match for pattern removed from the front of the string.
Thus, ${VAR#*:} removes everything up and including to the first :; ${VAR#*:*:} removes everything up to and including the second :.
The trailing *s on the end of the expansions given in the question don't have any use, and should be avoided: There's no reason whatsoever to use ${var#*:*:*} instead of ${var#*:*:} -- since these match the smallest amount of text possible, and * is allowed to expand to 0 characters, the final * matches and removes nothing.
If what you really want is an array, you might consider using a real array instead.
# read contents of string VAR into an array of states
IFS=: read -r -a states <<<"$VAR"
echo "${states[0]}" # will echo NJ
echo "${states[1]}" # will echo NY
echo "${#states[#]}" # count states; will emit 3
...which also gives you the ability to write:
printf ' - %s\n' "${states[#]}" # put *all* state names into an argument list

Access bash positional parameter through variable

How to access bash positional parameter through a variable?
e.g. I have a variable "pos", which can be anything between 1 to 6 (say).
If pos==1, I want to do: echo $1
If pos==2, I want to do: echo $2
So on.
Intuitively, I want to do something like: echo $$pos.
I want to do it in one line.
Use variable indirection:
echo "${!pos}"
Here are several solutions. Some may need a recent version of bash, others may still work with a very old one.
Let us set up first our environment...
$ set first second third fourth
$ pos=3
Substring expansion
$ printf 'Parameter %d is "%s"\n' "$pos" "${#:pos:1}"
Parameter 3 is "third"
This is very flexible:
Can match several consecutive parameters: "${#:pos:2}"
Can match all the remaining parameters starting from pos: "${#:pos}"
Works with literals, variable pos is not necessary: "${#:3:2}"
Works also with any arithmetic expression: "${#:(pos-1)*2:1}"
Works also with negative numbers (counts down from the last):
$ printf 'Last parameter is "%s"\n' "${#: -1}" # Mind the space!
Last parameter is "fourth"
$ printf 'Parameter %d is "%s" to last\n' "$pos" "${#: -pos:1}"
Parameter 3 is "second" to last
Intermediary array
Bash's arrays are very flexible. Just put your script parameters into an array and access its elements with pos:
$ args=( "$0" "$#" )
$ printf 'Parameter %d is "%s"\n' "$pos" "${args[pos]}"
Advantages:
Straightforward array notation array[pos]
Array index may be negative (counting down from the last element)
Array index may be any arithmetic expression: "${args[(pos-1)*2]}"
Can be combined with substring expansion, so all its advantages apply here too: "${args[#]:pos:2}"
Indirect expansion
The ${!var} syntax fetches the content of var and the whole is substituted with $content. If var contains a number n, the whole is substituted with the nth positional parameter.
$ printf 'Parameter %d is "%s"\n' "$pos" "${!pos}"
Parameter 3 is "third"
Drawbacks:
Less flexible than above solutions
eval
Father of all evils, eval may still be useful if you shell doesn't support any of the above:
$ eval "param=\${$pos}" # Just eval the assignment, nothing more
$ printf 'Parameter %d is "%s"\n' "$pos" "$param"
Parameter 3 is "third"
Advantages:
Works in any Bourne shell
Flexibility: with eval you can do anything (and this is also the problem with it)
Drawbacks:
eval is a beast that is difficult to tame. Limit its use to the strict minimum. For example, only eval the assignment given above in example and nothing more (this necessitates the temporary variable param). Of course, sanity check of pos is mandatory but this is also the case for the other commands given here.
Subshell
Since subshells inherit the positional parameters, and since what happens in subshells stays in subshells, we can use these properties to shift the parameters:
$ printf 'Parameter %d is "%s"\n' "$pos" "$(shift $((pos-1)); printf %s "$1")"
Parameter 3 is "third"
$ echo "$1" # Check that parameters weren't shifted in parent shell
first
Advantages:
Works in any Bourne shell
Arithmetic operations on pos

Resources