I am trying to write a bash-script which will create a certain number of files which are passed into an argument, and also fill that file with relevant information. However, for some reason I can't get it to work properly, and googling turned up nothing.
I want it to create files like the following:
FileName:
Prob_3_ch_17.cpp
Filled as the following:
/*
User: Johnny Smith
Prob: 3
Output:
*/
Where the first command line argument is the chapter number. The second is the starting number, and the third is the ending number. So if the following is ran
sh ../FileMaker.sh 17 1 10
It will make 10 files, each filled with the appropriate data and proper file names, for chapter 17. This is the script that I came up with.
#!/bin/bash
# Syntax:
# $1 = Chapter Number
# $2 = Starting Number
# $3 = Ending Number
CHAPTER=$1
FIRST=$2
LAST=$3
U_NAME="Johnny Smith"
# Name: Prob_$NUMBER_Ch_$1.cpp
for ((NUMBER=$FIRST; $NUMBER<=$LAST; $NUMBER++));
do
if [ -e "Prob_$NUMBER_Ch_$CHAPTER.cpp" ]; then
echo "File Prob_$NUMBER_Ch_$CHAPTER.cpp already exists!"
else
echo "/* \n User: $U_NAME \n Problem: $NUMBER \n Output: \n */" >> "Prob_$NUMBER_Ch_$CHAPTER.cpp"
done
However, it doesn't run. I get the following error:
../FileMaker.sh: line 21: syntax error near unexpected token `done'
../FileMaker.sh: line 21: `done'
I've googled and found other people have had the same problem, but I didn't fully understand what was meant in the solution. Can someone please help me? I'm not the best at shell scripting, and I'm trying to learn by making scripts like this.
Thanks in advanced.
Syntax for if block and for-loop is wrong. correct syntax would be -
for ((NUMBER=FIRST; NUMBER<=LAST; NUMBER++));
do
if [ -e "Prob_$NUMBER_Ch_$CHAPTER.cpp" ]; then
echo "File Prob_$NUMBER_Ch_$CHAPTER.cpp already exists!"
else
echo "/* \n User: $U_NAME \n Problem: $NUMBER \n Output: \n */" >> "Prob_$NUMBER_Ch_$CHAPTER.cpp"
fi
done
See example 11.12 for c style for loop.
To create files use "Prob_"$NUMBER"Ch"$CHAPTER".cpp" at all the places.
Most shells allow a set -x at the top. This show you tracing of the script as it executes.
You need a trailing fi to close the if statement.
Shell math need NUMBER=$(expr $NUMBER + 1), it doesn't understand $NUMBER++.
You might find the printf command more handy than echo.
You will still have your variable use wrong in the loop and are only creating one file. I hope you can figure that out as this seems like homework.
Related
I am setting up a directory structure with many different R & bash scripts in it. They all will be referencing files and folders. Instead of hardcoding the paths I would like to have a text file where each script can search for a descriptor in the file (see below) and read the relevant path from that.
Getting the search-append to work in R is easy enough for me; I am having trouble getting it to work in Bash, since I don't know the language very well.
My guess is it has something to do with the way awk works / stores the variable, or maybe the way the / works on the awk output. But I'm not familiar enough with it and would really appreciate any help
Text File "Master_File.txt":
NOT_DIRECTORY "/file/paths/Fake"
JOB_TEST_DIRECTORY "/file/paths/Real"
ALSO_NOT_DIRECTORY "/file/paths/Fake"
Bash Script:
#! /bin/bash
master_file_name="Master_File.txt"
R_SCRIPT="RScript.R"
SRCPATH=$(awk '/JOB_TEST_DIRECTORY/ { print $2 }' $master_file_name)
Rscript --vanilla $SRCPATH/$R_SCRIPT
The last line, $SRCPATH/$R_SCRIPT, seems to be replacing part of SRCPath with the name of $R_SCRIPT which outputs something like /RScript.Rs/Real instead of what I would like, which is /file/paths/Real/RScript.R.
Note: if I hard code the path path="/file/paths/Real" then the code $path/$R_SCRIPT outputs what I want.
The R Script:
system(command = "echo \"SUCCESSFUL_RUN\"", intern = FALSE, wait = TRUE)
q("no")
Please let me know if there's any other info that would be helpful, I added everything I could think of. And thank you.
Edit Upon Answer:
I found two solutions.
Solution 1 - By Mheni:
[ see his answer below ]
Solution 2 - My Adaptation of Mheni's Answer:
After seeing a Mehni's note on ignoring the " quotation marks, I looked up some more stuff, and found out it's possible to change the character that awk used to determine where to separate the text. By adding a -F\" to the awk call, it successfully separates based on the " character.
The following works
#!/bin/bash
master_file_name="Master_File.txt"
R_SCRIPT="RScript.R"
SRCPATH=$(awk -F\" -v r_script=$R_SCRIPT '/JOB_TEST_DIRECTORY/ { print $2 }' $master_file_name)
Rscript --vanilla $SRCPATH/$R_SCRIPT
Thank you so much everyone that took the time to help me out. I really appreciate it.
the problem is because of the quotes around the path, this change to the awk command ignores them when printing the path.
there was also a space in the shebang line that shouldn't be there as #david mentioned
#!/bin/bash
master_file_name="/tmp/data"
R_SCRIPT="RScript.R"
SRCPATH=$(awk '/JOB_TEST_DIRECTORY/ { if(NR==2) { gsub("\"",""); print $2 } }' "$master_file_name")
echo "$SRCPATH/$R_SCRIPT"
OUTPUT
[1] "Hello World!"
in my example the paths are in /tmp/data
NOT_DIRECTORY "/tmp/file/paths/Fake"
JOB_TEST_DIRECTORY "/tmp/file/paths/Real"
ALSO_NOT_DIRECTORY "/tmp/file/paths/Fake"
and in the path that corresponds to JOB_TEST_DIRECTORY i have a simple hello_world R script
[user#host tmp]$ cat /tmp/file/paths/Real/RScript.R
print("Hello World!")
I would use
Master_File.txt :
NOT_DIRECTORY="/file/paths/Fake"
JOB_TEST_DIRECTORY="/file/paths/Real"
ALSO_NOT_DIRECTORY="/file/paths/Fake"
Bash Script:
#!/bin/bash
R_SCRIPT="RScript.R"
if [[ -r /path/to/Master_File.txt ]]; then
. /path/to/Master_File.txt
else
echo "ERROR -- Can't read Master_File"
exit
fi
Rscript --vanilla $JOB_TEST_DIRECTORY/$R_SCRIPT
Basically, you create a configuration file Key=value, source it then use the the keys as variable for whatever you need throughout the script.
I have a script like this:
#!/bin/bash
x=${1:-20}
for ((i=1;i<=x;i++))
{
if ((i%3==0))
{
echo 'Fizz'
}
echo $i
}
I get an error color on the last brace in VIM and when I try to run the script I get a "syntax error near unexpected token" for that same brace. Without the nested if statement, this will print 1 through 20 on a new line for each number, which is the expected outcome. If the number is divisible by 3, it should print Fizz instead of that number. I'm not as worried about how to implement the replacement, that should be easy to figure out, but what I don't understand is why I cannot use a brace to close the for loop. If I take out the brace, I get an error that says end of file expected. So what is the proper syntax for ending a for loop with a nested if statement? I've looked around online and here on stack but haven't found a similar format to what I am trying to do. I don't like the
for f in *
format as it is not as easy to read for someone coming from a different coding language and I like to keep my code looking very similar across different languages (I use comments too, but just the same, I try to keep things as similar as possible which is why I used (( )) with the for loop.)
If I comment out the if statement and leave everything else intact, the error disappears and it will print
1
Fizz
2
Fizz
etc.
Any insight into this would be greatly appreciated. Thanks!
So here is what I was able to figure out thanks to #Cyrus:
x=${1:-20}
for ((i=1;i<=x;i++))
do
if ((i%3==0))
then
echo 'Fizz'
else
echo $i
fi
done
In many ways bash is simpler than most other languages but that makes it harder to work with when you are used to "higher" level languages.
So, to help out anyone else that's like me and just starting to code with bash, here is the full program I made, with comments as to why I coded it the way I did. If there are errors in my explanation or my formatting style, please point them out. Thanks! This was kind of fun to write, call me crazy.
# This will literally just print the string inside the single quotes on the screen
echo 'Try running this again but with something like this: fizzbuzz 25 pot kettle black'
# The $0 is the first index, in other words the file name of the executable,
# this will set the default value of x to 20 but will allow the user to input
# something else if they want.
x=${1:-20}
# This is the same but with string variables
f=${2:-FizzBuzz}
g=${3:-Fizz}
b=${4:-Buzz}
# We start the index variable at 1 because it's based on the input,
# otherwise it would echo 0 thru 19
for ((i=1;i<=x;1++))
do
# I recommend using (( )) for if statement arithmetic operations
# since the syntax is similar to other programming languages
if ((i%3==0 && i%5==0)); then echo $f
# you need to use a semicolon to separate if and then if they are
# on the same line, otherwise you can just go to the next line for
# your then statement
else if ((i%3==0)); then echo $g
else if ((i%5==0)); then echo $b
else echo $1
# You need fi in order to finish an if then statement
fi fi fi
done
this is my first stackoverflow question, regarding bash scripting. I am a beginner in this language, so be kind with me.
I am trying to write a comparison script. I tried to store all the outputs into variables, but only the last one is stored.
Example code:
me:1234567
you:2345678
us:3456789
My code:
#!bin/bash
while read -r forName forNumber
do
aName="$forName"
echo "$aName"
aNumber="$forNumber"
echo "$aNumber"
done < "exampleCodeFile.txt"
echo "$aNumber"
For the first time, everything will be printed out fine. However, the second echo will only print out "3456789", but not all the numbers again. Same with $aName. This is a problem because i have another file, which i stored a bunch of numbers to compare $aNumber with, using the same method listed above, called $aMatcher, consisting:
aMatcher:
1234567
2345678
3456789
So if i tried to run a comparison:
if [ "$aNumber" == "$aMatcher" ]; then
echo "match found!"
fi
Expected output (with bash -x "scriptname"):
'['1234567 == 1234567']'
echo "match found!"
Actual output (with bash -x "scriptname"):
'['3456789 == 3456789']'
echo "match found!"
Of course my end product would wish to list out all the matches, but i wish to solve my current issue before attempting anything else. Thanks!
When you run your following code
aNumber="$forNumber"
You are over-writing the variable $aNumber for every line of the file exampleCodeFile.txt rather than appending.
If you really want the values to be appended, change the above line to
aNumber="$aNumber $forNumber"
And while matching with $aMatcher, you again have to use a for/while loop to iterate through every value in $aNumber and $aMatcher.
I'm pretty new to bash and am having trouble with what I assume is formatting. I'm trying to edit the /etc/profile so it will display a login message for root and a different login message for anyone else. But I'm getting the error bash: syntax error near unexpected token else. I've tried all the different combinations of no semicolon, then on the next line etc but always get the same error. I've tried the lines separately and they display fine (except $HOSTNAME, can't get that to work). When run like this and login with root it will just jump to "Welcome $USER...".
Anyone suggestions would be appreciated!
if [ "$UID" -ne 0 ]; then
echo -e "\033[40;37;7m Danger! Root is doing stuff in `pwd`\033[0m"
else
echo "Welcome $USER! You are working on `$HOSTNAME` in `pwd`."
fi
As written, above works for me - but, as pointed out, you should change your test to -eq 0.
For the syntax error near unexpected token problem - I will guess that your file contains embedded 'control codes', i.e. most likely a carriage return \r.
Try:
cat -e ~/your_profile
see any non-printable characters? if so, remove them (cat options may vary - check you manpage) or
od -c ~/your_profile
Here's a simple version of my script which displays the failure:
#!/bin/bash
${something:="false"}
${something_else:="blahblah"}
${name:="file.ext"}
echo ${something}
echo ${something_else}
echo ${name}
When I echo the variables, I get the values I put in, but it also emits an error. What am I doing wrong?
Output:
./test.sh: line 3: blahblah: command not found
./test.sh: line 4: file.ext: command not found
false
blahblah
file.ext
The first two lines are being emitted to stderr, while the next three are being output to stdout.
My platform is fedora 15, bash version 4.2.10.
You can add colon:
: ${something:="false"}
: ${something_else:="blahblah"}
: ${name:="file.ext"}
The trick with a ":" (no-operation command) is that, nothing gets executated, but parameters gets expanded. Personally I don't like this syntax, because for people not knowing this trick the code is difficult to understand.
You can use this as an alternative:
something=${something:-"default value"}
or longer, more portable (but IMHO more readable):
[ "$something" ] || something="default value"
Putting a variable on a line by itself will execute the command stored in the variable. That an assignment is being performed at the same time is incidental.
In short, don't do that.
echo ${something:="false"}
echo ${something_else:="blahblah"}
echo ${name:="file.ext"}
It's simply
variable_name=value
If you use $(variable_name:=value} bash substitutes the variable_name if it is set otherwise it uses the default you specified.