簡體   English   中英

bash腳本需要多個文件中的行bash

[英]bash script required lines from multiple files bash

我需要一個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腳本中得到它? 另外,它應該只打印非空白行。

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數組包含文件句柄)。 盡管至少有一個尚未結束,但每行都打印了兩行。

下面的腳本呢,該腳本接受文件作為參數:

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 upToseq from increment upTo的語法,用於代替不接受變量的{from..upTo..increment}語法
  • $@是傳遞給腳本的參數數組
  • sed -n "$n{p;n;p}"sed命令,默認情況下不會顯示文本,但是將對$n行再次執行pnp p打印當前行, n轉到下一行

考慮四個類似的輸入文件:

$ 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

執行此操作時,每個輸入參數都添加到一個新的句柄中,並一次讀取$LINES行(在這種情況下,一次讀取2行)。 這僅適用於與OP存放長度相同的文件。

$ ./printer.sh file1.txt file2.txt file3.txt file4.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
line1 of file4.txt
line2 of file4.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
line3 of file4.txt
line4 of file4.txt

您可以將awkpaste一起使用以獲取輸出:

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行

很多答案。 這個是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

這可能不是最有效的方法,但如果您將所有文件都放在$ 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相對應的行

使用粘貼和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;
    }
}'

由於所有文件的長度相同,因此我們可以先粘貼所有文件,然后在行數為偶數時打印。

如果您不介意創建中間/臨時文件,則每個Linux發行版coreutils的一部分split(1)可能會很方便:

#!/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*

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM