Làm thế nào để lấy từ đầu tiên của đầu ra của lệnh trong bash?


126

Tôi có một lệnh, ví dụ : echo "word1 word2". Tôi muốn đặt một pipe ( |) và nhận word1 từ lệnh.

echo "word1 word2" | ....

Tôi không biết phải đặt gì sau đường ống.

Câu trả lời:


201

Awk là một lựa chọn tốt nếu bạn phải đối phó với khoảng trắng ở cuối bởi vì nó sẽ chăm sóc nó cho bạn:

echo "   word1  word2 " | awk '{print $1;}' # Prints "word1"

Cut sẽ không quan tâm đến điều này mặc dù:

echo "  word1  word2 " | cut -f 1 -d " " # Prints nothing/whitespace

'Cắt' ở đây in không có gì / khoảng trắng, bởi vì điều đầu tiên trước một không gian là một không gian khác.


Là bán đại tràng cần thiết?
Alice Purcell

1
Nó phải là khoảng trắng "hàng đầu" (ở đầu chuỗi), không phải là "dấu".
dùng202729

@AlicePurcell Tôi đã thử nó mà không có; và nó hoạt động với tôi (MBP 10.14.2)
Samy Bencherif

1
Điều này không hoạt động nếu chuỗi là ví dụ "FirstWord, secondWord" vì đó là dấu phân cách lệnh awk theo khoảng trắng
Roger Oba

@RogerOba Đó không phải là câu hỏi của OP, nhưng bạn có thể sử dụng -F","để ghi đè dấu tách trường mặc định (dấu cách) bằng dấu phẩy.
pjd

70

không cần sử dụng các lệnh bên ngoài. Bash chính nó có thể làm công việc. Giả sử "word1 word2" bạn nhận được từ đâu đó và được lưu trữ trong một biến, ví dụ:

$ string="word1 word2"
$ set -- $string
$ echo $1
word1
$ echo $2
word2

bây giờ bạn có thể gán $ 1, hoặc $ 2, vv cho một biến khác nếu bạn muốn.


11
+1 chỉ sử dụng shell dựng sẵn và stdin. @Matt M. --có nghĩa là stdin, vì vậy $stringđang được thông qua như stdin. stdinlà khoảng trắng tách thành đối số $1, $2, $3, vv - giống như khi một Bash đánh giá lại chương trình đối số (ví dụ như kiểm tra $1, $2vv), phương pháp này có lợi thế của xu hướng của vỏ để phân chia các stdinthành đối số tự động, loại bỏ sự cần thiết awkhay cut.
Caleb Xu

3
@CalebXu Không stdin, setđặt đối số shell.
Guido

9
word1=$(IFS=" " ; set -- $string ; echo $1)Đặt IFS để nhận dạng chính xác khoảng trắng giữa các từ. Gói trong ngoặc đơn để tránh ghi đè nội dung gốc của $ 1.
Steve Pitchers

Điều này bị phá vỡ vì nó có thể mở rộng tên đường dẫn. Hãy thử nó với string="*". Sự ngạc nhiên.
gniourf_gniourf

32

Tôi nghĩ một cách hiệu quả là sử dụng mảng bash:

array=( $string ) # do not use quotes in order to allow word expansion
echo ${array[0]}  # You can retrieve any word. Index runs from 0 to length-1

Ngoài ra, bạn có thể đọc trực tiếp các mảng trong một đường ống:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done

1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Trang chủ Boontawee

Điều này bị phá vỡ vì nó có thể mở rộng tên đường dẫn. Hãy thử nó với string="*". Sự ngạc nhiên.
gniourf_gniourf

Sử dụng whilecú pháp để lấy từng từ đầu tiên ở mỗi dòng. Nếu không, sử dụng phương pháp Boontawee Home. Ngoài ra, xin lưu ý rằng echo "${array[0]}"đã được trích dẫn để ngăn chặn sự mở rộng như được chú ý bởi gniourf-gniourf.
Ê-sai

Nếu bạn cố gắng truy cập vào một chỉ mục của mảng lớn hơn số lượng từ, thì bạn sẽ không gặp lỗi. Bạn sẽ chỉ nhận được một dòng trống
Dhumil Agarwal

26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Điều này có lợi thế là không sử dụng các lệnh bên ngoài và giữ nguyên các biến $ 1, $ 2, v.v.


Để $1, $2, …nguyên các biến là một tính năng cực kỳ hữu ích cho việc viết kịch bản!
Serge Stroobandt

14

Nếu bạn chắc chắn không có khoảng trắng hàng đầu, bạn có thể sử dụng thay thế tham số bash:

$ string="word1  word2"
$ echo ${string/%\ */}
word1

Xem ra cho thoát khỏi không gian duy nhất. Xem ở đây để biết thêm ví dụ về các mẫu thay thế. Nếu bạn có bash> 3.0, bạn cũng có thể sử dụng kết hợp biểu thức chính quy để đối phó với các khoảng trắng hàng đầu - xem tại đây :

$ string="  word1   word2"
$ [[ ${string} =~ \ *([^\ ]*) ]]
$ echo ${BASH_REMATCH[1]}
word1

11

Bạn có thể thử awk

echo "word1 word2" | awk '{ print $1 }'

Với awk, thật dễ dàng để chọn bất kỳ từ nào bạn thích ($ 1, $ 2, ...)


11

Sử dụng mở rộng tham số shell %% *

Đây là một giải pháp khác sử dụng mở rộng tham số shell . Nó chăm sóc nhiều không gian sau từ đầu tiên. Xử lý khoảng trắng ở phía trước của từ đầu tiên yêu cầu mở rộng thêm.

string='word1    word2'
echo ${string%% *}
word1

string='word1    word2      '
echo ${string%% *}
word1

Giải trình

Dấu %%hiệu xóa kết quả khớp dài nhất có thể của  *(một khoảng trắng theo sau bởi bất kỳ số lượng ký tự nào khác) trong phần dấu của string.


9

Tôi tự hỏi làm thế nào một số câu trả lời hàng đầu đo lường về tốc độ. Tôi đã thử nghiệm như sau:

1 @ mattbh

echo "..." | awk '{print $1;}'

2 @ ghostdog74

string="..."; set -- $string; echo $1

3 @ boontawee-nhà

echo "..." | { read -a array ; echo ${array[0]} ; }

4 @ boontawee-home

echo "..." | { read first _ ; echo $first ; }

Tôi đã đo chúng bằng thời gian của Python trong tập lệnh Bash trong thiết bị đầu cuối Zsh trên macOS, sử dụng chuỗi thử nghiệm với 215 từ 5 chữ cái. Có phải mỗi lần đo năm lần (kết quả là tất cả cho 100 vòng, tốt nhất là 3) và tính trung bình cho kết quả:

method       time
--------------------------------
1. awk       9.2ms
2. set       11.6ms (1.26 * "1")
3. read -a   11.7ms (1.27 * "1")
4. read      13.6ms (1.48 * "1")

Công việc tốt, cử tri Các phiếu bầu (như bài viết này) phù hợp với tốc độ của các giải pháp!


Thật kỳ lạ khi bạn có thể đo 3 trong dấu gạch ngang, vì dấu gạch ngang không hỗ trợ mảng ( read -akhông hợp lệ trong dấu gạch ngang).
gniourf_gniourf

Vâng, đó là lạ. Tôi loại trừ một trong số đó, thực hiện các bài kiểm tra tốc độ, sau đó nghĩ "tại sao tôi lại bỏ cái đó ra" và thêm nó vào. Xóa nó ngay bây giờ và tôi có thể chạy lại mọi thứ sau đó để chắc chắn rằng tôi không có lỗi gì cả
henry

6
echo "word1 word2" | cut -f 1 -d " "

cắt cắt trường thứ nhất (-f 1) từ danh sách các trường được phân tách bằng chuỗi "" (-d "")


đó là một cách, nhưng câu lệnh cắt của bạn sẽ không phân biệt nhiều khoảng trắng ở giữa các từ nếu anh ta muốn nhận word2 sau này
ghostdog74

vâng, giải pháp awk là tốt hơn.
lajuette

3

read là bạn của bạn:

  • Nếu chuỗi nằm trong một biến:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Nếu bạn đang làm việc trong một đường ống: trường hợp đầu tiên: bạn chỉ muốn từ đầu tiên của dòng đầu tiên:

    printf '%s\n' "word1 word2" "line2" | { read -r first _; printf '%s\n' "$first"; }

    trường hợp thứ hai: bạn muốn từ đầu tiên của mỗi dòng:

    printf '%s\n' "word1 word2" "worda wordb" | while read -r first _; do printf '%s\n' "$first"; done

Những công việc này nếu có không gian hàng đầu:

printf '%s\n' "   word1 word2" | { read -r first _; printf '%s\n' "$first"; }

0

Vì perl kết hợp chức năng của awk, điều này cũng có thể được giải quyết bằng perl:

echo " word1 word2" | perl -lane 'print $F[0]'

0

Tôi đã làm việc với một thiết bị nhúng không có perl, awk hoặc python và đã làm điều đó với sed thay thế. Nó hỗ trợ nhiều khoảng trắng trước từ đầu tiên (mà các giải pháp cutbashkhông xử lý).

VARIABLE="  first_word_with_spaces_before_and_after  another_word  "
echo $VARIABLE | sed 's/ *\([^ ]*\).*/\1/'

Điều này rất hữu ích khi grepping psID quá trình vì các giải pháp khác ở đây chỉ sử dụng bash không thể xóa các khoảng trắng đầu tiên pssử dụng để căn chỉnh.

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.