Giving input to a shell script through command line - shell

I have a file like this with .sh extension..
clear
echo -n "Enter file name: "
read FILE
gcc -Wall -W "$FILE" && ./a.out
echo
When I can execute this file, it asks for a .c file and when given, it compiles and gives output of the .c file.
For this, everytime I have to first execute this .sh file and then give it the .c file name when asked. Is there anyway, so that, I can just give the .c file in the command line itself, so that it takes that file and does the work...
What I mean is, if I give "./file.sh somecommand cfile.c", then it takes cfile.c as input, compiles it and gives the output...

Use '$1' variable:
clear
gcc -Wall -W $1 && ./a.out
echo
$1 means "first argument from the command line".
Alternatively, if you want to compile multiple files at once using your script, you can use $# variable, on example:
gcc -Wall -W $# && ./a.out
You will invoke your script as follows (assuming it's called 'script.sh'):
./script.sh file.c
Plase see section 3.2.5 of the following article.
If your project gets bigger, you may also want to consider using tools designated for building, like automake.

You can also have it do things either way:
if [ -n "$1" ] ; then
FILE="$1"
else
echo -n "Enter file name: "
read FILE
fi
gcc -Wall -W "$FILE" && ./a.out
This will use the command line argument if it is there, otherwise it asks for a file name.

Related

Bash script to compile a program, feed it with 15 input files and print stdout to file

I'm trying to write a Bash script that feeds my program (./program) with 15 input files named in sequence as (file01.txt, file02.txt, etc) and print the outputs to the file (result.out). Here is the code I wrote:
#!/bin/bash
#Compile the current version
g++ -std=c++11 -fprofile-arcs -ftest-coverage program.cpp -o program
#
#Output file
outFile=result.out
#Loop through files and print output
for i in *.txt; do
./program < $i > $outFile
done
I'm getting a segmentation fault when running this script and not sure what I did wrong. This is my first time to write a bash script, so any help will be appreciated.
Basically these are the points I learnt from my conversation with stackoverflow members:
1- The segmentation fault is not related to bash script. It is definitely related to the program the bash command is running.
2- The bash script that feeds a program with text files and insert the results in an output file is as follow:
#!/bin/bash
#Compile the current version
g++ -std=c++11 -fprofile-arcs -ftest-coverage program.cpp -o program
#
#Test output file
outFile=results.out
# print "Results" into outFile
printf "Results\n" > $outFile
# loops through text files, send files to stdin
# and insert stdout to outFile
for i in *.txt; do
printf "\n$i\n"
./program < "$i"
done >> $outFile

Write the output of time, and the command it was timing, as a single line

I am trying to time how long each file in my codebase takes to compile.
According to this SO answer, you can write a script which does times the actual compilation and then stores the results in a file
/tmp/time-build:
#!/bin/bash
{ time g++ "$#"; } 2> >(cat <(echo "g++ $#") - >> /tmp/results.txt)
You can then override CMAKE_CXX_COMPILER when calling cmake so that make uses the script to perform the compilation
cmake .. -DCMAKE_CXX_COMPILER=/tmp/time-build
make
This works as advertised, and yields results similar to the following:
real 0m1.190s
user 0m1.044s
sys 0m0.140s
g++ -Werror -Wall -Wextra ... /src/test/foo.cpp
However, to ease processing, I would like to store only the real time, and have it on the same line as the g++ command.
Question:
My command line fu is not up to the task of turning this:
{ time g++ "$#"; } 2> >(cat <(echo "g++ $#") - >> /tmp/results.txt)
Into a command which captures only the real output of time, and includes it along with the echo "g++ $#" on a single line.
I don't know what the >(cat <( and ) - parts of the above command mean, and my attempts to incorporate a grep real and echo have failed
How can I do that?
Alternately, if there is a more idiomatic way to get cmake to output timings for each file processed, that too would be ideal.
Probably the best way to get compile times for a CMake-based project is to use ninja instead of make.
To do this you have to specify ninja generator while configuring:
cmake -G Ninja <...>
Then build using ninja:
ninja
After that, look at the file called .ninja_log. First column is start time in milliseconds, second is end time, forth is target name and the last one is a hash of the command line used.
There are even some solutions for viewing and analyzing this log: ninjatracing converts .ninja_log to a format readable by Chrome's tracing tool.
cmd=(clang "$#")
{ time "${cmd[#]}"; } 2> >( tee >( real=$(grep -Po 'real\K.*'); echo "${cmd[*]} $real" >>result.txt ) )

Run a Bash Command on File Change?

My question is how can I run a Bash command on some file change?
For example, if I am writing a C program and every time the file is saved I run the command rm output; gcc program.c -o output; ./output automatically
You could use make and watch:
Makefile:
output: program.c
gcc program.c -o output
./output
then
$ watch make
in a separate terminal.
However, there will be a small delay between when you save program.c and when it gets run.
You can use inotifywait for this specific purpose.
while true; do
change=$(inotifywait -e close_write,moved_to,create .)
change=${change#./ * }
if [ "$change" = "program.c" ]; then rm output; gcc program.c -o output; ./output; fi
done

Bash Script to do operations based on file extensions where the input is the path to the file. MacOSX

EDIT2:
THIS POST IS SOLVED (if you would like to see the product that tis post resulted in please visit the site http://www.nasmagic.com )
here is my plan firstly
i am using Platypus (http://sveinbjorn.org/platypus) a program for OSX that lets you create little "droplets" that basically wrap a bash script in a fancy drag and drop GUI.
now im using this as a nice easy way of making myself a drag and drop Nasm assembler.
i have made a few of these "droplets" with simple bash scripts inside them, one example would be the folowing:-
#!/bin/bash
chmod u+x $1
this as you can see makes my scripts executable,... very handy.
The way they work is they take one variable only and that is the path to the file eg.
/Users/MiRAGE/Desktop/example.sh
now here is my conundrum for the day
i need to do the following command:-
/usr/local/bin/nasm -f macho example.asm && ld -macosx_version_min 10.7.0 -o example example.o
this is one command that works fine with the non variable filenames. however in the droplet context it has a problem.
it can execute each of these commands in two seperate droplets but without changing the command it will not find the outputed file of the first command as it is not 'cd'd into the directory where the file is outputed so it doesent know what to do.
now as i say at the moment i have successfully got it to compile with one droplet with this command:-
/usr/local/bin/nasm -f macho $1
which i drag the file into first and it spits out a ".o" file
then i drop that ".o" file into droplet2 which has this command inside:-
ld -macosx_version_min 10.7.0 -lSystem -o $1.5y $1
this command is much less elegant than the first.
the only way i could get it to compile the file is to append my made up extention otherwise it would just not work. the problem with this method is that while it does output the compiled binary it ends up looking like this "exampleFilename.o.5y".
now i could go in and delete that and i would, i guess be where i need to be. but its very messy. 2 droplets, renaming files..... not what i want i want a drag and drop hassle free assembler.
so heres the code i have in mind except this is not real and clearly doesn't work.
if [filename $1 == ".asm"] # if the file extension is ".asm"
then # then do next step
/usr/local/bin/nasm -f macho $1 # make mach-o file
fi # end
else if [filename $1 == ".o"] # else if the file extension is ".o"
then # then do this step
ld -macosx_version_min 10.7.0 -lSystem -o $1 $1.o # take the file ".o" and outfile with no extention but the same name.
fi #end
this way i can drag it in once, it will spit out the ".o" file, then i drag that in, and it then converts it to the executable binary. PERFECT
but i cant for the life of me find out how to write this properly if anyone can help i would be very very appreciative
many thanks in advance
MiRAGE
If my understanding is correct, you want to be able to drag the .asm file to the droplet, then drag the resulting .o file to the droplet to produce an executable, in two steps.
In which case, after downloading platypus and recent nasm, I find this script works for me:
#!/bin/bash
# Get the path to the input file, and enter that directory
pathname=$(dirname "$1")
cd $pathname
# attempt strip the .asm extension off the input filename (if there is one)
filestem=$(basename -s .asm "$1")
# If the input file was a .asm file, then assemble it:
if [ "${pathname}/${filestem}.asm" == "$1" ]; then
/usr/local/bin/nasm -f macho "$1"
fi
# attempt strip the .o extension off the input filename (if there is one)
filestem=$(basename -s .o "$1")
# If the input file was a .o file, then link it:
if [ "${pathname}/${filestem}.o" == "$1" ]; then
ld -macosx_version_min 10.7.0 -o "$filestem" "$1"
fi
This script takes care to make sure output files are placed into the same directory as the input files.
Conversely, if you want a script to assemble and link in one shot, this works for me:
#!/bin/bash
# Get the path to the input file, and enter that directory
pathname=$(dirname "$1")
cd $pathname
# attempt strip the .asm extension off the input filename (if there is one)
filestem=$(basename -s .asm "$1")
# If the input file was an .asm file, then assemble and link it:
if [ "${pathname}/${filestem}.asm" == "$1" ]; then
/usr/local/bin/nasm -f macho "$1" && ld -macosx_version_min 10.7.0 -o "${pathname}/$filestem" "${pathname}/${filestem}.o"
fi
Note that what you have here basically replicates a lot of make or similar build systems do for you.
Note also that third-party software like playtpus is not strictly needed for the drag-n-drop part. You can use the built-in automator application to create similar applications. e.g I created one that you just drag example.asm onto the automator application icon and it runs the same shell script for you.
Also, you can test the shell script at the command line, simply by calling:
./myscript.sh example.asm
or
./myscript.sh example.o
Try this...
#!/bin/bash
ext=${1/*./}
test $ext == "asm" && /usr/local/bin/nasm -f macho "$1"
output=${1/*\//}; output=${output/.*/}
test $ext == "o" && ld -macosx_version_min 10.7.0 -lSystem -o $output $1
If you want to try make to do this, the following will do it:
#!/bin/bash
target=$(dirname "$1")/$(basename -s ".${1/*./}" "$1")
export CC=ld
export LDFLAGS="-macosx_version_min 10.7.0 -lSystem"
make -f - "$target" <<EOF
%.o: %.asm
/usr/local/bin/nasm -f macho $<
.PRECIOUS: %.o
EOF
(Note there is a TAB character at the start of the nasm line.)
The beauty of using make is that targets are only rebuilt if they don't exist, or they are older than their prerequisites. Also make has built-in implicit rules for many things, including linking a .o to create an executable, which we make use of.
This script will accept a .asm file or .o file as input, from which it will derive a make target (the name of the expected executable) by stripping off the extension.
explicitly set the linker to be "ld" instead of the default of "cc"
set necessary linker flags in the LDFLAGS variable
call make with the derived target. Normally make will parse a Makefile for its rules, but in this case, we redirect a makefile using a bash here-document.
The redirected makefile has one implicit rule, which says to assemble x.asm into x.o
The redirected makefile also has a special .PRECIOUS rule, which prevents deletion of intermediate files (.o files in this case)

How to ignore mv error?

I'm making a Makefile that moves an output file (foo.o) to a different directory (baz).
The output file moves as desired to the directory. However since make won't recompile the output file if I type make again, mv gets an error when it tries to move the non-existent empty file to the directory baz.
So this is what I have defined in my rule make all after all compilation:
-test -e "foo.o" || mv -f foo.o ../baz
Unfortunately, I'm still getting errors.
Errors in Recipes (from TFM)
To ignore errors in a recipe line, write a - at the beginning of the
line's text (after the initial tab).
So the target would be something like:
moveit:
-mv foo.o ../baz
I notice nobody has actually answered the original question itself yet, specifically how to ignore errors (all the answers are currently concerned with only calling the command if it won't cause an error).
To actually ignore errors, you can simply do:
mv -f foo.o ../baz 2>/dev/null; true
This will redirect stderr output to null, and follow the command with true (which always returns 0, causing make to believe the command succeeded regardless of what actually happened), allowing program flow to continue.
+#[ -d $(dir $#) ] || mkdir -p $(dir $#)
is what I use to silently create a folder if it does not exist. For your problem something like this should work
-#[ -e "foo.o" ] && mv -f foo.o ../baz
-test -e "foo.o" || if [ -f foo.o ]; then mv -f foo.o ../baz; fi;
That should work
Something like
test -e "foo.o" && mv -f foo.o ../baz
should work: the operator should be && instead of ||.
You can experiment with this by trying these commands:
test -e testfile && echo "going to move the file"
test -e testfile || echo "going to move the file"
I faced the same problem and as I am generating files, they always have different time. Workaround is set the same time to the files: touch -d '1 June 2018 11:02' file. In that case, gzip generates the same output and same md5sum. In my scenario, I don't need the time for the files.

Resources