Cách tìm trường cuối cùng bằng cách sử dụng 'cắt'


310

Không sử dụng sedhoặc awk, chỉ cut , làm thế nào để tôi có được trường cuối cùng khi số lượng trường không xác định hoặc thay đổi với mỗi dòng?


8
Bạn có yêu thích cutlệnh :)? Tại sao không phải là bất kỳ lệnh Linux nào khác?
Jayesh Bhoi

7
Không có sedhoặc awk: perl -pe 's/^.+\s+([^\s]+)$/$1/'.
jordanm


4
@MestreLion Nhiều lần mọi người đọc một câu hỏi để tìm giải pháp cho một biến thể của một vấn đề. Điều này bắt đầu với tiền đề sai cuthỗ trợ một cái gì đó không. Nhưng tôi nghĩ nó hữu ích, ở chỗ nó buộc người đọc phải xem xét mã dễ theo dõi hơn. Tôi muốn một cách nhanh chóng, cách đơn giản để sử dụng cutmà không cần phải sử dụng nhiều cú pháp cho awk, grep, sedvv Các revđiều đã làm các trick; rất thanh lịch, và một cái gì đó tôi chưa bao giờ xem xét (ngay cả khi vụng về cho các tình huống khác). Tôi cũng thích đọc các cách tiếp cận khác từ các câu trả lời khác.
Beejor

3
Đến đây là một vấn đề thực tế: Tôi muốn tìm tất cả các phần mở rộng tệp khác nhau trong cây nguồn, để cập nhật tệp .gitattribut với. find | cut -d. -f<last>Xu hướng tự nhiên cũng vậy
studog

Câu trả lời:


680

Bạn có thể thử một cái gì đó như thế này:

echo 'maps.google.com' | rev | cut -d'.' -f 1 | rev

Giải trình

  • rev đảo ngược "maps.google.com" thành moc.elgoog.spam
  • cut sử dụng dấu chấm (tức là '.') làm dấu phân cách và chọn trường đầu tiên, đó là moc
  • cuối cùng, chúng tôi đảo ngược nó một lần nữa để có được com

6
Nó không chỉ sử dụng cutmà là không có sedhoặc awk. Vì vậy, OP nghĩ gì?
Jayesh Bhoi

7
@tom OP đã hỏi nhiều câu hỏi hơn chỉ trong vài giờ qua. Dựa trên các tương tác của chúng tôi với OP, chúng tôi biết rằng awk / sed / etc. không được phép làm bài tập về nhà, nhưng tài liệu tham khảo về rev chưa được thực hiện. Vì vậy, nó là giá trị một shot
zedfoxus

4
@zfus tôi thấy. Có thể muốn dính một cái khác revsau đó.
tom

17
nhân đôi revlý tưởng!
Ford Guo

6
Tuyệt vời, đơn giản, hoàn hảo, cũng nhờ lời giải thích - không đủ người giải thích từng bước trong chuỗi dài các lệnh đường ống
Pete

128

Sử dụng mở rộng tham số. Điều này hiệu quả hơn nhiều so với bất kỳ loại lệnh bên ngoài nào, cut(hoặc grep) bao gồm.

data=foo,bar,baz,qux
last=${data##*,}

Xem BashFAQ # 100 để biết giới thiệu về thao tác chuỗi gốc trong bash.


3
@ErwinWessels: Vì bash rất chậm. Sử dụng bash để chạy các đường ống, không xử lý hàng loạt dữ liệu. Ý tôi là, điều này thật tuyệt nếu bạn đã có sẵn một dòng văn bản trong biến shell hoặc nếu bạn muốn làm while IFS= read -ra array_var; do :;done <(cmd)để xử lý một vài dòng. Nhưng đối với một tệp lớn, rev | cut | rev có thể nhanh hơn! (Và tất nhiên awk sẽ nhanh hơn thế.)
Peter Cordes

2
@PeterCordes, awk sẽ nhanh hơn cho một tệp lớn, chắc chắn, nhưng phải mất một chút đầu vào để vượt qua chi phí khởi động yếu tố không đổi. (Ngoài ra còn có các shell - như ksh93 - với hiệu suất gần với awk hơn, trong đó cú pháp được đưa ra trong câu trả lời này vẫn hợp lệ; bash đặc biệt chậm chạp, nhưng nó thậm chí không gần với tùy chọn duy nhất khả dụng).
Charles Duffy

1
Cảm ơn @PeterCordes; như thường lệ, tôi đoán mỗi công cụ đều có trường hợp sử dụng.
Erwin Wessels

1
Đây là cách nhanh nhất và ngắn gọn nhất để cắt xén một biến duy nhất trong bashtập lệnh (giả sử bạn đã sử dụng bashtập lệnh). Không cần phải gọi bất cứ điều gì bên ngoài.
Ken Sharp

1
@Balmipour, ... Tuy nhiên, rev đặc trưng cho bất cứ điều gì hệ điều hành bạn đang sử dụng cung cấp nó - nó không được chuẩn hóa trên tất cả các hệ thống UNIX. Xem danh sách chương cho phần POSIX về các lệnh và tiện ích - nó không có ở đó. Và ${var##prefix_pattern}không thực tế bash cụ thể; đó là trong tiêu chuẩn sh POSIX , xem phần cuối của phần 2.6.2 (được liên kết), vì vậy không giống như rev, nó luôn có sẵn trên bất kỳ vỏ tuân thủ nào.
Charles Duffy

89

Nó không thể sử dụng chỉ cut. Đây là một cách sử dụng grep:

grep -o '[^,]*$'

Thay thế dấu phẩy cho các dấu phân cách khác.


3
Để làm điều ngược lại và tìm mọi thứ trừ trường cuối cùng làm:grep -o '^.*,'
Ariel

2
Điều này đặc biệt hữu ích, bởi vì revthêm một vấn đề về các ký tự unicode đa nhân trong trường hợp của tôi.
Brice

3
Tôi đã cố gắng thực hiện điều này trên MinGW nhưng phiên bản grep của tôi không hỗ trợ -o, vì vậy tôi đã sử dụng sed 's/^.*,//'thay thế tất cả các ký tự lên đến và bao gồm cả dấu phẩy cuối cùng bằng một chuỗi trống.
TamaMcGlinn

46

Không có awk? ... Nhưng nó thật đơn giản với awk:

echo 'maps.google.com' | awk -F. '{print $NF}'

AWK là một công cụ mạnh hơn để có trong túi của bạn. -F nếu đối với dấu tách trường thì NF là số trường (cũng là viết tắt của chỉ mục cuối cùng)


2
Điều này là phổ quát và nó hoạt động chính xác như mong đợi mọi lúc. Trong trường hợp này, sử dụng cutđể đạt được đầu ra cuối cùng của OP cũng giống như sử dụng muỗng để "cắt" bít tết (ý định chơi chữ :)). awklà con dao bít tết.
Hickory420

3
Tránh sử dụng không cần thiết echocó thể làm chậm tập lệnh cho các tệp dài sử dụng awk -F. '{print $NF}' <<< 'maps.google.com'.
Anil_M

14

Có nhiều cách. Bạn có thể sử dụng điều này quá.

echo "Your string here"| tr ' ' '\n' | tail -n1
> here

Rõ ràng, đầu vào không gian trống cho lệnh tr nên được thay thế bằng dấu phân cách bạn cần.


Cảm ơn bạn! một cái gì đó hoạt động trong busybox sh 1.0.0 :)
kevinf

1
Cảm giác này giống như câu trả lời đơn giản nhất đối với tôi, ít đường ống hơn và ý nghĩa rõ ràng hơn
joeButler

1
Điều đó sẽ không hoạt động cho toàn bộ tập tin, đó là ý nghĩa của OP.
Amir

7

Đây là giải pháp duy nhất có thể để sử dụng không có gì ngoài cắt:

tiếng vang "chuỗi" | cắt -d '.' -f2- [repeat_following_part_Forver_or_until thừng_of_memory:] | cắt -d '.' -f2-

Sử dụng giải pháp này, số lượng các lĩnh vực thực sự có thể không xác định và thay đổi theo thời gian. Tuy nhiên, vì độ dài dòng không được vượt quá các ký tự hoặc trường LINE_MAX, bao gồm cả ký tự dòng mới, nên một số trường tùy ý không bao giờ có thể là một phần như một điều kiện thực sự của giải pháp này.

Vâng, một giải pháp rất ngớ ngẩn nhưng là giải pháp duy nhất đáp ứng các tiêu chí mà tôi nghĩ.


2
Đẹp. Chỉ cần lấy cuối cùng '.' tắt "chuỗi" và điều này hoạt động.
Matt

2
Tôi yêu khi tất cả mọi người nói điều gì đó là không thể và sau đó ai đó bấm chuông với một câu trả lời làm việc. Ngay cả khi nó thực sự rất ngớ ngẩn.
Beejor

Người ta có thể lặp cut -f2-trong một vòng lặp cho đến khi đầu ra không còn thay đổi.
loa_in_

4

Nếu chuỗi đầu vào của bạn không chứa dấu gạch chéo về phía trước thì bạn có thể sử dụng basenamevà một chuỗi con:

$ basename "$(echo 'maps.google.com' | tr '.' '/')"

Điều này không sử dụng sedhoặc awknhưng nó cũng không sử dụngcut , vì vậy tôi không chắc liệu nó có đủ điều kiện để trả lời cho câu hỏi như đã nói hay không.

Điều này không hoạt động tốt nếu xử lý các chuỗi đầu vào có thể chứa dấu gạch chéo về phía trước. Một cách giải quyết cho tình huống đó là thay thế dấu gạch chéo bằng một số ký tự khác mà bạn biết không phải là một phần của chuỗi đầu vào hợp lệ. Ví dụ, |ký tự pipe ( ) cũng không được phép trong tên tệp, vì vậy điều này sẽ hoạt động:

$ basename "$(echo 'maps.google.com/some/url/things' | tr '/' '|' | tr '.' '/')" | tr '|' '/'


0

Nếu bạn có một tệp có tên filelist.txt là đường dẫn danh sách như sau: c: /dir1/dir2/file1.h c: /dir1/dir2/dir3/file2.h

sau đó bạn có thể làm điều này: rev filelist.txt | cắt -d "/" -f1 | vòng quay


0

Thêm một cách tiếp cận cho câu hỏi cũ này chỉ để giải trí:

$ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info

$ cat tmp.sh # showing off the script to do the job
#!/bin/bash
delim=';'
while read -r line; do  
    while [[ "$line" =~ "$delim" ]]; do
        line=$(cut -d"$delim" -f 2- <<<"$line")
    done
    echo "$line"
done < input.file

$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info

Ngoài bash, chỉ cắt được sử dụng. Vâng, và tiếng vang, tôi đoán.


Meh, tại sao không loại bỏ hoàn toàn cắt và chỉ sử dụng bash ... x] while read -r line; do echo ${line/*;}; done <input.filemang lại kết quả tương tự.
Kaffe Myers

-1

Tôi nhận ra nếu chúng ta chỉ đảm bảo một dấu phân cách tồn tại, nó hoạt động. Vì vậy, trong trường hợp của tôi, tôi có dấu phân cách dấu phẩy và khoảng trắng. Tôi thêm một khoảng trống ở cuối;

$ ans="a, b"
$ ans+=" "; echo ${ans} | tr ',' ' ' | tr -s ' ' | cut -d' ' -f2
b

ans="a, b, c"sản xuất b, không đáp ứng các yêu cầu của "số lượng trường không xác định hoặc thay đổi với mỗi dòng" .
jww
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.