Câu trả lời:
Chìa khóa để làm cho điều này hoạt động là nói sed
để loại trừ những gì bạn không muốn là đầu ra cũng như chỉ định những gì bạn muốn.
string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Điều này nói rằng:
-n
)p
)Nói chung, trong sed
bạn chụp các nhóm bằng dấu ngoặc đơn và xuất ra những gì bạn chụp bằng tham chiếu ngược:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
sẽ xuất "thanh". Nếu bạn sử dụng -r
( -E
cho OS X) cho regex mở rộng, bạn không cần phải thoát dấu ngoặc đơn:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
Có thể có tới 9 nhóm chụp và tài liệu tham khảo trở lại của họ. Các tham chiếu trở lại được đánh số theo thứ tự các nhóm xuất hiện, nhưng chúng có thể được sử dụng theo bất kỳ thứ tự nào và có thể được lặp lại:
echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'
xuất ra "một thanh a".
Nếu bạn có GNU grep
(nó cũng có thể hoạt động trong BSD, bao gồm cả OS X):
echo "$string" | grep -Po '\d+'
hoặc các biến thể như:
echo "$string" | grep -Po '(?<=\D )(\d+)'
Các -P
tùy chọn cho phép Perl Regular Expressions Tương thích. Xem man 3 pcrepattern
hoặc man
3 pcresyntax
.
sed
ví dụ, nếu bạn sử dụng -r
tùy chọn (hoặc -E
cho OS X, IIRC), bạn không cần phải thoát dấu ngoặc đơn. Sự khác biệt là giữa biểu thức chính quy cơ bản và biểu thức chính quy mở rộng ( -r
).
Sed có tới chín mẫu đã nhớ nhưng bạn cần sử dụng dấu ngoặc đơn thoát để ghi nhớ các phần của biểu thức chính quy.
Xem ở đây để biết ví dụ và chi tiết hơn
sed -e 's/version=\(.+\)/\1/' input.txt
điều này vẫn sẽ xuất ra toàn bộ input.txt
\+
thay vì +
. Và tôi không hiểu tại sao mọi người chỉ sử dụng -e
một lệnh sed.
sed -e -n 's/version=\(.+\)/\1/p' input.txt
xem: mikeplate.com/2012/05/09/ trên
sed -E
để sử dụng các biểu thức thông thường được gọi là "hiện đại" hoặc "mở rộng" trông gần gũi hơn với Perl / Java / JavaScript / Go / bất kỳ hương vị nào. (So sánh với grep -E
hoặc egrep
.) Cú pháp mặc định có các quy tắc thoát kỳ lạ đó và được coi là "lỗi thời". Để biết thêm thông tin về sự khác biệt giữa hai, chạy man 7 re_format
.
bạn có thể sử dụng grep
grep -Eow "[0-9]+" file
o
lựa chọn là có - unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, thảo mà chỉ-phù hợp với Chỉ một phần của một dòng tương rằng trận đấu MẪU
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
Tôi không biết làm thế nào bạn có thể yêu cầu hai biểu thức đó nằm trên một dòng ngoài đường ống từ một grep trước đó (vẫn không thể hoạt động nếu một trong hai mẫu khớp với nhiều hơn một dòng trên một dòng ).
Câu trả lời này hoạt động với bất kỳ số lượng các nhóm chữ số. Thí dụ:
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Có cách nào để bảo sed chỉ xuất ra các nhóm bị bắt không?
Đúng. thay thế tất cả văn bản bởi nhóm chụp:
$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123
s/[^0-9]* # several non-digits
\([0-9]\{1,\}\) # followed by one or more digits
[^0-9]* # and followed by more non-digits.
/\1/ # gets replaced only by the digits.
Hoặc với cú pháp mở rộng (ít backquote và cho phép sử dụng +):
$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123
Để tránh in văn bản gốc khi không có số, hãy sử dụng:
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
Và để khớp với một số số (và cũng in chúng):
$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456
Điều đó làm việc cho bất kỳ số lượng chữ số chạy:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Điều này rất giống với lệnh grep:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166
và mô hình:
/([\d]+)/
Sed không nhận ra cú pháp '\ d' (phím tắt). Tương đương ascii được sử dụng ở trên[0-9]
là không chính xác tương đương. Giải pháp thay thế duy nhất là sử dụng một lớp ký tự: '[[: chữ số:]] `.
Câu trả lời được chọn sử dụng các "lớp ký tự" như vậy để xây dựng giải pháp:
$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Giải pháp đó chỉ hoạt động cho (chính xác) hai lần chạy chữ số.
Tất nhiên, vì câu trả lời đang được thực thi bên trong shell, chúng ta có thể định nghĩa một vài biến để rút ngắn câu trả lời như vậy:
$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"
Nhưng, như đã được giải thích, sử dụng s/…/…/gp
lệnh sẽ tốt hơn:
$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987
Điều đó sẽ bao gồm cả các chữ số lặp đi lặp lại và viết một lệnh ngắn (er).
Tôi tin rằng mô hình được đưa ra trong câu hỏi chỉ bằng ví dụ và mục tiêu là phù hợp với bất kỳ mẫu .
Nếu bạn có một sed với phần mở rộng GNU cho phép chèn một dòng mới vào không gian mẫu, một gợi ý là:
> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers
Những ví dụ này là với tcsh (vâng, tôi biết nó là vỏ sai) với CYGWIN. (Chỉnh sửa: Đối với bash, xóa bộ và khoảng trắng xung quanh =.)
+
, bạn cần thoát nó hoặc sử dụng -r
tùy chọn ( -E
cho OS X). Bạn cũng có thể sử dụng \{1,\}
(hoặc -r
hoặc-E
không có lối thoát).
Từ bỏ và sử dụng Perl
Vì sed
không cắt nó, chúng ta hãy ném khăn và sử dụng Perl, ít nhất đó là LSB trong khi các grep
phần mở rộng GNU thì không :-)
In toàn bộ phần phù hợp, không cần nhóm phù hợp hoặc giao diện cần thiết:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
Đầu ra:
12
3456
Khớp đơn trên mỗi dòng, các trường dữ liệu thường có cấu trúc:
cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
a1 b2
a34 b56
EOS
Đầu ra:
1
34
Với cái nhìn:
cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
a1 b2
a34 b56
EOS
Nhiều lĩnh vực:
cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
a1 c0 b2 c0
a34 c0 b56 c0
EOS
Đầu ra:
1 2
34 56
Nhiều kết quả trùng khớp trên mỗi dòng, dữ liệu thường không có cấu trúc:
cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
a1 b2
a34 b56 a78 b90
EOS
Đầu ra:
1
34 78
Với cái nhìn:
cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
a1 b2
a34 b56 a78 b90
EOS
Đầu ra:
1
3478
Thử
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
Tôi đã nhận được điều này dưới cygwin:
$ (echo "asdf"; \
echo "1234"; \
echo "asdf1234adsf1234asdf"; \
echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
1234
1234 1234
1 2 3 4 5 6 7 8 9
$
Đó không phải là những gì OP yêu cầu (chụp các nhóm) nhưng bạn có thể trích xuất các số bằng cách sử dụng:
S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'
Cung cấp như sau:
123
987
sed
bật biểu thức chính quy mở rộng bằng-E
cờ.