La commande sed avec l'option -i échoue sur Mac, mais fonctionne sur Linux

303

J'ai utilisé avec succès la sedcommande suivante pour rechercher / remplacer du texte sous Linux:

sed -i 's/old_link/new_link/g' *

Cependant, lorsque je l'essaye sur mon Mac OS X, j'obtiens:

"la commande c attend \ suivie du texte"

Je pensais que mon Mac exécute un shell BASH normal. Quoi de neuf?

ÉDITER:

Selon @High Performance, cela est dû au fait que Mac sedest d'une saveur différente (BSD), donc ma question serait donc de savoir comment répliquer cette commande dans BSD sed?

ÉDITER:

Voici un exemple réel qui provoque cela:

sed -i 's/hello/gbye/g' *
Yarin
la source
1
Cela signifie que sedvoit un "c" dans vos données comme une commande. Utilisez-vous une variable? Veuillez publier quelque chose qui représente de plus près la commande réelle et certaines données que vous traitez. Vous pouvez obtenir une démonstration simple de cette erreur en faisant echo x | sed c.
pause jusqu'à nouvel ordre.
@Dennis, la simple commande ci-dessus provoque cela, bien que les données qu'elles traitent soient un site Web entier (je convertis tous les liens d'images), y compris les fichiers html et css ...
Yarin

Réponses:

397

Si vous utilisez l' -ioption, vous devez fournir une extension pour vos sauvegardes.

Si tu as:

File1.txt
File2.cfg

La commande (notez le manque d'espace entre -iet ''et -epour le faire fonctionner sur les nouvelles versions de Mac et sur GNU):

sed -i'.original' -e 's/old_link/new_link/g' *

Créez 2 fichiers de sauvegarde comme:

File1.txt.original
File2.cfg.original

Il n'y a aucun moyen portable d'éviter de créer des fichiers de sauvegarde car il est impossible de trouver un mélange de commandes sed qui fonctionne dans tous les cas:

  • sed -i -e ...- ne fonctionne pas sur OS X car il crée des -esauvegardes
  • sed -i'' -e ... - ne fonctionne pas sur OS X 10.6 mais fonctionne sur 10.9+
  • sed -i '' -e ... - ne fonctionne pas sur GNU

Remarque Étant donné qu'aucune commande sed ne fonctionne sur toutes les plates-formes, vous pouvez essayer d'utiliser une autre commande pour obtenir le même résultat.

Par exemple, perl -i -pe's/old_link/new_link/g' *

Sinetris
la source
4
J'ai eu le même problème. Merci pour cette solution. Mais là où j'ai essayé avec 'man sed' de trouver la description de '-i', rien sur l'utilisation de -i '' pour ignorer les sauvegardes n'est là. Ceci est mon premier blâme. Deuxièmement, lorsque l'erreur "commande attend \ suivie de texte" apparaît, pourquoi ne nous dit-elle pas directement qu'elle attend un nom de sauvegarde pour l'option '-i' !! ?? Une telle chose se produit partout: vous obtenez une erreur mais pas pourquoi l'erreur, puis vous recherchez le manuel qui n'explique rien à ce sujet. Ensuite, vous le google pour trouver quelqu'un d'autre a également le même problème. Je veux dire, pourquoi ne pas donner d'exemple dans le manuel?
lukmac
ou dites-nous pourquoi l'erreur est là au lieu d'un simple message sans information indiquant qu'une erreur se produit. Ceci est une suggestion à tous les fabricants d'outils, s'ils peuvent lire ce commentaire.
lukmac
5
@lukmac Autant que je sache sed, vous avez fourni un suffixe de sauvegarde. Le suffixe de sauvegarde est s/old_link/new_link/g. L'argument suivant est censé être les commandes d'édition. Parce qu'il a interprété les commandes comme le nom de sauvegarde, il a ensuite pris le premier nom de fichier comme commandes d'édition, mais elles n'étaient pas valides.
Barmar
2
Est-ce que ce sera toujours un problème? Existe-t-il un moyen pour Apple de créer une solution de contournement / package GNU sed avec OSX? Ou, GNU sed ne pouvait-il pas le supporter sed -i '' -e ...?
blong
5
sed -i'' -esemble ne pas fonctionner comme prévu sur mac 10.14
MoOx
64

Je crois que sur OS X lorsque vous utilisez -i, une extension pour les fichiers de sauvegarde est requise . Essayer:

sed -i .bak 's/hello/gbye/g' *

En utilisant GNU, sedl'extension est facultative .

En pause jusqu'à nouvel ordre.
la source
55

Cela fonctionne avec les versions GNU et BSD de sed:

sed -i'' -e 's/old_link/new_link/g' *

ou avec sauvegarde:

sed -i'.bak' -e 's/old_link/new_link/g' *

Notez l'espace manquant après l' -ioption! (Nécessaire pour GNU sed)

chipiik
la source
7
Le premier ne fonctionne pas sur OSX (je viens de le tester sur 10.6.8)
marcin
4
Pour moi sur OS X (10.10.3), le premier a créé des fichiers de sauvegarde suffixés avec -e. Pas bien. Le second était la seule chose qui fonctionnait pour moi de manière cohérente entre Ubuntu et OS X. Je ne voulais pas de fichiers de sauvegarde cependant, j'ai donc dû exécuter une rmcommande juste après pour le supprimer.
Brendan
1
La première ligne devrait être réécrite avec un espace pour travailler le 10.10: sed -i'' ...=>sed -i '' ...
Daniel Jomphe
3
@DanielJomphe Mais l'ajout de cet espace ne fonctionne pas sur GNU sed
Brice
1
@marcin premier fonctionne aussi pour moi sur OSX 10.11.2. La seule chose est qu'il crée un fichier de sauvegarde, qui doit être supprimé par la suite. La seconde semble meilleure, car nous n'avons pas besoin de comprendre le nom de fichier plus tard.
vikas027
41

A eu le même problème sous Mac et l'a résolu avec brew:

brew install gnu-sed

et utiliser comme

gsed SED_COMMAND

vous pouvez définir aussi bien sedque l'alias gsed(si vous le souhaitez):

alias sed=gsed
Sruthi Poddutur
la source
3
Pourquoi avez-vous donné exactement la même réponse que Ohad Kravchick ?
gniourf_gniourf
1
définir un alias comme celui-ci n'est pas une bonne idée
SantaXL
1
Au lieu d'un alias, comme recommandé dans la page de brassage correspondante, ajoutez-le au chemin: PATH = "$ (brew --prefix) / opt / gnu-sed / libexec / gnubin: $ PATH"
y.luis
24

Ou, vous pouvez installer la version GNU de sed dans votre Mac, appelée gsed, et l'utiliser en utilisant la syntaxe Linux standard.

Pour cela, installez en gsedutilisant les ports (si vous ne l'avez pas, obtenez-le sur http://www.macports.org/ ) en exécutant sudo port install gsed. Ensuite, vous pouvez exécutersed -i 's/old_link/new_link/g' *

Ohad Kravchick
la source
24
.. ou si vous utilisez homebrew, installezgnu-sed
Sudar
1
Merci @Sudar, double pouce!
Andrew Swan
9

Votre Mac exécute en effet un shell BASH, mais c'est plutôt une question d'implémentation de sed avec laquelle vous avez affaire. Sur un Mac sed vient de BSD et est subtilement différent de sed que vous pourriez trouver sur une boîte Linux typique. Je vous propose man sed.

Marque de haute performance
la source
3
Merci d'avoir signalé le problème BSD - mais je suis assez analphabète et j'ai juste besoin d'une solution rapide pour ma commande - un coup d'œil rapide sur l'homme ne me dit rien
Yarin
5
@Yarin - non, et si je jette un rapide coup d'œil à l'homme sed, cela ne vous dit rien non plus. Essayez un regard plus long.
High Performance Mark
21
La plupart des réponses SO sont enterrées quelque part chez un homme, mais c'est à cela que s'adressent les personnes occupées qui ont besoin de réponses de personnes intelligentes
Yarin
9

La réponse de Sinetris est juste, mais je l'utilise avec la findcommande pour être plus précis sur les fichiers que je souhaite modifier. En général, cela devrait fonctionner (testé sur osx /bin/bash):

find . -name "*.smth" -exec sed -i '' 's/text1/text2/g' {} \;

En général, l'utilisation de sedsans finddans des projets complexes est moins efficace.

Lucas
la source
-execest très beau! Je me demande simplement si la barre oblique à la fin est réellement nécessaire
Ye Liu
2
@YeLiu: sans le \, le ;obtenu interprété par le shell lorsque j'ai essayé un tel
serv-inc
2

J'ai créé une fonction pour gérer la seddifférence entre MacOS (testé sur MacOS 10.12) et d'autres OS:

OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() {
    if [ "$OS" = 'Darwin' ]; then
        # for MacOS
        sed -i '' -e "$1" "$2"
    else
        # for Linux and Windows
        sed -i'' -e "$1" "$2"
    fi
}

Usage:

$(replace_in_file 's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' "./mysql/.env")

Où:

, est un délimiteur

's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' est un modèle

"./mysql/.env" est le chemin vers le fichier

v.babak
la source
1

Voici comment appliquer des variables d'environnement au fichier modèle (pas besoin de sauvegarde).

1. Créez un modèle avec {{FOO}} pour un remplacement ultérieur.

echo "Hello {{FOO}}" > foo.conf.tmpl

2. Remplacez {{FOO}} par la variable FOO et exportez vers le nouveau fichier foo.conf

FOO="world" && sed -e "s/{{FOO}}/$FOO/g" foo.conf.tmpl > foo.conf

Fonctionnant à la fois sur macOS 10.12.4 et Ubuntu 14.04.5

Katopz
la source
0

Comme les autres réponses l'indiquent, il n'y a aucun moyen d'utiliser sed de manière portative sur OS X et Linux sans créer de fichiers de sauvegarde. J'ai donc plutôt utilisé ce Ruby one-liner pour le faire:

ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml

Dans mon cas, je devais l'appeler à partir d'une raketâche (c'est-à-dire à l'intérieur d'un script Ruby), j'ai donc utilisé ce niveau supplémentaire de citation:

sh %q{ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml}
Dan Kohn
la source
-1
sed -ie 's/old_link/new_link/g' *

Fonctionne à la fois sur BSD et Linux

ashokrajar
la source
3
Cela crée sur linux un autre nom de fichier avec en eannexe
Brice
1
Cassé, mieux vaut l'enlever.
sorin
Il fonctionne sur Mac et Linux. Quel est le problème avec cette solution?
Islam Azab
Non, cela ne fonctionne pas sur Mac BSD Sed - cela créerait un fichier de sauvegarde avec 'e' ajouté au nom de fichier.
Jesper Grann Laursen