Temukan string sambil mengetahui bagiannya dan kembalikan string

9

Saya punya string, misalnya

"Icecream123 AirplaneBCD CompanyTL1 ComputerYU1"

Katakanlah saya tahu bahwa string saya akan berisi IceCream substring pasti tapi saya tidak tahu apa yang mengikutinya.

Mungkin 123 seperti dalam contoh saya atau mungkin sesuatu yang berbeda.

Sementara saya bisa menggunakan grep untuk mendeteksi apakah "Icecream" substring ada di string saya dengan perintah berikut

echo $string | grep -oF 'Icecream';

Yang akan dicetak

Icecream

Saya ingin dengan perintah untuk mencetak seluruh substring, yang dalam contoh saya adalah

Icecream123

Tentu saja yang mengikuti Icecream adalah acak dan tidak diketahui sebelumnya jadi saya tidak bisa begitu saja melakukannya

$SUBSTRING=$(echo $string | grep -oF 'Icecream')
$SUBSTRINGTRAIL=123
echo $SUBSTRING$SUBSTRINGTRAIL
Sonamor
sumber
substring tetap / statis - selalu "Icecream", atau itu variabel?
Jeff Schaller
Apakah spasi akan mengindikasikan akhir dari akhiran yang diinginkan?
Jeff Schaller
@ JeffSchaller Sedihnya, saya tidak tahu itu. Saya benar-benar mendapatkan output multiline dari perintah lain, yang saya simpan dalam sebuah variabel, variabel ini adalah $ string saya, ketika itu mendapat gema itu menampilkan output multiline sebagai garis signle dengan spasi di antara mereka. Saya sebenarnya tidak tahu apakah itu spasi atau karakter khusus seperti LF. Saya pikir itu ruang.
Sonamor
Maksud saya, misalnya, Icecream123 AirplaneBCDAnda ingin berhenti 123. Apakah itu karena ada spasi setelah angka 3, atau yang lainnya?
Jeff Schaller
1
Jika Anda tidak yakin dengan data Anda, sulit untuk menulis solusi yang tepat. Semua jawaban sejauh ini mengasumsikan data Anda berada pada satu baris, seperti yang Anda tunjukkan. Saya mencoba mencari tahu apa pembatas Anda - di mana bagian "trailing" harus berhenti.
Jeff Schaller

Jawaban:

15

Jika Anda grepmendukung perl ekspresi reguler yang kompatibel, Anda dapat mencocokkan non-rakus hingga batas kata berikutnya:

echo "$string" | grep -oP 'Icecream.*?\b'

Jika tidak, cocok dengan urutan karakter non-kosong terpanjang:

echo "$string" | grep -o 'Icecream[^[:blank:]]*'

Atau simpan semua yang ada di shell dan hapus urutan karakter terpanjang yang dimulai dengan spasi:

echo "${string%% *}"
Steeldriver
sumber
2
Untuk PCRE, saya akan menggunakan 'Icecream\S+'beberapa karakter yang tidak kosong.
glenn jackman
Terima kasih atas komentar Anda, sepertinya versi grep saya tidak mendukung perl regex. Bisakah Anda menambahkan lebih detail tentang opsi ketiga Anda? Saya tidak yakin bagaimana cara mengimplementasikannya.
Sonamor
Setelah beberapa pengujian lagi nampaknya menggunakan salah satu echo "$ string" | grep -oP 'Icecream. *? \ b' atau 'Icecream \ S +' itu berfungsi. Terima kasih
Sonamor
itu benar-benar membingungkan bahwa walaupun variabel $ string Anda adalah string, Anda masih harus meletakkannya di antara tanda kutip ganda!
Sonamor
@Samoram dalam hal ini penawaran tidak sepenuhnya diperlukan; Namun ada begitu banyak kasus di mana itu adalah kebiasaan yang baik untuk dimasuki. Lihat misalnya Kapan perlu kutip ganda?
steeldriver
7

Menggunakan grepyang tahu tentang -o:

$ printf '%s\n' "$string" | grep -o '\<Icecream[^[:blank:]]*'
Icecream123

Pola \<Icecream[^[:blank:]]*cocok dengan string Icecream(di mana Ididahului oleh karakter non-kata, atau awal baris) diikuti oleh nol atau lebih non-kosong (bukan spasi atau tab).


Menggunakan awk:

$ printf '%s\n' "$string" | awk -v RS=' ' '/^Icecream/'       
Icecream123

The awkProgram membagi string menjadi catatan ruang yang dipisahkan, dan tes masing-masing. Ini akan mencetak yang dimulai dengan string Icecream.

Menggunakan mawkatau GNU awk, Anda juga dapat menggunakan

printf '%s\n' "$string" | awk -v RS='[[:blank:]]' '/^Icecream/'

karena mereka interpet RSsebagai ekspresi reguler jika mengandung lebih dari satu karakter.


Dengan sed, dengan cara yang sama seperti dengan grep:

$ printf '%s\n' "$string" | sed 's/.*\(\<Icecream[^[:blank:]]*\).*/\1/'
Icecream123

Menggunakan /bin/sh:

set -- Icecream123 AirplaneBCD CompanyTL1 ComputerYU1
for string; do
    case $string in
        Icecream*)
            printf '%s\n' "$string"
            break
    esac
done

Perl (dengan sedikit bantuan dari tr):

$ printf '%s\n' "$string" | tr ' ' '\n' | perl -ne '/Icecream\S*/ && print'
Icecream123

atau hanya

$ printf '%s\n' "$string" | perl -ne '/(Icecream\S*)/ && print $1, "\n"'
Icecream123
Kusalananda
sumber
Atau, bagi menjadi beberapa baris dan cocokkan dengan kuncinya:echo "$string" | grep -o '\S\+' | grep "Icecream"
Isaac
7

Karena Anda memberi tag pada bash:

[[ $string =~ (Icecream[^ ]*) ]] && result=${BASH_REMATCH[1]}

Secara umum, untuk istilah pencarian di $search:

[[ $string =~ ($search[^ ]*) ]] && result=${BASH_REMATCH[1]}

... atau dengan ekspansi parameter:

# remove any leading text up to -and through- the search text:
x=${string##*$search}

# remove any trailing space onwards
result=$search${x%% *}
Jeff Schaller
sumber
2

Misalnya, jika Anda menggunakan GNU grep:

$ echo "Icecream123 AirplaneBCD CompanyTL1 ComputerYU1" | grep -oP '\bIcecream.*?(\s|$)' --color

Itu menggunakan PCRE.

Arkadiusz Drabczyk
sumber
1

Mungkin sedikit lebih sederhana, terutama karena Anda mengatakan bahwa versi grep Anda tidak mendukung perl regex:

$ echo $string | tr ' ' '\n' | grep 'Icecream' Icecream123

The trmembagi string ke dalam garis dengan mengganti semua ruang dengan baris baru. Maka Anda bisa menggunakannya grepdengan mudah.

Anda juga dapat menulis yang berikut ini untuk mendapatkan hanya apa yang mengikuti kata yang Anda cari:

$ echo $string | tr ' ' '\n' | sed -n 's/Icecream//p' 123

Hukum29
sumber