Reputation: 137
I'm still learning bash on my free time and what I'm trying to do is format a file that lists data of which two lines at a time are directly related and insert this data into a single command...for example: My list file -
line 1 data
line 2 data related to line 1
line 3 data
line 4 data related to line 3
Now what I'm trying to do is to find a way to add both related lines to a single command, and continue to add the next two lines to the said command etc. until the list is done. The data would be inserted as options to the command...
COMMAND --OPTION1=line1data --OPTION2=line2data
The list should not be repeated and no line of data should be repeated. So far what I've done is use grep to format the file into two separate files, the first file listing only the first line of data (without the following 2nd line of related data) i.e. line1 line3 line5 etc., and the second file consists of a list of the second line of related data i.e. line2 line4 etc...Now I'm probably making this waaaay to hard for myself but I was trying to put the data from both files into the command using something like the for command to loop the process...but I don't know how to take the first line of each formatted file (then take the next line of each file) and insert it into one command...
So here I am. Also, is there anyway to make this easier and just take data two lines at a time from a file and input that into a command? Thanks in advance
Upvotes: 0
Views: 304
Reputation: 46896
One method would be this:
while read opt1; do
read opt2
COMMAND --OPTION1="$opt1" --OPTION2="$opt2"
done < input.txt
You can probably see the logic here -- we have a loop that reads a line into the variable opt1
, where the first command in the loop consumes the next line into the variable opt2
, and then uses the two variables to construct your command line. Done this way, it's a simple one-liner in bash (or POSIX shell -- there's nothing here which is exclusive to bash).
Another way to approach this is to use the paste
command, which is not strictly part of bash, but comes with your OS. Note the following:
$ printf '1\n2\n3\n4\n5\n6\n' | paste - -
1 2
3 4
5 6
This doesn't separate things for use the way you expressed in your question though, so a more complex loop would be in order. paste
by default uses a tab as a separator. Assuming your input data doesn't include tab characters, you could use a construct like this:
while IFS=$'\t' read opt1 opt2; do
COMMAND --OPTION1="$opt1" --OPTION2="$opt2"
done < <(paste - - < input.txt)
The logic here is that the paste
command will construct single lines of input for our read
commands, which gets its IFS set to the same separator that paste
uses. The <( ... )
construct is called "Process Substitution" in bash (you can find it in the man page), and is helpful if you want your while
loop to populate variables that will be used later in the shell script .. but you could avoid the bashism here by using a pipe:
paste - - < input.txt | while IFS=$'\t' read opt1 opt2; do
COMMAND --OPTION1="$opt1" --OPTION2="$opt2"
done
Note that the construct $'\t'
(often called "format substitution" but described in the "QUOTING" section of the bash man page) to generate the tab also may not be universal, but does exist in many other shells besides bash, so it's still fairly portable.
While it's a little longer and involves an extra command, I consider the second method to be the most clear and readable. You should use whatever approach makes the most sense to YOU, so that you can maintain it more easily in the future.
Consider whether it would be beneficial or problematic to use the -r
option for read, to handle backslashes and line continuation. I don't know your use case, so I can't make a recommendation on this other than that you be aware of it.
Upvotes: 3
Reputation: 2259
Suppose your file input.txt
has following content
1
2
3
4
5
6
7
8
Use awk
command to access the individual lines as follows:
awk '{if(NR%2 == 0) {print "COMMAND --OPTION1="data1" --OPTION2="$0;}else{data1=$0;}}' input.txt
In the above awk
command , NR
is variable which holds the current line number. $0
is the complete line.
NR%2
means checking whether the line number given by NR
is divisible by 2
using the mod %
operator.
if NR%2 equals 0
then the lines given by NR
are all even numbered i.e 2, 4, 6, 8...
if NR%2 not equals 0
then the lines given by NR
are all odd numbered i.e 1, 3, 5, 7...
Output:
COMMAND --OPTION1=1 --OPTION2=2
COMMAND --OPTION1=3 --OPTION2=4
COMMAND --OPTION1=5 --OPTION2=6
COMMAND --OPTION1=7 --OPTION2=8
Upvotes: 1