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.