Cách tách một chuỗi trong shell và lấy trường cuối cùng


293

Giả sử tôi có chuỗi 1:2:3:4:5và tôi muốn lấy trường cuối cùng của nó ( 5trong trường hợp này). Làm thế nào để tôi làm điều đó bằng cách sử dụng Bash? Tôi đã thử cut, nhưng tôi không biết cách chỉ định trường cuối cùng -f.

Câu trả lời:


430

Bạn có thể sử dụng toán tử chuỗi :

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

Điều này cắt mọi thứ từ phía trước cho đến khi ':', tham lam.

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

7
Trong khi điều này đang làm việc cho vấn đề đã cho, câu trả lời của William bên dưới ( stackoverflow.com/a/3163857/520162 ) cũng trả về 5nếu chuỗi là 1:2:3:4:5:(trong khi sử dụng toán tử chuỗi mang lại kết quả trống). Điều này đặc biệt hữu ích khi phân tích cú pháp các đường dẫn có thể chứa (hoặc không) một /ký tự hoàn thiện .
eckes

9
Làm thế nào bạn sẽ làm ngược lại với điều này? để lặp lại '1: 2: 3: 4:'?
Dobz

12
Và làm thế nào để giữ một phần trước khi phân tách cuối cùng? Rõ ràng bằng cách sử dụng ${foo%:*}. #- từ lúc bắt đầu; %- từ cuối. #, %- trận đấu ngắn nhất; ##, %%- trận đấu dài nhất.
Mihai Danila

1
Nếu tôi muốn lấy phần tử cuối cùng từ đường dẫn, tôi nên sử dụng nó như thế nào? echo ${pwd##*/}không hoạt động.
Putnik

2
@Putnik lệnh đó xem pwdnhư một biến. Hãy thử dir=$(pwd); echo ${dir##*/}. Làm việc cho tôi!
Stan Strum

344

Một cách khác là đảo ngược trước và sau cut:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

Điều này làm cho nó rất dễ dàng để có được trường cuối cùng nhưng một trường hoặc bất kỳ phạm vi trường nào được đánh số từ cuối.


17
Câu trả lời này rất hay vì nó sử dụng 'cắt', mà tác giả (có lẽ là) đã quen thuộc. Thêm vào đó, tôi thích câu trả lời này vì tôi đang sử dụng 'cắt' và có câu hỏi chính xác này, do đó tìm thấy chủ đề này thông qua tìm kiếm.
Dannid

6
Một số thức ăn gia súc cắt và dán cho những người sử dụng dấu cách làm dấu phân cách:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll

2
vòng quay | cắt -d -f1 | rev thật thông minh! Cảm ơn! Đã giúp tôi một bó (trường hợp sử dụng của tôi là rev | -d '' -f 2- | rev
EdgeCaseBerg

1
Tôi luôn quên về rev, chỉ là những gì tôi cần! cut -b20- | rev | cut -b10- | rev
shearn89

Tôi đã kết thúc với giải pháp này, nỗ lực của tôi đã cắt đường dẫn tệp bằng "awk -F" / "'{print $ NF}'" đã phá vỡ phần nào cho tôi, vì tên tệp bao gồm khoảng trắng cũng bị cắt rời
THX

76

Thật khó để có được trường cuối cùng bằng cách sử dụng cắt, nhưng đây là một số giải pháp trong awk và perl

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'

5
lợi thế lớn của giải pháp này so với câu trả lời được chấp nhận: nó cũng khớp với các đường dẫn có chứa hoặc không chứa /ký tự hoàn thiện : /a/b/c/d/a/b/c/d/mang lại kết quả tương tự ( d) khi xử lý pwd | awk -F/ '{print $NF}'. Câu trả lời được chấp nhận dẫn đến một kết quả trống rỗng trong trường hợp/a/b/c/d/
eckes

@eckes Trong trường hợp giải pháp AWK, trên bash GNU, phiên bản 4.3.48 (1) - điều đó không đúng, vì nó quan trọng bất cứ khi nào bạn có dấu gạch chéo hay không. Đơn giản chỉ cần đặt AWK sẽ sử dụng /làm dấu phân cách và nếu đường dẫn của bạn là /my/path/dir/nó sẽ sử dụng giá trị sau dấu phân cách cuối cùng, đó đơn giản là một chuỗi rỗng. Vì vậy, tốt nhất là tránh dấu gạch chéo nếu bạn cần làm một việc như tôi.
stamster

Làm thế nào tôi có được chuỗi con UNTIL trường cuối cùng?
blackjacx

1
@blackjacx Có một số quirks, nhưng một cái gì đó như awk '{$NF=""; print $0}' FS=: OFS=:thường hoạt động đủ tốt.
William Pursell

29

Giả sử sử dụng khá đơn giản (chẳng hạn như không có dấu phân cách), bạn có thể sử dụng grep:

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

Phân tích - tìm tất cả các ký tự không phải là dấu phân cách ([^:]) ở cuối dòng ($). -o chỉ in phần phù hợp.


-E có nghĩa là sử dụng cú pháp mở rộng; [^ ...] có nghĩa là bất cứ điều gì ngoại trừ char (s) được liệt kê; + một hoặc nhiều lần truy cập như vậy (sẽ lấy chiều dài tối đa có thể cho mẫu; mục này là phần mở rộng gnu) - ví dụ: char (s) tách biệt là dấu hai chấm.
Alexander Stohr

18

Một chiều:

var1="1:2:3:4:5"
var2=${var1##*:}

Khác, sử dụng một mảng:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}

Một cái khác với một mảng:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}

Sử dụng Bash (phiên bản> = 3.2) biểu thức chính quy:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}

11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Đơn giản chỉ cần dịch dấu phân cách thành một dòng mới và chọn mục cuối cùng với tail -1.


Nó sẽ thất bại nếu mục cuối cùng chứa a \n, nhưng đối với hầu hết các trường hợp là giải pháp dễ đọc nhất.
Yajo

7

Sử dụng sed:

$ echo '1:2:3:4:5' | sed 's/.*://' # => 5

$ echo '' | sed 's/.*://' # => (empty)

$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c

$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c

4

Nếu trường cuối cùng của bạn là một ký tự đơn, bạn có thể làm điều này:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

Kiểm tra thao tác chuỗi trong bash .


Điều này không làm việc: nó mang lại cho người cuối cùng nhân vật của a, không phải là cuối cùng lĩnh vực .
gniourf_gniourf

1
Đúng, đó là ý tưởng, nếu bạn biết độ dài của trường cuối cùng thì tốt. Nếu không, bạn phải sử dụng một cái gì đó khác ...
Ab Irato

4

Có nhiều câu trả lời hay ở đây, nhưng tôi vẫn muốn chia sẻ câu hỏi này bằng tên cơ sở :

 basename $(echo "a:b:c:d:e" | tr ':' '/')

Tuy nhiên, nó sẽ thất bại nếu đã có một số '/' trong chuỗi của bạn . Nếu dấu gạch chéo / là dấu phân cách của bạn thì bạn chỉ cần (và nên) sử dụng tên cơ sở.

Đây không phải là câu trả lời hay nhất nhưng nó chỉ cho thấy cách bạn có thể sáng tạo bằng cách sử dụng các lệnh bash.


2

Sử dụng Bash.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0

Có thể đã sử dụng echo ${!#}thay vì eval echo \$${#}.
Rafa

2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

Đầu tiên sử dụng xargs tách nó bằng cách sử dụng ":", - n1 có nghĩa là mỗi dòng chỉ có một phần. Sau đó, pring phần cuối cùng.


1
for x in `echo $str | tr ";" "\n"`; do echo $x; done

2
Điều này gặp vấn đề nếu có khoảng trắng trong bất kỳ trường nào. Ngoài ra, nó không trực tiếp giải quyết câu hỏi lấy trường cuối cùng .
chepner

1

Đối với những người thoải mái với Python, https://github.com/Russell91/pythonpy là một lựa chọn tốt để giải quyết vấn đề này.

$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'

Từ pythonpy giúp : -x treat each row of stdin as x.

Với công cụ đó, thật dễ dàng để viết mã python được áp dụng cho đầu vào.


1

Có thể hơi muộn với câu trả lời mặc dù một giải pháp đơn giản là đảo ngược thứ tự của chuỗi đầu vào. Điều này sẽ cho phép bạn luôn đạt được mục cuối cùng bất kể chiều dài.

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

Tuy nhiên, điều quan trọng cần lưu ý là, nếu sử dụng phương pháp này và các số lớn hơn 1 chữ số (Hoặc lớn hơn một ký tự trong mọi trường hợp), bạn sẽ cần chạy lệnh 'rev' khác trên đầu ra đường ống.

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

Hy vọng tôi có thể giúp đỡ, chúc mừng


1

Một giải pháp sử dụng nội dung đã đọc:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

Hoặc, để làm cho nó chung chung hơn:

echo "${fields[-1]}" # prints the last item

0

Nếu bạn thích python và có một tùy chọn để cài đặt một gói, bạn có thể sử dụng tiện ích python này .

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

trăn có thể làm điều này trực tiếp: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB

@MortenB Bạn nhầm rồi. Toàn bộ mục đích của pythonpgói là để làm cho bạn làm những điều tương tự như python -cvới ít kiểu chữ ký tự hơn. Xin hãy xem README trong kho lưu trữ.
đánh bom

0

Kết hợp Regex trong sedlà tham lam (luôn luôn đi đến lần xuất hiện cuối cùng), mà bạn có thể sử dụng để lợi thế của mình ở đây:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
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.