Bagaimana cara menggunakan perintah shell untuk hanya menampilkan kolom pertama dan kolom terakhir dalam file teks?


30

Saya butuh bantuan untuk mengetahui cara menggunakan perintah sed untuk hanya menampilkan kolom pertama dan kolom terakhir dalam file teks. Inilah yang saya miliki sejauh ini untuk kolom 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

Upaya lemah saya untuk mendapatkan kolom terakhir untuk ditampilkan juga adalah:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

Namun ini mengambil kolom pertama dan kolom terakhir dan menggabungkannya bersama dalam satu daftar. Apakah ada cara untuk mencetak kolom pertama dan kolom terakhir dengan jelas dengan perintah sed dan awk?

Input sampel:

foo|dog|cat|mouse|lion|ox|tiger|bar

5
Harap berikan beberapa input sampel.
jasonwryan

Jawaban:


51

Hampir sampai. Letakkan kedua referensi kolom di samping satu sama lain.

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

Perhatikan juga bahwa Anda tidak perlu di catsini.

sed 's/|/ /' logfile | awk '{print $1, $8}'

Perhatikan juga Anda dapat memberi tahu awkbahwa pemisah kolom adalah |, bukannya kosong, jadi Anda tidak perlu sedmelakukannya.

awk -F '|' '{print $1, $8}' logfile

Sesuai saran oleh Caleb , jika Anda menginginkan solusi yang masih menampilkan bidang terakhir, bahkan jika tidak ada delapan, Anda dapat menggunakan $NF.

awk -F '|' '{print $1, $NF}' logfile

Juga, jika Anda ingin output mempertahankan |separator, alih-alih menggunakan spasi, Anda dapat menentukan pemisah bidang output. Sayangnya, ini sedikit lebih canggung daripada hanya menggunakan -Fbendera, tetapi di sini ada tiga pendekatan.

  • Anda dapat menetapkan pemisah bidang input dan output dengan awksendirinya, di blok BEGIN.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • Anda dapat menetapkan variabel-variabel ini saat menelepon awkdari baris perintah, melalui -vflag.

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • atau hanya:

    awk -F '|' '{print $1 "|" $8}' logfile

4
Pekerjaan bagus merinci bagaimana masalah ini dapat disederhanakan. Anda dapat menambahkan catatan tentang cara menggunakan |sebagai pemisah keluaran daripada ruang default untuk penggabungan string. Anda juga bisa menjelaskan untuk menggunakan $NFalih-alih pengodean keras $8untuk mendapatkan kolom terakhir.
Caleb

12

Cukup ganti dari yang pertama hingga yang terakhir |dengan |(atau spasi jika Anda mau):

sed 's/|.*|/|/'

Perhatikan bahwa meskipun tidak ada sedimplementasi |yang spesial (selama ekspresi reguler yang diperluas tidak diaktifkan melalui -Eatau -rdalam beberapa implementasi), \|itu sendiri adalah spesial di beberapa seperti GNU sed. Jadi Anda tidak harus melarikan diri |jika Anda bermaksud untuk mencocokkan |karakter.

Jika mengganti dengan spasi dan jika input mungkin sudah berisi garis dengan hanya satu |, maka, Anda harus memperlakukannya secara khusus karena |.*|tidak akan cocok dengan itu. Itu bisa:

sed 's/|\(.*|\)\{0,1\}/ /'

(yaitu menjadikan .*|bagian itu opsional) Atau:

sed 's/|.*|/ /;s/|/ /'

atau:

sed 's/\([^|]*\).*|/\1 /'

Jika Anda ingin bidang pertama dan kedelapan terlepas dari jumlah bidang dalam input, maka itu hanya:

cut -d'|' -f1,8


(semua itu akan bekerja dengan utilitas yang sesuai POSIX dengan asumsi input berupa teks yang valid (khususnya, sedyang umumnya tidak akan berfungsi jika input memiliki byte atau urutan byte yang tidak membentuk karakter yang valid di lokal saat ini seperti misalnya printf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'di lokal UTF-8)).


11

Anda awktetap menggunakan :

awk '{ print $1, $NF }' file

2
Tidakkah Anda perlu menentukan pemisah bidang input (karena dalam hal ini sepertinya |lebih banyak ruang) dengan -F\|atau serupa? Juga bagaimana jika dia ingin menggunakan pembatas yang sama untuk output?
Caleb

@ Caleb Mungkin: Saya sedang menunggu OP untuk mengkonfirmasi seperti apa sebenarnya input itu, daripada mencoba menebak berdasarkan contoh yang tidak berfungsi ...
jasonwryan

1
Perhatikan bahwa anggapan input berisi setidaknya 2 bidang.
Stéphane Chazelas

@ StéphaneChazelas OP dengan jelas menyatakan dalam kode bahwa ia memiliki delapan bidang, selalu.
michaelb958 - Reinstate Monica

3
@ michaelb958 Saya pikir "jelas" melebih-lebihkan kasus ini, hanya sedikit :)
jasonwryan

4

Jika Anda mendapati diri Anda canggung dan sed-kurang, Anda dapat mencapai hal yang sama dengan coreutils:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)

cutlebih bersih dan lebih kompak daripada awk / sed ketika Anda hanya tertarik pada kolom pertama, atau jika delimeter diperbaiki (yaitu bukan jumlah spasi variabel).
Sridhar Sarnobat

2

Sepertinya Anda mencoba untuk mendapatkan bidang teks pertama dan terakhir yang dibatasi oleh |.

Saya berasumsi file log Anda berisi teks seperti di bawah ini,

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

Dan Anda ingin output seperti,

foo bar
bar foo

Jika ya, maka inilah perintah untuk Anda

Melalui GNU sed,

sed -r 's~^([^|]*).*\|(.*)$~\1 \2~' file

Contoh:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~\1 \2~'
foo bar

Kolom tidak dibatasi oleh pipa | tetapi mereka berada di kolom, saya tertarik menggunakan sed tetapi tidak menggunakan perintah awk seperti yang Anda lakukan pada perintah Anda: sed -r's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ 1 \ 2 ~ 'file
user70573

"Kolom tidak dibatasi oleh pipa | tetapi mereka berada di kolom", maksud Anda kolom dipisahkan oleh spasi?
Avinash Raj

Input sampel dan output akan lebih baik.
Avinash Raj

1

Anda mungkin harus melakukannya dengan sed- saya akan tetap - tetapi, hanya karena belum ada yang menulis ini:

while IFS=\| read col1 cols
do  printf %10s%-s\\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

KELUARAN

     foo | bar
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.