Perintah untuk mendapatkan baris ke-STDOUT

210

Apakah ada perintah bash yang memungkinkan Anda mendapatkan baris ke-10 dari STDOUT?

Artinya, sesuatu yang akan mengambil ini

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

dan melakukan sesuatu seperti

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

Saya menyadari ini akan menjadi praktik yang buruk ketika menulis skrip yang dimaksudkan untuk digunakan kembali, TETAPI ketika bekerja dengan shell setiap hari itu akan berguna bagi saya untuk dapat menyaring STDOUT saya sedemikian rupa.

Saya juga menyadari ini akan menjadi perintah semi-sepele untuk menulis (buffer STDOUT, mengembalikan baris tertentu), tetapi saya ingin tahu apakah ada beberapa perintah shell standar untuk melakukan ini yang akan tersedia tanpa saya meletakkan skrip pada tempatnya.

Alan Storm
sumber
5
Saya baru saja memilih ini untuk penggunaan katamagic-command
Sevenearths

Jawaban:

332

Menggunakan sed, hanya untuk variasi:

ls -l | sed -n 2p

Menggunakan alternatif ini, yang terlihat lebih efisien karena berhenti membaca input ketika baris yang diperlukan dicetak, dapat menghasilkan SIGPIPE dalam proses pengumpanan, yang pada gilirannya dapat menghasilkan pesan kesalahan yang tidak diinginkan:

ls -l | sed -n -e '2{p;q}'

Saya telah melihat bahwa cukup sering bahwa saya biasanya menggunakan yang pertama (yang lebih mudah untuk mengetik, toh), meskipun lsbukan perintah yang mengeluh ketika mendapat SIGPIPE.

Untuk rentang garis:

ls -l | sed -n 2,4p

Untuk beberapa rentang garis:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'
Jonathan Leffler
sumber
Apa arti p? seperti 2p 3p? Dalam arti saya mengerti bahwa ini untuk 2/3 baris, tapi apa teori / pemahaman di baliknya?
Leo Ufimtsev
4
@LeoUfimtsev: padalah sedpernyataan yang mencetak garis yang diidentifikasi oleh rentang garis yang mendahuluinya. Dengan demikian 2,4pberarti mencetak baris 2, 3, 4.
Jonathan Leffler
3
Dan nberarti TIDAK mencetak semua baris lainnya
Timo
85
ls -l | head -2 | tail -1
massa
sumber
13
+2, tapi saya sarankan head -n 2 | tail -n 1- kepala dan ekor modern cenderung mengeluarkan peringatan sebaliknya.
Michael Krelin - hacker
2
Menggunakan dua proses untuk memfilter data sedikit berlebihan (tetapi, mengingat kekuatan mesin saat ini, mereka mungkin akan mengatasinya). Generalisasi untuk menangani satu rentang tidak sepenuhnya sepele (untuk rentang N...M, Anda menggunakan head -n M | tail -n M-N+1), dan generalisasi untuk menangani beberapa rentang tidak dimungkinkan.
Jonathan Leffler
3
kepala disalurkan menjadi ekor? mengerikan. Banyak cara yang lebih baik untuk melakukannya.
camh
16
Hebat tentang * nix command line: satu juta dan satu cara untuk melakukan segalanya dan semua orang punya favorit dan membenci semua cara lain ... :-)
beggs
1
Cara mencetak berbagai baris dengan cara lain: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(dapat menambahkan lebih banyak kondisi lebih mudah, beberapa rentang, dll ...)
user10607
41

Alternatif cara kepala / ekor yang bagus:

ls -al | awk 'NR==2'

atau

ls -al | sed -n '2p'
ChristopheD
sumber
1
awk saya lebih baik, tetapi sed bagus.
Michael Krelin - hacker
Tidak perlu "jika" - lihat jawaban peretas (kecuali untuk bagian tentang menemukan setiap file pada sistem). ;-)
Dijeda hingga pemberitahuan lebih lanjut.
apa artinya '2p'?
merobek
1
@shredding jawaban singkat cetak baris kedua; long -> Saat digunakan, hanya garis yang cocok dengan pola yang diberikan perintah setelah alamat. Secara singkat, ketika digunakan dengan flag / p, garis yang cocok dicetak dua kali: file sed '/ POLA / p'. Dan tentu saja POLA adalah ekspresi reguler yang lebih
Medhat
bagaimana jika 2suatu variabel? Setiap contoh menggunakan tanda kutip tunggal, tetapi dengan var Anda perlu tanda kutip ganda. Bisakah kita "memprogram" awk untuk bekerja juga dengan tanda kutip tunggal ketika menggunakan vars? Contoh line =1; ls | awk 'NR==$line'. Saya tahu itu bashmenggunakan tanda kutip ganda dengan vars.
Timo
21

Dari sed1line :

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

Dari awk1line :

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files
Mark Edgar
sumber
9

Demi kelengkapan ;-)

kode yang lebih pendek

find / | awk NR==3

hidup yang lebih pendek

find / | awk 'NR==3 {print $0; exit}'
Michael Krelin - hacker
sumber
Anda tidak bercanda tentang kelengkapan!
Dijeda sampai pemberitahuan lebih lanjut.
Saya bukan ;-) Sebenarnya, saya tidak tahu mengapa semua orang menggunakan ls- pertanyaan asli adalah tentang seseorang STDOUT, jadi saya pikir lebih baik untuk membuatnya lebih besar.
Michael Krelin - hacker
Setidaknya dengan GNU awk, tindakan standarnya adalah { print $0 }, jadi `awk 'NR == 3' adalah cara yang lebih pendek untuk menulis yang sama.
ephemient
Anda mungkin harus menambahkan "; keluar" ke tindakan itu. Tidak ada gunanya memproses sisa baris ketika Anda tidak akan melakukan apa pun dengan mereka.
camh
@camh: Lihat jawaban Jonathan Leffer tentang itu: "dapat menghasilkan SIGPIPE dalam proses pemberian makan, yang pada gilirannya dapat menghasilkan pesan kesalahan yang tidak diinginkan".
ephemient
7

Coba sedversi ini :

ls -l | sed '2 ! d'

Dikatakan "hapus semua baris yang bukan yang kedua".

Dijeda sampai pemberitahuan lebih lanjut.
sumber
6

Anda dapat menggunakan awk:

ls -l | awk 'NR==2'

Memperbarui

Kode di atas tidak akan mendapatkan apa yang kita inginkan karena kesalahan satu per satu: baris pertama perintah ls-l adalah baris total . Untuk itu, kode revisi berikut akan berfungsi:

ls -l | awk 'NR==3'
Hai Vu
sumber
4

Apakah Perl mudah tersedia untuk Anda?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Jelas mengganti nomor apa pun yang Anda inginkan untuk 7.

makanan kucing
sumber
Ini adalah favorit saya karena tampaknya menjadi yang paling mudah untuk digeneralisasi dengan apa yang saya pribadi setelah setiap tanggal , perl -n -e 'if ($.% 7 == 0) {print; keluar (0); ''
mat kelcey
@matkelcey juga dapat Anda lakukan $ awk 'NR % 7' filenameuntuk mencetak setiap baris ke-7 dalam file.
user10607
3

Poster lain menyarankan

ls -l | head -2 | tail -1

tetapi jika Anda kepala pipa menjadi ekor, sepertinya semuanya hingga garis N diproses dua kali.

Pipa ekor ke kepala

ls -l | tail -n +2 | head -n1

akan lebih efisien?

tak seorangpun
sumber
0

Ya, cara paling efisien (seperti yang telah ditunjukkan oleh Jonathan Leffler) adalah menggunakan sed dengan print & berhenti:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

sumber
Penggunaan pipefail dan PIPESTATUS sangat menarik dan ini menambah banyak halaman ini.
Mike S
0

Hmm

sed tidak bekerja dalam kasus saya. Saya melamar:

untuk baris "ganjil" 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

untuk "datar" baris 2,4,6,8 ls | awk '0 == (NR)% 2'

stan
sumber
-1

Untuk kelengkapan lebih lanjut ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Buang garis sampai Anda mendapatkan yang kedua, lalu cetak baris pertama setelah itu. Jadi, ia mencetak baris ke-3.

Jika itu hanya baris kedua ..

ls -l | (read; head -n1)

Letakkan sebanyak mungkin bacaan yang diperlukan.

Shizzmo
sumber
-1

Saya menambahkan ini di .bash_profile saya

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

Dan itu berhasil

gdf <file name>
Joel AZEMAR
sumber
Saya bingung - apa hubungannya dengan pertanyaan ini?
Jonathan Leffler