How to count number of lines in a file in a Makefile? - bash

I have a Makefile in which I have a text file and a certain number. I would like to compare the number of lines in that text file to see if it is equal to or greater than that number, but haven't had success using the wc bash command.
I've tried using wc on the text file, but $(wc -l < filename.txt) always evaluates as empty, making ifeq($(wc -l < filename.txt), number)) error out. I've tried using the bash syntax for if statements, but that didn't work either.
e.g.
TEST="$(wc -l < missing_test_line_count.txt)"
TEST1=$(wc -l missing_test_line_count.txt)
TEST2=`wc -l missing_test_line_count.txt`
TEST3="$(wc -l missing_test_line_count.txt)"
Doing #echo $(value TEST) for any of these variables (TEST thru TEST3) results in an empty output.
I've also tried to put the number of lines in the text file into another text file (e.g. linecount.txt with a single line that says '30'). Then I tried to get that file content stored into a variable so I could compare it to the number, but that hasn't worked because I cannot define the variable at the beginning.
Any suggestions? Is this possible, or do I have to write a script separately? Would like to do it within the Makefile if possible.

First of all, you should specify, that you need GNU make's syntax (your reference to ifeq is one's only clue to that).
Back to your actual question, there is no wc function in gmake itself. What you're trying to do is to execute an external command. You do that with a shell function:
TEST= $(shell wc -l < missing_test_line_count.txt)
In BSD make's the same is achieved with the !=-assignment:
TEST!= wc -l < missing_test_line_count.txt

To run a shell as part of a variable expansion in a GNUMakefile, you need to use the shell function:
TEST:=$(shell wc -l < missing_test_line_count.txt)
will run the command in question when this line is read in the Makefile, setting TEST to be the result. Alternately you can use
TEST=$(shell wc -l < missing_test_line_count.txt)
which will run the command each time the $(TEST) variable is expanded.
Note that this function (and functions in general) is a GNUMake extension. Normal make does not support doing this.

Related

Replication and expansion of program flags in BASH script

I am working with a program that combines individuals files, and I am incorporating this program into a BASH pipeline that I'm putting together. The program requires a flag for each file, like so:
program -V file_1.g.vcf -V file_2.g.vcf -V file_3.g.vcf -O combined_output.g.vcf
In order to allow the script to work with any number of samples, I would like to read the individual files names within a directory, and expand the path for each file after a '-V' flag.
I have tried adding the file paths to a variable with the following, but have not had success with proper expansion:
GVCFS=('-V' `ls gvcfs/*.g.vcf`)
Any help is greatly appreciated!
You can do this by using a loop to populate an array with the options:
options=()
for file in gvcfs/*.g.vcf; do # Don't parse ls, just use a direct wildcard expression
options+=(-V "${file##*/}") # If you want the full path, leave off ##*/
done
program "${options[#]}" -O combined_output.g.vcf
printf can help:
options=( $(printf -- "-V %s " gvcfs/*.g.vcf ) )
Though this will not deal gracefully with whitespace in filenames.
Also consider realpath to generate absolute filenames.

How do I set a variable to an expr using other predefined variables in UNIX?

I'm writing a code in UNIX to translate a txt document to html.
The first parameter I pass in is the number of lines in the file to be used in the title.
The second is the file to translate.
So far I have:
numLinesInTitle=$1
lineCount=`wc -l $2`
firstInput=`head -$numLinesInTitle $2`
This all works so far.
Here's where I get the problem:
numBodyLines=`expr $lineCount - $1`
It says expr syntax error
The problem you're having is likely that your $lineCount variable has more in it than you think.
Try running wc -l /etc/passwd and see what you get. Now take that result (the whole result) and feed it to expr, and see what you get. (Spoiler: "syntax error".)
The problem is that the wc command shows you the name of the file along with the number of lines in that file. To avoid this behaviour, you can ask it to tell you the number of lines FROM STANDARD INPUT instead. For example:
lineCount=`wc -l < $2`
That should solve this particular issue. Here are a couple of hints that may solve future issues.
Use modern quoting constructs. lineCount=$(wc -l < $2) would be better.
Quote your variables. What happens if the filename has a space in it? lineCount=$(wc -l < "$2") would be better.

locating all argument numbers in bash script

I have a script file written in Bash. what I'm trying to do is locate all the arguments used in the script ($1,$2,$3....)
I've tried using:
grep -o '$[0-9]' $1
but that gives me seperate digits. for example, if I have $54 somewhere in the script,
I will only get $5. how can I get the whole number?
You can use quantifier + to grab one or more digits:
grep -o '$[0-9]\+' "$1"

Storing text and numeric variable from file to use in perl script

I am trying to prepare a bash script for use with gnu parallel. The script should take a filename, store the prefix of the file name as a describer, and store the row count (wc -l) as a numeric variable. Both if these become variables to use in a perl script. The describer is working fine.
But my storage of the number of rows, or my use of ${mm} is not generating a numeric variable that the perl script recognises. Any corrections appreciated.
#!/bin/bash
# Get the filename and strip suffix
sample=$1
describer=$(echo ${sample} | sed 's/.sync//')
echo ${describer} # works fine
# Get the number of rows
mm=$(cat ${sample} | wc -l)
echo ${mm} # works fine but is this a numeric variable?
# run the script using the variables;
# the ${mm} is where the perl script says its not numeric
perl script.pl --input ${describer}.sync --output ${describer}.genepop --region ${describer}:1-${mm}
This not an answer. I just wanted to rewrite your script in a better bash style. You know, you don't need to refer to variables with curly brackets all the time! E.g., $mm is fine enough, ${mm} is not needed in your cases. Moreover, your sed statement to strip the comment can be replaced with a bash equivalent. I added double quotes here and there so that you'll also be able to use everything with filenames that contain spaces and other funny symbols. I also removed the useless use of cat.
#!/bin/bash
# Get the filename and strip suffix
sample=$1
describer=${sample%.sync}
echo "$describer" # works fine
# Get the number of rows
mm=$(wc -l < "$sample")
echo "$mm" # works fine but is this a numeric variable?
# run the script using the variables;
# the $mm is where the perl script says its not numeric
perl script.pl --input "$sample" --output "$describer.genepop" --region "$describer:1-$mm"
Regarding your main problem: the problem might be in the perl program.
Regarding your question is this a numeric variable?, the answer is: variables have no types. They all are strings. Now read on:
Some versions of wc add spaces in front of the number they output, e.g.,
$ wc -l < file
42
(notice the spaces in front of 42). You should be able to notice if your version of wc behaves that way by running the version of the script I gave you (with the proper quotings). If you see some spaces in front of the number, that might be the cause of your problems.
If this is the case, you should replace the line
mm=$(wc -l < "$sample")
with
read mm < <(wc -l < "$sample")
Hope this helps!

How to name output file according to a command line argument in a bash script?

These lines work when copy-pasted to the shell but don't work in a script:
ls -l file1 > /path/`echo !#:2`.txt
ls -l file2 > /path/`echo !#:2`.txt
 
ls -l file1 > /path/$(echo !#:2).txt
ls -l file2 > /path/$(echo !#:2).txt
What's the syntax for doing this in a bash script?
If possible, I would like to know how to do this for one file and for all files with the same extension in a folder.
Non-interactive shell has history expansion disabled.
Add the following two lines to your script to enable it:
set -o history
set -o histexpand
(UPDATE: I misunderstood the original question as referring to arguments to the script, not arguments to the current command within the script; this is a rewritten answer.)
As #choroba said, history is disabled by default in scripts, because it's not really the right way to do things like this in a script.
The preferred way to do things like this in a script is to store the item in question (in this case the filename) in a variable, then refer to it multiple times in the command:
fname=file1
ls -l "$fname" > "/path/$fname.txt"
Note that you should almost always put variable references inside double-quotes (as I did above) to avoid trouble if they contain spaces or other shell metacharacters. If you want to do this for multiple files, use a for loop:
for fname in *; do # this will repeat for each file (or directory) in the current directory
ls -l "$fname" > "/path/$fname.txt"
done
If you want to operate on files someplace other than the current directory, things are a little more complicated. You can use /inputpath/*, but it'll include the path along with each filename (e.g. it'd run the loop with "/inputpath/file1", "/inputpath/file2", etc), and if you use that directly in the output redirect you'll get something like > /path/inputpath/file1.txt (i.e. the two different paths will get appended together), probably not what you want. In this case, you can use the basename command to strip off the unwanted path for output purposes:
for fpath in /inputpath/*; do
ls -l "$fpath" > "/path/$(basename "$fpath").txt"
done
If you want a list of files with a particular extension, just use *.foo or /inputpath/*.foo as appropriate. However, in this case you'll wind up with the output going to files named e.g. "file1.foo.txt"; if you don't want stacked extensions, basename has an option to trim that as well:
for fpath in /inputpath/*.foo; do
ls -l "$fpath" > "/path/$(basename "$fpath" .foo).txt"
done
Finally, it might be neater (depending how complex the actual operation is, and whether it occurs multiple times in the script) to wrap this in a function, then use that:
doStuffWithFile() {
ls -l "$1" > "/path/$(basename "$1" "$2").txt"
}
for fpath in /inputpath/*.foo; do
doStuffWithFile "$fpath" ".foo"
done
doStuffWithFile /otherpath/otherfile.bar .bar

Resources