I want to pass an empty string as one of the values to a bash for-loop – like this:
for var in "" A B C; do
...
done
This works. However, I would like to store the possible values in a variable, like this:
VARS="" A B C
for var in $VARS; do
...
Here, the empty string is ignored (or all values are concatenated if I use for var in "$VARS"). Is there an easy way to solve this?
You can't. Don't do that. Use an array.
This is a version of Bash FAQ 050.
VARS=("" A B C)
for var in "${VARS[#]}"; do
: ...
done
And you almost never want to use an unquoted variable (like for var in $VARS).
I would suggest using an array
#!/bin/bash
array=("" 1 2 "")
for i in "${array[#]}";do
echo $i
done
Related
I'm trying to split a variable based on the type of characters.
I need this:
var = "word12345-54321" #No spaces to help out.
to turn into this:
var2 = "word"
var3 = "12345"
var4 = "54321"
I'm able to extract the word with regex, but I can't find a way to separate the numbers.
As you're already using a RegEx I'd suggest using a pattern like (\D*)(\d*)-(\d*) if the strings will always be in a LETTERSNUMBERS-NUMBERS format.
I'm using the npm module pg with Node.js, and have the following query:
query = "SELECT * FROM territories WHERE '($1, -122.26), (47.57, -122.36)'::box \
#> point(nwlat, nwlng) ORDER BY nwlat, nwlng;"
client.query(query, [lat+.05], callback);
When I run this, I get the following error:
invalid input syntax for type box: "($1, -122.26), (47.57, -122.36)"
But when I replace $1 with a decimal literal, like 47.67, it executes normally. What am I doing incorrectly?
Your problem is that this:
'$1'
doesn't have a placeholder in it, it is a string literal that just happens to contain some characters that look like a numbered placeholder. So this:
'($1, -122.26), (47.57, -122.36)'
doesn't have a placeholder either, that's just a string literal that happens to contain the characters $ and 1. Consider the difference between this:
let x = 6;
let y = 'x'; // String that contains the name of a variable.
and this:
let x = 6;
let y = x; // The actual variable itself.
in JavaScript, same idea.
You can build your box string using string concatenation:
WHERE ('(' || $1 || ', -122.26), (47.57, -122.36)')::box
but that's not very pretty. A cleaner solution would be to bypass strings and casting altogether by using the point and box functions:
WHERE box(point($1, -122.26), point(47.57, -122.36))
Extending on the answer by #mu is too short
With pg-promise query formatting you would get exactly what you expect ;)
Is it possible to substitute characters according to a list in Lua, like tr in Perl? For example, I would like to substitute A to B and B to A (e.g. AABBCC becomes BBAACC).
In Perl, the solution would be $str ~= tr/AB/BA/. Is there any native way of doing this in Lua? If not, I think the best solution would be iterating through the entire string, since separate substitutions need to use a special symbol to distinguish characters that were already substituted and characters that weren't.
Edit: my goal was to calculate the reverse complement of a DNA string, as described here.
string.gsub can take a table as the third argument. The table is queried for each match, using the first capture as the key, and the associated value is used as the replacement string. If the value is nil, the match is not changed.
So you can build a helper table like this:
local s = "AABBCC"
local t = {A = "B", B = "A"}
local result = string.gsub(s, "[AB]", t)
print(result)
or this same one-liner:
print((string.gsub("AABBCC", "[AB]", {A = "B", B = "A"})))
Output:
BBAACC
For a one character pattern like "[AB]", "." can work as well because whatever not found in the table won't be changed. (But I don't think that's more efficient) But for some more complicated cases, a good pattern is needed.
Here is an example from Programming in Lua: this function substitutes the value of the global variable varname for every occurrence of $varname in a string:
function expand (s)
return (string.gsub(s, "$(%w+)", _G))
end
The code below will replace each character with a desired mapping (or leave alone if no mapping exists). You could modify the second parameter to string.gsub in tr to be more specific if you know the exact range of characters.
s = "AABBCC"
mappings = {["A"]="B",["B"]="A"}
function tr(s,mappings)
return string.gsub(s,
"(.)",
function(m)
-- print("found",m,"replace with",mappings[m],mappings[m] or m)
if mappings[m] == nil then return m else return mappings[m] end
end
)
end
print(tr(s,mappings))
Outputs
henry#henry-pc:~/Desktop$ lua replace.lua
found A replace with B B
found A replace with B B
found B replace with A A
found B replace with A A
found C replace with nil C
found C replace with nil C
BBAACC 6
declare -a MY_ARRAY=()
Does the declaration of array in this way in bash will initiate all the array elements to 0?
If not, How to initiate array element to 0?
Your example will declare/initialize an empty array.
If you want to initialize array members, you do something like this:
declare -a MY_ARRAY=(0 0 0 0) # this initializes an array with four members
If you want to initialize an array with 100 members, you can do this:
declare -a MY_ARRAY=( $(for i in {1..100}; do echo 0; done) )
Keep in mind that arrays in bash are not fixed length (nor do indices have to be consecutive). Therefore you can't initialize all members of the array unless you know what the number should be.
Default Values with Associative Arrays
Bash arrays are not fixed-length arrays, so you can't pre-initialize all elements. Indexed arrays are also not sparse, so you can't really use default values the way you're thinking.
However, you can use associative arrays with an expansion for missing values. For example:
declare -A foo
echo "${foo[bar]:-baz}"
This will return "baz" for any missing key. As an alternative, rather than just returning a default value, you can actually set one for missing keys. For example:
echo "${foo[bar]:=baz}"
This alternate invocation will not just return "baz," but it will also store the value into the array for later use. Depending on your needs, either method should work for the use case you defined.
Yes, it initiates an empty array and assigns it to MY_ARRAY. You could verify with something like this:
#!/bin/bash
declare -a MY_ARRAY=()
echo ${#MY_ARRAY} # this prints out the length of the array
I would like to define a function which returns the string "NaN" or sprintf("%g",val) depending on whether val is a string or a numeric value. Initially I was trying to test if val was defined (using the gnuplot "exists" function) but it seems that I cannot pass any undefined variable to a function (an error is issued before the function is evaluated). Therefore: is there a way to test inside a function whether the argument is a string or numeric?
I search for a function isstring which I can use somehow like
myfunc(val)=(isstring(val)?"NaN":sprintf("%g",val))
The goal is to output the values of variables without risking errors in case they are undefined. However I need it as a function if I want a compact code for many variables.
Gnuplot doesn't really have the introspection abilities that many other languages have. In fact, it treats strings and numbers (at least integers) very similarly:
print "1"+2 #prints 3
a=1
print "foo".a #prints foo1
I'm not exactly sure how this is implemented internally. However, what you're asking is very tricky to get to work.
Actually, I think your first attempt (checking if a variable exists) is more sensible as type-checking in gnuplot is impossible*. You can pass the variable name to the function as a string, but the problem is that you don't seem to have a handle on the value. All seems lost -- But wait, gnuplot has an eval statement which when given a string will evaluate it. This seems great! Unfortunately, it's a statement, not a function (so it can't be used in a function -- argv!). The best solution I can come up with is to write a function which returns an expression that can be evaluated using eval. Here goes:
def exists_func(result,var)=sprintf("%s=exists('%s')?sprintf('%g',var):'NaN'",result,var,var)
Now when you want to use it, you just prefix it with eval
a=3
eval exists_func("my_true_result","a")
print my_true_result #3
eval exists_func("my_false_result","b")
print my_false_result #NaN
This goes against the grain a little bit. In most programming languages, you'd probably want to do something like this:
my_true_result=exists_func(a)
But alas, I can't figure out how to make that form work.
Of course, the same thing goes here that always goes with eval. Don't use this function with untrusted strings.
*I don't actually know that it's impossible, but I've never been able to get it to work
EDIT
In response to your comment above on the question, I think a function like this would be a little more intuitive:
def fmt(x)=(x==x)?sprintf("%g",x):"NaN"
With this function, your "sentinal/default" value should be NaN instead of "undefined", but it doesn't seem like this should make too much of a difference...(Really, if you're willing to live with "nan" instead of "NaN" you don't need this function at all -- sprintf will do just fine. (Note that this works because according to IEEE, NaN doesn't equal anything (even itself)).
You helped me a lot these days with gnuplot. I want to give you something back because I have found a solution to check if a variable is numeric or not. This helps to decide which operators can be used on it (e.g. == for numbers, eq for strings).
The solution is not very simple, but it works. It redirects gnuplot's print command to a temp file, writes the variable to the file with print myvar and evaluates the file's first line with system("perl -e '<isnumeric(line#1 in temp file)?>' ") (<> is pseudo-code). Let me know if there's room for imrpovements and let me hear your suggestions!
Example: myvar is a float. Any integer (1 or "1") or string value ("*") works too!
myvar = -2.555
# create temporary file for checking if variables are numeric
Int_tmpfle = "tmp_isnumeric_check"
# redirect print output into temp file (existing file is overwritten)
set print Int_tmpfle
# save variable's value to file
print myvar
# check if file is numeric with Perl's 'looks_like_number' function
isnumeric = system("perl -e 'use Scalar::Util qw(looks_like_number); \
open(FLE,".Int_tmpfle."); $line = < FLE >; \
if (looks_like_number($line) > 0) {print qq(y)} ' ")
# reset print output to < STDOUT> (terminal)
set print "-"
# make sure to use "," when printing string and numeric values
if (isnumeric eq "y") {print myvar," is numeric."} else {print myvar," is not numeric."}