Powiedzmy, że masz plik txt, jakie jest polecenie, aby wyświetlić 10 pierwszych wierszy i 10 ostatnich wierszy pliku jednocześnie?
tzn. jeśli plik ma 200 linii, wyświetlaj wiersze 1-10 i 190-200 za jednym razem.
Powiedzmy, że masz plik txt, jakie jest polecenie, aby wyświetlić 10 pierwszych wierszy i 10 ostatnich wierszy pliku jednocześnie?
tzn. jeśli plik ma 200 linii, wyświetlaj wiersze 1-10 i 190-200 za jednym razem.
Odpowiedzi:
Możesz po prostu:
(head; tail) < file.txt
A jeśli z jakiegoś powodu potrzebujesz rur, to w ten sposób:
cat file.txt | (head; tail)
Uwaga: wydrukuje zduplikowane linie, jeśli liczba linii w pliku.txt jest mniejsza niż domyślne linie nagłówka + domyślne linie końca.
head
zużyciu pierwszych 10 wierszy pliku. (Porównaj to z head < file.txt; tail < file.txt
plikiem zawierającym mniej niż 20 linii). Tylko bardzo drobna kwestia, o której należy pamiętać. (Ale wciąż +1.)
head
tylko pierwsze 10 wierszy swojego wejścia, nie ma gwarancji, że nie zużywa go więcej w celu znalezienia końca dziesiątego wiersza, pozostawiając mniej wejścia do wyświetlenia. less
seq 100 | (head; tail)
daje mi tylko pierwsze 10 liczb. Tylko przy znacznie większym rozmiarze wejściowym (takim jak seq 2000
) ogon otrzymuje pewne dane wejściowe.
W przypadku czystego strumienia (np. Wyjście z polecenia), możesz użyć „tee”, aby rozwidlić strumień i wysłać jeden strumień do głowy i jeden do końca. Wymaga to użycia funkcji '> (list)' w bash (+ / dev / fd / N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )
lub używając / dev / fd / N (lub / dev / stderr) plus podpowłoki ze skomplikowanym przekierowaniem:
( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1
(Żadne z nich nie będzie działać w csh ani tcsh).
Aby uzyskać trochę lepszą kontrolę, możesz użyć tego polecenia perla:
COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
COMMAND | { tee >(head >&2) | tail; } |& other_commands
cat >/dev/null
naprawia to:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
head
i tail
: \ ...
head -10 file.txt; tail -10 file.txt
Poza tym będziesz musiał napisać własny program / skrypt.
cat
i / head
lub tail
rurami, dobrze wiedzieć, że mogę ich używać indywidualnie!
{ head file; tail file; } | prog
(wymagane są odstępy wewnątrz nawiasów klamrowych i końcowy średnik)
Na podstawie komentarza JF Sebastiana :
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1
W ten sposób możesz przetwarzać pierwszą linię i resztę inaczej w jednej rurze, co jest przydatne podczas pracy z danymi CSV:
{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N * 2 2 4 6
Problem polega na tym, że programy zorientowane strumieniowo nie znają z wyprzedzeniem długości pliku (ponieważ może go nie być, jeśli jest to prawdziwy strumień).
narzędzia takie jak tail
buforowanie ostatnich n widzianych wierszy i czekanie na koniec strumienia, a następnie drukowanie.
jeśli chcesz to zrobić za pomocą jednego polecenia (i chcesz, aby działało z dowolnym przesunięciem i nie powtarzaj wierszy, jeśli się nakładają), będziesz musiał emulować to zachowanie, o którym wspomniałem.
spróbuj tego awk:
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
a.out | awk -v ...
Skończyło się na tym rozwiązaniu, które wydaje się być jedynym, które obejmowało wszystkie przypadki użycia (do tej pory):
command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
'{
if (NR <= offset) print;
else {
a[NR] = $0;
delete a[NR-offset];
printf "." > "/dev/stderr"
}
}
END {
print "" > "/dev/stderr";
for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
{ print a[i]}
}'
Lista funkcji:
Od jakiegoś czasu szukałem tego rozwiązania. Wypróbowałem to sam z sedem, ale problem braku znajomości długości pliku / strumienia był nie do pokonania. Ze wszystkich opcji dostępnych powyżej podoba mi się rozwiązanie awk Camille Goudeseune. Zanotował, że jego rozwiązanie pozostawiło dodatkowe puste wiersze na wyjściu z wystarczająco małym zestawem danych. Tutaj podaję modyfikację jego rozwiązania, która usuwa dodatkowe linie.
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
Cóż, zawsze możesz je połączyć. Tak jak tak
head fiename_foo && tail filename_foo
. Jeśli to nie wystarczy, możesz napisać sobie funkcję bash w swoim pliku .profile lub dowolnym pliku logowania, którego używasz:
head_and_tail() {
head $1 && tail $1
}
A później powołać go z powłoki wiersza: head_and_tail filename_foo
.
Pierwsze 10 wierszy pliku file.ext, a następnie jego ostatnie 10 wierszy:
cat file.ext | head -10 && cat file.ext | tail -10
Ostatnie 10 wierszy pliku, a następnie pierwsze 10:
cat file.ext | tail -10 && cat file.ext | head -10
Następnie możesz potokować wyjście również w innym miejscu:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
tail
i / head
lub, tworząc jej alias.
Napisałem prostą aplikację w Pythonie, aby to zrobić: https://gist.github.com/garyvdm/9970522
Obsługuje potoki (strumienie), a także pliki.
Aby obsłużyć potoki (strumienie), a także pliki, dodaj to do pliku .bashrc lub .profile:
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }
Wtedy możesz nie tylko
headtail 10 < file.txt
ale również
a.out | headtail 10
(To nadal dodaje fałszywe puste wiersze, gdy 10 przekracza długość wejścia, w przeciwieństwie do zwykłego starego a.out | (head; tail)
. Dziękuję, poprzednie osoby odpowiadające).
Uwaga: headtail 10
nie headtail -10
.
Opierając się na tym, co @Samus_ wyjaśnił tutaj o tym, jak działa polecenie @Aleksandra Zalcman, ta odmiana jest przydatna, gdy nie można szybko zlokalizować początku ogona bez liczenia linii.
{ head; echo "####################\n...\n####################"; tail; } < file.txt
Lub jeśli zaczniesz pracować z czymś innym niż 20 liniami, liczba linii może nawet pomóc.
{ head -n 18; tail -n 14; } < file.txt | cat -n
Aby wydrukować pierwsze 10 i ostatnie 10 wierszy pliku, możesz spróbować tego:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
UWAGA : Zmienna aFile zawiera pełną ścieżkę do pliku .
Powiedziałbym, że w zależności od rozmiaru pliku, aktywne czytanie jego zawartości może nie być pożądane. W takiej sytuacji myślę, że wystarczy prosty skrypt powłoki.
Oto, jak ostatnio sobie z tym poradziłem w przypadku wielu bardzo dużych plików CSV, które analizowałem:
$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done
Spowoduje to wydrukowanie pierwszych 10 wierszy i ostatnich 10 wierszy każdego pliku, jednocześnie wypisując nazwę pliku i kilka wielokropków przed i po.
W przypadku jednego dużego pliku możesz po prostu uruchomić następujące polecenie, aby uzyskać ten sam efekt:
$ head somefile.csv && echo ... && tail somefile.csv
Zużywa stdin, ale jest prosty i działa w 99% przypadków użycia
#!/usr/bin/env bash
COUNT=${1:-10}
IT=$(cat /dev/stdin)
echo "$IT" | head -n$COUNT
echo "..."
echo "$IT" | tail -n$COUNT
$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100