2011-11-29 91 views

回答

14

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

解决的办法是创建另一个新的文件句柄,其工作方式与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<&- 
+0

也许我误解了这个问题,但即使没有输入的诡计,只是调用read read也可以很好地工作。 – Tgr

+2

再次准备,我也不知道为什么我解释了如何创建一个新的文件句柄,而不是使用stdio: -/ –

+2

感谢你们,我用它作为我解决方案的基础。这工作,但额外的文件句柄不是必需的。请参阅下面的解决方案,但是不需要第二个句柄。 – Fmstrat

1

只需使用一个for循环:

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

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

+0

是的,在bash中没有明确的“读取N行”。你必须用值串联来构造它。 @choroba在这里的正确轨道,但需要提供输入源和管理多个输入通道(每N次读取重置“行”)。 Awk确实是针对这些问题而设计的(但是没有明确的'读取N行'函数),您需要在awk中用'NR'变量管理N行。 Sed也可以工作,但实际上会很丑陋,并且需要大量的搞乱才能使N通用而不是硬编码处理。祝你们好运。 – shellter

+0

我试过像:for i in $(seq 1 4); do read line <101127_2_aa_1.fastq; lines + = $ line $'\ n';完成 但似乎没有用... – user815408

+0

其实,如何提供输入? – user815408

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 
5

我不认为有一种方法在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 

在这个循环$块包含10条输入线路在每次迭代中,除了最后一个,其中将包含输入的最后几行,这可能是小于数据线块10但总是超过0.

+0

我真的很喜欢这个解决方案,但你怎么称呼它?你如何传递一个文件名? – jasonmclose

+0

上面的while循环将从标准输入读取。如果你想输入一个文件给它,只需要将它输入到标准输入。 – albarji

+0

啊。所以'cat $ filename | readlines 10'? – jasonmclose

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功能将读取stdin$1线(使用重定向,使其从文件中读取,就像内置read命令)。由于read的退出码保持不变,您可以在循环中使用read_n,如上例所示。

+0

请注意,如果行数不是5的倍数,则不会输出最后1到4行。 – Arjan

+0

读取最后一行时失败,或者如果整个文件长度小于5行,则失败。 – mathieu

15

虽然选定的答案有效,但确实不需要单独的文件句柄。只需在原始句柄上使用读命令就可以正常工作。

下面是两个例子,一个是字符串,一个具有一个文件:

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

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

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

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

运行上面作为脚本将输出(对于两个环路相同的输出):

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

这个答案值得的方式比它有更多的信用。 –

+0

也许更新这个使用更有效的方式来读取文件[这里](https://stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash) (不用调用'cat')。 – hnefatl

1

我知道你问庆典,但我很惊讶,这一点也适用zsh

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

Unfortunatel Ÿ,这不适用于bash,至少我试过的版本。

“魔术师”这里是-d\4(这并不在bash工作),即设置行分隔符是EOT字符,它会在你cat的末尾。或者任何产生输出的命令。

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

编辑

一些尝试后,我才发现,这个工程使用bash:

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

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

7

最简单的方法 - 相当不言自明,它类似于@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 
+0

有人知道吗?怎么运行的????? –

11

随着Bash≥4可以使用mapfile像这样:

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

这是一次读取10行。

+3

不能相信这个优秀的答案是投票表决++ – anubhava

+1

这似乎是最有效和最优雅的方法,因此应该是被接受的答案。如果添加了对&&(($ {#ary [@]}))的解释,那就太好了。 – codeforester

1

echo模拟了两行输入一个文件,如果需要使用head -2paste前:

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) 
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 
0

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

+0

这是如何回答这个问题的?我将此举标记为_不是答案,但该标志因此消息被拒绝:_flags不应用于指示技术上的不准确或完全错误的答案。 –