2016-10-04 4 views
16

У меня есть файл длиной ~ 50 000 строк, и мне нужно получить определенные строки. Я попробовал следующую команду:Как использовать sed для извлечения строк в указанном порядке?

sed -n 'Np;Np;Np' inputFile.txt > outputFile.txt 

(«N» быть конкретные строки, я хочу, чтобы извлечь)

Это прекрасно работает, но команда извлекает строки в порядке (т.е. переупорядочиваем мой вход) ex. если я стараюсь:

sed -n '200p;33p;40,000p' inputFile.txt > outputFile.txt 

Я получаю текстовый файл со строками упорядоченными, как: 33, 200, 40 000 (который не работает для моей цели). Есть ли способ поддерживать порядок, в котором строки появляются в команде?

ответ

3

Можете ли вы также использовать другие команды bash? В этом случае это работает:

for i in 200 33 40000; do 
    sed -n "${i}p" inputFile.txt 
done > outputFile.txt 

Возможно, это медленнее, чем использование массива внутри sed, но это более практично.

+4

Если вы собираетесь анализировать файл несколько раз, то, по крайней мере, выйдите после печати нужной строки: 'sed -n '$ {i} {p; q}" ' –

4

с perl, сохраняет входные строки в переменной хэш с номером строки в качестве ключа

$ seq 12 20 | perl -nle ' 
@l = (5,2,3,1); 
$a{$.} = $_ if(grep { $_ == $. } @l); 
END { print $a{$_} foreach @l } ' 
16 
13 
14 
12 
  • $. является номером строки и grep { $_ == $. } @l проверки, если это номер строки присутствует в массиве @l, который содержит требуемые строки в порядке требуется


как один-лайнер, @l декларации внутриизбежать инициализацию каждой итерации, а также гарантирует отсутствие пустых строк, если номер строки из диапазона:

$ seq 50000 > inputFile.txt 
$ perl -nle 'BEGIN{@l=(200,33,40000)} $a{$.}=$_ if(grep {$_ == $.} @l); END { $a{$_} and print $a{$_} foreach (@l) }' inputFile.txt > outputFile.txt 
$ cat outputFile.txt 
200 
33 
40000 

Для достаточно малого входа, можно сохранить строки в массиве и индексов печати требуется.Обратите внимание на регулировку, выполненный в виде индекса начинается с 0

$ seq 50000 | perl -e '$l[0]=0; push @l,<>; print @l[200,33,40000]' 
200 
33 
40000 


Solution с head и tail комбо:

$ for i in 200 33 40000; do head -"${i}" inputFile.txt | tail -1 ; done 
200 
33 
40000 


сравнения производительности для входного файла seq 50000 > inputFile.txt

$ time perl -nle 'BEGIN{@l=(200,33,40000)} $a{$.}=$_ if(grep {$_ == $.} @l); END { $a{$_} and print $a{$_} foreach (@l) }' inputFile.txt > outputFile.txt 

real 0m0.044s 
user 0m0.036s 
sys 0m0.000s 

$ time awk -v line_order="200 33 40000" ' 
    BEGIN { 
     n = split(line_order, inorder) 
     for (i=1; i<=n; i++) linenums[inorder[i]] 
    } 
    NR in linenums {cache[NR]=$0} 
    END {for (i=1; i<=n; i++) print cache[inorder[i]]} 
' inputFile.txt > outputFile.txt 

real 0m0.019s 
user 0m0.016s 
sys 0m0.000s 

$ time for i in 200 33 40000; do sed -n "${i}{p;q}" inputFile.txt ; done > outputFile.txt 

real 0m0.011s 
user 0m0.004s 
sys 0m0.000s 

$ time sed -n '33h; 200{p; g; p}; 40000p' inputFile.txt > outputFile.txt 

real 0m0.009s 
user 0m0.008s 
sys 0m0.000s 

$ time for i in 200 33 40000; do head -"${i}" inputFile.txt | tail -1 ; done > outputFile.txt 

real 0m0.007s 
user 0m0.000s 
sys 0m0.000s 
13

Вы должны держаться не на линию 33, пока вы видели линии 200:

sed -n '33h; 200{p; g; p}; 40000p' file 

Смотрите руководство для дальнейшего объяснения: https://www.gnu.org/software/sed/manual/html_node/Other-Commands.html

awk может быть более удобным для чтения:

awk ' 
    NR == 33 {line33 = $0} 
    NR == 200 {print; print line33} 
    NR == 40000 {print} 
' file 

Если у вас есть произвольное количество строк для печати в определенном порядке, вы можете это обобщить:

awk -v line_order="11 3 5 1" ' 
    BEGIN { 
     n = split(line_order, inorder) 
     for (i=1; i<=n; i++) linenums[inorder[i]] 
    } 
    NR in linenums {cache[NR]=$0} 
    END {for (i=1; i<=n; i++) print cache[inorder[i]]} 
' file 
+0

Я опробовал sed и первые awk-решения, но строка 33 печатает пустую строку. последнее awk solition выполняется правильно. –

Смежные вопросы