Làm thế nào để trích xuất hai ký tự đầu tiên của một chuỗi trong kịch bản shell?


123

Ví dụ, đã cho:

USCAGoleta9311734.5021-120.1287855805

Tôi chỉ muốn giải nén:

US

6
Cảm ơn mọi người. Tôi đã kết thúc bằng cách sử dụng 'cut -c1-2', thành thật mà nói tôi thậm chí không biết 'cut' ở đó. Tôi muốn nói rằng tôi khá có kinh nghiệm về dòng lệnh - nhưng dường như tôi còn rất nhiều điều để học hỏi.
Greg

1
@Greg, chỉ cần lưu ý rằng quá trình cắt được chạy như một quy trình riêng - nó sẽ chậm hơn so với giải pháp nội bộ mà tôi đã đăng cùng với nó trong câu trả lời của mình. Điều đó sẽ không tạo ra bất kỳ sự khác biệt nào trừ khi bạn đang xử lý các tập dữ liệu khổng lồ nhưng bạn cần ghi nhớ điều đó.
paxdiablo 10/09/09

Chỉnh sửa Trên thực tế, tôi nghĩ dòng mã này có thể sẽ được thực thi khoảng 50.000 lần cho mỗi báo cáo. Vì vậy, tôi có thể chỉ sử dụng phương pháp Bash nội bộ - như bạn đã nói sẽ tiết kiệm một số tài nguyên cần thiết.
Greg

Câu trả lời:


180

Có lẽ phương pháp hiệu quả nhất, nếu bạn đang sử dụng bashshell (và bạn dường như, dựa trên nhận xét của bạn), là sử dụng biến thể chuỗi con của mở rộng tham số:

pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US

Đây sẽ shortlà hai ký tự đầu tiên của long. Nếu longngắn hơn hai ký tự, shortsẽ giống với nó.

Phương pháp in-shell này thường tốt hơn nếu bạn định thực hiện nó nhiều (như 50.000 lần mỗi báo cáo như bạn đề cập) vì không có chi phí tạo quy trình. Tất cả các giải pháp sử dụng các chương trình bên ngoài sẽ bị ảnh hưởng bởi chi phí đó.

Nếu bạn cũng muốn đảm bảo độ dài tối thiểu , bạn có thể độn nó ra trước bằng những thứ như:

pax> long="A"
pax> tmpstr="${long}.."
pax> short="${tmpstr:0:2}" ; echo "${short}"
A.

Điều này sẽ đảm bảo rằng bất kỳ thứ gì có độ dài ít hơn hai ký tự đều được đệm vào bên phải bằng dấu chấm (hoặc thứ gì khác, chỉ bằng cách thay đổi ký tự được sử dụng khi tạo tmpstr). Không rõ là bạn cần cái này nhưng tôi nghĩ tôi đã đưa nó vào cho hoàn chỉnh.


Phải nói rằng, có bất kỳ cách nào để thực hiện việc này với các chương trình bên ngoài (chẳng hạn như nếu bạn không có bashsẵn cho bạn), một số trong số đó là:

short=$(echo "${long}" | cut -c1-2)
short=$(echo "${long}" | head -c2)
short=$(echo "${long}" | awk '{print substr ($0, 0, 2)}'
short=$(echo "${long}" | sed 's/^\(..\).*/\1/')

Hai ( cuthead) đầu tiên giống hệt nhau đối với một chuỗi một dòng - về cơ bản cả hai đều chỉ trả lại cho bạn hai ký tự đầu tiên. Chúng khác nhau ở chỗ cutsẽ cung cấp cho bạn hai ký tự đầu tiên của mỗi dòng vàhead sẽ cung cấp cho bạn hai ký tự đầu tiên của toàn bộ dữ liệu đầu vào

Cái thứ ba sử dụng awkhàm chuỗi con để trích xuất hai ký tự đầu tiên và cái thứ tư sử dụng sedcác nhóm bắt (sử dụng ()\1) để nắm bắt hai ký tự đầu tiên và thay thế toàn bộ dòng bằng chúng. Cả hai đều tương tự như cut- chúng cung cấp hai ký tự đầu tiên của mỗi dòng trong đầu vào.

Điều đó không quan trọng nếu bạn chắc chắn đầu vào của mình là một dòng duy nhất, tất cả chúng đều có tác dụng giống nhau.


Tôi thà sử dụng printf '%s'thay vì echotrong trường hợp có ký tự lạ trong chuỗi: stackoverflow.com/a/40423558/895245 Đối với POSIX bị ám ảnh: head -ckhông phải là POSIX, cut -cawk substrlà, sed \1không chắc chắn.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 996ICU 六四 事件 sử dụng printf, bạn thậm chí không cần một chương trình bổ sung. Hãy xem câu trả lời của tôi .
bschlueter

60

cách dễ nhất là

${string:position:length}

Nơi này trích xuất $lengthchuỗi con từ $stringtại$position .

Đây là một nội trang bash nên không bắt buộc phải có awk hoặc sed.


Đây là cách ngắn gọn, dễ hiểu và dễ dàng nhất để lấy chuỗi con.
ani627

34

Bạn đã nhận được nhiều câu trả lời tốt và tôi muốn đi với Bash BUILTIN bản thân mình, nhưng kể từ khi bạn được hỏi về sedawkvà ( hầu như ) không ai khác cung cấp các giải pháp dựa trên họ, tôi cung cấp cho bạn những:

echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'

echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'

Một awktrong những lẽ ra phải khá rõ ràng, nhưng đây là lời giải thích về sedmột trong những:

  • thay thế "s /"
  • nhóm "()" gồm hai ký tự bất kỳ ".." bắt đầu ở đầu dòng "^" và theo sau là bất kỳ ký tự nào "." lặp lại không hoặc nhiều lần "*" (cần có dấu gạch chéo ngược để thoát khỏi một số ký tự đặc biệt)
  • bởi "/" nội dung của nhóm đầu tiên (và duy nhất, trong trường hợp này) (ở đây dấu gạch chéo ngược là một lối thoát đặc biệt đề cập đến một biểu thức con phù hợp)
  • làm xong "/"

1
Trong chuỗi awk bắt đầu từ chỉ mục 1, vì vậy bạn nên sử dụng substr($0,1,2).
Isaac

8

Nếu bạn tham gia bash, bạn có thể nói:

bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab

Đây có thể là những gì bạn cần…


câu trả lời dễ nhất và đơn giản nhất! làm việc như một sự quyến rũ
aloha

7

Chỉ cần grep:

echo 'abcdef' | grep -Po "^.."        # ab

Phù hợp với nhu cầu của tôi. Bạn có thể xóa -Ptùy chọn để làm cho nó ngắn hơn. Tất cả các regex sẽ hiểu mô hình đó.
datashaman,

6

Bạn có thể sử dụng printf:

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US

5

colrm - xóa cột khỏi tệp

Để để lại hai ký tự đầu tiên, chỉ cần xóa các cột bắt đầu từ 3

cat file | colrm 3

4

Thực sự khá muộn nhưng nó đây rồi

sed 's/.//3g'

Hoặc là

awk NF=1 FPAT=..

Hoặc là

perl -pe '$_=unpack a2'

2

Nếu bạn muốn sử dụng shell scripting và không dựa vào phần mở rộng không phải posix (chẳng hạn như cái gọi là bashisms), bạn có thể sử dụng các kỹ thuật không yêu cầu các công cụ bên ngoài như grep, sed, cut, awk, v.v., sau đó làm cho tập lệnh của bạn kém hiệu quả hơn. Có thể hiệu quả và tính di động posix không quan trọng trong trường hợp sử dụng của bạn. Nhưng trong trường hợp đó là (hoặc chỉ là một thói quen tốt), bạn có thể sử dụng phương thức tùy chọn mở rộng tham số sau để trích xuất hai ký tự đầu tiên của một biến shell:

$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab

Điều này sử dụng mở rộng tham số "tiền tố nhỏ nhất" để xóa hai ký tự đầu tiên (đây là ${var#??}phần), sau đó mở rộng tham số "hậu tố nhỏ nhất" ( ${var%phần) để xóa chuỗi tất cả trừ hai ký tự đầu tiên khỏi chuỗi gốc giá trị.

Phương pháp này trước đây đã được mô tả trong câu trả lời này cho câu hỏi "Shell = Kiểm tra xem biến có bắt đầu bằng # hay không". Câu trả lời đó cũng mô tả một vài phương pháp mở rộng tham số tương tự có thể được sử dụng trong ngữ cảnh hơi khác với ngữ cảnh áp dụng cho câu hỏi ban đầu ở đây.


Câu trả lời tốt nhất, nên ở trên đầu trang. không có dĩa, không có bashisms. hoạt động ngay cả với các vỏ nhỏ như gạch ngang.
khởi hành

1

Nếu hệ thống của bạn đang sử dụng một shell khác (không phải bash), nhưng hệ thống của bạn có bash, thì bạn vẫn có thể sử dụng thao tác chuỗi vốn có của bashbằng cách gọi bashvới một biến:

strEcho='echo ${str:0:2}' # '${str:2}' if you want to skip the first two characters and keep the rest
bash -c "str=\"$strFull\";$strEcho;"

Điều này sử dụng cùng một phương pháp như câu trả lời chính , chỉ gọi bashnếu bạn chưa sử dụng nó.
palswim

Thật không may, điều này đi kèm với tất cả chi phí của việc gọi một quy trình khác, nhưng đôi khi chi phí đó không quan trọng bằng sự đơn giản và quen thuộc.
palswim

1

Chỉ vì mục đích giải trí Tôi thêm một vài điều rằng, mặc dù chúng quá phức tạp và vô dụng, chúng đã không được đề cập:

head -c 2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

echo 'USCAGoleta9311734.5021-120.1287855805' | dd bs=2 count=1 status=none

sed -e 's/^\(.\{2\}\).*/\1/;' <( echo 'USCAGoleta9311734.5021-120.1287855805')

cut -c 1-2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

python -c "print(r'USCAGoleta9311734.5021-120.1287855805'[0:2])"

ruby -e 'puts "USCAGoleta9311734.5021-120.1287855805"[0..1]'


0

nếu mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

sẽ in US

trong đó 0 là vị trí bắt đầu và 2 là cách đọc các ký tự meny


Nói ... đó không phải là GW-BASIC? Ồ, chờ đã, đó awk. Xin lỗi, tôi không thể nói lúc đầu.
Tạm dừng cho đến khi có thông báo mới.

0

Đây có phải là những gì bạn muốn?

my $string = 'USCAGoleta9311734.5021-120.1287855805';

my $first_two_chars = substr $string, 0, 2;

ref: substr


1
cho rằng anh ấy / cô ấy có khả năng gọi điều này từ vỏ, một hình thức tốt hơn sẽ làperl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
Chas. Owens
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.