Bash create a path with variables divided by _ and check if exist - bash

In Bash, I am trying to create a path with two variables within:
/path/to/my/file/${variable1_-}${variable2}/Still/some/path
My variable2 is always set, but the variable1 might be empty and in that case I don't want to print the "_"
I have tried the line above, but doesn't seem to be correct.
Can someone help in getting the right path printed?
Thanks in advance for your suggestions!

You have a simple typo (the underscore should be after the separator, not part of the variable name) and you want to include the underscore if variable1 is set, not it it's unset (so plus instead of minus in the parameter expansion; and add a colon to also cover the set but empty case). Presumably you also want to include the actual value of variable1 when it's set.
/path/to/my/file/${variable1}${variable1:+_}${variable2}/Still/some/path
or equivalently, nested
/path/to/my/file/${variable1:+${variable1}_}${variable2}/Still/some/path
where the braces before the underscore are necessary to separate the variable name from the literal text.

You can use this.
https://linux.die.net/man/1/bash
${parameter:+word}
set also variable1
variable1=VAR1
variable2=VAR2
variable3=${variable1:+_}
echo /path/to/my/file/${variable1}${variable3}${variable2}/Still/some/path
set only variable2
variable1=
variable2=VAR2
variable3=${variable1:+_}
echo /path/to/my/file/${variable1}${variable3}${variable2}/Still/some/path

With a few more lines of code this could work:
run () {
prefix="" # empty
if [ -n "$variable1" ]; then
prefix="${variable1}_"
fi
echo "/path/to/my/file/${prefix}${variable2}/Still/some/path"
}
# set only variable2
variable2=var2
run
# set also variable1
variable1=var1
run
output:
/path/to/my/file/var2/Still/some/path
/path/to/my/file/var1_var2/Still/some/path
description:
-n tests if the string is not empty, in that case I fill prefix with variable1 and the underscore

Related

How to get dictionary value to parse variables referenced in value using Bash?

I have a dictionary that has variable names in the value string.
I'm trying to lookup the dictionary value, then parse the result with the actual variable value in the string (not the variable name).
How would I do it?
Example:
asset_symbol='BTC'
counter_asset_symbol='ETH'
Dictionary entry:
['ct']=https://charts.cointrader.pro/charts.html?coin=$asset_symbol%3A$counter_asset_symbol
When calling the value I want it to parse $asset_symbol and $counter_asset_symbol" to "BTC" and "ETH"
So in this case I want:
https://charts.cointrader.pro/charts.html?coin=BTC%3AETH
Examples of what didn't work:
#!/bin/bash
chart_engines=('ct cg')
# No ''
declare -A search_urls=(
['ct']=https://charts.cointrader.pro/charts.html?coin=$asset_symbol%3A$counter_asset_symbol
['cg']=https://beta.coinigy.com/markets/$exchange_symbol/$asset_symbol/$counter_asset_symbol
)
# With single quotes
declare -A search_urls=(
['ct']='https://charts.cointrader.pro/charts.html?coin=$asset_symbol%3A$counter_asset_symbol'
['cg']='https://beta.coinigy.com/markets/$exchange_symbol/$asset_symbol/$counter_asset_symbol'
)
# With double quotes
declare -A search_urls=(
['ct']='https://charts.cointrader.pro/charts.html?coin=$asset_symbol%3A$counter_asset_symbol'
['cg']='https://beta.coinigy.com/markets/$exchange_symbol/$asset_symbol/$counter_asset_symbol'
)
asset_name='Bitcoin'
asset_symbol='BTC'
counter_asset_name='Ethereum'
counter_asset_symbol='ETH'
exchange_name='Binance'
exchange_symbol="BINA"
open_charts_urls(){
for i in ${chart_engines[#]}; do
# Get URL
# Dictonary lookup doesn't fill in the variables.
local charts_url="${search_urls[$i]}"
echo "$charts_url"
# Direct reference does.
# echo "https://beta.coinigy.com/markets/$exchange_symbol/$asset_symbol/$counter_asset_symbol"
# echo "https://charts.cointrader.pro/charts.html?coin=$asset_symbol%3A$counter_asset_symbol"
# local subs="$charts_url"
# echo "subs:$subs"
done
}
open_charts_urls
You are referencing $asset_symbol and $counter_asset_symbol before they are declared. Declare the $search_urls array below the declaration of these symbols. Also, use double quotes for the values within the array. Single quotes will prevent the parameter substitution. See quoting. In your example below the comment # With double quotes you are still using single quotes.

Variable in variablenames

i have a a couple of variables with a number in its names. e.g
SERVER_IP48_SUBNET
..
SERVER_IP60_SUBNET
And an additional variable
SERVER_IP
Im trying to expand/concatenate them in the following way:
ALLIPs=${SERVER_IP}
for i in {48..64}; do
ALLIPs=${ALLIPs},${SERVER_IP${i}_SUBNET}
done
as you can imagine this script fails saying:
Wrong substitution
Does anybody of you know a good solution for this problem?
Thanks so far
Use a nameref with bash version 4.3 +
ALLIPs=${SERVER_IP}
for i in {48..64}; do
declare -n tmp="SERVER_IP${i}_SUBNET"
ALLIPs+=",$tmp"
done
But you should really be using an array in the first place:
server_ip=0.0.0.0
subnet_ip=(
[48]=1.1.1.1
[49]=2.2.2.2
# ...
[64]=16.16.16.16
)
all_ips=( "$server_ip" )
for i in {48..64}; do
all_ips+=( "${subnet_ip[i]}" )
done
(
IFS=,
echo "ALLIPs = ${all_ips[*]}"
)
Get out of the habit of using ALLCAPS variable names, leave those as
reserved by the shell. One day you'll write PATH=something and then
wonder why
your script is broken.
I just noticed, if you just want a to join the IP addresses with commas, and you're using an array, you don't need a loop at all:
all_ips=$(
IFS=,
set -- "$server_ip" "${subnet_ip[#]}"
echo "$*"
)
You can use ${!varprefix#} or ${!varprefix*} to expand to all variables with that common prefix (the difference is the same as $# and $*):
SERVER_IP48_SUBNET=48sub
SERVER_IP49_SUBNET=49sub
SERVER_IP50_SUBNET=50sub
SERVER_IP=1.2.3.4
# set this as empty since !SERVER_IP# also matches SERVER_IP
ALLIPS=""
for var in "${!SERVER_IP#}"; do
ALLIPS=$ALLIPS,${!var}
done
This would probably be more practical if you could invert the names like this, since we can only match prefixes:
SERVER_IP_SUBNET_48=48sub
SERVER_IP_SUBNET_49=49sub
SERVER_IP_SUBNET_50=50sub
SERVER_IP=1.2.3.4
ALLIPS=$SERVER_IP
for var in "${!SERVER_IP_SUBNET_#}"; do
ALLIPS=$ALLIPS,${!var}
done
More info on this feature in the bash manual.
One idea:
SERVER_IP48_SUBNET=48sub
SERVER_IP49_SUBNET=49sub
SERVER_IP50_SUBNET=50sub
SERVER_IP=1.2.3.4
ALLIPs=${SERVER_IP}
for i in {48..50}
do
tmpvar="SERVER_IP${i}_SUBNET" # build the variable name
ALLIPs="${ALLIPs},${!tmpvar}" # indirect variable reference via tmpvar
done
echo "ALLIPs = $ALLIPs}"
This generates:
ALLIPs = 1.2.3.4,48sub,49sub,50sub

Conditional on non-instantiated variable

I am new to Bash scripting, having a lot more experience with C-type languages. I have written a few scripts with a conditional that checks the value of a non-instantiated variable and if it doesn't exist or match a value sets the variable. On top of that the whole thing is in a for loop. Something like this:
for i in ${!my_array[#]}; do
if [ "${my_array[i]}" = true ]
then
#do something
else
my_array[i]=true;
fi
done
This would fail through a null pointer in Java since my_array[i] is not instantiated until after it is checked. Is this good practice in Bash? My script is working the way I designed, but I have learned that just because a kluge works now doesn't mean it will work in the future.
Thanks!
You will find this page on parameter expansion helpful, as well as this one on conditionals.
An easy way to test a variable is to check it for nonzero length.
if [[ -n "$var" ]]
then : do stuff ...
I also like to make it fatal to access a nonexisting variable; this means extra work, but better safety.
set -u # unset vars are fatal to access without exception handling
if [[ -n "${var:-}" ]] # handles unset during check
then : do stuff ...
By default, referencing undefined (or "unset") variable names in shell scripts just gives the empty string. But is an exception: if the shell is run with the -u option or set -u has been run in it, expansions of unset variables are treated as errors and (if the shell is not interactive) cause the shell to exit. Bash applies this principle to array elements as well:
$ array=(zero one two)
$ echo "${array[3]}"
$ echo "array[3] = '${array[3]}'"
array[3] = ''
$ set -u
$ echo "array[3] = '${array[3]}'"
-bash: array[3]: unbound variable
There are also modifiers you can use to control what expansions do if a variable (or array element) is undefined and/or empty (defined as the empty string):
$ array=(zero one '')
$ echo "array[2] is ${array[2]-unset}, array[3] is ${array[3]-unset}"
array[2] is , array[3] is unset
$ echo "array[2] is ${array[2]:-unset or empty}, array[3] is ${array[3]:-unset or empty}"
array[2] is unset or empty, array[3] is unset or empty
There are a bunch of other variants, see the POSIX shell syntax standard, section 2.6.2 (Parameter Expansion).
BTW, you do need to use curly braces (as I did above) around anything other than a plain variable reference. $name[2] is a reference to the plain variable name (or element 0 if it's an array), followed by the string "[2]"; ${name[2]}, on the other hand, is a reference to element 2 of the array name. Also, you pretty much always want to wrap variable references in double-quotes (or include them in double-quoted strings), to prevent the shell from "helpfully" splitting them into words and/or expanding them into lists of matching files. For example, this test:
if [ $my_array[i] = true ]
is (mostly) equivalent to:
if [ ${my_array[0]}[i] = true ]
...which isn't what you want at all. But this one:
if [ ${my_array[i]} = true ]
still doesn't work, because if my_array[i] is unset (or empty) it'll expand to the equivalent of:
if [ = true ]
...which is bad test expression syntax. You want this:
if [ "${my_array[i]}" = true ]

Bash Automatically replacing [0:100] with 1

I'm writing a simple graphing script that uses gnuplot, and I use a helper function to construct a .gscript file.
add_gscript() {
echo "->>"
echo $1
echo $1 >> plot.gscript
cat plot.gscript
}
However after passing the following argument into the function
echo "--"
echo 'set xrange [0:$RANGE]'
echo "--"
add_gscript "set xrange [0:100]"
where $RANGE has been defined beforehand, I get the following output
--
set xrange [0:$RANGE]
--
->>
set xrange 1
set datafile separator ","
set term qt size 800,640
set size ratio .618
set xrange 1
Is bash evaluating [0:100] to 1 somehow?
A fix was accurately described in comments on the question:
Always double-quote variable references to have their values treated as literals; without quoting, the values are (in most contexts) subject to shell expansions, including word splitting and pathname expansion. [0:100] happens to be a valid globbing pattern that matches any file in the current dir. named either 0 or : or 1. – mklement0
so echo "$1"; echo "$1" >> plot.gscript and any other unquoted vars. Good luck. – shellter
Double quoting the variables did indeed fix my issue. Thanks!

How to make a script read a value from a property file and pass it to the same script?

I am new to linux shell script. i want that my script read a property file and save the value in any variable , the same which i can pass in same script..
as i wrote script is not fulfilling my requirement:
!/bin/bash
. test1
flat
if [ "$1" == test1 ]; then
flat=$1; /assign value to var flat
echo "flat"
fi
test1 is property file which includes :
la=12
tu=15
now i want when i run:
./myscript la
it read it from property file and store the value in flat variable.
Please help me.
You just need to use indirect referencing, but to do so, you need to store the value of the special parameter $1 in a regular parameter first.
!/bin/bash
. test1
var="$1"
# Only assign to flat if the variable specified in var is defined
if [ -n "${!var:-}" ]; then
flat="${!var}"; # assign value to var flat
echo "flat"
fi
First, ${!var} expands to the value of the variable whose name is in var. If var is "foo", it's the same as $foo. If var is "baz", it's the same as $baz.
${var:-default} expands to the value of var if it is set and has a non-null value. Otherwise, it expands to whatever you have after the ':-', in this case the string default. If there is no string, it uses the null value. So ${var:-} would expand to the null string if var was not set (or was already the null string).
Combining the two, ${!var:-} takes the variable var, and uses its value as a variable name. It then tries to expand that variable, and if it isn't set or is null, expand to the null string. So if var is la, it expands to the value of la. If var is re, and there is no variable re set, it expands to the null string.
Finally, the -n operator tests if its argument is non-zero length. In other words, it checks that the result of trying to expand the variable whose name is in var is not the null string. If that's true, then expand it again (yes, it's a little redundant) and assign its value to flat.
As the answer is written above, the variable flat is undefined if the argument to the script is not the name of a variable set in test1. If you don't mind flat being set regardless (say, flat=""), you don't need the if statement. You can just use one line to set the value of flat:
#!/bin/bash
. test1
var="$1"
flat="${!var:-}"
If I understood correctly you want to achieve an indirect variable dereferencing (see e.g. this example).
The solution is to use eval:
eval flat=\$$1

Resources