Pobierz określoną linię z pliku tekstowego za pomocą skryptu powłoki


101

Próbuję uzyskać określoną linię z pliku tekstowego.

Do tej pory w Internecie widziałem tylko rzeczy takie jak sed (mogę używać tylko sh -not bash lub sed lub czegoś podobnego). Muszę to zrobić tylko przy użyciu podstawowego skryptu powłoki.

cat file | while read line
    do
       #do something
    done

Wiem, jak iterować po liniach, jak pokazano powyżej, ale co, jeśli potrzebuję tylko uzyskać zawartość określonej linii


znasz numer linii?
— Mehul Rathod

1
Wtedy będziesz liczyć.
— Ignacio Vazquez-Abrams

tak, numer linii to 5 @MehulRathod
— GangstaGraham,

3
Dlaczego jest w catporządku, ale sednie jest? To nie ma sensu.
— William Pursell,

5
Ponieważ nikt nie może odmówić cat. Och ... uroczy cat!

Odpowiedzi:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

A co z poleceniem sh, nie mogę używać sed, awk. Powinienem to wyjaśnić w pytaniu.
— GangstaGraham,

@GangstaGraham, powiedziałeś, że wiesz, jak iterować po liniach, a co powiesz na dodanie licznika? jeśli licznik osiągnie docelowy numer linii, weź tę linię i przerwij pętlę. czy to pomaga?
— Kent

4
@KanagaveluSugumar przeczytaj stronę informacyjną seda. 5!doznacza usuń wszystkie linie z wyjątkiem 5. shell var jest możliwe, potrzebujesz podwójnych cudzysłowów.
— Kent

13
Sugerowałbym dodanie innego wariantu: sed -n 5pwydaje się to bardziej logiczne do zapamiętania dla początkujących, ponieważ -noznacza "domyślnie brak wyjścia" i poznacza "drukuj" i nie ma potencjalnie mylącej wzmianki o usuwaniu (kiedy ludzie mówią o plikach, usuwanie linii ma tendencję do znaczy coś innego).
— Josip Rodin

1
@JosipRodin masz rację, -n '5p'działa też w przypadku tego problemu. Różnica polega na tym, 5!dże możesz dodać, -iaby zapisać zmianę z powrotem do pliku. jednakże, -n 5pco do tego sed -n '5p' f > f2&& mv f2 fznowu, zgadzam się z pańską opinią na to pytanie.
— Kent

21

Zakładając, że linejest to zmienna, która przechowuje wymagany numer linii, jeśli możesz użyć headi tail, to jest całkiem proste:

head -n $line file | tail -1

Jeśli nie, to powinno działać:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

To -eqporównanie dotyczy liczb całkowitych, więc potrzebny jest numer wiersza, a nie zawartość wiersza ( $line). Należy to naprawić, definiując np. want=5Przed pętlą, a następnie używając -eqporównania na $want. [przeniesione z odrzuconej edycji]
— Josip Rodin

1
@JosipRodin Zrobiłem niezależną sugestię edycji na podstawie twojego komentarza, z którym się zgadzam. Miejmy nadzieję, że tym razem nie zostanie odrzucony.
— Victor Zamanian

16

Możesz użyć sed -n 5p file.

Możesz również uzyskać zakres, np sed -n 5,10p file.


11

Najlepsza metoda wykonania

sed '5q;d' file

Ponieważ sedprzestaje czytać wszystkie wiersze po piątej

Zaktualizuj eksperyment pana Rogera Duecka

Zainstalowałem wcanadian-insane (6,6 MB) i porównałem sed -n 1p / usr / share / dict / words i sed '1q; d' / usr / share / dict / words używając polecenia time; pierwsza trwała 0,043 s, druga tylko 0,002 s, więc użycie „q” zdecydowanie poprawia wydajność!


1
Jest to również powszechnie napisane:sed -n 5q
— William Pursell

3
Podoba mi się to rozwiązanie, ponieważ sedprzestaje czytać jakiekolwiek wiersze po piątej.
— Anthony Geoghegan

1
Zainstalowałem wcanadian-insane (6,6 MB) i porównałem sed -n 1p /usr/share/dict/wordsi sed '1q;d' /usr/share/dict/wordsużywając timepolecenia; pierwsza trwała 0,043 s, druga tylko 0,002 s, więc użycie „q” zdecydowanie poprawia wydajność!
— Roger Dueck

5

Jeśli na przykład chcesz uzyskać wiersze od 10 do 20 pliku, możesz użyć każdej z tych dwóch metod:

head -n 20 york.txt | tail -11

lub

sed -n '10,20p' york.txt 

p w powyższym poleceniu oznacza drukowanie.

Oto, co zobaczysz: wprowadź opis obrazu tutaj


2

Standardowym sposobem na to jest użycie narzędzi zewnętrznych. Niedopuszczanie do używania zewnętrznych narzędzi podczas pisania skryptu powłoki jest absurdalne. Jeśli jednak naprawdę nie chcesz używać narzędzi zewnętrznych, możesz wydrukować wiersz 5 z:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Zwróć uwagę, że spowoduje to wydrukowanie linii logicznej 5. Oznacza to, że jeśli input-filezawiera kontynuacje linii, zostaną one policzone jako pojedynczy wiersz. Możesz zmienić to zachowanie, dodając -rdo polecenia odczytu. (Co jest prawdopodobnie pożądanym zachowaniem).


1
$((++i))wydaje się być bashizmem; jeśli OP jest ograniczony w korzystaniu z zewnętrznych narzędzi, nie zakładałbym, że będą mieli dostęp do czegoś więcej niż zwykłego/bin/sh
— Josip Rodin

@JosipRodin Nie, to funkcja POSIX (ale obsługa ++przyrostów jest specjalnie oznaczona jako opcjonalna).
— tripleee

@tripleee nie działa z nowoczesnym myślnikiem jako / bin / sh, więc nie polegałbym na tym.
— Josip Rodin

Ale proste obejście, takie jak $((i+=1))działa również w Dash.
— tripleee

$(($i+1))to proste obejście, o którym myślałem.
— Josip Rodin

1

Równolegle z odpowiedzią Williama Pursella , oto prosta konstrukcja, która powinna działać nawet w oryginalnej powłoce Bourne'a v7 (a więc także w miejscach, w których Bash nie jest dostępny).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Zwróć także uwagę na optymalizację breakwyjścia z pętli, gdy otrzymaliśmy szukaną linię.


0

Nie podobała mi się żadna z odpowiedzi.

Oto jak to zrobiłem.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

Łatwo z Perlem! Jeśli chcesz pobrać linie 1, 3 i 5 z pliku, powiedz / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'ale smartmatch jest eksperymentalny i odradza się jego używania
— Sorin

Żadne z pozostałych rozwiązań nie jest tak zwięzłe ani nie pozwala na tak dużą elastyczność. (Dlaczego wydaje się, że wszystko, co oszczędza czas i ułatwia życie, jest „zniechęcane” przez „inteligentnych ludzi”, czy wszyscy mamy cały dzień gapić się na ekrany?)
— dagelf

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
czy mógłbyś chociaż trochę opisać, dlaczego ta praca jest bardziej zrozumiała dla osoby, która zadała pytanie?
— ted

Tak więc pierwszy grep wybiera wszystkie wiersze, dodając numery wierszy na ich początku. Następnie drugi grep wybiera konkretną linię, dopasowując numer linii na początku. I na koniec numer linii jest przycinany od początku linii w echu.
— Odra

Jest to zarówno złożone, jak i nieefektywne w porównaniu do sed -n 5p, które oczywiście można jeszcze zoptymalizować do czegoś takiegosed -n '5!d;p;q'
— tripleee
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.