Question

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

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

Comment puis-je échapper à la chaîne $ REPLACE afin qu'elle soit acceptée sans risque par sed en tant que remplacement littéral?

REMARQUE: le KEYWORD est une sous-chaîne muette sans correspondance, etc. Elle n'est pas fournie par l'utilisateur.

Était-ce utile?

La solution

Avertissement : cela n'envisage pas de nouvelles lignes. Pour une réponse plus détaillée, voir cette SO-question à la place. (Merci, Ed Morton et Niklas Peter)

Notez que tout échapper est une mauvaise idée. Sed a besoin de beaucoup de caractères pour être échappé afin de tirer son sens particulier. Par exemple, si vous échappez un chiffre dans la chaîne de remplacement, il se transformera en une référence arrière.

Comme l'a dit Ben Blank, il ne faut échapper que trois caractères dans la chaîne de remplacement (ils s'échappent, une barre oblique pour la fin de l'instruction et pour tout remplacer):

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

Si vous avez besoin d'échapper à la chaîne KEYWORD , voici ce dont vous avez besoin:

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

N'oubliez pas que si vous utilisez un caractère autre que / comme délimiteur, vous devez remplacer la barre oblique dans les expressions ci-dessus par le caractère que vous utilisez. Voir le commentaire de PeterJCLaw pour des explications.

Modifié: En raison de certains cas particuliers jusqu'ici non pris en compte, les commandes ci-dessus ont été modifiées à plusieurs reprises. Consultez l'historique des modifications pour plus de détails.

Autres conseils

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 guillemets ne posent pas de problème.

Les trois seuls caractères littéraux traités spécialement dans la clause de remplacement sont / (pour fermer la clause), \ (pour les caractères d'échappement, la référence arrière, & amp; c .) et & amp; (pour inclure la correspondance dans le remplacement). Par conséquent, il vous suffit d'échapper à ces trois personnages:

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

Sur la base des expressions régulières de Pianosaurus, j'ai créé une fonction bash qui échappe à la fois au mot clé et au remplacement.

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

Voici comment vous l'utilisez:

sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf

Il est un peu tard pour répondre ... mais il existe un moyen beaucoup plus simple de procéder. Il suffit de changer le délimiteur (c'est-à-dire le caractère qui sépare les champs). Ainsi, 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 vilaine clause DEFINER.

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

J'ai eu la même question & amp; fait la même erreur. Si vous utilisez bash, vous n'avez pas besoin d'utiliser sed pour effectuer des remplacements de chaîne (et il est beaucoup beaucoup plus propre d'utiliser la fonctionnalité de remplacement intégrée à 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 exclusivement les fonctionnalités bash:

INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"

Utilisez awk, c’est plus propre:

$ awk -v R='//addr:\\file' '{ sub("THIS", R, <*>); print <*> }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare

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

ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds

Cela semble excessif, mais d’une manière ou d’une autre, cette combinaison de guillemets permet de conserver l’imprimé en tant que littéral. Ensuite, si je me souviens bien, les vaiables sont simplement entourées de guillemets comme celui-ci: "$ 1". Essayez-le, laissez-moi savoir comment cela fonctionne avec SED.

J'ai une amélioration par rapport à la fonction sedeasy qui rompra avec des caractères spéciaux tels que tabulation.

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

Alors, qu'est-ce qui est différent? $ 1 et $ 2 entourés de guillemets pour éviter les extensions du shell et préserver les tabulations ou les doubles espaces.

Tuyauterie supplémentaire | sed -e 's: \ t: \\ t: g' (j'aime : comme marque) qui transforme un onglet en \ t .

n'oubliez pas tout le plaisir que procure la limitation de shell 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"

Si le cas échéant, vous générez un mot de passe aléatoire à transmettre au modèle sed , vous choisissez de faire attention au jeu de caractères de la chaîne aléatoire. Si vous choisissez un mot de passe créé en codant une valeur en base64, seul le caractère possible en base64 est également un caractère spécial dans le modèle sed replace. 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'`;

Si vous souhaitez simplement remplacer la valeur de variable dans la commande sed, supprimez-le. Exemple:

sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test

Une méthode plus simple consiste simplement à créer une chaîne et à l’utiliser comme paramètre pour sed

.
rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring  test.txt
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top