web-dev-qa-db-fra.com

La commande sed avec l'option -i (édition sur place) fonctionne bien sous Ubuntu mais pas sur Mac

Je ne connais rien à Sed mais j'ai besoin de cette commande (qui fonctionne très bien sous Ubuntu) pour fonctionner sous Mac OSX:

sed -i "/ $domain .*#drupalpro/d" /etc/hosts

Je suis en train:

sed: 1: "/etc/hosts": extra characters at the end of h command
89

Ubuntu est livré avec GNU sed, où le suffixe de l'option -i Est optionnel. OS X est livré avec BSD sed, où le suffixe est obligatoire. Essayez sed -i ''

145
microtherion

Pour compléter la réponse utile, précise de microtherion :

  • avec une solution portable
  • avec des informations de fond

tl; dr:

L'équivalent de ce GNU sed (standard sur la plupart des Linux distros) commande:

sed -i    's/foo/bar/' file

est-ce que la commande BSD/macOSsed:

sed -i '' 's/foo/bar/' file  # Note the '' as a *separate argument*

Avec BSD/macOSsed, les commandes suivantes donottravail du tout ou pas comme prévu:

sed -i    's/foo/bar/' file  # Breaks; script is misinterpreted as backup-file suffix
sed -i''  's/foo/bar/' file  # Ditto
sed -i -e 's/foo/bar/' file  # -e is misinterpreted as backup-file suffix

Pour une discussion surallles différences entre GNU sed et BSD/macOS sed, voir cette réponse à moi.

Approche portable:

Remarque:Portablesignifie ici que la commande fonctionne avec les deux implémentations décrites. Il n’est pas portable dans un POSIX sense, car l’option -i n’est pas POSIX -compliant .

# Works with both GNU and BSD/macOS Sed, due to a *non-empty* option-argument:
# Create a backup file *temporarily* and remove it on success.
sed -i.bak 's/foo/bar/' file && rm file.bak

Pour une explication, voir ci-dessous; pour des solutions alternatives, y compris une solution compatible POSIX, voir cette réponse associée of mine.


Informations d'arrière-plan

Dans GNU sed (standard sur la plupart des distributions Linux) etBSD/macOSsed, l'option -i, qui exécutemise à jour sur place[1] de ses fichiers d’entrée, accepte unargument-optionqui spécifie quelsuffixe(extension du nom de fichier) à utiliser pour lefichier de sauvegardedu fichier en cours de mise à jour}.

Par exemple, dansbothimplémentations, le fichier suivant conserve le fichier d'origine, file, en tant que fichier de sauvegarde file.bak:

sed -i.bak 's/foo/bar/' file  # Keep original as 'file.bak'; NO SPACE between -i and .bak

Même si avec GNU sed l'argument de suffixe estoptionnel , alors qu'avec BSD/macOSsed c'estobligatoire , la syntaxe ci-dessus fonctionne avec les implémentations deboth, car accolant directement à l'option-argument (.bak) à l'option (-i) - -i.bak, par opposition à -i .bak - fonctionne à la fois comme unoptionnelet aobligatoireargument-option:

  • La syntaxe -i.bak est la formeseulementforme qui fonctionne pour un/optionneloption-argument.
  • La syntaxe -i.bakaussifonctionne comme unobligatoireargument-option, comme unalternativeto -i .bak, c’est-à-dire en spécifiant l’option et son argumentséparément.

Ne spécifiant pas de suffixe _ - ce qui est souvent le cas - signifie que aucun fichier de sauvegarde} doit être conservé, et c'est là que l'incompatibilité se produit:

  • Avec GNU sed, ne pas spécifier de suffixe signifie simplement utiliser -iseul.

  • AvecBSD/macOSsed, ne pas spécifier de suffixe signifie spécifierla chaîne videen tant que - obligatoire - suffixe et pourtechniqueraisons, la chaîne vide ne peut être transmise que sous la forme d'unséparéargument: c'est-à-dire -i ''pas-i''.

-i'' ne fonctionne pas, car pour sed, il est impossible de le distinguer de -i, car leShelleffectivementsupprimeles guillemets vides (il concatène -i et '' et supprime les citations avec une fonction syntaxique) et ne fait que passer -i dansles deuxcas.

Avec (effectivement) juste -i spécifié, c'est l'argumentnextqui est interprété comme argument-option:

sed -i 's/foo/bar/' file # BREAKS with BSD/macOS Sed

's/foo/bar/' - destiné à la Sedscript(commande) - est maintenant interprété comme lesuffixe, et le mot file est interprété comme un script.
Interpréter un tel mot en tant que script conduit alors à masquer un message d'erreur tel que
sed: 1: "file": invalid command code f,
car le f est interprété comme une commande Sed (fonction).

De même, avec:

sed -i -e 's/foo/bar/' file # CREATES BACKUP FILE 'file-e'

-e est interprété comme l'argumentsuffixe, et PAS comme l'option de Sed [-e} (qui peut être utilisé pour spécifier les commandesmultiple, si nécessaire).
Par conséquent, au lieu de conserver AUCUNE sauvegarde, vous obtenez un fichier de sauvegarde portant le suffixe -e.

Le fait que cette commande ne fonctionne pas comme prévu est moins évident, car la mise à jour sur place réussit, étant donné que l'exigence de syntaxe de l'argument de suffixe est satisfaite par l'argument -e.

Que la création accidentelle de ces fichiers de sauvegarde passe facilement inaperçue est l'explication la plus probable de réponse incorrecte de Crt et cette réponse incorrecte à une question similaire ayant reçu autant de votes positifs (comme de cette écriture).


[1] À proprement parler, un fichier temporaire est créé dans les coulisses, puisremplacele fichier d'origine; cette approche peut être problématique: voir la moitié inférieure de cette réponse de la mienne.

72
mklement0

l'homme est ton ami.

OS X

 -i extension
         Edit files in-place, saving backups with the specified extension.
         If a zero-length extension is given, no backup will be saved.  It
         is not recommended to give a zero-length extension when in-place
         editing files, as you risk corruption or partial content in situ-
         ations where disk space is exhausted, etc.
21

Sous OS X, vous pouvez utiliser la version GNU de sed: gsed.

# if using brew
brew install gnu-sed

#if using ports
Sudo port install gsed

Ensuite, si votre script doit être portable, vous pouvez définir la commande à utiliser en fonction de votre système d'exploitation.

SED=sed
unamestr=`uname`
if [[ "$unamestr" == "Darwin" ]] ; then
    SED=gsed
    type $SED >/dev/null 2>&1 || {
        echo >&2 "$SED it's not installed. Try: brew install gnu-sed" ;
        exit 1;
    }
fi
# here your sed command, e.g.:
$SED -i "/ $domain .*#drupalpro/d" /etc/hosts
5
jcarballo