sed sur insert OSX à une certaine ligne

24

J'utilise donc 'sed' sur linux depuis un certain temps, mais j'ai eu un peu de difficulté à l'utiliser sur OSX depuis que 'POSIX sed' et 'GNU sed' ont tellement de petites différences. Actuellement, j'ai du mal à insérer une ligne de texte après un certain numéro de ligne. (dans ce cas, ligne 4)

Sur Linux, je ferais quelque chose comme ceci:

sed --in-place "4 a\  mode '0755'" file.txt

Donc, sur OSX, j'ai essayé ceci:

sed -i "" "4 a\ mode '0755'" file.txt

Cependant, cela continue de me donner une erreur «caractères supplémentaires après \ à la fin d'une commande». Des idées ce qui ne va pas ici? Ai-je une faute de frappe? Ou ne comprends-je pas une autre différence entre les versions de sed?

CRThaze
la source
2
Peut confirmer que cela ne fonctionne pas sur MacOS 10.6.8. Fonctionne très bien sur debian 6.0.3.
tink

Réponses:

24

À proprement parler, la spécification POSIX poursed requiert une nouvelle ligne après a\:

[1addr]a\
text

Écrivez le texte sur la sortie standard comme décrit précédemment.

Cela rend l' écriture one-liners un peu d'une douleur, ce qui est probablement la raison de ce qui suit extension GNU au a, iet les ccommandes:

En tant qu'extension GNU, si entre la aet la nouvelle ligne il y a autre chose qu'une \séquence d' espaces , alors le texte de cette ligne, commençant au premier caractère non blanc après la a, est pris comme première ligne du bloc de texte . (Cela permet une simplification du scriptage d'un ajout sur une seule ligne.) Cette extension fonctionne également avec les commandes iet c.

Ainsi, pour être portable avec votre sedsyntaxe, vous aurez besoin d'inclure une nouvelle ligne après le a\quelque part. Le moyen le plus simple consiste à insérer simplement un saut de ligne entre guillemets:

$ sed -e 'a\
> text'

(où $et >sont les invites du shell). Si vous trouvez qu'un problème, bash[1] a la $' 'syntaxe de citation pour insérer des échappements de style C, utilisez simplement

sed -e 'a\'$'\n''text'

[1] et mksh (r39b +) et certains shells bourne non bash (par exemple, / bin / sh dans FreeBSD 9+)

jw013
la source
Réponse très instructive. Merci pour le conseil sur la syntaxe bash de citation.
cjackson
14

Comme décrit dans cette réponse , il est utile lors de l'utilisation de commandes sed comme iet ad'utiliser plusieurs -e "..."clauses. Chacune de ces clauses sera considérée comme étant séparée par des retours à la ligne. Les iet les acommandes sont difficiles à utiliser dans des scripts sed en ligne autrement (ils sont conçus pour être utilisés dans un fichier script sed multiligne appelé à l' aide sed -f file ...). Il semble que vous ne puissiez pas utiliser la nouvelle ligne implicite introduite à la fin d'une -eclause pour séparer la a\et la ligne de texte à ajouter. Mais vous pouvez l'utiliser pour terminer la ligne de texte à ajouter.

Dans ce cas précis, ce que vous essayez de faire pourrait en fait être accompli avec une seule -e ...clause. Il vous suffit d'utiliser acorrectement la commande. Selon la norme POSIX, adoit être suivi d'un \, puis qui doit être suivi d'un retour à la ligne, puis le reste de la ligne suivante sera inséré (jusqu'à ce qu'un retour à la ligne ou la fin de la -eclause soit rencontré). Vous pourriez donc faire:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt
dubiousjim
la source
2
Si vous souhaitez trier les fonctionnalités sur lesquelles vous vous êtes appuyés, c'est Gnu-isms, cette page peut vous aider: wiki.alpinelinux.org/wiki/Regex . Cependant, il est limité aux seules fonctionnalités d'expression régulière; pas les autres commandes sed.
dubiousjim
5

GNU sed semble être beaucoup plus utilisé que POSIX sed, basé sur des exemples / tutoriels que j'ai utilisés de toute façon.

J'ai trouvé qu'il est beaucoup plus facile d'installer GNU sed sur n'importe quelle machine OSX que j'utilise. Si vous êtes intéressé, la meilleure façon de le faire est de l'installer via Homebrew .

Vous pouvez soit simplement $ brew install gnu-sed, soit obtenir tous les utilitaires GNU courants avec $ brew install coreutils.

Ensuite, lorsque vous rencontrez un problème de syntaxe avec dateou un autre programme, vous pouvez simplement utiliser la version GNU. Finalement, j'ai simplement décidé qu'il était plus facile de toujours utiliser les versions GNU et de les mettre plus tôt que les versions système dans mon PATH.

doyen
la source
5

Perl ne souffre pas de telles particularités GNU dépendantes de la plate-forme vs non-GNU vs "propriétaires". Vous pourriez faire:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

Le -n' option creates a loop that reads every line of the input file. Unlike its cousin-p (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -esignale le début du script.

Le $.désigne le numéro de ligne, en commençant par la ligne-1. Ainsi, la ligne de commande lit «fichier» et lorsque le numéro de ligne est égal à 4, il imprime cette ligne suivie de tout ce que vous voulez injecter.

Je m'empresse d'ajouter que Perl doit ses racines à sedAWK et C, donc la syntaxe des substitutions simples, etc. n'est pas une courbe d'apprentissage très abrupte.

JRFerguson
la source
Bonne réponse! Mais vous pouvez le simplifier un peu. Essayez ceci: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. Le -pcommutateur fonctionne bien ici (puisque nous voulons imprimer chaque ligne); il suffit de changer la logique de "imprimer après la ligne 4" à "imprimer avant la ligne 5". Et le -lcommutateur fait en sorte que "\ n" soit imprimé automatiquement avec chaque print, vous n'avez donc pas à l'imprimer vous-même.
JL