Rimuovi un prefisso / suffisso fisso da una stringa in Bash


485

Nel mio bashscript ho una stringa e il suo prefisso / suffisso. Devo rimuovere il prefisso / suffisso dalla stringa originale.

Ad esempio, supponiamo che io abbia i seguenti valori:

string="hello-world"
prefix="hell"
suffix="ld"

Come posso ottenere il seguente risultato?

result="o-wor"


14
Diffidare quando si collega alla cosiddetta Guida di scripting Advanced Bash; contiene una miscela di buoni consigli e terribili.
Tripleee

Risposte:


720
$ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo "${foo}"
o-wor

40
Ci sono anche ## e %%, che rimuovono il più possibile se $ prefix o $ suffix contengono caratteri jolly.
punti

28
C'è un modo per combinare i due in una riga? Ho provato ${${string#prefix}%suffix}ma non funziona.
static_rtti

28
@static_rtti No, sfortunatamente non è possibile nidificare la sostituzione dei parametri in questo modo. Lo so, è un peccato.
Adrian Frühwirth,

87
@ AdrianFrühwirth: tutta la lingua è un peccato, ma è così utile :)
static_rtti

8
Nvm, "sostituzione bash" in Google ha trovato quello che volevo.
Tyler,

89

Usando sed:

$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

All'interno del comando sed, il ^ carattere corrisponde al testo che inizia con $prefixe il finale $corrisponde al testo che termina con $suffix.

Adrian Frühwirth fa alcuni buoni punti nei commenti qui sotto, ma sed a questo scopo può essere molto utile. Il fatto che i contenuti di $ prefix e $ suffix siano interpretati da sed può essere buono o cattivo- finché si presta attenzione, si dovrebbe andare bene. Il bello è che puoi fare qualcosa del genere:

$ prefix='^.*ll'
$ suffix='ld$'
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

che può essere quello che vuoi ed è sia più elaborato che più potente della sostituzione della variabile bash. Se ricordi che da un grande potere derivano grandi responsabilità (come dice Spiderman), dovresti stare bene.

Una rapida introduzione a sed è disponibile all'indirizzo http://evc-cit.info/cit052/sed_tutorial.html

Una nota riguardante la shell e il suo uso delle stringhe:

Per il particolare esempio fornito, funzionerebbe anche:

$ echo $string | sed -e s/^$prefix// -e s/$suffix$//

... ma solo perché:

  1. a echo non importa quante stringhe ci sono nella sua lista di argomenti, e
  2. Non ci sono spazi in $ prefisso e $ suffisso

In genere è buona norma citare una stringa nella riga di comando perché anche se contiene spazi verrà presentato al comando come singolo argomento. Citiamo $ prefisso e $ suffisso per lo stesso motivo: ogni comando di modifica su sed verrà passato come una stringa. Utilizziamo virgolette doppie perché consentono l'interpolazione variabile; se avessimo usato virgolette singole, il comando sed sarebbe diventato letterale $prefixe $suffixquesto non è certamente quello che volevamo.

Notate anche il mio uso di virgolette singole durante l'impostazione delle variabili prefixe suffix. Certamente non vogliamo che nulla venga interpretato nelle stringhe, quindi le citiamo singolarmente in modo che non avvenga alcuna interpolazione. Ancora una volta, potrebbe non essere necessario in questo esempio, ma è un'abitudine molto buona per entrare.


8
Sfortunatamente, questo è un cattivo consiglio per diversi motivi: 1) Non quotato, $stringè soggetto a frazionamento e frantumazione di parole. 2) $prefixe $suffixpuò contenere espressioni che sedinterpretano, ad esempio espressioni regolari o il carattere utilizzato come delimitatore che interromperà l'intero comando. 3) sedNon è necessario chiamare due volte (è possibile -e 's///' -e '///'invece) e si potrebbe anche evitare la pipa. Ad esempio, considerare string='./ *'e / o prefix='./'e vederlo rompersi orribilmente a causa di 1)e 2).
Adrian Frühwirth,

Nota divertente: sed può prendere quasi qualsiasi cosa come delimitatore. Nel mio caso, poiché stavo analizzando le directory dei prefissi dai percorsi, non potevo usare /, quindi ho usato sed "s#^$prefix##invece. (Fragilità: i nomi dei file non possono contenere #. Dato che controllo i file, siamo al sicuro lì.)
Olie,

@Olie I nomi dei file possono contenere qualsiasi carattere tranne la barra e il carattere null, quindi a meno che tu non abbia il controllo non puoi assumere un nome file che non contenga determinati caratteri.
Adrian Frühwirth,

Sì, non so cosa stavo pensando lì. iOS forse? Boh. I nomi dei file possono certamente contenere "#". Non ho idea del perché l'ho detto. :)
Olie,

@Olie: Quando ho capito il tuo commento originale, stavi dicendo che la limitazione della tua scelta di usare #come delimitatore di sed significava che non potevi gestire i file contenenti quel personaggio.
P Daddy,

17

Conosci la lunghezza del prefisso e del suffisso? Nel tuo caso:

result=$(echo $string | cut -c5- | rev | cut -c3- | rev)

O più generale:

result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev)

Ma la soluzione di Adrian Frühwirth è davvero fantastica! Non lo sapevo!


14

Uso grep per rimuovere prefissi dai percorsi (che non sono gestiti bene da sed):

echo "$input" | grep -oP "^$prefix\K.*"

\K rimuove dalla partita tutti i personaggi precedenti.


grep -Pè un'estensione non standard. Più potenza per te se è supportato sulla tua piattaforma, ma questo è un dubbio consiglio se il tuo codice deve essere ragionevolmente portatile.
triplo

@tripleee Effettivamente. Ma penso che un sistema con GNU Bash installato abbia anche un grep che supporta PCRE.
Vladimir Petrakovich,

1
No, per esempio MacOS ha Bash pronto all'uso ma non GNU grep. Le versioni precedenti in realtà avevano l' -Popzione da BSD grepma l'hanno rimossa.
triplo

9
$ string="hello-world"
$ prefix="hell"
$ suffix="ld"

$ #remove "hell" from "hello-world" if "hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}

$ #remove "ld" from "o-world" if "ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor

Appunti:

# $ prefix: aggiungendo # si assicura che la sottostringa "hell" venga rimossa solo se si trova all'inizio. Suffisso% $: l'aggiunta di% assicura che la sottostringa "ld" venga rimossa solo se viene trovata alla fine.

Senza questi, le sottostringhe "inferno" e "ld" verranno rimosse ovunque, anche se si trova nel mezzo.


Grazie per le note! qq: nel tuo esempio di codice hai anche una barra in avanti /subito dopo la stringa, a cosa serve?
Diego Salazar,

1
/ separa la stringa corrente e la stringa secondaria. sotto-stringa qui è il suffisso nella domanda postata.
Vijay Vat,


6

Soluzione piccola e universale:

expr "$string" : "$prefix\(.*\)$suffix"

1
Se stai usando Bash, probabilmente non dovresti usare expraffatto. Era una sorta di comoda utility per lavello da cucina ai tempi della shell Bourne originale, ma ora è ben oltre la sua data di scadenza.
triplo

5

Utilizzando la risposta di @Adrian Frühwirth:

function strip {
    local STRING=${1#$"$2"}
    echo ${STRING%$"$2"}
}

usalo in questo modo

HELLO=":hello:"
HELLO=$(strip "$HELLO" ":")
echo $HELLO # hello

0

Vorrei utilizzare i gruppi di acquisizione in regex:

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ set +H # Disables history substitution, can be omitted in scripts.
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/" <<< $string
o-wor
$ string1=$string$string
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/g" <<< $string1
o-woro-wor

((?:(?!(${suffix})).)*)si assicura che il contenuto di ${suffix}verrà escluso dal gruppo di acquisizione. In termini di esempio, è la stringa equivalente a [^A-Z]*. Altrimenti otterrai:

$ perl -pe "s/${prefix}(.*)${suffix}/\1/g" <<< $string1
o-worldhello-wor
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.