Can't run simple bash script - bash

I try executed some like this
#!/bin/bash
${postid=41930}
while ${postid} < 42000;
do
`node title.js ${postid}`
${postid} = ${postid} +1;
done
I have this error:
: command not found30
run.sh: line 8: syntax error: unexpected end of file
$ echo $SHELL
/bin/bash
$
From man sh
while *list*;do *list*;done
sh version 3.2

There are several places in your script needs to be fixed:
As chepner said you can't assign value to an evaluated result like ${postid}, instead use postid directly in the left hand side of your assignment
There should be some invisible characters in your script. Try to run dos2unix myscript.sh or try hand typing the the following code into an new file
https://gist.github.com/1651190
sorry i can't format the code correctly, so please check the gist at https://gist.github.com/1651190

Another quick way, using only bash features is:
#!/bin/env bash
for postid in {41930..41999} ; do node title.js ${postid} ; done
References: http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion

Probably, you want
for((postid=41930;postid<42000;++postid)) do
node title.js $postid
done

The other answers are probably what you want to use. FYI, here's where your errors came from.
${postid=41930}
To assign 41930 to posted, just use postid=41930. Note there are no spaces around the equals sign!
while ${postid} < 42000;
The {} around postid are optional; $postid works just as well.. You do need to wrap that conditional expansion in a command, as the while loop can't use a bare expression. Something like while [ $postid < 42000 ];. Note that in this case, you must have spaces separating the [ and ] from the rest of the expression.
do
`node title.js ${postid}`
${postid} = ${postid} +1;
In order to assign a value to a variable, bash does not allow spaces around the equal sign. With spaces, it interprets this line by expanding $postid and treating that as a command to run, with = as the first argument. Use postid=$postid + 1;. On the left, no dollar sign is needed, as you are not expanding the value of a variable, but assigning to a name. On the right, you need the dollar sign to get the value of posted.
done

Related

Extracting git commit information in GitHub action workflow- use of '$' symbol [duplicate]

This question already has answers here:
Backticks vs braces in Bash
(3 answers)
Brackets ${}, $(), $[] difference and usage in bash
(1 answer)
Closed 4 years ago.
I have two questions and could use some help understanding them.
What is the difference between ${} and $()? I understand that ()
means running command in separate shell and placing $ means passing
the value to variable. Can someone help me in understanding
this? Please correct me if I am wrong.
If we can use for ((i=0;i<10;i++)); do echo $i; done and it works fine then why can't I use it as while ((i=0;i<10;i++)); do echo $i; done? What is the difference in execution cycle for both?
The syntax is token-level, so the meaning of the dollar sign depends on the token it's in. The expression $(command) is a modern synonym for `command` which stands for command substitution; it means run command and put its output here. So
echo "Today is $(date). A fine day."
will run the date command and include its output in the argument to echo. The parentheses are unrelated to the syntax for running a command in a subshell, although they have something in common (the command substitution also runs in a separate subshell).
By contrast, ${variable} is just a disambiguation mechanism, so you can say ${var}text when you mean the contents of the variable var, followed by text (as opposed to $vartext which means the contents of the variable vartext).
The while loop expects a single argument which should evaluate to true or false (or actually multiple, where the last one's truth value is examined -- thanks Jonathan Leffler for pointing this out); when it's false, the loop is no longer executed. The for loop iterates over a list of items and binds each to a loop variable in turn; the syntax you refer to is one (rather generalized) way to express a loop over a range of arithmetic values.
A for loop like that can be rephrased as a while loop. The expression
for ((init; check; step)); do
body
done
is equivalent to
init
while check; do
body
step
done
It makes sense to keep all the loop control in one place for legibility; but as you can see when it's expressed like this, the for loop does quite a bit more than the while loop.
Of course, this syntax is Bash-specific; classic Bourne shell only has
for variable in token1 token2 ...; do
(Somewhat more elegantly, you could avoid the echo in the first example as long as you are sure that your argument string doesn't contain any % format codes:
date +'Today is %c. A fine day.'
Avoiding a process where you can is an important consideration, even though it doesn't make a lot of difference in this isolated example.)
$() means: "first evaluate this, and then evaluate the rest of the line".
Ex :
echo $(pwd)/myFile.txt
will be interpreted as
echo /my/path/myFile.txt
On the other hand ${} expands a variable.
Ex:
MY_VAR=toto
echo ${MY_VAR}/myFile.txt
will be interpreted as
echo toto/myFile.txt
Why can't I use it as bash$ while ((i=0;i<10;i++)); do echo $i; done
I'm afraid the answer is just that the bash syntax for while just isn't the same as the syntax for for.
your understanding is right. For detailed info on {} see bash ref - parameter expansion
'for' and 'while' have different syntax and offer different styles of programmer control for an iteration. Most non-asm languages offer a similar syntax.
With while, you would probably write i=0; while [ $i -lt 10 ]; do echo $i; i=$(( i + 1 )); done in essence manage everything about the iteration yourself

Bash function with parameters

I am trying to understand how to work with functions (that receive a argument) in bash. I did not get my code to work, the following code exemplifies my difficulties:
#!/bin/bash
fruit_code () {
if [[ "$1" == "apple" ]]; then
code=1
else
code=0
fi
return $code
}
for var in apple orange banana apple; do
code=fruit_code $var
echo $code
done
The shell (bash) complains saying:
apple: command not found
orange: command not found
banana: command not found
apple: command not found
So it seems the passing of parameters is not working propperly. I can not see where I am going wrong. Any help is very much appreciated. Why does it not work? What changes shoyld I do to make it work? Wish to thank you all in advance.
Kind regards
Miguel
The first problem is that if you want to execute a command and capture its output to a variable, you need to use command substitution:
code=$(fruit_code "$var")
The second problem is that your function doesn't write anything to standard output; it returns an exit status. That is automatically assigned to $? after you call the function. Use this instead:
fruit_code "$var"
echo $?
Finally, the convention in shell is for a 0 exit status to indicate success and a non-zero value failure. In this case, your function "succeeds" if the argument is not apple.
You have a syntax error in this line:
code=fruit_code $var
A syntax like this:
foo=bar quux
means that you want to run command quux in an environment where variable foo has value bar.
In your case, quux is $var, so it will take the value of $var and try to run it as a command. That's why you are getting errors saying apple, orange, etc. are not commands.
I think what you actually want is to run fruit_code $var and store the output in variable code, right?
In that case you can use this:
code=$(fruit_code $var)
or this:
code=`fruit_code $var`
I prefer the former because it looks clearer to me and allows easy nesting.
Update: Returning strings in functions
Also, as noted by chepner on his answer and as you also noticed in a comment below, you are trying to use return for returning that string value as the result of the function, but return can only be used for returning numeric values as the result of the function (success / error / etc.) so you try to work it around by using numeric values that represent the strings you want to return.
That's overcomplex.
If you want to return a string, use echo instead and you can use $() or `` as explained above to capture the string returned by your function.
So, putting all this together, your code would look like this:
#!/bin/bash
fruit_code () {
echo $1
}
for var in apple orange banana apple; do
code=$(fruit_code $var)
echo $code
done

Using a bash script to insert into an SQL table with $s

I'm using a bash script to make changes to an SQL database. One of the values i'm updating uses dollar signs. The current value being something like "$$$$$" and i need to change it to "$$$$$$$$$$". However, a a $ in a bash script is used for variables.
How can i allow this small section of my bash script to used a $ as a normal character?
function prep() {
DATE_STAMP=$(date +%m%d%Y)
log "Changing mask to 10 characters"
log "$(/opt/CPU/bin/connx-query -q "update TYPE set TYPE.MASK = '$$$$$$$$$$'")"
}
As it stands right now, its just replacing each dollar sign with some random number found earlier in my script.
Bash provides different types of quoting, each with different rules about substitution (single quote ', double quote ", here document/string <<<"string" and and $'.
The double quote (used in the log ... update) will enable variable substitution, replacing each pair of $$ with the current shell PID (looks like random number).
Few options:
Consider quoting each '$' to prevent expansion
log "$(/opt/CPU/bin/connx-query -q "update TYPE set TYPE.MASK = '\$\$\$\$\$\$\$\$\$\$'")"
Over thought my own question. I can just escape the $. '\$\$\$\$\$\$\$\$\$\$'

Get the contents of an expanded expression given to eval through Bash internals

I'm writing some shell functions that allow to print stack traces when errors occur. For this I'm using the BASH_LINENO array which contain the line number for each frame. Then I retrieve the line from the file using BASH_SOURCE array and a subprocess like line="$(tail -n+$lineno "$file" | head -n1)".
Anyway, it works well, except when an error occur within an eval. The problem is that the line number corresponds to the line after the expression given to eval has been expanded. Therefore, when I retrieve the line with head and tail, obviously it's now the wrong one, or it's not a line at all (lineno is superior to the number of lines in the file).
So I wonder how I could get the actual expanded line. I looked at the variables provided by Bash, but none seems to help in this case.
Example, script1.sh:
#!/usr/bin/env bash
eval "$(./script2.sh)"
script2.sh:
#!/usr/bin/env bash
echo
echo
echo
echo false
When I hit the false line when executing script1.sh, the line number I get is 4, and the file source I get is script1.sh, so it's wrong.
When the line is out of the file, I could detect it, and print the first previous eval line instead, but it's very hacky and I'm sure there are a few different cases to handle. And if the line is within the file, then I cannot even know if it's the right one or not.
eval is hell :'(
Ideally, the BASH_COMMAND would be an array as well, and I could retrieve the commands from it instead of reading the files.
Another idea I just have would be to force the user to pipe the result of the expression into a command that will compress it on one line. Any ideas how, or programs to do that? A simple join on ";" seems to naive (again, lots of edge cases).
P.S.: sorry for the title, I have difficulty giving a meaningful title to this one :/
Eventually I found a workaround: by overriding the eval command with my own function, I was able to change the way I print the stack trace for errors happening in eval statements.
eval() {
# pre eval logic
command eval "$#"
# post eval logic
}
Anyway, please don't use eval, or if you do, use only one line arguments:
# GOOD: "easy" to deal with
for i in ...; do
eval "$(some command)"
done
# BAD: this will mess up your line numbers
eval "$(for i in ...; do
some command $i
done)"

error in shell script: unexpected end of file

The following script is showing me "unexpected end of file" error. I have no clue why am I facing this error. My all the quotes are closed properly.
#!/usr/bin/sh
insertsql(){
#sqlite3 /mnt/rd/stats_flow_db.sqlite <<EOF
echo "insert into flow values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)"
#.quit
}
for i in {1..100}
do
src_ip = "10.1.2."+$i
echo $src_ip
src_ip_octets = ${src_ip//,/}
src_ip_int = $src_ip_octets[0]*1<<24+$src_ip_octets[1]*1<<16+$src_ip_octets[2]*1<<8+$src_ip_octets[3]
dst_ip = "10.1.1."+$i
dst_ip_octets = ${dst_ip//,/}
dst_ip_int = $dst_ip_octets[0]*1<<24+$dst_ip_octets[1]*1<<16+$dst_ip_octets[2]*1<<8+$dst_ip_octets[3]
insertsql(1, 10000, $dst_ip, 20000, $src_ip, "2012-08-02,12:30:25.0","2012-08-02,12:45:25.0",0,0,0,"flow_a010105_a010104_47173_5005_1_50183d19.rrd",0,12,$src_ip_int,$dst_ip_int,3,50000000,80000000)
done
That error is caused by <<. When encountering that, the script tries to read until it finds a line which has exactly (starting in the first column) what is found after the <<. As that is never found, the script searches to the end and then complains that the file ended unexpectedly.
That will not be your only problem, however. I see at least the following other problems:
You can only use $1 to $9 for positional parameters. If you want to go beyond that, the use of the shift command is required or, if your version of the shell supports it, use braces around the variable name; e.g. ${10}, ${11}...
Variable assignments must not have whitespace arount the equal sign
To call your insertsql you must not use ( and ); you'd define a new function that way.
The cass to your insertsql function must pass the parameters whitespace separated, not comma separated.
A couple of problems:
There should be no space between equal sign and two sides of an assignment: e.g.,: dst_ip="10.1.1.$i"
String concatenation is not done using plus sign e.g., dst_ip="10.1.1.$i"
There is no shift operator in bash, no <<: $dst_ip_octets[0]*1<<24 can be done with expr $dst_ip_octets[0] * 16777216 `
Functions are called just like shell scripts, arguments are separated by space and no parenthesis: insertsql 1 10000 ...
That is because you don't follow shell syntax.
To ser variable you are not allowed to use space around = and to concatenate two parts of string you shouldn't use +. So the string
src_ip = "10.1.2."+$i
become
src_ip="10.1.2.$i"
Why you're using the string
src_ip_octets = ${src_ip//,/}
I don't know. There is absolutely no commas in you variable. So even to delete all commas it should look like (the last / is not required in case you're just deleting symbols):
src_ip_octets=${src_ip//,}
The next string has a lot of symbols that shell intepreter at its own way and that's why you get the error about unexpected end of file (especially due to heredoc <<)
src_ip_int = $src_ip_octets[0]*1<<24+$src_ip_octets[1]*1<<16+$src_ip_octets[2]*1<<8+$src_ip_octets[3]
So I don't know what exactly did you mean, though it seems to me it should be something like
src_ip_int=$(( ${src_ip_octets%%*.}+$(echo $src_ip_octets|sed 's/[0-9]\+\.\(\[0-9]\+\)\..*/\1/')+$(echo $src_ip_octets|sed 's/\([0-9]\+\.\)\{2\}\(\[0-9]\+\)\..*/\1/')+${src_ip_octets##*.} ))
The same stuff is with the next strings.
You can't do this:
dst_ip_int = $dst_ip_octets[0]*1<<24+$dst_ip_octets[1]*1<<16+$dst_ip_octets[2]*1<<8+$dst_ip_octets[3]
The shell doesn't do math. This isn't C. If you want to do this sort of calculation, you'll need to use something like bc, dc or some other tool that can do the sort of math you're attempting here.
Most of those operators are actually shell metacharacters that mean something entirely different. For example, << is input redirection, and [ and ] are used for filename globbing.

Resources