Usuń ostatni znak ciągu za pomocą manipulacji ciągiem w skrypcie powłoki


187

Chciałbym usunąć ostatni znak ciągu, wypróbowałem ten mały skrypt:

#! /bin/sh 

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

ale wypisuje „lkj”, co robię źle?

Odpowiedzi:


115

W powłoce POSIX składnia ${t:-2}oznacza coś innego - rozwija się do wartości tif tjest ustawiona i różna od null, a w przeciwnym razie do wartości 2. Aby przyciąć pojedynczy znak poprzez rozwinięcie parametru, prawdopodobnie potrzebna jest składnia${t%?}

Zauważ, że w ksh93, bashalbo zsh, ${t:(-2)}albo ${t: -2}(uwaga przestrzeni) prawną jako ekspansji podciągu ale prawdopodobnie nie są, co chcesz, ponieważ zwracają podciąg rozpoczynający się w położeniu 2 znaki od końca (czyli usuwa pierwszy znak iz następujących ciąg ijk).

Aby uzyskać więcej informacji, zobacz sekcję Rozszerzanie parametrów powłoki podręcznika Bash Reference:


4
Czy chciałbyś wyjaśnić, na czym polega magia „%”? ?
afraisse

8
@afraisse ${parameter%word}usuwa najkrótsze dopasowanie wzorca sufiksu word- patrz sekcja Rozbudowa parametruman bash
steeldriver

3
Działa to dobrze w przypadku wersji Bash 4.1.2: $ {t%?} Dla osób, które utknęły w CentOS / RHEL 6.x
Joey T

185

W wersji bash4.2 i nowszej możesz:

${var::-1}

Przykład:

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

Zauważ, że w przypadku starszych bash(na przykład bash 3.2.5w systemie OS X) należy pozostawić spacje między dwukropkami i po nich:

${var: : -1}

13
Działa to w bashwersji 4.2-alfa i wyższej, szkoda, że ​​wersja, do której mam dostęp, jest wcześniejsza. : - /
hjk

2
@iamaziz: Z dziennika zmian basha ujemna długość ${var:offset:lenght}została dodana tylko w bash 4.2. Może OSX doda własną łatkę bash.
cuonglm

1
@cuonglm none work: /
iamaziz

1
Nie działa na Macu.
shinzou,

1
MACsters, spójrz w dół na odpowiedź Russa
P i

67

do usuwania ostatnich nznaków z linii, która nie używa sedOR awk:

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

więc na przykład możesz usunąć ostatni znak, one characterużywając tego:

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

> lk

ze revstrony man:

OPIS
Narzędzie rev kopiuje określone pliki na standardowe wyjście, odwracając kolejność znaków w każdym wierszu. Jeśli nie określono żadnych plików, standardowe wejście jest odczytywane.

AKTUALIZACJA:

jeśli nie znasz długości łańcucha, spróbuj:

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

61

Używanie sed powinno być tak szybkie jak

sed 's/.$//'

Twoje pojedyncze echo jest wtedy echo ljk | sed 's/.$//'.
Korzystając z tego, łańcuch 1-liniowy może mieć dowolny rozmiar.


10
Zauważ, że w ogólnym przypadku nie usuwa ostatniego znaku ciągu , ale ostatni znak każdego wiersza ciągu .
Stéphane Chazelas

44

Kilka opcji w zależności od powłoki:

  • POSIX: t=${t%?}
  • Kres: 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 *?

Zauważ, że chociaż wszystkie powinny usuwać ostatni znak , niektóre implementacje (te, które nie obsługują znaków wielobajtowych) usuwają zamiast tego ostatni bajt (więc prawdopodobnie uszkodziłby ostatni znak, jeśli byłby wielobajtowy ).

exprWariant zakłada $tnie kończy się w więcej niż jeden znak nowej linii. Zwróci również niezerowy status wyjścia, jeśli powstały ciąg będzie 0( 000lub nawet -0z niektórymi implementacjami). Może również dawać nieoczekiwane wyniki, jeśli ciąg znaków zawiera nieprawidłowe znaki.


Ładne i dokładne! Ale ... Zakładam, że wszystkie te powłoki obsługują POSIX, więc każdy powinien po prostu użyć tego, aby był najbardziej przenośny. Również najmniejsza liczba postaci!
Russ

@Russ, t=${t%?}to nie Bourne, ale w dzisiejszych czasach raczej nie spotkasz powłoki Bourne'a. ${t%?}działa jednak we wszystkich pozostałych.
Stéphane Chazelas

Nie podano opcji skorupy ryby! Prawdopodobnie obecnie bardziej popularny niż ksh93 ...
rien333

@ rien333. Poczekam, aż interfejs się trochę ustabilizuje. fishjest w toku. 2.3.0, która wprowadziła stringwbudowaną wersję, nie została wydana w czasie pytań i odpowiedzi. W wersji, na której testuję, potrzebujesz string replace -r '(?s).\z' '' -- $t(i spodziewałbym się, że chcieliby to zmienić, powinni zmienić flagi, które przekazują do PCRE) lub bardziej skomplikowane. Słabo radzi sobie również z postaciami nowej linii i wiem, że planują to również zmienić.
Stéphane Chazelas,

Wybrano odpowiedź POSIX. potwierdził pracę nad Bash 3.2.57 (1)
Avindra Goolcharan

26

Najbardziej przenośna i najkrótsza odpowiedź to prawie na pewno:

${t%?}

Działa to w bash, sh, ash, dash, busybox / ash, zsh, ksh itp.

Działa przy użyciu old-schoolowego rozszerzenia parametrów powłoki. W szczególności %określa, aby usunąć najmniejszy zgodny sufiks parametru, tktóry pasuje do wzorca globalnego ?(tj. Dowolny znak).

Zobacz „Usuń najmniejszy wzór sufiksu” tutaj, aby uzyskać (znacznie) bardziej szczegółowe wyjaśnienie i dodatkowe tło. Zobacz także dokumentację swojej powłoki (np . man bash:) w „rozszerzaniu parametrów”.


Na marginesie, jeśli zamiast tego chcesz usunąć pierwszy znak, użyj go ${t#?}, ponieważ #dopasowania z przodu łańcucha (przedrostek) zamiast z tyłu (przyrostek).

Warto również zauważyć, że zarówno %i #mają %%i ##wersje, które pasują do najdłuższej wersji danego wzorca zamiast najkrótszej. Oba ${t%%?}i ${t##?}w tym przypadku zrobiłyby to samo, co ich pojedynczy operator (więc nie dodawaj niepotrzebnego dodatkowego znaku). Jest tak, ponieważ podany ?wzorzec pasuje tylko do jednego znaku. Wymieszaj *z kilkoma symbolami wieloznacznymi i rzeczy stają się bardziej interesujące z %%i ##.

Zrozumienie rozszerzeń parametrów, a przynajmniej wiedza o ich istnieniu i umiejętność ich wyszukiwania, jest niezwykle przydatna do pisania i deszyfrowania skryptów powłoki wielu smaków. Rozszerzenia parametrów często wyglądają jak tajemne voodoo dla wielu ludzi, ponieważ ... no cóż ... tajemnymi voodoo shellami (chociaż całkiem dobrze udokumentowane, jeśli wiesz, jak szukać „rozszerzenia parametrów”). Zdecydowanie dobrze jest mieć pasek narzędzi, gdy utkniesz w skorupie.


Krótkie i słodkie, działa na MacOS i Linux!
dbernard

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

Otrzymasz podciąg od 0 do długości łańcucha -1. Zauważ jednak, że to odejmowanie jest specyficzne dla bash i nie będzie działać na innych powłokach.

Na przykład dashnie jest w stanie nawet parsować

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

Na przykład w systemie Ubuntu /bin/shjestdash


15

Możesz także użyć headdo wydrukowania wszystkich znaków oprócz ostatniego.

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

Ale niestety niektóre wersje headnie zawierają wiodącej -opcji. Tak jest w przypadku headsystemu OS X.


5

Łatwo jest to zrobić, używając wyrażenia regularnego:

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

5

Niektóre udoskonalenia. Aby usunąć więcej niż jeden znak, możesz dodać wiele znaków zapytania. Na przykład, aby usunąć dwa ostatnie znaki ze zmiennej: $SRC_IP_MSGmożesz użyć:

SRC_IP_MSG=${SRC_IP_MSG%??}

4

Aby ukończyć niektóre możliwe zastosowania czystej bash:

#!/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/%\ /}'"

Pierwsza składnia pobiera podciąg z ciągu, składnia to Dla drugiego zauważ znak, który oznacza „z końca linii”, a składnia to
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}

A oto dwie krótsze formy wyżej wymienionych

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

Tutaj zauważ ponownie %znak oznaczający „Usuń (to znaczy zamień na”) najkrótszy dopasowany wzór (tutaj reprezentowany przez spację „\” z końca PARAMETRU - tutaj o nazwie STR


1

Jak możemy również użyć php w wierszu poleceń lub skryptów powłoki. Czasami jest przydatny do parsowania chirurgicznego.

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

Z lamówką:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.