Reputation: 73
Say, I have two files.
File1 contains:
line1
line2
line3
line4
line5
line6
line7
line8
line9
line10
and File2 contains:
anotherline1
anotherline2
anotherline3
I want to do a while loop so that it outputs this into a File3:
line1
line2
anotherline1
line3
line4
anotherline2
line5
line6
anotherline3
line7
line8
anotherline1
line9
line10
anotherline2
I've searched here for an answer but I can't figure out how to do it. I must say I'm pretty noob at shell scripting...
Also: The "while loop" should be until reaching end of file1. Doesn't matter how long is File2, actually, in my personal scenario, File2 will always be shorter than File1. Output should be: File1Line1, File1Line2, File2Line1, File1Line3, File1Line4, File2Line2.... etc
Any help?
note: if this was already answered before, then mods please close this question. I just couldn't find any answers after googling for some hours.
EDIT: Added clarification of when to end the loop.
Upvotes: 6
Views: 1279
Reputation: 296049
Easily done in native shell -- no external tools such as awk
needed. Open two file descriptors, tied to different files, and iterate over both.
while :; do
read -r line1 <&3 && printf '%s\n' "$line1" || break
read -r line2 <&3 && printf '%s\n' "$line2" || break
read -r line3 <&4 || { exec 4<file2 && read -r line3 <&4; }
printf '%s\n' "$line3"
done 3<file1 4<file2 1>file3
In this case, 3
is the file descriptor number on which we opened file1
, and 4
is the file descriptor number on which we opened file2
. 1>file3
, or simply >file3
, reroutes output on file descriptor 1
(aka stdout) to the output file.
You could pick other numbers if you liked, though only the range between 3-9 is guaranteed to be free for use on all POSIX shells. (0-2 are stdin, stdout and stderr; above 10 may be used by the shell for its own use, though this is rarely the case in newer and more modern shells).
Upvotes: 1
Reputation: 33956
I don't know of a convenient way to do something that specific in pure shell script. You're probably better off just writing a python/perl/etc. script. Something like this awk script would work, but it's somewhat messy:
awk '{
print; getline; print;
if (!getline < "file2") {
close("file2");
getline < "file2"
};
print
}' file1
The print; getline; print
will print the next line from file1, get the line after that, and then print it. The if (!getline < "file2")
tries to read the next line from file2, and if it fails, closes it, implicitly reopens it, and reads the first line from file2. The final print
will print that line from file2 (next or first).
Upvotes: 2
Reputation: 46896
If I'm interpreting your question correctly, you want to
file1
start to finish, andfile2
after every two lines, rotating back to the top of file2
whenever you run out of content.If that's the goal, you could do it with awk in a number of ways, but the following shell snippet would probably do.
#!/usr/bin/env bash
mapfile -t file2 < file2
max=${#file2[@]}
while read line; do
((count++))
echo "$line"
if [[ $((count%2)) == 0 ]]; then
if [[ next -ge max ]]; then
next=0
fi
echo "${file2[next++]}"
fi
done < file1
This has the down side of loading all of file2
into memory as an array. If that's a problem because the file is too big, you can perhaps add some shell glop to go find specific lines at each invocation of the loop, though that will be very slow. Better yet, define your problem more specifically, or consider alternate tools! :-)
Note also that the mapfile
command is a bash-4-ism, so if you're on an older Linux, or OSX (which still ships with bash 3), you'll need a different solution ... or an upgrade to bash.
Upvotes: 3
Reputation: 786359
This awk should work:
awk 'FNR==NR{a[++i]=$0; next} FNR%2{p=$0; curr=(FNR==1 || curr==i) ? 1 : curr+1; next}
{print p ORS $0 ORS a[curr]}' file2 file1
line1
line2
anotherline1
line3
line4
anotherline2
line5
line6
anotherline3
line7
line8
anotherline1
line9
line10
anotherline2
EDIT: Even shorter awk
(thanks to @geirha):
awk 'FNR==NR{a[n++]=$0;next}1;!(FNR%2){print a[i++%n]}' file2 file1
Upvotes: 2