I have a text file which contains this:
John
Jack
Jerry
I have another text file:
2017
2015
2018
And I want to make them like this:
John2017
John2015
John2018
Jack2017
Jack2015
Jack2018
Jerry2017
Jerry2015
Jerry2018
How can I do this?
I have a text file which contains this:
John
Jack
Jerry
I have another text file:
2017
2015
2018
And I want to make them like this:
John2017
John2015
John2018
Jack2017
Jack2015
Jack2018
Jerry2017
Jerry2015
Jerry2018
How can I do this?
Assuming that the files are small, it may be viable to read them into arrays (with bash
or ksh93
):
names=( $( <names.txt ) )
years=( $( <years.txt ) )
You may then construct a double loop that combines each name with each year in turn:
for n in "${names[@]}"; do
for y in "${years[@]}"; do
printf '%s%s\n' "$n" "$y"
done
done
If years.txt
is small, but names.txt
is large:
years=( $( <years.txt ) )
while read n; do
for y in "${years[@]}"; do
printf '%s%s\n' "$n" "$y"
done
done <names.txt
Result:
John2017
John2015
John2018
Jack2017
Jack2015
Jack2018
Jerry2017
Jerry2015
Jerry2018
Treating the files as single-column, header-less, CSV files:
$ csvsql -H --query "SELECT * FROM names CROSS JOIN years" names.txt years.txt | sed -e '1d' -e 's/,//'
This performs a relational cross join operation on the data in the two tables and then goes on to strip off the first line of the output along with all commas (csvsql
creates properly formatted CSV with a header line and comma-separated values).
csvsql
is part of csvkit
.
$( <names.txt )
does split+glob (split only in zsh). bash
has readarray
as a better way to read lines of a file into an array. zsh
/ fish
shells have builtin support to do the cartesian product of 2 arrays ($^a$^b
in zsh, $a$b
in fish)
– Stéphane Chazelas
Oct 27 '22 at 19:08
$ join -j2 -t : names year | tr -d :
Field 2 is empty and equal for all the element in names
and year
so join
will concatenate each name with all the years: it is in fact calculating the Cartesian product.
join
. Replacing -j 2
with -1 2 -2 2
would make it standard, but even then I didn't find another implementation beside GNU's where that works.
– Stéphane Chazelas
Oct 27 '22 at 19:18
The solution that @Kusalananda provided is superior to this one. I provided it without fully thinking through what $(cat ...)
does in terms of memory usage. I'll leave it here for reference – it'll work and isn't terrible for if the files are small.
#!/bin/bash
for name in $(cat names); do
for year in $(cat years); do
echo "${name}${year}"
done
done
years
) is read once for every line in the first file (names
), which may be slow if the files have any kind of non-trivial size.
– Kusalananda
Feb 11 '17 at 22:46
$(cat names)
incorrectly. @Kusalananda 's solution is better all around.
– Andy Dalton
Feb 12 '17 at 01:25