Làm thế nào để sử dụng lệnh shell để chỉ hiển thị cột đầu tiên và cột cuối cùng trong tệp văn bản?


30

Tôi cần một số trợ giúp để tìm ra cách sử dụng lệnh sed để chỉ hiển thị cột đầu tiên và cột cuối cùng trong một tệp văn bản. Đây là những gì tôi có cho đến cột 1:

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

Nỗ lực yếu đuối của tôi trong việc lấy cột cuối cùng để hiển thị là:

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

Tuy nhiên, điều này lấy cột đầu tiên và cột cuối cùng và hợp nhất chúng lại với nhau trong một danh sách. Có cách nào để in cột đầu tiên và cột cuối cùng rõ ràng bằng các lệnh sed và awk không?

Đầu vào mẫu:

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

5
Vui lòng cung cấp một số mẫu đầu vào.
jasonwryan

Câu trả lời:


51

Gần đến rồi. Chỉ cần đặt cả hai tham chiếu cột cạnh nhau.

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

Cũng lưu ý rằng bạn không cần catở đây.

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

Cũng lưu ý rằng bạn có thể nói awkrằng các dấu phân cách cột |, thay vì khoảng trắng, vì vậy bạn cũng không cần sed.

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

Theo đề xuất của Caleb , nếu bạn muốn một giải pháp vẫn đưa ra trường cuối cùng, ngay cả khi không có chính xác tám, bạn có thể sử dụng $NF.

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

Ngoài ra, nếu bạn muốn đầu ra giữ lại các |dấu phân cách, thay vì sử dụng khoảng trắng, bạn có thể chỉ định các dấu tách trường đầu ra. Thật không may, nó vụng về hơn một chút so với việc chỉ sử dụng -Fcờ, nhưng đây là ba cách tiếp cận.

  • Bạn có thể chỉ định các dấu tách trường đầu vào và đầu ra awktrong khối BEGIN.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • Bạn có thể gán các biến này khi gọi awktừ dòng lệnh, thông qua -vcờ.

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • hoặc đơn giản:

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

4
Làm tốt công việc phá vỡ làm thế nào vấn đề này có thể được đơn giản hóa. Bạn có thể thêm một lưu ý về cách sử dụng |như một dấu tách đầu ra thay vì không gian mặc định để nối chuỗi. Ngoài ra, bạn có thể giải thích để sử dụng $NFthay vì mã hóa cứng $8để có được cột cuối cùng.
Caleb

12

Chỉ cần thay thế từ đầu đến cuối |bằng một |(hoặc khoảng trắng nếu bạn thích):

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

Lưu ý rằng mặc dù không có sedtriển khai nào |đặc biệt (miễn là các biểu thức chính quy mở rộng không được bật thông qua -Ehoặc -rtrong một số triển khai), \|bản thân nó cũng đặc biệt trong một số như GNU sed. Vì vậy, bạn không nên trốn thoát |nếu bạn có ý định phù hợp với |nhân vật.

Nếu thay thế bằng không gian và nếu đầu vào có thể chỉ chứa một dòng |, thì bạn sẽ phải đối xử đặc biệt với việc |.*|không khớp với những dòng đó. Đó có thể là:

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

(đó là làm cho .*|phần tùy chọn) Hoặc:

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

hoặc là:

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

Nếu bạn muốn các trường đầu tiên và thứ tám bất kể số lượng trường trong đầu vào, thì đó chỉ là:

cut -d'|' -f1,8


(tất cả những người sẽ làm việc với bất kỳ tiện ích tương thích POSIX giả định các hình thức nhập văn bản có hiệu lực (đặc biệt là những sedngười thường sẽ không làm việc nếu đầu vào có byte hoặc trình tự của byte mà không hình thành ký tự hợp lệ trong miền địa phương hiện tại như ví dụ printf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'trong một miền địa phương UTF-8)).


11

Bạn vẫn đang sử dụng awk:

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

2
Bạn sẽ không cần chỉ định dấu phân cách trường đầu vào (vì trong trường hợp này dường như là |không gian đó) với -F\|hoặc tương tự? Ngoài ra, nếu anh ta muốn sử dụng cùng một dấu phân cách cho đầu ra thì sao?
Caleb

@Caleb Có lẽ: Tôi đang chờ OP xác nhận chính xác đầu vào trông như thế nào, thay vì cố gắng đoán dựa trên các ví dụ không hoạt động ...
jasonwryan

1
Lưu ý rằng giả sử đầu vào chứa ít nhất 2 trường.
Stéphane Chazelas

@ StéphaneChazelas OP đã nêu rõ trong mã rằng nó luôn có tám trường.
michaelb958 - Phục hồi Monica

3
@ michaelb958 Tôi nghĩ "rõ ràng" đang nói quá về vụ án, chỉ một chút thôi :)
jasonwryan

4

Nếu bạn thấy mình tỉnh táo và ít quyến rũ, bạn có thể đạt được điều tương tự với coreutils:

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

cutsạch hơn và gọn hơn awk / sed khi bạn chỉ quan tâm đến cột đầu tiên, hoặc nếu các dấu phân cách được cố định (nghĩa là không phải là một số lượng không gian thay đổi).
Sridhar Sarnobat

2

Có vẻ như bạn đang cố gắng để có được các trường văn bản đầu tiên và cuối cùng được phân định bởi |.

Tôi giả sử tệp nhật ký của bạn chứa văn bản như dưới đây,

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

Và bạn muốn đầu ra như,

foo bar
bar foo

Nếu có, thì đây là lệnh cho của bạn

Thông qua GNU sed,

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

Thí dụ:

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

Các cột không được giới hạn bởi một đường ống | nhưng chúng ở trong các cột, tôi thích sử dụng sed nhưng không sử dụng lệnh awk như bạn đã làm trong lệnh của mình: sed -r 's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ Tệp 1 \ 2 ~ '
user70573

"Các cột không được giới hạn bởi một đường ống | nhưng chúng nằm trong các cột", ý bạn là các cột được phân tách bằng khoảng trắng?
Avinash Raj

Một đầu vào mẫu và đầu ra sẽ tốt hơn.
Avinash Raj

1

Có lẽ bạn nên làm điều đó với sed- dù sao thì tôi cũng vậy - nhưng, chỉ vì chưa có ai viết cái này cả:

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

ĐẦU RA

     foo | bar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.