จะดึงคำแรกของผลลัพธ์ของคำสั่งใน bash ได้อย่างไร?


127

ฉันมีคำสั่งตัวอย่างเช่น: echo "word1 word2". ฉันต้องการใส่ท่อ ( |) และรับ word1 จากคำสั่ง

echo "word1 word2" | ....

ไม่รู้จะใส่ท่ออะไรหลัง

คำตอบ:


201

Awk เป็นตัวเลือกที่ดีหากคุณต้องจัดการกับช่องว่างต่อท้ายเพราะมันจะดูแลคุณ:

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

Cut จะไม่ดูแลสิ่งนี้แม้ว่า:

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

"ตัด" ที่นี่จะไม่พิมพ์อะไรเลย / เว้นวรรคเพราะสิ่งแรกก่อนเว้นวรรคคือช่องว่างอื่น


เซมิ - ลำไส้ใหญ่จำเป็นหรือไม่?
Alice Purcell

1
ควรเป็นช่องว่าง "นำหน้า" (ที่จุดเริ่มต้นของสตริง) ไม่ใช่ "ต่อท้าย"
user202729

@AlicePurcell ฉันพยายามโดยไม่ต้อง; และมันก็ใช้ได้สำหรับฉัน (MBP 10.14.2)
Samy Bencherif

1
สิ่งนี้ใช้ไม่ได้ถ้าสตริงเป็นเช่น "firstWord, secondWord" เนื่องจากคำสั่ง awk นั้นคั่นด้วยช่องว่าง
Roger Oba

@RogerOba นั่นไม่ใช่คำถามของ OP แต่คุณสามารถใช้ได้ -F","เพื่อแทนที่ตัวคั่นฟิลด์เริ่มต้น (ช่องว่าง) ด้วยลูกน้ำ
pjd

70

ไม่จำเป็นต้องใช้คำสั่งภายนอก Bash เองก็สามารถทำงานได้ สมมติว่า "word1 word2" คุณได้มาจากที่ไหนสักแห่งและเก็บไว้ในตัวแปรเช่น

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

ตอนนี้คุณสามารถกำหนด $ 1 หรือ $ 2 ฯลฯ ให้กับตัวแปรอื่นได้หากต้องการ


11
+1 สำหรับการใช้เฉพาะเชลล์ในตัวและstdin. @Matt M. --หมายถึงstdinจึง$stringถูกส่งต่อเป็นstdin. stdinเป็นช่องว่างแยกออกเป็นข้อโต้แย้ง$1, $2, $3ฯลฯ - เช่นเดียวกับเมื่อทุบตีประเมินโปรแกรมการขัดแย้ง (เช่นการตรวจสอบ$1, $2ฯลฯ ) วิธีนี้จะได้ประโยชน์จากแนวโน้มเปลือกที่จะแยกstdinออกเป็นข้อโต้แย้งโดยอัตโนมัติลบความจำเป็นในการหรือawk cut
Caleb Xu

3
@CalebXu ไม่ใช่ stdin setตั้งค่าอาร์กิวเมนต์เชลล์
Guido

9
word1=$(IFS=" " ; set -- $string ; echo $1)ตั้งค่า IFS เพื่อรับรู้ช่องว่างระหว่างคำอย่างถูกต้อง ใส่วงเล็บเพื่อหลีกเลี่ยงการอุดตันเนื้อหาต้นฉบับจำนวน $ 1
Steve Pitchers

สิ่งนี้เสียเนื่องจากอาจมีการขยายชื่อพา ธ ลองใช้กับstring="*". แปลกใจ
gniourf_gniourf

32

ฉันคิดว่าวิธีหนึ่งที่มีประสิทธิภาพคือการใช้ bash arrays:

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

นอกจากนี้คุณสามารถอ่านอาร์เรย์โดยตรงในเส้นท่อ:

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

1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
บุญทวีโฮม

สิ่งนี้เสียเนื่องจากอาจมีการขยายชื่อพา ธ ลองใช้กับstring="*". แปลกใจ
gniourf_gniourf

ใช้whileไวยากรณ์เพื่อดึงทุกคำแรกในแต่ละบรรทัด มิฉะนั้นให้ใช้บุญทวีโฮม นอกจากนี้โปรดทราบว่าecho "${array[0]}"มีการอ้างถึงเพื่อป้องกันการขยายตัวตามที่ gniourf-gniourf สังเกตเห็น
Isaías

หากคุณพยายามเข้าถึงดัชนีของอาร์เรย์ที่มากกว่าจำนวนคำคุณจะไม่ได้รับข้อผิดพลาด คุณจะได้รับสายว่าง
Dhumil Agarwal

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

สิ่งนี้มีข้อดีที่ไม่ได้ใช้คำสั่งภายนอกและปล่อยให้ตัวแปร $ 1, $ 2 และอื่น ๆ เหมือนเดิม


การปล่อยให้ตัวแปร$1, $2, …ยังคงอยู่เป็นคุณสมบัติที่มีประโยชน์อย่างยิ่งสำหรับการเขียนสคริปต์!
Serge Stroobandt

15

หากคุณแน่ใจว่าไม่มีช่องว่างนำหน้าคุณสามารถใช้การแทนที่พารามิเตอร์ bash:

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

ระวังการหลบหนีพื้นที่เดียว ดูตัวอย่างรูปแบบการทดแทนเพิ่มเติมได้ที่นี่ หากคุณมี bash> 3.0 คุณยังสามารถใช้การจับคู่นิพจน์ทั่วไปเพื่อจัดการกับช่องว่างนำหน้าได้ - ดูที่นี่ :

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

11

คุณสามารถลอง awk

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

ด้วย awk มันง่ายมากที่จะเลือกคำที่คุณชอบ ($ 1, $ 2, ... )


11

การใช้การขยายพารามิเตอร์เชลล์ %% *

นี่คือวิธีการแก้ปัญหาอื่นโดยใช้การขยายตัวพารามิเตอร์เปลือก ดูแลช่องว่างหลายช่องหลังคำแรก การจัดการช่องว่างหน้าคำแรกจำเป็นต้องมีการขยายเพิ่มเติมหนึ่งครั้ง

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

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

คำอธิบาย

%%หมายลบการแข่งขันเป็นไปได้ที่ยาวที่สุดของ *(เว้นวรรคตามด้วยหมายเลขของสิ่งที่ตัวละครอื่น ๆ ) ในท้ายstringส่วนหนึ่งของ


9

ฉันสงสัยว่าคำตอบยอดนิยมหลายข้อวัดได้อย่างไรในแง่ของความเร็ว ฉันทดสอบสิ่งต่อไปนี้:

1 @ mattbh's

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

2 @ ghostdog74's

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

3 @ บุญทวีโฮมส์

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

และ4 @ boontawee-home's

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

ฉันวัดด้วยเวลาของ Python ในสคริปต์ Bash ในเทอร์มินัล Zsh บน macOS โดยใช้สตริงทดสอบที่มีคำ 5 ตัวอักษร 215 คำ ทำการวัดแต่ละครั้ง 5 ครั้ง (ผลลัพธ์ทั้งหมดเป็น 100 ลูปดีที่สุด 3) และเฉลี่ยผลลัพธ์:

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")

เยี่ยมมากผู้มีสิทธิเลือกตั้ง👏คะแนน (ณ วันที่เขียนนี้) ตรงกับความเร็วของการแก้ปัญหา!


แปลกที่คุณสามารถวัด 3 ในขีดกลางได้เนื่องจาก dash ไม่รองรับอาร์เรย์ ( read -aเป็นเส้นประไม่ถูกต้อง)
gniourf_gniourf

ใช่มันแปลก ฉันตัดข้อหนึ่งออกทำการทดสอบความเร็วแล้วคิดว่า "ทำไมฉันถึงปล่อยมันออกไป" และเพิ่มเข้ามานำออกตอนนี้และฉันอาจจะรันสิ่งต่างๆอีกครั้งในภายหลังเพื่อให้แน่ใจว่าฉันไม่มีข้อผิดพลาด
เฮนรี่

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

ตัดตัดฟิลด์ที่ 1 (-f 1) ออกจากรายการของฟิลด์ที่คั่นด้วยสตริง "" (-d "")


นั่นเป็นวิธีหนึ่ง แต่คำสั่งตัดของคุณจะไม่แยกช่องว่างหลายช่องระหว่างคำหากเขาต้องการได้ word2 ในภายหลัง
ghostdog74

ใช่โซลูชัน awk เป็นวิธีที่ดีกว่า
lajuette

3

read เป็นเพื่อนของคุณ:

  • หากสตริงอยู่ในตัวแปร:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • หากคุณกำลังทำงานในไพพ์: กรณีแรก: คุณต้องการเพียงคำแรกของบรรทัดแรก:

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

    กรณีที่สอง: คุณต้องการคำแรกของแต่ละบรรทัด:

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

สิ่งเหล่านี้ใช้ได้หากมีช่องว่างนำหน้า:

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

0

เนื่องจาก perl รวมฟังก์ชันการทำงานของ awk สิ่งนี้สามารถแก้ไขได้ด้วย perl ด้วย:

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

0

ฉันกำลังทำงานกับอุปกรณ์ฝังตัวที่ไม่มีทั้ง perl, awk หรือ python และทำด้วย sed แทน มันสนับสนุนช่องว่างหลายก่อนที่คำแรก (ที่cutและbashการแก้ปัญหาไม่ได้จัดการ)

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

สิ่งนี้มีประโยชน์มากเมื่อทำการ grepping psสำหรับ ID กระบวนการเนื่องจากโซลูชันอื่น ๆ ที่นี่โดยใช้ bash เพียงอย่างเดียวไม่สามารถลบช่องว่างแรกที่psใช้ในการจัดแนวได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.