indicateur in-situ sed qui fonctionne à la fois sur Mac (BSD) et Linux


237

Existe-t-il une invocation de la sedmodification sur place des tâches sans sauvegardes qui fonctionne à la fois sur Linux et Mac? Alors que le BSD sedlivré avec OS X semble avoir besoin sed -i '' …, les distributions GNU sedLinux sont généralement fournies avec les citations comme nom de fichier d'entrée vide (au lieu de l'extension de sauvegarde), et en ont sed -i …plutôt besoin .

Existe-t-il une syntaxe de ligne de commande qui fonctionne avec les deux versions, donc je peux utiliser le même script sur les deux systèmes?


Perl n'est-il pas une option? Devez- vous utiliser sed?
Noufal Ibrahim

Peut-être installez la version GNU et utilisez-la! topbug.net/blog/2013/04/14/… ? J'essaierais ça d'abord. Là encore, ce genre de suce aussi.
Dmitry Minkovsky

2
@dimadima: Cela pourrait être intéressant pour d'autres personnes parcourant cette question qui ont des scripts personnels qui se cassent sur leur machine OS X. Dans mon cas, cependant, j'en avais besoin pour le système de construction d'un projet open source, où dire à votre utilisateur d'installer GNU sed en premier aurait échoué à l'objectif initial de cet exercice (patcher quelques fichiers de manière "fonctionne partout") .
dnadlinger du

@klickverbot oui, c'est logique. J'ai d'abord ajouté le commentaire comme réponse, puis je l'ai supprimé, réalisant que ce n'était pas une réponse à votre question :).
Dmitry Minkovsky

1
Référence croisée: comment obtenir la portabilité avec sed -i (édition sur place)? sur Unix & Linux Stack Exchange.
MvG

Réponses:


212

Si vous voulez vraiment simplement utiliser sed -ila méthode «facile», les éléments suivants fonctionnent sur GNU et BSD / Mac sed:

sed -i.bak 's/foo/bar/' filename

Notez le manque d'espace et le point.

Preuve:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

Évidemment, vous pouvez alors simplement supprimer les .bakfichiers.


2
C'est la voie à suivre. Ajoutez quelques caractères et c'est portable. La suppression du fichier de sauvegarde est triviale (vous avez déjà le nom du fichier lors de l'appel sed)
slezica

Cela a fonctionné sur mac, mais sur ubuntu 16.04, il écrase le fichier d'origine à 0 octet et crée le fichier .bak également avec 0 octet?
house9

1
Il convient de noter, sur macOS Sierra (et peut - être plus tôt, je n'ai pas une machine à portée de main), sedn'accepte pas de position commandargument lorsque invoqué avec -i- il doit être fourni avec un autre drapeau, -e. Ainsi, la commande devient:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE

5
Cela crée des sauvegardes, tandis que l'OP demande spécifiquement une modification sur place.
Marc-André Lafortune

8
La solution complète est donc:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle

112

Cela fonctionne avec GNU sed, mais pas sous OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

Cela fonctionne sous OS X, mais pas avec GNU sed:

sed -i '' -e 's/foo/bar/' target.file

Sous OS X, vous

  • ne peut pas être utilisé sed -i -ecar l'extension du fichier de sauvegarde serait définie sur-e
  • ne peut pas utiliser sed -i'' -epour les mêmes raisons - il a besoin d'un espace entre -iet ''.

1
@AlexanderMills Dit à sed que l'argument suivant est une commande - peut également être ignoré ici.
Benjamin

4
Notez que -iet -i''sont identiques au moment de l'analyse du shell; sed ne peut pas se comporter différemment sur ces deux premières invocations, car il obtient exactement les mêmes arguments.
wchargin

44

Sous OSX, j'installe toujours la version GNU sed via Homebrew, pour éviter les problèmes dans les scripts, car la plupart des scripts ont été écrits pour les versions GNU sed.

brew install gnu-sed --with-default-names

Ensuite, votre BSD sed sera remplacé par GNU sed.

Alternativement, vous pouvez installer sans noms par défaut, mais ensuite:

  • Changez votre PATHcomme indiqué après l'installationgnu-sed
  • Vérifiez vos scripts pour choisir entre gsedou en sedfonction de votre système

4
le a --with-default-namesété supprimé de homebrew-core , plus d'informations dans cette réponse. lors de l'installation gnu-sedmaintenant, les instructions d'installation spécifient que vous devez ajouter gnubinà votre PATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgericson

18

Comme le demande Noufal Ibrahim , pourquoi ne pouvez-vous pas utiliser Perl? Tout Mac aura Perl, et il y a très peu de distributions Linux ou BSD qui n'incluent pas de version de Perl dans le système de base. L'un des seuls environnements qui pourrait manquer de Perl serait BusyBox (qui fonctionne comme GNU / Linux pour -i, sauf qu'aucune extension de sauvegarde ne peut être spécifiée).

Comme le recommande ismail ,

Depuis perl est disponible partout où je fais juste perl -pi -e s,foo,bar,g target.file

et cela semble être une meilleure solution dans presque tous les cas que les scripts, les alias ou d'autres solutions de contournement pour faire face à l'incompatibilité fondamentale sed -ientre GNU / Linux et BSD / Mac.


J'adore cette solution. perlfait partie de la spécification Linux Standard Base depuis le 1er mai 2009. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/…
OregonTrail

1
C'est facilement la meilleure solution pour la portabilité
ecnepsnai

17

Il n'y a aucun moyen de le faire fonctionner.

Une façon consiste à utiliser un fichier temporaire comme:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

Cela fonctionne à la fois


Y a-t-il une raison pour laquelle SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo "$ SOME_FILE"> certains / fichier; ne fonctionnera pas à la place
Jason Gross

On dirait que vous seriez limité par la taille maximale d'une variable bash, non? Pas sûr que cela fonctionnera avec les fichiers GB.
analogique

3
Si vous créez un fichier temporaire, pourquoi ne pas simplement donner une extension pour créer un fichier de sauvegarde (que vous pouvez ensuite supprimer) comme le suggère @kine ci-dessous?
Alex Dupuy

13

Réponse: non

La réponse initialement acceptée ne fait pas ce qui est demandé (comme indiqué dans les commentaires). (J'ai trouvé cette réponse en cherchant la raison pour laquelle file-ea apparaissait "au hasard" dans mes répertoires.)

Il n'y a apparemment aucun moyen de sed -itravailler de manière cohérente sur MacOS et Linuces.

Ma recommandation, pour ce qu'elle vaut, n'est pas de mettre à jour sur place sed(qui a des modes de défaillance complexes), mais de générer de nouveaux fichiers et de les renommer ensuite. En d'autres termes: évitez -i.


9

L' -ioption ne fait pas partie de POSIX Sed . Une méthode plus portable serait d'utiliser Vim en mode Ex:

ex -sc '%s/alfa/bravo/|x' file
  1. % sélectionner toutes les lignes

  2. s remplacer

  3. x sauver et fermer


Ceci est la bonne réponse. Une caractéristique clé de POSIX est la portabilité.
Kajukenbo

9

Voici une autre version qui fonctionne sur Linux et macOS sans utiliser evalet sans avoir à supprimer les fichiers de sauvegarde. Il utilise des tableaux Bash pour stocker les sedparamètres, ce qui est plus propre que d'utiliser eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

Cela ne crée pas de fichier de sauvegarde, ni un fichier avec des guillemets ajoutés.


2
c'est la bonne réponse. Je simplifierais un peu, mais l'idée fonctionne parfaitement sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
Alex Skrypnyk

Bon produit! Je préfère cependant la version plus longue pour la lisibilité.
nwinkler

1
C'est une meilleure façon pour moi d'être compatible avec différents systèmes.
Victor Choy

6

La réponse de Steve Powell est tout à fait correcte, la consultation de la page MAN de sed sur OSX et Linux (Ubuntu 12.04) met en évidence la non-compatibilité dans l'utilisation sed sur place entre les deux systèmes d'exploitation.

JFYI, il ne devrait y avoir aucun espace entre le -i et les guillemets (qui dénotent une extension de fichier vide) utilisant la version Linux de sed, donc

Page de manuel Linux sed

#Linux
sed -i"" 

et

page de manuel sed OSX

#OSX (notice the space after the '-i' argument)
sed -i "" 

J'ai contourné cela dans un script en utilisant une commande alias et la sortie du nom du système d'exploitation de « uname » dans un bash «if». La tentative de stockage des chaînes de commande dépendant du système d'exploitation dans des variables a été aléatoire lors de l'interprétation des guillemets. L'utilisation de ' shopt -s expand_aliases ' est nécessaire pour développer / utiliser les alias définis dans votre script. l'utilisation de shopt est traitée ici .


1

Si vous avez besoin de faire sedsur place dans un bashscript, et que vous ne voulez PAS que le résultat sur place avec des fichiers .bkp, et vous avez un moyen de détecter le système d'exploitation (par exemple, en utilisant ostype.sh ), - alors le le hack suivant avec le bashshell intégré evaldevrait fonctionner:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`

0

Vous pouvez utiliser une éponge. Sponge est un ancien programme Unix, trouvé dans le paquet moreutils (à la fois dans Ubuntu et probablement Debian, et dans Homebrew sous Mac).

Il mettra en mémoire tampon tout le contenu du tuyau, attendra que le tuyau soit fermé (ce qui signifie probablement que le fichier d'entrée est déjà fermé), puis écrasera:

Depuis la page de manuel :

Synopsis

fichier sed '...' | grep '...' | fichier éponge


3
Belle approche - malheureusement, n'aide pas vraiment dans la situation d'origine car la raison du recours à sed était d'avoir quelque chose à utiliser dans un script d'aide multiplateforme utilisé sur les machines clientes, où vous ne pouvez pas dépendre d'autre chose que du système outils en cours d'installation. Cela pourrait être utile à quelqu'un d'autre, cependant.
dnadlinger

0

Ce qui suit fonctionne pour moi sur Linux et OS X:

sed -i' ' <expr> <file>

par exemple pour un fichier fcontenantaaabbaaba

sed -i' ' 's/b/c/g' f

rendements aaaccaacasur Linux et Mac. Notez qu'il existe une chaîne entre guillemets contenant un espace , sans espace entre les-i et la chaîne. Les guillemets simples ou doubles fonctionnent tous les deux.

Sous Linux, j'utilise la bashversion 4.3.11 sous Ubuntu 14.04.4 et sur la version Mac 3.2.57 sous OS X 10.11.4 El Capitan (Darwin 15.4.0).


1
Wow, je suis content de ne pas avoir écouté tout le monde ci-dessus dire que c'était impossible et j'ai continué à lire! Cela fonctionne vraiment. Merci!
Personman

4
Ne fonctionne pas pour moi sur Mac OS ... un nouveau fichier avec un espace ajouté à la fin du nom de fichier est créé
Frédéric

Ne fonctionne pas non plus pour moi sur Mac (/ usr / bin / sed) - J'ai maintenant un fichier de sauvegarde supplémentaire avec un espace ajouté au nom du fichier :-(
paul_h

Essayez de supprimer l'espace des guillemets simples, cela fonctionne pour moi.
dps

0

J'ai rencontré ce problème. La seule solution rapide a été de remplacer le sed en mac par la version gnu:

brew install gnu-sed

Cela reproduit une réponse existante avec beaucoup plus de détails de 2015.
tripleee
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.