Syntax error running command bc under bash 4.1 - bash

I have a bash script with the following line. The variables start_time and start_files[$i] are floating point numbers. I want to compare them using the command bc as follows:
result1=$(bc -l <<< $start_time'>='${start_files[$i]})
When I run the script I always receive the following error.
(standard_in) 1: syntax error
I've checked that this error is due to this line. What am I doing wrong? The thing that this happens to me when using bash 4.1, with bash 4.3 runs fine. However I need to run the script with bash 4.1.
The bc command works however the input doesn't work. The variable start_files is read from a file with this command
IFS=, read -r -a start_files <<< $(head -n 1 file.txt)
Basically I want to read all the values from the first line separated by a coma and store them to different positions of the array. However using bash 4.1 all the values end up stored in start_files[0]. How can I solve this? This lines works for bash 4.3

I would suggest that you use double quotes:
result1=$(bc -l <<<"$start_time >= ${start_files[$i]}")
This way, you are echoing a single string to bc, containing your variables.

Are you reading the values from a file with DOS-style line endings perhaps?
$ start_time=$'1.5\r'
$ i=1
$ start_files[$i]=$'2.5\r'
$ bc -l <<< "$start_time >= ${start_files[$i]}"
(standard_in) 1: illegal character: ^M
(standard_in) 1: illegal character: ^M

You may avoid the here-string, add double quotes and remove a $:
result1=$(echo "$start_time >= ${start_files[i]}" | bc -l)

It would help to give the version of bc you're using. I'd guess it's not the same on the two systems. By the way, your error has nothing to do with Bash.
POSIX bc clearly states that a comparison (referred to as relational_expression in POSIX' bc Specification can only appear in a while, for or if construct.
So, first thing you'll try is this:
bc <<< "if($start_time >= ${start_files[$i]}) 1 else 0"
This might not work, as POSIX' bc doesn't allow an else clause in an if statement. (As amazing as it may seem, you read that last sentence correctly).
If your bc is really POSIX stuck up, then you'll have to do some ugly stuff as, e.g.,
bc <<< "ret=0; if($start_time >= ${start_files[$i]}) { ret=1 } ret"
or even something worse (sorry, I don't have a POSIX bc here, so I can't experiment—if someone has one at hand or remembers the syntax off the top of his head, please edit this line with the proper syntax).
Good luck!

Related

Bash bad substitution when trying to uppercase first letter

I am banging my head against the wall about the Bad substitution error in Bash. Consider the following code:
getApiName() {
IFS='-' # hyphen (-) is set as delimiter
read -ra array <<< "$1" # str is read into an array as tokens separated by IFS
for i in "${array[#]}"; do # access each element of array
output+=${i^} #set first letter to uppercase
done
IFS=' '
echo ${output}
}
When I do the following:
getApiName "vl-date-picker"
I get line 21: ${i^}: bad substitution
I have no clue on what's wrong.
Can you guys help me please?
Thanks in advance.
Regards
General Answer
I cannot reproduce your problem. I see two possible reasons:
You are using a non-bash shell.
Check this by adding the command ps to the script and look at the output. If there is no bash in the output, then you are running something different. A shebang #! /bin/bash at the beginning of your script helps to ensure that bash is used but is not a guarantee. ✱
You have an old version of bash which does not support ${i^}
(for instance that 15 (!) year old version pre-installed on Mac OS X).
You can check your bash version using bash --version. ${i^} was introduced in bash 4.0, as can be read here (search for hh. There are new case-modifying word expansions) or here.
Either way, you can use a different command which should work in all Posix shells.
If you have the GNU version of sed (check with sed --version) this command could be
getApiName() {
printf %s "$1" | sed -E 's/(^|-+)(.)/\U\2/g'
}
Nmp-Specific Answer
✱
The documentation of npm-run-script states
The actual shell your script is run within is platform dependent. By default, on Unix-like systems it is the /bin/sh command, on Windows it is the cmd.exe. The actual shell referred to by /bin/sh also depends on the system. As of npm#5.1.0 you can customize the shell with the script-shell configuration.
So to fix your problem you simply have to configure npm such that bash is used instead.
As a workaround, you could also call bash directly in your script. The simplest way to do so is a here-document:
bash -s -- "$#" <<"EOF"
# your original script here
EOF

Shell syntax IFS

I'm trying to run a simple shell script example:
STR="qwe;ert;rty;tii"
IFS=';' read -r NAMES <<< "$STR"
This is giving me the following error message:
syntax error: got <&, expecting Word
I'm not exactly sure why this isn't working. I thought the syntax I used was
correct and I tried comparing to other examples and I saw the syntax was almost
identical.
Any feedback would help
Thanks
This is MKS bash, not GNU bash. It's not really bash, and doesn't support the genuine shell's syntax.
There are perfectly good (...well, reasonably adequate) builds of GNU bash for Windows. Use them.
Particularly, in real bash, to split a semicolon-separated string into an array of names:
str='qwe;ert;rty;tii'
IFS=';' read -r -a names <<<"$str"
...which you can then verify with
declare -p names
or emit one-to-a-line with
printf '%s\n' "${names[#]}"

Syntax error with use of pipe in bash

I'm diving headfirst into bash with no prior experience and have hit a bit of a snag: I wrote a small bash script to determine the average of values (it's just a total right now) being returned by a c executable.
#Sample value of #s1total: 0+0.000117+0.000149+0.000106
printf "\n%s" $s1total
#The following line works
printf "\nTotal: %s\n" $(bc <<< $s1total)
#the following also works
echo
echo -n "Total: "
echo $s1total | bc
#The following line does not work
printf "\nTotal: %s\n" $($s1total|bc)
I did eventually find that the last line can be made to work by changing it to $(echo $s1total|bc) but i don't understand why that works ...
If I run as-is, I get an error:./sievetest.sh: line 25: 0+0.000117+0.000149+0.000106: command not found
Is the string being run before the pipe? Why? Why does adding "echo" fix it?
How is the third method different than the first and second?
(as an aside, I thought the heredoc redirection operator was << , why the extra < ?)
You have this:
$($s1total|bc)
which is basically
$(0+0.000117+0.000149+0.000106 | bc)
e.g. run a command named 0+.... and pipe it to bc.
It should be
$(echo $s1total|bc)

Using a BASH script variable into an executed command

Can someone please help with this because I can't seem to find a solution. I have the following script that works fine:
#!/bin/bash
#Checks the number of lines in the userdomains file
NUM=`awk 'END {print NR}' /etc/userdomains.hristian`;
echo $NUM
#Prints out a particular line from the file (should work with $NUM eventually)
USER=`sed -n 4p /etc/userdomains.hristian`
echo $USER
#Edits the output so that only the username is left
USER2=`echo $USER | awk '{print $NF}'`
echo $USER2
However, when I substitute the 4 on line 12 with the variable $NUM, like this, it doesn't work:
USER=`sed -n $NUMp /etc/userdomains.hristian`
I tried a number of different combinations of quotes and ${}, however none of them seem to work because I'm a BASH newbie. Help please :)
I'm not sure exactly what you've already tried but this works for me:
$ cat out
line 1
line 2
line 3
line 4
line 5
$ num=4
$ a=`sed -n ${num}p out`
$ echo "$a"
line 4
To be clear, the issue here is that you need to separate the expansion of $num from the p in the sed command. That's what the curly braces do.
Note that I'm using lowercase variable names. Uppercase ones should be be reserved for use by the shell. I would also recommend using the more modern $() syntax for command substitution:
a=$(sed -n "${num}p" out)
The double quotes around the sed command aren't necessary but they don't do any harm. In general, it's a good idea to use them around expansions.
Presumably the script in your question is a learning exercise, which is why you have done all of the steps separately. For the record, you could do the whole thing in one go like this:
awk 'END { print $NF }' /etc/userdomains.hristian
In the END block, the values from the last line in the file can still be accessed, so you can print the last field directly.
Your trying to evaluate the variable $NUMp rather than $NUM. Try this instead:
USER=`sed -n ${NUM}p /etc/userdomains.hristian`

I'm having trouble performing arithmetic expressions in UNIX

I have the following script:
#!/bin/sh
r=3
r=$((r+5))
echo r
However, I get this error:
Syntax error at line 3: $ unexpected.
I don't understand what I'm doing wrong. I'm following this online guide to the letter http://www.unixtutorial.org/2008/06/arithmetic-operations-in-unix-scripts/
This sounds fine if you're using bash, but $((r+5)) might not be supported if you're using another shell. What does /bin/sh point to? Have you considered replacing it with /bin/bash if it's available?
The shebang line is your problem. bash is not sh. Change it to #!/bin/bash and it will work. You'll also want echo $r instead of echo r.
It works for me (printing 8), if you change echo r to echo $r. What version of sh do you have installed? What unix distribution?
You might want to try the following:
#!/bin/sh
r=3
r=$((r + 5))
echo $r
For doing maths (including decimals/floats), you can use awkor bc/dc.
awk -vr="$r" 'BEGIN{r=r+5;print r}'
or
echo "$r+5" | bc

Resources