Jak usunąć ostatnie n znaków z ciągu w Bash?


202

Mam zmienną varw skrypcie Bash zawierającą ciąg, na przykład:

echo $var
"some string.rtf"

Chcę usunąć ostatnie 4 znaki tego ciągu i przypisać wynik do nowej zmiennej var2, aby

echo $var2
"some string"

W jaki sposób mogę to zrobić?


Odpowiedzi:


15

Po pierwsze, powinieneś wyraźnie powiedzieć, czego chcesz. Jeśli wiesz, że ciąg się kończy .rtfi chcesz usunąć .rtf, możesz użyć var2=${var%.rtf}. Jeśli nie wiesz, co to jest przyrostek, ale chcesz usunąć wszystko, zaczynając od ostatniego ., możesz użyć var2=${var%.*}. Jeśli chcesz zachować wszystko na pierwszym miejscu ., możesz użyć var2=${var%%.*}. Te dwie ostatnie opcje mają ten sam wynik, jeśli jest tylko jeden okres, ale jeśli może być więcej niż jeden, powinieneś zdecydować, który chcesz.

Jeśli naprawdę chcesz zawsze usuwać dokładną liczbę znaków, oto kilka opcji.

Podałeś konkretnie bash, więc zaczniemy od wbudowanych bash. Najdłużej działała ta sama składnia usuwania sufiksów, której użyłem powyżej: aby usunąć cztery znaki, użyj var2=${var%????}. Musisz usunąć jeden znak zapytania na znak, więc staje się nieporęczny w przypadku większych długości podciągu.

Nieco nowszy jest podciąg ekstrakcji: var2=${var::${#var}-4}. Tutaj możesz wstawić dowolną liczbę zamiast, 4aby usunąć inną liczbę znaków. (Symbol ${#var}zastępowany jest długością łańcucha, więc w rzeczywistości prosi o zachowanie pierwszych (długości - 4) znaków).

Nowsze wersje bash (szczególnie 4+, co oznacza, że ​​ta dostarczana z MacOS nie będzie działać) pozwalają to uprościć var2=${var::-4}.

Jeśli tak naprawdę nie używasz bash, ale jakiejś innej powłoki typu POSIX, usuwanie sufiksu będzie nadal działać, nawet w zwykłym starym myślniku (gdzie żadna reszta nie będzie). W zsh, że wszystkie prace, ale trzeba umieścić 0między dwukropkiem: var2=${var:0:-4}itd. W ksh, potrzebny jest 0, a także mieć możliwość korzystania z wyraźnej długości 4 wyrażenie: var2=${var:0:${#var}-4}.

Możesz oczywiście użyć zastępowania poleceń, aby to zrobić za pomocą programu narzędziowego; jest wiele, które będą działać, ale coś takiego var2=$(cut -c -4 <<<"$var")jest prawdopodobnie najkrótszą opcją.


240

Możesz to zrobić w następujący sposób:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"

9
bash 4+ Myślę, że.
Etan Reisner,

69
Jest to bash4+, ale we wcześniejszej wersji możesz użyć nieco dłuższego ${v::${#v}-4}.
chepner

8
Nie zadziałało dla mnie, bash mówi „Zła substytucja”
Ivan Marjanovic

15
z jakiegoś powodu w zsh ${v::-4}wykrzykiwa „zsh: oczekiwany nawias zamykający”. Ale poniższa odpowiedź @fredtantini ${v:0:-4}działa dobrze.
Pierre D

6
Błąd z: -2: wyrażenie podłańcuchowe <0
Edward

172

Aby usunąć cztery znaki z końca ciągu, użyj ${var%????}.

Aby usunąć wszystko po ostatnim .użyciu ${var%.*}.


12
Odpowiedź w czystej powłoce i będzie działać w BASH 3.x, który można znaleźć w wielu systemach, które odmówiły wdrożenia BASH 4.x z powodu problemów licencyjnych.
David W.

1
Jest to fajne, ponieważ jest odporne na krótsze łańcuchy; warianty przesunięcia indeksu zawodzą.
Raphael

działa w bash 3.2.25 - najlepsza możliwa odpowiedź - tylko to, czego szukałem
capser

Doskonała odpowiedź. Co powiesz na usunięcie z przodu sznurka?
user2023370 24.0119

3
@ user2023370 Zapoznanie się z dokumentacją powinno ujawnić, że istnieje odpowiednie zastąpienie parametru ${var#????}.
tripleee

48

Dla mnie zadziałało:

echo "hello world" | rev | cut -c5- | rev
# hello w

Ale użyłem go do przycięcia linii w pliku, dlatego wygląda to niezręcznie. Rzeczywistym zastosowaniem było:

cat somefile | rev | cut -c5- | rev

cutprowadzi cię tylko do przycinania z pewnej pozycji początkowej, co jest złe, jeśli potrzebujesz rzędów o zmiennej długości. To rozwiązanie odwraca ( rev) ciąg znaków i teraz odnosimy się do jego końcowej pozycji, a następnie używa, cutjak wspomniano, i odwraca (ponownie rev) do pierwotnej kolejności.


To nie jest poprawne i nie jest odpowiedzią na to, o co się pyta. revodwraca wiersze, a nie ciągi znaków. echo $SOME_VAR | rev | ...prawdopodobnie nie zachowa się tak, jak można by się spodziewać.
Mohammad Nasirifar,

2
@Mohammad Z powodu broken cytowanie, który jest w jednej linii.
tripleee

38

Korzystanie ze zmiennego rozszerzenia / zamiany podciągów :

$ {var /% Pattern / Replacement}

Jeśli sufiks var pasuje do Wzorca, zamień Zastąp na Wzorzec.

Możesz więc zrobić:

~$ echo ${var/%????/}
some string

Alternatywnie,

Jeśli masz zawsze te same 4 litery

~$ echo ${var/.rtf/}
some string

Jeśli zawsze kończy się na .xyz:

~$ echo ${var%.*}
some string

Możesz także użyć długości ciągu:

~$ len=${#var}
~$ echo ${var::len-4}
some string

lub po prostu echo ${var::-4}


len=${#var}; echo ${var::len-4}można skrócić do echo ${var:0:-4}:-) EDYCJA: lub jak zauważył @iyonizer, po prostu echo ${var::-4}...
anishsane

26

Możesz użyć sed,

sed 's/.\{4\}$//' <<< "$var"

Przykład:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string

1
i jak przypisać wynik var2?
becko

to dobrze, ponieważ działa na jednej linii jak ta ... | sed 's /. \ {4 \} $ //'. Można go używać bez używania zmiennych
Sam

3

Wypróbowałem następujące i zadziałało to dla mnie:

#! /bin/bash

var="hello.c"
length=${#var}
endindex=$(expr $length - 4)
echo ${var:0:$endindex}

Wynik: hel


1

Mam nadzieję, że poniższy przykład pomoże,

echo ${name:0:$((${#name}-10))} -> ${name:start:len}

  • W powyższym poleceniu nazwa jest zmienną.
  • start jest punktem początkowym łańcucha
  • len to długość ciągu, który należy usunąć.

Przykład:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}

Wynik:

    Enter:Siddharth Murugan
    Siddhar

Uwaga: Bash 4.2 dodał obsługę ujemnego podłańcucha


Jest to o wiele bardziej szczegółowe niż proste zastępowanie wzorów zgodne z Bourne, jak w odpowiedzi Etana Reisnera.
tripleee

0

To działało dla mnie, obliczając rozmiar łańcucha.
Łatwo jest powtórzyć wartość, którą chcesz zwrócić, a następnie zapisać ją jak poniżej

removechars(){
        var="some string.rtf"
        size=${#var}
        echo ${var:0:size-4}  
    }
    removechars
    var2=$?

trochę sznurka


0

W takim przypadku możesz użyć basename przy założeniu, że masz taki sam przyrostek na plikach, które chcesz usunąć.

Przykład:

basename -s .rtf "some string.rtf"

Zwróci to „jakiś ciąg”

Jeśli nie znasz przyrostka i chcesz usunąć wszystko po ostatniej kropce włącznie:

f=file.whateverthisis
basename "${f%.*}"

wypisuje „plik”

% oznacza chop,. to, co siekasz, * to symbol wieloznaczny

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.