bash脚本需要来自多个文件bash的行

时间:2016-07-19 15:14:08

标签: linux bash shell awk scripting

我需要一个shell脚本来设计从三个文件中打印一个模式中的行。

file1.txt, file2.txt,file3.txt

我需要输出

line1 of file1.txt
line2 of file1.txt
line1 of file2.txt
line2 of file2.txt
line1 of file3.txt
line2 of file3.txt
line3 of file1.txt
line4 of file1.txt
line3 of file2.txt
line4 of file2.txt
line3 of file3.txt
line4 of file3.txt

...

我们怎样才能在shell脚本中得到它? 它也应该只打印非空行。

8 个答案:

答案 0 :(得分:3)

Perl救援:

perl -e 'open $FH[ @FH ], "<", $_ or die $! for @ARGV;
         while (grep !eof $_, @FH) {
             for my $fh (@FH) {
                 print scalar <$fh> for 1, 2;
             }
         }' -- file*.txt

它保持所有文件同时打开(@FH数组包含文件句柄)。虽然至少有一个尚未结束,但它会打印两行。

答案 1 :(得分:2)

以下脚本如何接受文件作为参数:

TOTAL_LINES=$(wc -l < "$1")
for n in $(seq 1 2 $TOTAL_LINES); do
  for file in "$@"; do
    sed -n "$n{p;n;p}" $file
  done
done

我认为所有文件的行数与评论中建议的行数相同,但如果您将最长的文件作为第一个参数传递,则它也会起作用。

对于你不太可能知道的剧本部分的一点解释:

  • seq将生成一系列数字for将迭代。它的语法是seq from increment upTo,而且它使用的是{from..upTo..increment}语法,它不接受变量
  • $@是传递给脚本的参数数组
  • sed -n "$n{p;n;p}"sed命令,默认情况下不会显示文字,但会再次执行pnp$n行; p打印当前行,n转到下一行

答案 2 :(得分:2)

考虑四个类似的输入文件:

Future.get

我们按如下方式创建$ cat file1.txt line1 of file1.txt line2 of file1.txt line3 of file1.txt line4 of file1.txt

printer.sh

执行此操作时,输入参数每次都添加到新句柄并一次读取#!/bin/bash LINES=2 # Configure this to set the number of consecutive lines per file MAX_HANDLE=3 # Create descriptors 3,4,... for filename1,filename2.... for var in "$@" do eval exec "$MAX_HANDLE"'<"$var"' ((MAX_HANDLE++)) done # Start infinite loop while : do # First descriptor is 3 COUNTER=3 # Loop over all open file descriptors from 3 to MAX_HANDLE - 1 while [ $COUNTER -lt $MAX_HANDLE ]; do # Read $LINES lines from the open file descriptor LINE_COUNTER=0 while [ $LINE_COUNTER -lt $LINES ]; do read -r line <&"$COUNTER" || DONE=true if [[ "$DONE" = true ]]; then exit fi # Print the line that was read echo "$line" ((LINE_COUNTER++)) done ((COUNTER++)) done done 行(在这种情况下,每次2行)。这仅适用于与OP假定的相同长度的文件。

$LINES

答案 3 :(得分:2)

您可以pasteawk一起使用以获得输出:

paste -d $'\01' file[123].txt |
awk -F '\01' 'NR%2{for (i=1; i<=NF; i++) a[i]=$i; next} 
    {for (i=1; i<=NF; i++) print a[i] ORS $i}'

line1 of file1.txt
line2 of file1.txt
line1 of file2.txt
line2 of file2.txt
line1 of file3.txt
line2 of file3.txt
line3 of file1.txt
line4 of file1.txt
line3 of file2.txt
line4 of file2.txt
line3 of file3.txt
line4 of file3.txt
  • 使用paste我们创建并排control-A(ASCII 1)分隔输出
  • awk字段分隔符用作control-A,我们从每列输出2行

答案 4 :(得分:2)

很多答案。这个是awk

创建测试文件

for f in file{1,2,3}.txt; do rm $f; for n in {1,2,3,4}; do echo "line $n of file $f" >> $f; done; done

和awk程序

awk '
    FNR == 1 && NR>1 {
        exit # exit after completing the first file
    }
    {
        # print 2 lines from the first file
        if (NF) print
        getline; if (NF) print
        # print 2 lines from each other file
        for (i=2; i<ARGC; i++) {
            getline < ARGV[i]; if (NF) print
            getline < ARGV[i]; if (NF) print
        }
    }
' file{1,2,3}.txt

if (NF) print行排除空白行,因为以空格分隔的字段数为零。

line 1 of file file1.txt
line 2 of file file1.txt
line 1 of file file2.txt
line 2 of file file2.txt
line 1 of file file3.txt
line 2 of file file3.txt
line 3 of file file1.txt
line 4 of file file1.txt
line 3 of file file2.txt
line 4 of file file2.txt
line 3 of file file3.txt
line 4 of file file3.txt

答案 5 :(得分:1)

这可能不是最有效的方法,但这可行,假设您的所有文件都在$ files中,$ total_lines包含每个文件中的行数:

for line in $(seq 1 $total_lines)
do
    for file in $files
    do
        sed '/^$/d' $file | sed $line'!d'
    done
done

sed'/ ^ $ / d'从流中删除所有空行;

sed $ line'!d'打印出对应于$ line

的行

答案 6 :(得分:1)

使用粘贴和awk。

$ cat test.sh 
paste -d '|' file* | awk -F\| '{
    if(NR % 2 == 1) {
        file1 = $1; 
        file2 = $2; 
        file3 = $3; 
    } else {
        file1 = file1 "\n" $1; 
        file2 = file2 "\n" $2; 
        file3 = file3 "\n" $3; 
        print file1;
        print file2;
        print file3;
    }
}'

由于所有文件的长度相同,我们可以先粘贴所有文件,然后在行号均匀时打印。

答案 7 :(得分:1)

如果您不介意创建中间/临时文件,split(1)是每个Linux发行版的 coreutils 的一部分,可能会很方便:

#!/bin/bash

# Split files every 2 lines using a numeric suffix 
for f in file*.txt; do
    split -d -l 2 "${f}" "${f}"split
done

# Reverse intermediate file names, so we can glob them in numeric order 
for f in file*split*; do
    mv "${f}" "reversed$(echo ${f}|rev)"
done

cat reversed* && rm reversed*