Sostituisci una sottostringa con un'altra stringa nello script della shell


823

Ho "Adoro Suzi e Marry" e voglio cambiare "Suzi" in "Sara".

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
# do something...

Il risultato deve essere così:

firstString="I love Sara and Marry"

18
Inizierei deludendo delicatamente Suzi. :)
codesniffer il

Non sei tu sono io ! quella avrebbe dovuto essere la stringa per parlare con Suzi
GoodSp33d

Risposte:


1360

Per sostituire la prima occorrenza di un modello con una determinata stringa, utilizzare :${parameter/pattern/string}

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/$secondString}"    
# prints 'I love Sara and Marry'

Per sostituire tutte le occorrenze, utilizzare :${parameter//pattern/string}

message='The secret code is 12345'
echo "${message//[0-9]/X}"           
# prints 'The secret code is XXXXX'

(Questo è documentato nel l' Bash Reference Manual , §3.5.3 "Shell parametri di espansione" .)

Nota che questa funzione non è specificata da POSIX - è un'estensione Bash - quindi non tutte le shell Unix la implementano. Per la documentazione POSIX pertinente, consultare Le specifiche di base dello standard tecnico Open Group, Numero 7 , il volume Shell & Utilities , §2.6.2 "Espansione dei parametri" .


3
@ruakh come posso scrivere questa dichiarazione con una o condizione. Solo se voglio sostituire Suzi o Marry con una nuova stringa.
Priyatham51,

3
@ Priyatham51: non esiste una funzione integrata per questo. Sostituisci solo uno, poi l'altro.
Ruakh,

4
@ Bu: No, perché \nin quel contesto rappresenterebbe se stesso, non una nuova riga. Non ho Bash a portata di mano in questo momento di prova, ma si dovrebbe essere in grado di scrivere qualcosa di simile, $STRING="${STRING/$'\n'/<br />}". (Anche se probabilmente vuoi STRING//- sostituisci tutto - invece di solo STRING/.)
Ruakh

25
Per essere chiari, poiché questo mi ha confuso per un po ', la prima parte deve essere un riferimento variabile. Non si può fare echo ${my string foo/foo/bar}. Avresti bisognoinput="my string foo"; echo ${input/foo/bar}
Henrik N,

2
@mgutt: questa risposta si collega alla relativa documentazione. La documentazione non è perfetta - per esempio, invece di menzionare //direttamente, menziona cosa succede "Se il modello inizia con ' /'" (che non è nemmeno accurato, poiché il resto di quella frase presuppone che il extra /non sia effettivamente parte di lo schema) - ma non conosco alcuna fonte migliore.
ruakh,

208

Questo può essere fatto interamente con la manipolazione della stringa bash:

first="I love Suzy and Mary"
second="Sara"
first=${first/Suzy/$second}

Ciò sostituirà solo la prima occorrenza; per sostituirli tutti, raddoppia la prima barra:

first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now "Sara, Sara, Sara"

9
Qual è il punto di avere una risposta che duplica quella accettata, invece di modificare quella accettata con l'opzione di sostituzione globale? E aggiungendo che regexps sono supportati, mentre a questo. E spiegando che succede con "Sostituzione errata". Hai abbastanza punti, @Kevin :)
Dan Dascalescu,

6
Sembra che entrambi abbiano risposto esattamente nello stesso minuto: O
Jamie Hutber,

76

Per Dash tutti i post precedenti non funzionano

La shsoluzione compatibile POSIX è:

result=$(echo "$firstString" | sed "s/Suzi/$secondString/")

1
Ho ottenuto questo: $ echo $ (echo $ firstString | sed 's / Suzi / $ secondString / g') Adoro $ secondString e Marry
Qstnr_La

2
@Qstnr_La usa le virgolette doppie per la sostituzione delle variabili:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
emc

1
Più 1 per mostrare come eseguire l'output anche su una variabile. Grazie!
Chef Faraone,

2
Ho corretto le virgolette singole e ho anche aggiunto le virgolette mancanti attorno echoall'argomento. Funziona ingannevolmente senza virgolette con stringhe semplici, ma si rompe facilmente su qualsiasi stringa di input non banale (spaziatura irregolare, metacaratteri shell, ecc.).
tripla il

In sh (AWS Codebuild / Ubuntu sh) ho scoperto che alla fine ho bisogno di una singola barra, non di una doppia. Ho intenzione di modificare il commento poiché i commenti sopra mostrano anche una singola barra.
Tim

67

prova questo:

 sed "s/Suzi/$secondString/g" <<<"$firstString"

19
In realtà non hai bisogno di Sed per questo; Bash supporta questo tipo di sostituzione in modo nativo.
Ruakh,

12
Immagino che questo sia etichettato "bash" ma è venuto qui perché aveva bisogno di qualcosa di semplice per un'altra shell. Questa è una bella alternativa succinta a ciò che wiki.ubuntu.com/… ha fatto sembrare che avrei bisogno.
natevw,

3
Questo funziona alla grande per ash / dash o qualsiasi altro sh POSIX.
Yoshua Wuyts,

2
Ottengo l'errore sed: -e espressione # 1, carattere 9: opzione sconosciuta a `s
Nam G VU

1
Prima risposta che funziona con stdin, ottimo per stringhe di pipelining.
Dinei,

46

È meglio usare bash che sedse le stringhe hanno caratteri RegExp.

echo ${first_string/Suzi/$second_string}

È portatile per Windows e funziona con almeno almeno Bash 3.1.

Per mostrare che non devi preoccuparti molto dell'evasione, cambiamo questo:

/home/name/foo/bar

In questo:

~/foo/bar

Ma solo se /home/nameè all'inizio. Non abbiamo bisogno sed!

Dato che bash ci fornisce variabili magiche $PWDe $HOMEpossiamo:

echo "${PWD/#$HOME/\~}"

EDIT: Grazie per Mark Haferkamp nei commenti per la nota sulla citazione / fuga ~. *

Nota come la variabile $HOMEcontiene barre, ma ciò non ha interrotto nulla.

Ulteriori letture: Guida avanzata agli script di Bash .
Se l'utilizzo sedè obbligatorio, assicurati di sfuggire a tutti i personaggi .


Questa risposta mi ha impedito di utilizzare sedil pwdcomando per evitare di definire una nuova variabile ogni volta che la mia $PS1esecuzione personalizzata . Bash fornisce un modo più generale delle variabili magiche per utilizzare l'output di un comando per la sostituzione di stringhe? Per quanto riguarda il tuo codice, ho dovuto sfuggire a ~per evitare che Bash lo espandesse in $ HOME. Inoltre, cosa fa il #comando al comando?
Mark Haferkamp,

1
@MarkHaferkamp Vedi questo dal link "ulteriori letture consigliate". Informazioni su "escape the ~": nota come ho citato cose. Ricorda di citare sempre cose! E questo non funziona solo per le variabili magiche: qualsiasi variabile è in grado di sostituire, ottenere la lunghezza della stringa e altro, entro bash. Congratulazioni per aver provato al tuo $PS1digiuno: potresti anche essere interessato a $PROMPT_COMMANDse ti senti più a tuo agio in un altro linguaggio di programmazione e vuoi codificare un prompt compilato.
Camilo Martin,

Il link "ulteriore lettura" spiega "#". Su Bash 4.3.30, echo "${PWD/#$HOME/~}"non sostituisco il mio $HOMEcon ~. Sostituzione ~con \~o '~'funziona. Ognuno di questi lavori su Bash 4.2.53 su un'altra distro. Puoi aggiornare il tuo post per citare o scappare ~per una migliore compatibilità? Quello che intendevo con la mia domanda sulle "variabili magiche" era: posso usare la sostituzione delle variabili di Bash, ad esempio, sull'output unamesenza prima salvarlo come variabile? Per quanto riguarda il mio personale $PROMPT_COMMAND, è complicato .
Mark Haferkamp,

@MarkHaferkamp Whoa, hai perfettamente ragione, mia cattiva. Aggiornerà la risposta ora.
Camilo Martin,

1
@MarkHaferkamp Bash e le sue oscure insidie ​​...: P
Camilo Martin

19

Se domani decidi di non amare Marry, anche lei può essere sostituita:

today=$(</tmp/lovers.txt)
tomorrow="${today//Suzi/Sara}"
echo "${tomorrow//Marry/Jesica}" > /tmp/lovers.txt

Ci devono essere 50 modi per lasciare il tuo amante.


9

Dal momento che non posso aggiungere un commento. @ruaka Per rendere l'esempio più leggibile scrivilo in questo modo

full_string="I love Suzy and Mary"
search_string="Suzy"
replace_string="Sara"
my_string=${full_string/$search_string/$replace_string}
or
my_string=${full_string/Suzy/Sarah}

Finché non sono arrivato al tuo esempio, avevo capito l'ordine al contrario. Ciò ha contribuito a chiarire cosa sta succedendo
raghav710

5

Pure POSIX metodo di conchiglia, che a differenza di Romano Kazanovskyi 's sedrisposta basata su non ha bisogno di strumenti esterni, a pochi propri nativi della shell espansioni di parametri . Si noti che i nomi di file lunghi sono ridotti a icona in modo che il codice si adatti meglio su una riga:

f="I love Suzi and Marry"
s=Sara
t=Suzi
[ "${f%$t*}" != "$f" ] && f="${f%$t*}$s${f#*$t}"
echo "$f"

Produzione:

I love Sara and Marry

Come funziona:

  • Rimuovi il modello di suffisso più piccolo. "${f%$t*}"restituisce " I love" se il suffisso $t" Suzi*" è in $f" ". I love Suzi and Marry

  • Ma if t=Zelda, quindi "${f%$t*}"non elimina nulla e restituisce l'intera stringa " I love Suzi and Marry".

  • Questo è usato per verificare se $tè dentro $fcon [ "${f%$t*}" != "$f" ]cui valuterà vero se la $fstringa contiene " Suzi*" e falso in caso contrario.

  • Se il test restituisce true , costruisci la stringa desiderata utilizzando Rimuovi modello suffisso più piccolo ${f%$t*} " I love" e Rimuovi modello prefisso più piccolo ${f#*$t} " and Marry", con la seconda stringa $s" Sara" in mezzo.


5

So che questo è vecchio ma dal momento che nessuno ha menzionato l'uso di awk:

    firstString="I love Suzi and Marry"
    echo $firstString | awk '{gsub("Suzi","Sara"); print}'

1
Questo trucco è la strada da percorrere se ciò che stai cercando di dividere non è in realtà in una variabile ma in un file di testo. Grazie, @Payam!
Felipe SS Schneider,

2
echo [string] | sed "s/[original]/[target]/g"
  • "s" significa "sostituto"
  • "g" significa "globale, tutte le occorrenze corrispondenti"
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.