Bagaimana cara mengambil kata pertama dari output perintah di bash?

127

Saya memiliki perintah, misalnya: echo "word1 word2". Saya ingin meletakkan pipa ( |) dan mendapatkan kata1 dari perintah.

echo "word1 word2" | ....

Saya tidak tahu apa yang harus dimasukkan setelah pipa.

Neuquino
sumber

Jawaban:

201

Awk adalah pilihan yang baik jika Anda harus berurusan dengan spasi kosong karena itu akan mengurusnya untuk Anda:

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

Cut tidak akan menangani ini:

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

'cut' di sini tidak mencetak apa pun / spasi, karena hal pertama sebelum spasi adalah spasi lain.

mattbh
sumber
Apakah titik koma diperlukan?
Alice Purcell
1
Ini harus spasi kosong "memimpin" (di awal string), bukan "di belakang".
pengguna202729
@AlicePurcell Saya mencobanya tanpa; dan itu berhasil untuk saya (MBP 10.14.2)
Samy Bencherif
1
Ini tidak berfungsi jika stringnya adalah misalnya "firstWord, secondWord" sebagai pembatas perintah awk menurut spasi
Roger Oba
@RogerOba Itu bukan pertanyaan OP, tetapi Anda dapat menggunakan -F","untuk mengganti pemisah bidang default (spasi) dengan koma.
pjd
70

tidak perlu menggunakan perintah eksternal. Bash sendiri bisa melakukan pekerjaan itu. Dengan asumsi "kata1 kata2" Anda dapatkan dari suatu tempat dan disimpan dalam variabel, misalnya

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

sekarang Anda dapat menetapkan $ 1, atau $ 2 dll ke variabel lain jika Anda mau.

anjing hantu74
sumber
11
1 untuk hanya menggunakan shell built-in dan stdin. @Matt M. --artinya stdin, $stringditeruskan sebagai stdin. stdinadalah spasi-dipisahkan menjadi argumen $1, $2, $3, dll - seperti ketika Bash Program mengevaluasi argumen (misalnya cek $1, $2, dll), pendekatan ini mengambil keuntungan dari kecenderungan shell untuk membagi stdinke dalam argumen otomatis, menghilangkan kebutuhan untuk awkatau cut.
Caleb Xu
3
@CalebXu Bukan stdin, setmenyetel argumen shell.
Guido
9
word1=$(IFS=" " ; set -- $string ; echo $1)Atur IFS untuk mengenali spasi di antara kata dengan benar. Bungkus dalam tanda kurung agar tidak mengganggu konten asli $ 1.
Steve Pitchers
Ini rusak karena tunduk pada perluasan nama jalur. Cobalah dengan string="*". Mengherankan.
gniourf_gniourf
32

Saya pikir salah satu cara yang efisien adalah penggunaan bash array:

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

Selain itu, Anda bisa langsung membaca array dalam pipa-line:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done
Isaías
sumber
1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home
Ini rusak karena tunduk pada perluasan nama jalur. Cobalah dengan string="*". Mengherankan.
gniourf_gniourf
Gunakan whilesintaks untuk mengambil setiap kata pertama di setiap baris. Jika tidak, gunakan pendekatan Rumah Boontawee. Juga, harap dicatat bahwa echo "${array[0]}"telah dikutip untuk mencegah perluasan seperti yang diperhatikan oleh gniourf-gniourf.
Isaías
Jika Anda mencoba mengakses indeks larik yang lebih besar dari jumlah kata, maka Anda tidak akan mendapatkan kesalahan. Anda hanya akan mendapatkan baris kosong
Dhumil Agarwal
26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Ini memiliki keuntungan yaitu tidak menggunakan perintah eksternal dan membiarkan variabel $ 1, $ 2, dll tetap utuh.

John Marter
sumber
Membiarkan variabel $1, $2, …utuh adalah fitur yang sangat berguna untuk penulisan skrip!
Serge Stroobandt
15

Jika Anda yakin tidak ada spasi di depan, Anda dapat menggunakan substitusi parameter bash:

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

Hati-hati untuk keluar dari ruang tunggal. Lihat di sini untuk lebih banyak contoh pola substitusi. Jika Anda memiliki bash> 3.0, Anda juga dapat menggunakan pencocokan ekspresi reguler untuk mengatasi spasi di depan - lihat di sini :

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

Anda bisa mencoba awk

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

Dengan awk, sangat mudah untuk memilih kata apa pun yang Anda suka ($ 1, $ 2, ...)

mfloryan.dll
sumber
11

Menggunakan ekspansi parameter shell %% *

Berikut adalah solusi lain menggunakan ekspansi parameter shell . Ini menangani banyak spasi setelah kata pertama. Menangani spasi di depan kata pertama membutuhkan satu ekspansi tambahan.

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

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

Penjelasan

The %%menandakan menghapus pertandingan terpanjang kemungkinan  *(spasi diikuti oleh sejumlah apapun karakter lain) di membuntuti bagian dari string.

Serge Stroobandt
sumber
9

Saya bertanya-tanya bagaimana beberapa jawaban teratas diukur dalam kaitannya dengan kecepatan. Saya menguji yang berikut ini:

1 @ mattb

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

2 @ ghostdog74's

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

3 @ boontawee-home's

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

dan 4 @ boontawee-home

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

Saya mengukurnya dengan timeit Python dalam skrip Bash di terminal Zsh di macOS, menggunakan string uji dengan 215 kata 5 huruf. Lakukan setiap pengukuran lima kali (hasilnya semua untuk 100 loop, terbaik dari 3), dan ratakan hasilnya:

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

Kerja bagus, pemilih 👏 Suara (saat tulisan ini dibuat) cocok dengan kecepatan solusi!

henry
sumber
Aneh bahwa Anda dapat mengukur 3 dengan tanda hubung, karena tanda hubung tidak mendukung array ( read -atanda hubung tidak valid).
gniourf_gniourf
Ya itu aneh. Saya mengesampingkan yang satu itu, melakukan tes kecepatan, lalu berpikir "mengapa saya membiarkan yang itu" dan menambahkannya. Menghapusnya sekarang, dan saya dapat menjalankan ulang nanti untuk memastikan saya tidak melakukan kesalahan
henry
6
echo "word1 word2" | cut -f 1 -d " "

cut memotong bidang pertama (-f 1) dari daftar bidang yang dipisahkan oleh string "" (-d "")

lajuette
sumber
itu salah satu cara, tetapi pernyataan cut Anda tidak akan membedakan banyak spasi di antara kata-kata jika dia ingin mendapatkan word2 nanti
ghostdog74
ya, solusi awk adalah yang lebih baik.
lajuette
3

read apakah temanmu:

  • Jika string ada dalam variabel:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Jika Anda mengerjakan pipa: kasus pertama: Anda hanya menginginkan kata pertama dari baris pertama:

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

    kasus kedua: Anda menginginkan kata pertama dari setiap baris:

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

Ini berfungsi jika ada spasi di depan:

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

Karena perl menggabungkan fungsionalitas awk, ini juga dapat diselesaikan dengan perl:

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

Saya bekerja dengan perangkat tertanam yang tidak memiliki perl, awk atau python dan melakukannya dengan sed. Ini mendukung banyak spasi sebelum kata pertama (yang tidak ditangani oleh solusi cutdan bash).

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

Ini sangat berguna ketika psmencari ID proses karena solusi lain di sini yang hanya menggunakan bash tidak dapat menghapus spasi pertama yang psdigunakan untuk menyelaraskan.

Johan Bjäreholt
sumber