Hapus karakter terakhir dari string menggunakan manipulasi string dalam skrip shell


187

Saya ingin menghapus karakter terakhir dari sebuah string, saya mencoba skrip kecil ini:

#! /bin/sh 

t="lkj"
t=${t:-2}
echo $t

tetapi mencetak "lkj", apa yang saya lakukan salah?

Jawaban:


115

Dalam shell POSIX, sintaksnya ${t:-2}berarti sesuatu yang berbeda - itu berkembang ke nilai tjika tdiatur dan bukan nol, dan sebaliknya ke nilai 2. Untuk memangkas satu karakter dengan ekspansi parameter, sintaks yang mungkin Anda inginkan adalah${t%?}

Perhatikan bahwa dalam ksh93, bashatau zsh, ${t:(-2)}atau ${t: -2}(perhatikan spasi) adalah legal sebagai perluasan substring tetapi mungkin bukan yang Anda inginkan, karena mereka mengembalikan substring mulai dari posisi 2 karakter dari akhir (yaitu menghilangkan karakter pertamai dari string ijk).

Lihat bagian Perluasan Parameter Shell pada Bash Reference Manual untuk info lebih lanjut:


4
Maukah Anda menjelaskan apa keajaiban di balik '%?' ?
afraisse

8
@afraisse ${parameter%word}menghapus pencocokan pola sufiks terpendek word- lihat bagian Ekspansi Parameterman bash
steeldriver

3
Ini bekerja dengan baik untuk Bash 4.1.2: $ {t%?} Untuk orang-orang yang terjebak dengan CentOS / RHEL 6.x
Joey T

185

Dengan bash4.2 dan di atasnya, Anda dapat melakukan:

${var::-1}

Contoh:

$ a=123
$ echo "${a::-1}"
12

Perhatikan bahwa untuk yang lebih lama bash(misalnya, bash 3.2.5pada OS X), Anda harus meninggalkan spasi di antara dan setelah titik dua:

${var: : -1}

13
Ini berfungsi untuk bashversi 4.2-alpha dan di atasnya, sayang sekali versi yang saya akses sebelumnya. : - /
hjk

2
@ iamaziz: Dari bash changelog, panjang negatif dalam ${var:offset:lenght}ditambahkan hanya dalam bash 4.2. Mungkin OSX menambahkan tambalannya sendiri bash.
cuonglm

1
@cuonglm tidak bekerja: /
iamaziz

1
Tidak berfungsi di mac.
shinzou

1
MACsters, lihat jawaban Russ
P i

67

untuk menghapus nkarakter terakhir dari garis yang tidak menggunakan sedOR awk:

> echo lkj | rev | cut -c (n+1)- | rev

jadi misalnya Anda dapat menghapus karakter terakhir one charactermenggunakan ini:

> echo lkj | rev | cut -c 2- | rev

> lk

dari revmanpage:

DESKRIPSI
Utilitas rev menyalin file yang ditentukan ke output standar, membalik urutan karakter di setiap baris. Jika tidak ada file yang ditentukan, input standar dibaca.

MEMPERBARUI:

jika Anda tidak tahu panjang tali, coba:

$ x="lkj"
$ echo "${x%?}"
lk

62

Menggunakan sed itu harus secepat

sed 's/.$//'

Kemudian gema tunggal Anda echo ljk | sed 's/.$//'.
Dengan menggunakan ini, string 1-line dapat berukuran berapa saja.


10
Perhatikan bahwa dalam kasus umum, itu tidak menghapus karakter terakhir dari string , tetapi karakter terakhir dari setiap baris string .
Stéphane Chazelas

44

Beberapa opsi tergantung pada shell:

  • POSIX: t=${t%?}
  • Maksud: t=`expr " $t" : ' \(.*\).'`
  • zsh / yash: t=${t[1,-2]}
  • bash / zsh: t=${t:0:-1}
  • ksh93 / bash / zsh / mksh: t=${t:0:${#t}-1}
  • ksh93 / bash / zsh / mksh: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
  • es: @ {t=$1} ~~ $t *?

Perhatikan bahwa sementara semua seharusnya menghapus karakter terakhir , Anda akan menemukan bahwa beberapa implementasi (yang tidak mendukung karakter multi-byte) malah menghapus byte terakhir (sehingga kemungkinan akan merusak karakter terakhir jika multi-byte) ).

The exprvarian mengasumsikan $ttidak berakhir di lebih dari satu karakter baris baru. Ini juga akan mengembalikan status keluar non-nol jika string yang dihasilkan berakhir menjadi 0(atau 000atau bahkan -0dengan beberapa implementasi). Itu juga bisa memberikan hasil yang tidak terduga jika string berisi karakter yang tidak valid.


Bagus dan teliti! Tapi ... Saya menganggap semua cangkang itu mendukung POSIX, jadi semua orang harus menggunakan cangkang itu untuk menjadi yang paling portabel. Hitungan karakter terkecil juga!
Russ

@Russ, t=${t%?}bukan Bourne tetapi Anda tidak akan menemukan shell Bourne saat ini. ${t%?}tidak bekerja di semua yang lain sekalipun.
Stéphane Chazelas

Tidak ada opsi tempurung ikan yang diberikan! Mungkin lebih populer hari ini daripada ksh93 ...
rien333

@ rien333. Saya akan menunggu antarmuka sedikit stabil. fishsedang dalam proses. 2.3.0 yang memperkenalkan stringbuiltin tidak dirilis pada saat tanya jawab. Dengan versi yang saya uji, Anda perlu string replace -r '(?s).\z' '' -- $t(dan saya berharap mereka ingin mengubahnya, mereka harus mengubah flag yang mereka berikan ke PCRE) atau yang lebih berbelit-belit. Itu juga berurusan dengan buruk dengan karakter baris baru, dan saya tahu mereka berencana untuk mengubah itu juga.
Stéphane Chazelas

Terpilih untuk jawaban POSIX. dikonfirmasi bekerja di Bash 3.2.57 (1)
Avindra Goolcharan

26

Jawaban yang paling portabel, dan terpendek, hampir pasti:

${t%?}

Ini berfungsi di bash, sh, ash, dash, busybox / ash, zsh, ksh, dll.

Ini bekerja dengan menggunakan ekspansi parameter shell old-school. Secara khusus, %menentukan untuk menghapus akhiran pencocokan terkecil dari parameter tyang cocok dengan pola glob ?(yaitu: karakter apa saja).

Lihat "Hapus Pola Sufiks Terkecil" di sini untuk penjelasan (lebih banyak) lebih rinci dan lebih banyak latar belakang. Lihat juga dokumen untuk shell Anda (misalnya:) di man bashbawah "ekspansi parameter".


Sebagai catatan, jika Anda ingin menghapus karakter pertama , Anda akan menggunakan ${t#?}, karena #kecocokan dari depan string (awalan) alih-alih dari belakang (akhiran).

Juga diperhatikan adalah bahwa kedua %dan #memiliki %%dan ##versi yang cocok dengan terpanjang versi pola yang diberikan bukannya terpendek. Keduanya ${t%%?}dan ${t##?}akan melakukan hal yang sama sebagai operator tunggal mereka dalam hal ini (jadi jangan tambahkan karakter tambahan yang tidak berguna). Ini karena ?pola yang diberikan hanya cocok dengan satu karakter. Gabungkan *dengan beberapa non-wildcard dan hal-hal menjadi lebih menarik dengan %%dan ##.

Memahami ekspansi parameter, atau setidaknya mengetahui tentang keberadaannya dan mengetahui cara mencarinya, sangat berguna untuk menulis dan menguraikan skrip shell dari banyak rasa. Ekspansi parameter sering terlihat seperti voodoo shell misterius bagi banyak orang karena ... well ... mereka adalah voodoo shell misterius (meskipun cukup baik didokumentasikan jika Anda tahu untuk mencari "ekspansi parameter"). Pasti bagus untuk ada di sabuk alat saat Anda terjebak dalam cangkang.


Pendek dan manis, dan berfungsi di MacOS dan Linux!
dbernard

18
t=lkj
echo ${t:0:${#t}-1}

Anda mendapatkan substring dari 0 hingga panjang string -1. Namun perlu dicatat bahwa substraksi ini khusus untuk bash, dan tidak akan berfungsi pada shell lainnya.

Misalnya, bahkan dashtidak dapat menguraikan

echo ${t:0:$(expr ${#t} - 1)}

Misalnya, di Ubuntu, /bin/shadalahdash


15

Anda juga dapat menggunakan headuntuk mencetak semua kecuali karakter terakhir.

$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin

Namun sayangnya beberapa versi headtidak menyertakan -opsi unggulan . Ini adalah kasus untuk headyang datang dengan OS X.


5

Cukup mudah dilakukan dengan menggunakan ekspresi reguler:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"

5

Beberapa perbaikan. Untuk menghapus lebih dari satu karakter, Anda dapat menambahkan beberapa tanda tanya. Misalnya, untuk menghapus dua karakter terakhir dari variabel:, $SRC_IP_MSGAnda dapat menggunakan:

SRC_IP_MSG=${SRC_IP_MSG%??}

4

Hanya untuk melengkapi beberapa kemungkinan penggunaan bash murni:

#!/bin/bash

# Testing substring removal
STR="Exemple string with trailing whitespace "
echo "'$STR'"
echo "Removed trailing whitespace: '${STR:0:${#STR}-1}'"
echo "Removed trailing whitespace: '${STR/%\ /}'"

Sintaks pertama mengambil substring dari string, sintaksnya adalah Untuk yang kedua, perhatikan tanda, yang berarti 'dari ujung garis' dan sintaksnya adalah
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}

Dan berikut adalah dua bentuk yang lebih pendek dari yang disebutkan di atas

echo "Removed trailing whitespace: '${STR::-1}'"
echo "Removed trailing whitespace: '${STR%\ }'"

Di sini perhatikan lagi %tanda, yang berarti 'Hapus (yaitu, ganti dengan' ') pola cocok terpendek (di sini diwakili oleh ruang melarikan diri ' \ ' dari ujung PARAMETER - di sini bernama STR


1

Seperti kita juga bisa menggunakan php di command line, atau skrip shell. Terkadang berguna untuk operasi parsing.

php -r "echo substr('Hello', 0, -1);" 
// Output hell

Dengan perpipaan:

echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.