bash: Execute a string as a command - string

See my previous question on assembling a specific string here.
I was given an answer to that question, but unfortunately the information didn't actually help me accomplish what I was trying to achieve.
Using the info from that post, I have been able to assemble the following set of strings: gnuplot -e "filename='output_N.csv'" 'plot.p' where N is replaced by the string representation of an integer.
The following loop will explain: (Actually, there is probably a better way of doing this loop, which you may want to point out - hopefully the following code won't upset too many people...)
1 #!/bin/bash
2 n=0
3 for f in output_*.csv
4 do
5 FILE="\"filename='output_"$n".csv'\""
6 SCRIPT="'plot.p'"
7 COMMAND="gnuplot -e $FILE $SCRIPT"
8 $COMMAND
9 n=$(($n+1))
10 done
Unfortunately this didn't work... gnuplot does run, but gives the following error message:
"filename='output_0.csv'"
^
line 0: invalid command
"filename='output_1.csv'"
^
line 0: invalid command
"filename='output_2.csv'"
^
line 0: invalid command
"filename='output_3.csv'"
^
line 0: invalid command
...
So, as I said before, I'm no expert in bash. My guess is that something isn't being interpreted correctly - either something is being interpreted as a string where it shouldn't or it is not being interpreted as a string where it should? (Just a guess?)
How can I fix this problem?
The first few (relevant) line of my gnuplot script are the following:
(Note the use of the variable filename which was entered as a command line argument. See this link.)
30 fit f(x) filename using 1:4:9 via b,c,e
31
32 plot filename every N_STEPS using 1:4:9 with yerrorbars title "RK45 Data", f(x) title "Landau Model"

Easy fix - I made a mistake with the quotation marks. ("")
Essentially, the only reason why the quotation marks " and " are required around the text filename='output_"$n".csv' is so that this string is interpreted correctly by bash, before executing the command! So indeed it is correct that the program runs when the command gnuplot -e "filename='output_0.csv'" 'plot.p' is entered into the terminal directly, but the quotation marks are NOT required when assembling the string beforehand. (This is a bit difficult to explain, but hopefully it is clear in your mind the difference between the 2.)
So the corrected version of the above code is:
1 #!/bin/bash
2 n=0
3 for f in output_*.csv
4 do
5 FILE="filename='output_"$n".csv'"
6 SCRIPT='plot.p'
7 COMMAND="gnuplot -e $FILE $SCRIPT"
8 $COMMAND
9 n=$(($n+1))
10 done
That is now corrected and working. Note the removal of the escaped double quotes.

Related

Bashscript throws command error when populating variable [duplicate]

This question already has answers here:
How do I set a variable to the output of a command in Bash?
(15 answers)
Bash variable from command with pipes, quotes, etc
(2 answers)
Variable variable assignment error -"command not found"
(1 answer)
Closed 1 year ago.
i have the following two lines in a batch script
iperf_options=" -O 10 -V -i 10 --get-server-output -P " $streams
$iperf_options=$iperf_options $proto
and
$streams = 2
$proto = -u
but when i run this i get the following error.
./bandwidth: line 116: -O: command not found
I am simply trying to wrote a string and then append it to a variable so why does it throw the error on the -O?
I have looked about the web but i jsut seem to find stuff about spaces around the "="
any help greatfully recived.
Thankyou
code block to show error
proto=-u
streams=2
iperf_options=" -O 10 -V -i 10 --get-server-output -P " $streams
$iperf_options=$iperf_options $proto
running this will give this out put
./test
./test: line 3: 2: command not found
./test: line 4: =: command not found
There are two main mistakes here, in a variety of combinations.
Use $ to get the value of a variable, never when setting the variable (or changing its properties):
$var=value # Bad
var=value # Good
var=$othervar # Also good
Spaces are critical delimiters in shell syntax; adding (or removing) them can change the meaning of a command in unexpected ways:
var = value # Runs `var` as a command, passing "=" and "value" as arguments
var=val1 val2 # Runs `val2` as a command, with var=val1 set in its environment
var="val1 val2" # Sets `var1` to `val1 val2`
So, in this command:
iperf_options=" -O 10 -V -i 10 --get-server-output -P " $streams
The space between iperf_options="..." and $streams means that it'll expand $streams and try to run it as a command (with iperf_options set in its environment). You want something like:
iperf_options=" -O 10 -V -i 10 --get-server-output -P $streams"
Here, since $streams is part of the double-quoted string, it'll be expanded (variable expand inside double-quotes, but not in single-quoted), and its value included in the value assigned to iperf_options.
There's actually a third mistake (or at least dubious scripting practice): building lists of options as simple string variables. This works in simple cases, but fails when things get complex. If you're using a shell that supports arrays (e.g. bash, ksh, zsh, etc, but not dash), it's better to use those instead, and store each option/argument as a separate array element, and then expand the array with "${arrayname[#]}" to get all of the elements out intact (yes, all those quotes, braces, brackets, etc are actually needed).
proto="-u" # If this'll always have exactly one value, plain string is ok
streams=2 # Same here
iperf_options=(-O 10 -V -i 10 --get-server-output -P "$streams")
iperf_options=("${iperf_options[#]}" "$proto")
# ...
iperf "${iperf_options[#]}"
Finally, I recommend shellcheck.net to sanity-check your scripts for common mistakes. A warning, though: it won't catch all errors, since it doesn't know your intent. For instance, if it sees var=val1 val2 it'll assume you meant to run val2 as a command and won't flag it as a mistake.

How to use right the dialog command in the sh script? [duplicate]

This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 5 years ago.
I wrote some script:
#!/bin/sh
dialog --menu \
"Please select a partition from the following list to use for your \
root (/) Linux partition." 13 70 3 \
"/dev/hda2" "Linux native 30724312K" "/dev/hda4" "Linux native 506047K"
DISKS='"disk1" "50 Gb" "disk2" "100 Gb"'
dialog --menu \
"Please select a partition from the following list to use for your \
root (/) Linux partition." 13 70 3 \
$DISKS
The first call looks good.
The second call looks bad.
I need to transmit information about the drives through the variable. Please tell me why the second version of the call does not work as expected?
Let's ask shellcheck:
$ shellcheck myscript
In myscript line 9:
DISKS='"disk1" "50 Gb" "disk2" "100 Gb"'
^-- SC2089: Quotes/backslashes will be treated literally. Use an array.
In myscript line 14:
$DISKS
^-- SC2090: Quotes/backslashes in this variable will not be respected.
The detailed error page offers an explanation:
Bash does not interpret data as code. Consider almost any other languages, such as Python:
print 1+1 # prints 2
a="1+1"
print a # prints 1+1, not 2
Here, 1+1 is Python syntax for adding numbers. However, passing a literal string containing this expression does not cause Python to interpret it, see the + and produce the calculated result.
Similarly, "My File.txt" is Bash syntax for a single word with a space in it. However, passing a literal string containing this expression does not cause Bash to interpret it, see the quotes and produce the tokenized result.
The solution is to use an array instead, whenever possible.
Ok, let's try that:
#!/bin/bash
DISKS=("disk1" "50 Gb" "disk2" "100 Gb")
dialog --menu \
"Please select a partition from the following list to use for your \
root (/) Linux partition." 13 70 3 \
"${DISKS[#]}"
This works.
However, this is a bash specific solution. If you want it to work for sh, in this case, you can pick a delimiter other than whitespace by setting the Internal Field Separator:
#!/bin/sh
IFS=":" # Split on colons
set -f # Prevent globbing
DISKS='disk1:50 Gb:disk2:100 Gb'
dialog --menu \
"Please select a partition from the following list to use for your \
root (/) Linux partition." 13 70 3 \
$DISKS
This also works as expected. If your script is longer than this, you'd want to set IFS back to its original value.

Concatenating string read from file with string literals creates jumbled output

My problem is that the result is jumbled. Consider this script:
#!/bin/bash
INPUT="filelist.txt"
i=0;
while read label
do
i=$[$i+1]
echo "HELLO${label}WORLD"
done <<< $'1\n2\n3\n4'
i=0;
while read label
do
i=$[$i+1]
echo "HELLO${label}WORLD"
done < "$INPUT"
filelist.txt
5
8
15
67
...
The first loop, with the immediate input (through something I believe is called a herestring (the <<< operator) gives the expected output
HELLO1WORLD
HELLO2WORLD
HELLO3WORLD
HELLO4WORLD
The second loop, which reads from the file, gives the following jumbled output:
WORLD5
WORLD8
WORLD15
WORLD67
I've tried echo $label: This works as expected in both cases, but the concatenation fails in the second case as described. Further, the exact same code works on my Win 7, git-bash environment. This issue is on OSX 10.7 Lion.
How to concatenate strings in bash |
Bash variables concatenation |
concat string in a shell script
Well, just as I was about to hit post, the solution hit me. Sharing here so someone else can find it - it took me 3 hours to debug this (despite being on SO for almost all that time) so I see value in addressing this specific (common) use case.
The problem is that filelist.txt was created in Windows. This means it has CRLF line endings, while OSX (like other Unix-like environments) expects LF only line endings. (See more here: Difference between CR LF, LF and CR line break types?)
I used the answer here to convert the file before consumption. Using sed I managed to replace only the final line's carriage return, so I stuck to known guns and went for the perl approach. Final script is below:
#!/bin/bash
INPUTFILE="filelist.txt"
INPUT=$(perl -pe 's/\r\n|\n|\r/\n/g' "$INPUTFILE")
i=0;
while read label
do
i=$[$i+1]
echo "HELLO${label}WORLD"
done <<< $'INPUT'
Question has been asked in a different form at Bash: Concatenating strings fails when read from certain files

Dividing in Linux with date in two files

I must take 100 located in File2 and divide it by 5 located in File1. This has to be done in a script. The book is not too clear on how to proceed. I have tried many different iterations of the script but always come back with an error referencing "/". I have put the expression in backtick, double parenthesis and brackets. The OS is Red Hat Linux.
The script:
cat File1 File2
#!/bin/bash
var3=$[$var2 / $var1]
This is what I get:
var1=5
var2=100
/home/Student/MyFiles/student/week3prog3student.scr: line 3: / : syntax error: operand expected (error token is "/ ")
#!/bin/bash
var3=$(($var2 / $var1))
Here's a nifty way of doing floating point division that is scriptable. Bash only does integer division via $((x/y))
var2=10
var1=3
echo "scale=2; $var2/$var1" | bc
scale is the number of decimal digits after the decimal point.

bash syntax error when using case statement

I have bash script that I use regularly in my job to automate a large job. I was making some changes today, but everything seemed fine. The script itself is about 1700 lines long. The first part of the script is all good and runs through all the user input and logic just fine. It then proceeds into the core of the script and stops working at exactly line 875 (tested the script with bash -x to find the break point). However, it breaks with the following error:
script.sh: line 1341: syntax error near unexpected token `;;'
script.sh: line 1341: ` ;;'
Line 1341 is in the middle of a case statement. The following code is the beginning of that block of code where it is breaking:
if [[ $VAR1 = "TRUE" && $VAR2 = "VAL2" ]]; then
VERSION=`XXXXXXXXXXXXXXXX`
## Set variables based on location $VAR3
case $VAR3 in
STR1 )
case $VERSION in
STR2 )
VAR4 = "STR5"
VAR5 = "STR6"
VAR6 = "STR7"
VAR7 = "STR8"
Line 1341 ---> ;;
STR3 )
VAR4="STR9"
VAR5="STR10"
VAR6="STR11"
VAR7="STR12"
;;
STR4 )
VAR4="STR13"
VAR5="STR14"
VAR6="STR15"
VAR7="STR16"
;;
esac
VAR8="STR17"
VAR9="STR18"
VAR10=1
VAR11="STR19"
;;
Because of the sensitive nature of what I do, I obviously had to remove quite a bit of information. I know this may make things more difficult to help me with. However, all VAR##="STR##" are standard variable declarations with string values, nothing special (no variable substitution, etc). All the variables are used later in the script. The code for VERSION returns a string value, which is used in the nested case.
The script was working fine up until my changes today, but I really didn't touch this section, with the exception of tweaking some of the STR values. I tried setting $VAR3 and $VERSION variables in quotes "", as well as the STR values used as the cases. I tried taking out this block entirely, only to have it fail on the next block (STR1 has a different value thus change the variable declarations). I have it output to the console what it is doing as well as checks for errors after most functions. There is nothing out of the ordinary on the console and nothing in the error log.
Any help would be appreciated, and I know I'm asking a lot.
By the way here is the code around line 875 where the script stops running (no errors generated based on the code here). Again, with bash -x I could see the VAR2 variable get set, but the script breaks before the next for loop starts.
## Create file ##
echo 'Creating files . . . '
j=0
p=1111
if [ $VAR1 = "TRUE" ]
then
VAR2=1
else
VAR2=2
fi
for i in `seq 1 $HOWMANY`; do <----Line 875
echo -n "Creating file . . . "
echo "XXXXXXXXXXX
Thanks again.
The problem is likely somewhere between line 875 (or a bit earlier) and line 1341. It maybe a misplaced quote or something less subtle. It will be essentially impossible for us to debug without all the original material between those lines.
Suggestion 1: run with 'bash -n -v' and see whether that gives you any insight into the problem.
Suggestion 2: split the script into smaller pieces that are more easily managed - and that can be separately debugged. The biggest scripts I have (out of 400 in my bin directory) are from the autoconf suite - they weigh in at just under 1100 lines; the next biggest is mine, and the 750 line script is too d..n big. The next biggest scripts are between 600 and 700 lines of Perl (including Perl documentation).
Having said 'missing quote', I see that your fragment close to line 875 has:
echo -n "Creating file . . . "
echo "XXXXXXXXXXX
with a missing close double quote from the second echo.
You also mentioned making changes, albeit not close to the point where the script breaks. Since you have the code under version control (you wouldn't dream of playing with a 1700 line script without backups, would you?), you should look at the actual changes again.
Or even back up to the previous working version, and make the changes again, one at a time, carefully, until you see why you broke something.
You have spaces around your equal signs in this section:
case $VERSION in
STR2 )
VAR4 = "STR5"
VAR5 = "STR6"
VAR6 = "STR7"
VAR7 = "STR8"
Take those out and you may be OK (unless that's a posting error).

Resources