2011-11-29 106 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不應用於指示技術上的不準確或完全錯誤的答案。 –