使用 Bash 一次读取 n 行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8314499/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:15:46  来源:igfitidea点击:

Read n lines at a time using Bash

bash

提问by LookIntoEast

I read the help readpage, but still don't quite make sense. Don't know which option to use.

我读了help read页面,但仍然不太明白。不知道使用哪个选项。

How can I read N lines at a time using Bash?

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

采纳答案by Aaron Digulla

This is harder than it looks. The problem is how to keep the file handle.

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

The solution is to create another, new file handle which works like stdin(file handle 0) but is independent and then read from that as you need.

解决方案是创建另一个新的文件句柄,它的工作方式类似于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<&-

回答by Fmstrat

While the selected answer works, there is really no need for the separate file handle. Just using the read command on the original handle will function fine.

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

Here are two examples, one with a string, one with a file:

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

# 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"

Running the above as a script would output (the same output for both loops):

将上述内容作为脚本运行将输出(两个循环的输出相同):

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

回答by gniourf_gniourf

With Bash≥4 you can use mapfilelike so:

使用 Bash≥4,您可以mapfile像这样使用:

while mapfile -t -n 10 ary && ((${#ary[@]})); do
    printf '%s\n' "${ary[@]}"
    printf -- '--- SNIP ---\n'
done < file

That's to read 10 lines at a time.

那就是一次阅读10行。

回答by Mike Furlender

Simplest method - pretty self-explanatory. It is similar to the method provided by @Fmstrat, except the second readstatement is before the do.

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

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

You can use this by piping multiline input to it:

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

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

output:

输出:

1 2
3 4
5 6
7 8
9 10

回答by naviram

That is much more simple! :)

那要简单得多!:)

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

or

或者

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

回答by albarji

I don't think there is a way to do it natively in bash, but one can create a convenient function for doing so:

我不认为有一种方法可以在 bash 中本地执行此操作,但是可以为此创建一个方便的函数:

#
# Reads N lines from input, keeping further lines in the input.
#
# Arguments:
#   : 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=""
    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
}

With this one can easily loop over N-line chunks of the data by doing something like

有了这个,可以通过执行类似的操作轻松地遍历 N 行数据块

while chunk=$(readlines 10)
do
    echo "$chunk" | ... # Whatever processing
done

In this loop $chunk will contain 10 input lines at each iteration, except for the last one, which will contain the last lines of input, which might be less than 10 but always more than 0.

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

回答by Steve Prentice

I came up with something very similar to @albarji's answer, but more concise.

我想出了与@albarji 的答案非常相似的东西,但更简洁。

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

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

The read_nfunction will read $1lines from stdin(use redirection to make it read from a file, just like the built-in readcommand). Because the exit code from readis maintained, you can use read_nin a loop as the above example demonstrates.

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

回答by elzilrac

Depending on what you're trying to do, you can just store the previous lines.

根据您要执行的操作,您可以只存储前几行。

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

回答by choroba

Just use a forloop:

只需使用一个for循环:

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

In bash version 4, you can also use the mapfilecommand.

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

回答by Luís Guilherme

I know you asked about bash, but I am amazed that this works with zsh

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

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

Unfortunately, this doesn't work with bash, at least the versions I tried.

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

The "magic" here is the -d\4(this doesn't work in bash), that sets the line delimiter to be the EOTcharacter, which will be found at the end of your cat. or any command that produces output.

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

If you want to read an array of Nitems, bash has readarrayand mapfilethat can read files with Nlines and save every line in one position of the array.

如果你想读的阵列N项目,bash有readarraymapfile可与读取文件N的线条和保存每一行的阵列中的一个位置。

EDIT

编辑

After some tries, I just found out that this works with bash:

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

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

However, I could not make { cat /tmp/file ; echo '#'; } | read -d# a bto work :(

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