Escape une chaîne pour un modèle de remplacement sed

dans mon script bash j'ai une chaîne externe (reçue de l'utilisateur), que je devrais utiliser dans le modèle sed.

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

Comment puis-je échapper à la chaîne $REPLACE pour qu'elle soit acceptée en toute sécurité par sed comme un remplacement littéral?

NOTE: le KEYWORD est un substrat muet sans correspondance etc. Il n'est pas fourni par l'utilisateur.

254
demandé sur codeforester 2009-01-02 20:44:15

14 réponses

Warning : ceci fait et non considèrent newlines. Pour une réponse plus détaillée, voir cette SO-question à la place. (Merci, Ed Morton & Niklas Pierre)

notez que tout échapper est une mauvaise idée. Sed a besoin de beaucoup de caractères pour être échappé à obtenir leur signification particulière. Par exemple, si vous échappez à un chiffre de la chaîne de remplacement, celui-ci se retournera vers un backreference.

comme Ben Blank l'a dit, il n'y a que trois caractères qui doivent être échappés dans la chaîne de remplacement (échappés eux-mêmes, barre oblique avant pour la fin de la déclaration et & pour remplacer tous):

sed -e 's/[\/&]/\&/g'

si vous avez besoin d'échapper à la chaîne KEYWORD , la suivante est celle dont vous avez besoin:

sed -e 's/[]\/$*.^[]/\&/g'

rappelez-vous, si vous utilisez un caractère autre que / comme délimiteur, vous devez remplacer la barre oblique dans le expressions ci-dessus avec le caractère que vous utilisez. Voir le commentaire de PeterJCLaw pour une explication.

modifié: en raison de certains cas de coin qui n'ont pas été pris en compte auparavant, les commandes ci-dessus ont changé plusieurs fois. Consultez l'historique d'édition pour plus de détails.

222
répondu Pianosaurus 2017-12-11 16:16:39

la commande sed vous permet d'utiliser d'autres caractères au lieu de / comme séparateur:

sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'

les doubles guillemets ne sont pas un problème.

62
répondu scre_www 2016-06-12 18:31:59

les trois seuls caractères littéraux qui font l'objet d'un traitement spécial dans la clause de remplacement sont / (pour fermer la clause), \ (pour échapper aux caractères, rétroréférence, etc.), et & (pour inclure la correspondance dans le remplacement). Par conséquent, tout ce que vous devez faire est d'échapper à ces trois caractères:

sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\/\\/g; s/\//\\//g; s/&/\\&/g')/g"

exemple:

$ export REPLACE="'\"|\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\/\\/g; s/\//\\//g; s/&/\\&/g')/g"
foo'"|\/><&!bar
37
répondu Ben Blank 2016-06-01 02:14:06

basé sur les expressions régulières du Pianosaurus, j'ai créé une fonction de bash qui échappe à la fois au mot-clé et au remplacement.

function sedeasy {
  sed -i "s/$(echo  | sed -e 's/\([[\/.*]\|\]\)/\&/g')/$(echo  | sed -e 's/[\/&]/\&/g')/g" 
}

Voici comment vous l'utilisez:

sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf
28
répondu Gurpartap Singh 2012-05-06 01:46:06

il est un peu tard pour répondre... mais il y a une façon beaucoup plus simple de le faire. Il suffit de changer le délimiteur (i.e., le caractère qui sépare les champs). Donc, au lieu de s/foo/bar/ vous écrivez s|bar|foo .

et, voici le moyen facile de le faire:

sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'

la sortie résultante est dépourvue de cette clause de définition désagréable.

12
répondu user2460464 2015-06-09 10:53:44

il s'avère que vous posez la mauvaise question. J'ai aussi posé la mauvaise question. La raison pour laquelle il est erroné est le début de la première phrase: "dans mon bash script...".

j'ai eu la même question et fait la même erreur. Si vous utilisez bash, vous n'avez pas besoin d'utiliser sed pour effectuer des remplacements de chaînes (et c'est le nettoyeur much pour utiliser la fonctionnalité de remplacement intégrée dans bash).

au lieu de quelque chose comme, par exemple:

function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A="$(escape-all-funny-characters 'KEYWORD')"
B="$(escape-all-funny-characters '<funny characters here>')"
OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"

vous pouvez utiliser les fonctionnalités de bash exclusivement:

INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"
8
répondu destenson 2016-08-18 18:07:27

Utiliser awk - c'est plus propre:

$ awk -v R='//addr:\file' '{ sub("THIS", R, "151900920"); print "151900920" }' <<< "http://file:\_THIS_/path/to/a/file\is\\a\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\is\\a\ nightmare
3
répondu greggster 2015-06-09 10:54:37

voici un exemple d'un AWK que j'ai utilisé il y a un certain temps. C'est un AWK qui imprime de nouveaux AWKS. AWK et SED étant similaires, il peut être un bon modèle.

ls | awk '{ print "awk " "'"'"'"  " {print ,,} " "'"'"'"  " "  ".old_ext > "  ".new_ext"  }' > for_the_birds

il semble excessif, mais d'une certaine façon cette combinaison de citations fonctionne pour garder le ' imprimé comme littérales. Alors, si je me souviens bien, les vaiables sont juste entourées de citations comme celle-ci: "$1". Essayez, dites-moi comment ça marche avec SED.

1
répondu Alex 2009-01-02 17:58:37

j'ai une amélioration par rapport à la fonction sedeasy, qui va rompre avec les caractères spéciaux comme tab.

function sedeasy_improved {
    sed -i "s/$(
        echo "" | sed -e 's/\([[\/.*]\|\]\)/\&/g' 
            | sed -e 's:\t:\t:g'
    )/$(
        echo "" | sed -e 's/[\/&]/\&/g' 
            | sed -e 's:\t:\t:g'
    )/g" ""
}

alors, qu'est-ce qui est différent? et enveloppés dans des guillemets pour éviter les agrandissements de la coque et préserver les onglets ou les espaces doubles.

tuyauterie supplémentaire | sed -e 's:\t:\t:g' (j'aime : comme jeton) qui transforme un onglet dans \t .

1
répondu Francisco De Zuviria 2017-09-30 21:27:06

si le cas se trouve que vous générez un mot de passe aléatoire pour passer à sed remplacer le motif, alors vous choisissez d'être prudent sur quel ensemble de caractères dans la chaîne aléatoire. Si vous choisissez un mot de passe fait en encodant une valeur comme base64, alors il n'y a que le caractère is qui est à la fois possible dans base64 et est aussi un caractère spécial dans sed remplacer le motif. Ce caractère est "/", et est facilement supprimé du mot de passe que vous générez:

# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;
0
répondu Mark Stosberg 2016-06-01 02:24:35

Juste d'échapper à tout dans le REMPLACER varible:

echo $REPLACE | awk '{gsub(".", "\\&");print}'
-1
répondu PEZ 2009-01-02 19:03:02

n'oubliez pas tout le plaisir qui se produit avec la limitation de la coque autour de " et

so (en ksh)

Var=">New version of \"content' here <"
printf "%s" "${Var}" | sed "s/[&\/\\*\"']/\&/g' | read -r EscVar

echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"
-1
répondu NeronLeVelu 2013-11-22 15:29:11

si vous cherchez juste à remplacer la valeur de la Variable dans la commande sed alors il suffit de supprimer Exemple:

sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test
-1
répondu shailender chauhan 2017-09-01 06:38:06

une façon plus facile de faire cela est tout simplement de construire la chaîne avant main et de l'utiliser comme paramètre pour sed

rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring  test.txt
-2
répondu Javonne Martin 2017-10-06 06:54:20