使用Bash一次读取n行

时间:2011-11-29 16:50:20

标签: bash

我阅读了help read页面,但仍然没有意义。不知道使用哪个选项。

如何使用Bash一次读取N行?

17 个答案:

答案 0 :(得分:30)

虽然所选答案有效,但实际上不需要单独的文件句柄。只需在原始句柄上使用read命令就可以正常运行。

这是两个例子,一个带字符串,一个带文件:

# Create a dummy file
echo -e "1\n2\n3\n4" > testfile.txt

# Loop through and read two lines at a time
while read -r ONE; do
    read -r TWO
    echo "ONE: $ONE TWO: $TWO"
done < testfile.txt

# Create a dummy variable
STR=$(echo -e "1\n2\n3\n4")

# Loop through and read two lines at a time
while read -r ONE; do
    read -r TWO
    echo "ONE: $ONE TWO: $TWO"
done <<< "$STR"

运行上面的脚本将输出(两个循环的输出相同):

ONE: 1 TWO: 2
ONE: 3 TWO: 4
ONE: 1 TWO: 2
ONE: 3 TWO: 4

答案 1 :(得分:23)

如果Bash≥4,您可以使用commentEvent(event),如下所示:

mapfile

一次只读10行。

答案 2 :(得分:20)

这比它看起来更难。问题是如何保持文件句柄。

解决方案是创建另一个新文件句柄,其工作方式类似于stdin(文件句柄0),但是是独立的,然后根据需要从中读取。

#!/bin/bash

# Create dummy input
for i in $(seq 1 10) ; do echo $i >> input-file.txt ; done

# Create new file handle 5
exec 5< input-file.txt

# Now you can use "<&5" to read from this file
while read line1 <&5 ; do
        read line2 <&5
        read line3 <&5
        read line4 <&5

        echo "Four lines: $line1 $line2 $line3 $line4"
done

# Close file handle 5
exec 5<&-

答案 3 :(得分:16)

最简单的方法 - 非常不言自明。它类似于@Fmstrat提供的方法,除了第二个read语句在do之前。

while read first_line; read second_line
do
    echo "$first_line" "$second_line"
done

您可以通过管道多行输入来使用它:

seq 1 10 | while read first_line; read second_line 
do
    echo "$first_line" "$second_line"
done

输出:

1 2
3 4
5 6
7 8
9 10

答案 4 :(得分:6)

我认为有一种方法可以在bash中本地执行,但是可以创建一个方便的功能:

#
# Reads N lines from input, keeping further lines in the input.
#
# Arguments:
#   $1: number N of lines to read.
#
# Return code:
#   0 if at least one line was read.
#   1 if input is empty.
#
function readlines () {
    local N="$1"
    local line
    local rc="1"

    # Read at most N lines
    for i in $(seq 1 $N)
    do
        # Try reading a single line
        read line
        if [ $? -eq 0 ]
        then
            # Output line
            echo $line
            rc="0"
        else
            break
        fi
    done

    # Return 1 if no lines where read
    return $rc
}

通过这样做,可以通过执行类似

的操作轻松地遍历数据的N行块
while chunk=$(readlines 10)
do
    echo "$chunk" | ... # Whatever processing
done

在这个循环中,$ chunk在每次迭代时将包含10个输入行,除了最后一行,它将包含最后一行输入,可能小于10但总是大于0。

答案 5 :(得分:3)

那要简单得多! :)

cat input-file.txt | xargs -L 10 ./do_something.sh

cat input-file.txt | xargs -L 10 echo

答案 6 :(得分:2)

我提出了与@ albarji的答案非常相似的内容,但更简洁。

read_n() { for i in $(seq $1); do read || return; echo $REPLY; done; }

while lines="$(read_n 5)"; do
    echo "========= 5 lines below ============"
    echo "$lines"
done < input-file.txt

read_n函数将从$1读取stdin行(使用重定向使其从文件中读取,就像内置的read命令一样)。由于保留了read的退出代码,因此您可以在循环中使用read_n,如上例所示。

答案 7 :(得分:1)

根据您要执行的操作,您只需存储前一行。

LINE_COUNT=0
PREVLINE1=""
PREVLINE2=""
while read LINE
  do LINE_COUNT=$(($LINE_COUNT+1));
    if [[ $LINE_COUNT == 3 ]]; then
       LINE_COUNT=0
       # do whatever you want to do with the 3 lines
    done
    PREVLINE2="$PREVLINE1"
    PREVLINE1="$LINE"
  done
done < $FILE_IN

答案 8 :(得分:1)

我知道你问过bash,但我很惊讶这适用于zsh

#!/usr/bin/env zsh    
cat 3-lines.txt | read -d\4 my_var my_other_var my_third_var

不幸的是,这不适用于bash,至少是我尝试过的版本。

这里的“魔法”是-d\4(这在bash中不起作用),它将行分隔符设置为EOT字符,可以在cat结尾处找到{1}}。或任何产生输出的命令。

如果您想阅读一系列N项,bash有readarraymapfile,可以读取N行的文件,并将每行保存在一个位置数组。

修改

经过一些尝试,我发现这适用于bash:

$ read -d# a b
Hello
World
#
$ echo $a $b
Hello World
$

但是,我无法让{ cat /tmp/file ; echo '#'; } | read -d# a b工作:(

答案 9 :(得分:1)

只需使用for循环:

for i in $(seq 1 $N) ; do read line ; lines+=$line$'\n' ; done

在bash版本4中,您还可以使用mapfile命令。

答案 10 :(得分:1)

echo模拟包含两行输入的文件,如果需要,请在head -2之前使用paste

IFS=\; read A B < <(echo -en "X1 X2\nY1 Y2\n" | paste -s -d\;)

如果你想在循环中读取行并创建对,并且行中只有单个单词,请使用:

while read NAME VALUE; do 
    echo "$NAME=$VALUE"; 
done < <(echo -en "M\n1\nN\n2\nO\n3\n" | xargs -L2 echo)

答案 11 :(得分:0)

以下是另一种方法:

//This will open the file and users can start adding variables.
cat > file
//After finished ctrl + D will close it
cat file|while read line;
do
  //do some stuff here
done

答案 12 :(得分:0)

在这种情况下,awk是一种有趣的方式:

~$ cat test.txt 
tom
tom@gmail.com
jack
jack@gmail.com
marry
marry@gmail.com
gogo
gogo@gmail.com
~$ cat test.txt | awk 'BEGIN{c=1}{a=c;if(a==2){print b" "$0;c=1} if(a==1){b=$0;c=2}}'
tom tom@gmail.com
jack jack@gmail.com
marry marry@gmail.com
gogo gogo@gmail.com

答案 13 :(得分:0)

从文件中读取n + 2行:-

2

4

6

8

等等

您可以尝试这种方式:-

cat fileName | awk '!((NR - 0) % 2)'

答案 14 :(得分:0)

您也可以使用 awk 对行进行分组:

$ seq -s ' ' 23 > file

$ cat file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

$ awk '(NR % 6 == 1) {print; for(i=1; i<6 && getline ; i++) { print }; printf "\n"}' RS=' ' ORS=' ' file
1 2 3 4 5 6 
7 8 9 10 11 12 
13 14 15 16 17 18 
19 20 21 22 23
 

答案 15 :(得分:-1)

mapfile -n通过Bash读取超出分配给数组的最后一行的额外行。已在4.2.35中修复

答案 16 :(得分:-1)

看完所有答案后,我认为以下是最简单的,即,更多的脚本编写者比任何其他解决方案都更了解它,但仅适用于少数项目:

while read -r var1 && read -r var2; do 
    echo "$var1" "$var2"
done < yourfile.txt

multi-command approach也很出色,但是虽然语法很直观,但鲜为人知:

while read -r var1; read -r var2; do 
    echo "$var1" "$var2"
done < yourfile.txt

它的优点是,对于大量项目,不需要连续行:

while 
    read -r var1
    read -r var2
    ...
    read -r varN
do 
    echo "$var1" "$var2"
done < yourfile.txt

xargs answer posted在理论上也不错,但是在实践中处理合并行并不是很明显。例如,我使用此技术想到的一种解决方案是:

while read -r var1 var2; do 
    echo "$var1" "$var2"
done <<< $(cat yourfile.txt | xargs -L 2 )

,但是它再次使用鲜为人知的<<<运算符。但是,这种方法的优势在于,如果您的脚本最初是

while read -r var1; do 
    echo "$var1"
done <<< yourfile.txt

然后将其扩展为多行是很自然的:

while read -r var1 var2; do 
    echo "$var1" "$var2"
done <<< $(cat endpoints.txt | xargs -L 2 )

straightforward solution

while read -r var1; do
    read -r var2
    echo "$var1" "$var2"
done < yourfile.txt
为了简单起见,

是我要考虑的唯一的另一个,但从语法上讲,它不那么具有表现力。与&&版本或多命令版本相比,它感觉不正确。