2016-10-04 48 views
16

Mam plik, który ma ~ 50 000 linii i muszę pobrać określone linie. Próbowałem następujące polecenia:jak korzystać z sed, aby wyodrębnić linie w określonej kolejności?

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

(„N” są konkretne linie, chcę, aby wyodrębnić)

Działa to dobrze, ale polecenie wyodrębnia wierszy w kolejności (czyli re-ZLECENIA moje wejście) gdy próbuję:

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

dostaję plik tekstowy z linii zamówionych jak: 33, 200, 40000 (który nie działa dla moich celów). Czy istnieje sposób na utrzymanie porządku wyświetlania wierszy w komendzie?

Odpowiedz

3

Czy możesz użyć również innych poleceń Basha? W takim przypadku to działa:

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

Prawdopodobnie jest to wolniejsze niż użycie macierzy wewnątrz sed, ale jest bardziej praktyczne.

+4

Jeśli zamierzasz parsować plik kilka razy, to przynajmniej zakończ po wydrukowaniu żądanej linii: 'sed -n" $ {i} {p; q} "' –

4

z perl oszczędza linie wejściowe w zmiennej mieszania z liczbą linii, jak klucz

$ seq 12 20 | perl -nle ' 
@l = (5,2,3,1); 
$a{$.} = $_ if(grep { $_ == $. } @l); 
END { print $a{$_} foreach @l } ' 
16 
13 
14 
12 
  • $. ma numer linii i grep { $_ == $. } @l sprawdza, czy liczba linii jest obecny w tablicy @l który zawiera żądane linii w celu wymagane


jako jedna liniowej @l zgłoszenia wewnątrz BEGIN uniknąć inicjalizacja każdej iteracji, a także zapewnienie żadnych pustych linii, czy numer linii jest poza zakresem:

$ 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 

wystarczająco małego wejścia, można zapisać wiersze w tablicy i indeksy drukowania wymagane.Uwaga korektę wykonaną jako wskaźnik zaczyna 0

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


rozwiązanie z head i tail combo:

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


porównanie wydajności dla pliku wejściowego 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

Trzeba trzymać się przewodem 33 aż po widzieliście Linia 200:

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

Zobacz instrukcję dalszego wyjaśnienia: https://www.gnu.org/software/sed/manual/html_node/Other-Commands.html

awk może być bardziej czytelny:

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

Jeśli masz dowolną liczbę linii do wydrukowania w określonej kolejności, możesz to uogólnić:

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

Testowałem sed i pierwsze rozwiązania awk, ale linia 33 drukuje pustą linię. ostatnie uruchomienie awk działa poprawnie. –