使用与 A B C 中的文件 B 相同的行数将多个文件合并为一个文件
cat multiple files into one using same amount of rows as file B from A B C
这是一个奇怪的问题,我一直在四处寻找,但找不到与我想做的事情相匹配的东西。
我想做的是;
文件A,文件B,文件C
5 行,3 行,2 行。
将所有文件合并到一个文件中,匹配相同数量的文件B
输出应该是
文件A,文件B,文件C
3 行,3 行,3 行。
所以在文件 A 中我必须删除两行,在文件 C 中我必须复制 1 行以便我可以匹配与文件 B 相同的行。
我想先数一下每个文件有多少行
count1=`wc -l FileA| awk '{print }'`
count2=`wc -l FileB| awk '{print }'`
count3=`wc -l FileC| awk '{print }'`
Then to do a gt then file B remove lines, else add lines.
但我迷路了,因为我不确定如何继续这样做,我从未见过有人尝试这样做。
任何人都可以给我一个想法吗?
输出应该如下图所示;
Output
谢谢。
您可以使用 head
命令 resp sed
获取文件的前 n 行。
您可以使用 echo
.
生成新行
我将使用 sed
,因为它允许就地编辑文件(因此您不必处理临时文件):
#!/bin/bash
fix_numlines() {
local filename=
local wantlines=
local havelines=$(grep -c . "${filename}")
head -${wantlines} "${filename}"
if [ $havelines -lt $wantlines ]; then
for i in $(seq $((wantlines-havelines))); do echo; done
fi
}
lines=$(grep -c . fileB)
fix_numlines fileA ${lines}
fix_numlines fileB ${lines}
fix_numlines fileC ${lines}
如果你想要分栏输出,那就更简单了:
paste fileA fileB fileC | head -$(grep -c . fileB)
另一个用于 GNU awk 的列输出:
$ gawk -v seed=$RANDOM -v n=2 ' # n parameter is the file index number
BEGIN { # ... which defines the record count
srand(seed) # random record is printed when not enough records
}
{
a[ARGIND][c[ARGIND]=FNR]=[=10=] # hash all data to a first
}
END {
for(r=1;r<=c[n];r++) # loop records
for(f=1;f<=ARGIND;f++) # and fields for below output
printf "%s%s",((r in a[f])?a[f][r]:a[f][int(rand()*c[f])+1]),(f==ARGIND?ORS:OFS)
}' a b c # -v n=2 means the second file ie. b
输出:
a1 b1 c1
a2 b2 c2
a3 b3 c1
如果您不喜欢随机选择记录,请将 int(rand()*c[f])+1]
替换为 c[f]
。
$ gawk ' # remember GNU awk only
NR==FNR { # count given files records
bnr=FNR
next
}
{
print # output records of a b c
if(FNR==bnr) # ... up to bnr records
nextfile # and skip to next file
}
ENDFILE { # if you get to the end of the file
if(bnr>FNR) # but bnr not big enough
for(i=FNR;i<bnr;i++) # loop some
print # and duplicate the last record of the file
}' b a b c # first the file to count then all the files to print
能否请您尝试以下。我已经将 @
作为分隔符,您也可以根据需要更改它。
paste -d'@' file1 file2 file3 |
awk -v file2_lines="$(wc -l < file2)" '
BEGIN{
FS=OFS="@"
}
FNR<=file2_lines{
=?:prev_first
=?:prev_third
print
prev_first=
prev_third=
}'
以上代码的运行示例:
假设以下是 Input_file(s):
cat file1
File1_line1
File1_line2
File1_line3
File1_line4
File1_line5
cat file2
File2_line1
File2_line2
File2_line3
cat file3
File3_line1
File3_line2
当我运行以上脚本形式的代码将输出:
./script.ksh
File1_line1@File2_line1@File3_line1
File1_line2@File2_line2@File3_line2
File1_line3@File2_line3@File3_line2
要使文件具有 n
行,您可以使用以下函数(用法:toLength n file
)。如果文件太长,这将省略末尾的行,如果文件太短,则重复最后一行。
toLength() {
{ head -n"" ""; yes "$(tail -n1 "")"; } | head -n""
}
要将所有文件设置为 FileB 的长度并并排显示,请使用
n="$(wc -l < FileB)"
paste <(toLength "$n" FileA) FileB <(toLength "$n" FileC) | column -ts$'\t'
正如用户所观察到的 并排输出让事情变得更加简单。但是,他们使用空行来填充短文件。以下解决方案重复最后一行以使短文件更长。
stretch() {
cat ""
yes "$(tail -n1 "")"
}
paste <(stretch FileA) FileB <(stretch FileC) | column -ts$'\t' |
head -n"$(wc -l < FileB)"
这是一种使用 awk
的简洁方法,我们只读取每个文件一次:
awk -v n=2 '
BEGIN{ while(1) {
for(i=1;i<ARGC;++i) {
if (b[i]=(getline tmp < ARGV[i])) a[i] = tmp
}
if (b[n]) for(i=1;i<ARGC;++i) print a[i] > ARGV[i]".new"
else {break}
}
}' f1 f2 f3 f4 f5 f6
其工作方式如下:
- 引导文件由索引
n
定义。这里我们选择引导文件为f2
.
- 我们不按顺序处理标准读取记录字段中的文件,但我们使用并行读取文件的
BEGIN
块。
- 我们执行一个无限循环
while(1)
,如果引导文件没有更多输入,我们将在该循环中中断。
- 每个周期,我们使用
getline
读取每个文件的新行。如果文件i
有换行,将其存入a[i]
,并将getline
的结果置入b[i]
。如果文件 i
已到达结尾,请记住最后一行。
- 用
b[n]
检查引导文件的结果。如果我们仍然读取一行,则将所有行打印到文件f1.new
、f2.new
、...,否则,跳出无限循环。
这是一个奇怪的问题,我一直在四处寻找,但找不到与我想做的事情相匹配的东西。
我想做的是;
文件A,文件B,文件C 5 行,3 行,2 行。
将所有文件合并到一个文件中,匹配相同数量的文件B 输出应该是
文件A,文件B,文件C 3 行,3 行,3 行。
所以在文件 A 中我必须删除两行,在文件 C 中我必须复制 1 行以便我可以匹配与文件 B 相同的行。
我想先数一下每个文件有多少行
count1=`wc -l FileA| awk '{print }'`
count2=`wc -l FileB| awk '{print }'`
count3=`wc -l FileC| awk '{print }'`
Then to do a gt then file B remove lines, else add lines.
但我迷路了,因为我不确定如何继续这样做,我从未见过有人尝试这样做。
任何人都可以给我一个想法吗?
输出应该如下图所示;
Output 谢谢。
您可以使用 head
命令 resp sed
获取文件的前 n 行。
您可以使用 echo
.
我将使用 sed
,因为它允许就地编辑文件(因此您不必处理临时文件):
#!/bin/bash
fix_numlines() {
local filename=
local wantlines=
local havelines=$(grep -c . "${filename}")
head -${wantlines} "${filename}"
if [ $havelines -lt $wantlines ]; then
for i in $(seq $((wantlines-havelines))); do echo; done
fi
}
lines=$(grep -c . fileB)
fix_numlines fileA ${lines}
fix_numlines fileB ${lines}
fix_numlines fileC ${lines}
如果你想要分栏输出,那就更简单了:
paste fileA fileB fileC | head -$(grep -c . fileB)
另一个用于 GNU awk 的列输出:
$ gawk -v seed=$RANDOM -v n=2 ' # n parameter is the file index number
BEGIN { # ... which defines the record count
srand(seed) # random record is printed when not enough records
}
{
a[ARGIND][c[ARGIND]=FNR]=[=10=] # hash all data to a first
}
END {
for(r=1;r<=c[n];r++) # loop records
for(f=1;f<=ARGIND;f++) # and fields for below output
printf "%s%s",((r in a[f])?a[f][r]:a[f][int(rand()*c[f])+1]),(f==ARGIND?ORS:OFS)
}' a b c # -v n=2 means the second file ie. b
输出:
a1 b1 c1
a2 b2 c2
a3 b3 c1
如果您不喜欢随机选择记录,请将 int(rand()*c[f])+1]
替换为 c[f]
。
$ gawk ' # remember GNU awk only
NR==FNR { # count given files records
bnr=FNR
next
}
{
print # output records of a b c
if(FNR==bnr) # ... up to bnr records
nextfile # and skip to next file
}
ENDFILE { # if you get to the end of the file
if(bnr>FNR) # but bnr not big enough
for(i=FNR;i<bnr;i++) # loop some
print # and duplicate the last record of the file
}' b a b c # first the file to count then all the files to print
能否请您尝试以下。我已经将 @
作为分隔符,您也可以根据需要更改它。
paste -d'@' file1 file2 file3 |
awk -v file2_lines="$(wc -l < file2)" '
BEGIN{
FS=OFS="@"
}
FNR<=file2_lines{
=?:prev_first
=?:prev_third
print
prev_first=
prev_third=
}'
以上代码的运行示例:
假设以下是 Input_file(s):
cat file1
File1_line1
File1_line2
File1_line3
File1_line4
File1_line5
cat file2
File2_line1
File2_line2
File2_line3
cat file3
File3_line1
File3_line2
当我运行以上脚本形式的代码将输出:
./script.ksh
File1_line1@File2_line1@File3_line1
File1_line2@File2_line2@File3_line2
File1_line3@File2_line3@File3_line2
要使文件具有 n
行,您可以使用以下函数(用法:toLength n file
)。如果文件太长,这将省略末尾的行,如果文件太短,则重复最后一行。
toLength() {
{ head -n"" ""; yes "$(tail -n1 "")"; } | head -n""
}
要将所有文件设置为 FileB 的长度并并排显示,请使用
n="$(wc -l < FileB)"
paste <(toLength "$n" FileA) FileB <(toLength "$n" FileC) | column -ts$'\t'
正如用户所观察到的
stretch() {
cat ""
yes "$(tail -n1 "")"
}
paste <(stretch FileA) FileB <(stretch FileC) | column -ts$'\t' |
head -n"$(wc -l < FileB)"
这是一种使用 awk
的简洁方法,我们只读取每个文件一次:
awk -v n=2 '
BEGIN{ while(1) {
for(i=1;i<ARGC;++i) {
if (b[i]=(getline tmp < ARGV[i])) a[i] = tmp
}
if (b[n]) for(i=1;i<ARGC;++i) print a[i] > ARGV[i]".new"
else {break}
}
}' f1 f2 f3 f4 f5 f6
其工作方式如下:
- 引导文件由索引
n
定义。这里我们选择引导文件为f2
. - 我们不按顺序处理标准读取记录字段中的文件,但我们使用并行读取文件的
BEGIN
块。 - 我们执行一个无限循环
while(1)
,如果引导文件没有更多输入,我们将在该循环中中断。 - 每个周期,我们使用
getline
读取每个文件的新行。如果文件i
有换行,将其存入a[i]
,并将getline
的结果置入b[i]
。如果文件i
已到达结尾,请记住最后一行。 - 用
b[n]
检查引导文件的结果。如果我们仍然读取一行,则将所有行打印到文件f1.new
、f2.new
、...,否则,跳出无限循环。