web-dev-qa-db-fra.com

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

Existe-t-il un appel de sed à faire une édition sur place sans sauvegardes qui fonctionne à la fois sous Linux et Mac? Bien que le BSD sed fourni avec OS X semble avoir besoin de sed -i '' …, the GNU sed Les distributions Linux viennent généralement avec interprètent les guillemets comme un nom de fichier d'entrée vide (au lieu de l'extension de sauvegarde), et ont besoin de sed -i … au lieu.

Existe-t-il une syntaxe de ligne de commande qui fonctionne avec les deux types, de sorte que je puisse utiliser le même script sur les deux systèmes?

204
klickverbot

Si vous voulez vraiment utiliser simplement sed -i De la manière "facile", ce qui suit fonctionne avec GNU et avec 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 fichiers .bak.

172
kine

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

Sur OS X vous

  • ne peut pas utiliser sed -i -e puisque l’extension du fichier de sauvegarde serait définie sur -e
  • ne peut pas utiliser sed -i'' -e pour les mêmes raisons: il faut un espace entre -i et ''.
102
klickverbot

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

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

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

Alternativement, vous pouvez installer sans default-names, mais ensuite:

  • Changez votre PATH comme indiqué après l’installation de gnu-sed
  • Vérifiez vos scripts pour choisir entre gsed ou sed en fonction de votre système
40
Lewy

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

Une solution consiste à utiliser un fichier temporaire tel que:

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

Cela fonctionne à la fois

17
analogue

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

Comme ismail recommande,

Puisque Perl est disponible partout, je ne fais que 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 autres solutions palliatives pour traiter l'incompatibilité fondamentale de sed -i entre GNU/Linux et BSD/Mac.

16
Alex Dupuy

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 un file-e Apparaît "de manière aléatoire" dans mes répertoires.)

Il n'y a apparemment aucun moyen d'obtenir que sed -i Fonctionne de manière cohérente à la fois sur MacOS et Linuces.

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

13
Steve Powell

L'option -i Ne fait pas partie de POSIX Sed . Une méthode plus portable consisterait à utiliser Vim en mode Ex:

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

  2. s replace

  3. x enregistrer et fermer

8
Steven Penny

Voici une autre version qui fonctionne sous Linux et macOS sans utiliser eval et sans avoir à supprimer les fichiers de sauvegarde. Il utilise des tableaux Bash pour stocker les paramètres sed, 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 de fichier avec des guillemets ajoutés.

7
nwinkler

La réponse de Steve Powell est tout à fait correct, consulter la page MAN pour sed sur OSX et Linux (Ubuntu 12.04) met en évidence l'in-compatibilité dans l'utilisation sed 'in-situ' à travers les deux systèmes d'exploitation.

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

sed Linux Man Page

#Linux
sed -i"" 

et

page de manuel sed OSX

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

J'ai résolu ce problème dans un script en utilisant une commande alias'd et la sortie du nom du système d'exploitation de ' uname ' dans un bash 'if'. Tenter de stocker des chaînes de commande dépendantes 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 .

6
Big Rich

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

brew install gnu-sed
1
vikrantt

Si vous devez utiliser sed sur place dans un script bash, et que vous ne voulez PAS que celui-ci résulte avec des fichiers .bkp, vous avez un moyen de détecter le système d'exploitation ( dire, en utilisant ostype.sh ), - alors le hack suivant avec le bash shell intégré eval devrait 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""`
1
sdaau

Vous pouvez utiliser une éponge. Sponge est un ancien programme unix, qui se trouve dans le paquetage moreutils (à la fois sous ubuntu et probablement debian, et en homebrew sous mac).

Il va mettre en mémoire tampon tout le contenu du canal, attendre que le canal soit fermé (ce qui signifie probablement que le fichier d'entrée est déjà fermé), puis écraser:

De la page de manuel :

Synopsis

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

0
Guillermo

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

sed -i' ' <expr> <file>

par exemple. pour un fichier f contenant aaabbaaba

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

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

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

0
David G