J'ai un makefile (développé pour gmake sur Linux) que j'essaie de porter sur OSX, mais il semble que sed ne veuille pas coopérer. Ce que je fais, c'est utiliser GCC pour générer automatiquement des fichiers de dépendance, puis les ajuster un peu en utilisant sed. La partie pertinente du makefile:
$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
$(CPPC) -MM -MD $< -o $@
sed -i 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@
Bien que cela fonctionne sans problème sous GNU/Linux, j'obtiens des erreurs comme celles-ci lorsque j'essaie de construire sur OSX:
sed: 1: "test/obj/equipmentConta ...": undefined label 'est/obj/equipmentContainer_utest.d'
sed: 1: "test/obj/dice_utest.d": undefined label 'est/obj/dice_utest.d'
sed: 1: "test/obj/color-string_u ...": undefined label 'est/obj/color-string_utest.d'
Il semblerait que sed coupe un personnage, mais je ne vois pas la solution.
OS X sed
gère le -i
argument différemment de la version Linux .
Vous pouvez générer une commande qui pourrait "fonctionner" pour les deux en ajoutant -e
de cette façon:
# vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@
OS X sed -i
interprète la chose suivante après le -i
comme extension de fichier pour une copie de sauvegarde de la modification sur place. (La version Linux ne fait cela que s'il n'y a pas d'espace entre le -i
et l'extension.) Évidemment, un effet secondaire de l'utilisation de ceci est que vous obtiendrez un fichier de sauvegarde avec -e
en tant qu'extension, ce que vous pourriez ne pas vouloir. Veuillez vous référer aux autres réponses à cette question pour plus de détails et des approches plus propres qui peuvent être utilisées à la place.
Le comportement que vous voyez est dû au fait que OS X sed
consomme le s|||
comme extension (!) interprète ensuite l'argument suivant comme une commande - dans ce cas, il commence par t
, que sed
reconnaît comme une branche -to-label commandant le label cible comme argument - d'où l'erreur que vous voyez.
Si vous créez un fichier test
vous pouvez reproduire l'erreur:
$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'
En fait .. faire
sed -i -e "s/blah/blah/" files
ne fait pas ce que vous attendez sous OS X non plus. Au lieu de cela, il crée des fichiers de sauvegarde avec l'extension "-e".
Le bon pour OS est
sed -i "" -e "s/blah/blah/" files
La réponse actuellement acceptée comporte deux failles très importantes.
Avec BSD sed (la version OSX), l'option -e
Est interprétée comme une extension de fichier et crée donc un fichier de sauvegarde avec une extension -e
.
Tester le noyau darwin comme suggéré n'est pas une approche fiable pour une solution multiplateforme car GNU ou BSD sed pourrait être présent sur un nombre illimité de systèmes.
Un test beaucoup plus fiable serait de simplement tester l'option --version
Qui ne se trouve que dans la version GNU de sed.
sed --version >/dev/null 2>&1
Une fois que la version correcte de sed est déterminée, nous pouvons alors exécuter la commande dans sa syntaxe appropriée.
Syntaxe GNU sed pour l'option -i:
sed -i -- "$@"
Syntaxe sed BSD pour l'option -i:
sed -i "" "$@"
Enfin, mettez tout cela ensemble dans une fonction multiplateforme pour exécuter une commande d'édition sed sur place:
sedi () {
sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@"
}
Exemple d'utilisation:
sedi 's/old/new/g' 'some_file.txt'
Cette solution a été testée sur OSX, Ubuntu, Freebsd, Cygwin, CentOS, Red Hat Enterprise et Msys.
Ce n'est pas tout à fait une réponse à la question, mais on peut obtenir un comportement équivalent à Linux via
brew install gnu-sed
# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
(auparavant, il y avait --with-default-names
option pour brew install gnu-sed
mais qui a été récemment supprimé)
J'ai également rencontré ce problème et j'ai pensé à la solution suivante:
darwin=false;
case "`uname`" in
Darwin*) darwin=true ;;
esac
if $darwin; then
sedi="/usr/bin/sed -i ''"
else
sedi="sed -i"
fi
$sedi 's/foo/bar/' /home/foobar/bar
fonctionne pour moi ;-), YMMV
Je travaille dans une équipe multi-OS où ppl s'appuie sur Windows, Linux et OS X. Certains utilisateurs d'OS X se sont plaints car ils ont eu une autre erreur - ils avaient le port GNU de sed installé, donc j'avais pour spécifier le chemin complet.
la réponse utile de Martin Clayton fournit une bonne explication du problème[1], mais une solution qui - comme il le dit - a un effet secondaire potentiellement indésirable.
Voici des solutions sans effets secondaires :
Attention: Résoudre le -i
seul problème de syntaxe, comme ci-dessous, peut ne pas être suffisant, car il existe de nombreuses autres différences entre GNU sed
et BSD/macOS sed
(pour un discussion complète, voir cette réponse à moi).
-i
: Créez un fichier de sauvegarde temporairement, puis nettoyez-le:Avec un argument d'option non vide suffixe (extension du nom de fichier du fichier de sauvegarde) (une valeur qui est pas la chaîne vide), vous peut utiliser -i
d'une manière qui fonctionne avec BSD/macOS sed
et GNU sed
, par en ajoutant directement le suffixe au -i
option.
Cela peut être utilisé pour créer un fichier de sauvegarde temporairement que vous pouvez nettoyer immédiatement:
sed -i.bak 's/foo/bar/' file && rm file.bak
Évidemment, si vous souhaitez conserver la sauvegarde, omettez simplement le && rm file.bak
partie.
mv
:Si seul un fichier single doit être modifié sur place, le -i
l'option peut être contournée pour éviter l'incompatibilité.
Si vous limitez votre script sed
et d'autres options à fonctionnalités compatibles POSIX , ce qui suit est un entièrement portable solution (notez que -i
est pas compatible POSIX ).
sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
Cette commande écrit simplement les modifications dans un fichier temporaire et, si la commande sed
réussit (&&
), remplace le fichier d'origine par le fichier temporaire.
mv
qui renomme d'abord l'original. Attention: Fondamentalement, c'est ce que -i
le fait aussi, sauf qu'il essaie de conserver les autorisations et les attributs étendus (macOS) du fichier d'origine; cependant, si le fichier d'origine est un symlink, cette solution et -i
remplacera le lien symbolique par un fichier normal.
Voir la moitié inférieure de cette réponse à moi pour plus de détails sur la façon dont -i
travaux.
[1] Pour une explication plus approfondie, voir cette réponse à moi.
J'ai corrigé la solution publiée par @thecarpy:
Voici une solution multiplateforme appropriée pour sed -i
:
sedi() {
case $(uname) in
Darwin*) sedi=('-i' '') ;;
*) sedi='-i' ;;
esac
LC_ALL=C sed "${sedi[@]}" "$@"
}