Recherche récursive et remplacement dans des fichiers texte sur Mac et Linux


127

Dans le shell Linux, la commande suivante recherchera et remplacera récursivement toutes les instances de «this» par «that» (je n'ai pas de shell Linux devant moi, mais cela devrait le faire).

find . -name "*.txt" -print | xargs sed -i 's/this/that/g'

À quoi ressemblera une commande similaire sur OSX?


Devrait probablement être déplacé apple.stackexchange.comcar ce n'est pas assez générique pour Linux ni pour tous les développeurs.
AlikElzin-kilaka

Réponses:


246

OS X utilise un mélange d'outils BSD et GNU, il est donc préférable de toujours vérifier la documentation (même si je l'avais qui lessn'était même pas conforme à la page de manuel OS X):

https://web.archive.org/web/20170808213955/https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/sed.1.html

sed prend l'argument après -icomme extension pour les sauvegardes. Fournissez une chaîne vide ( -i '') pour aucune sauvegarde.

Ce qui suit devrait faire:

LC_ALL=C find . -type f -name '*.txt' -exec sed -i '' s/this/that/ {} +

C'est -type fjuste une bonne pratique; sed se plaindra si vous lui donnez un répertoire ou plus. -execest préféré à xargs; vous n'avez pas besoin de vous soucier de -print0quoi que ce soit. Le {} +à la fin signifie qu'il findajoutera tous les résultats en tant qu'arguments à une instance de la commande appelée, au lieu de la réexécuter pour chaque résultat. (Une exception est lorsque le nombre maximal d'arguments de ligne de commande autorisés par le système d'exploitation est violé; dans ce cas, findil exécutera plus d'une instance.)


2
Mon "ceci" dans cette substitution contient une barre oblique (localhost / site) - Je remplace des parties d'une URL dans un fichier .html .... comment faire une telle substitution. J'ai essayé de mettre entre guillemets, mais cela échoue.
Timothy T.

6
Syntaxe sed permet d' utiliser presque tout caractère à la place de la barre oblique, par exemple , vous pouvez utiliser le %caractère: sed "s%localhost/site%blah/blah%". Une autre alternative consiste à échapper au backslasher-séparateur: sed "s/localhost\/site/blah\/blah/".
TaylanUB

Merci, laissez-moi essayer. J'ai cependant essayé d'utiliser {} pour séparer la barre oblique et j'ai toujours une erreur ...
Timothy T.

16
Quelqu'un d'autre a une illegal byte sequenceerreur? si oui, essayez:, LC_ALL=C find . -type f -name '*.txt' -exec sed -i '' s/this/that/ {} +cela a fonctionné pour moi.
ColdTuna

10
Cela remplacera une seule occurrence par fichier, à utiliser /gpour plusieurs devises commeLC_ALL=C find . -type f -exec sed -i '' s/search/replace/g {} +
jamesjara

152

Pour le mac, une approche plus similaire serait la suivante:

find . -name '*.txt' -print0 | xargs -0 sed -i "" "s/form/forms/g"

10
J'aimerais pouvoir voter pour cela chaque fois que je reviens et que je l'utilise. Ce serait à +15 maintenant, facile.
Droogans

Pour une raison quelconque, cela ne fonctionne pas pour moi. Cela ne fait rien. Je suis dans le dossier form360 et j'essaye de changer toutes les instances de chaîne avec le nom easyform en form360, j'exécute la commande suivante:find . -name '*.php' -print0 | xargs -0 sed -i "" "s/easyform/form360/g"
Andres Ramos

2
pour moi, cela devrait être la bonne réponse. C'est le seul qui a fonctionné pour moi.
nosequeldeebee

1
-print0 | xargs -0 ne fonctionne pas sur mon mac lorsque le nom de fichier contient de l'espace.
user2807219

1
sed:.: l'édition sur place ne fonctionne que pour les fichiers
normaux

14

Comme solution alternative, j'utilise celle-ci sur Mac OSX 10.7.5

grep -ilr 'old-word' * | xargs -I@ sed -i '' 's/old-word/new-word/g' @

Le mérite revient à: la réponse de Todd Cesere


Celui-ci fonctionne bien! D'autres scripts ajoutent une fin de ligne supplémentaire dans certains cas sur OSX! Merci beaucoup!
Sebastien Filion

14

Sur Mac OSX 10.11.5, cela fonctionne bien:

grep -rli 'old-word' * | xargs -I@ sed -i '' 's/old-word/new-word/g' @

xargs -I @ sed -i '' 's / old-word / new-word / g' @ a fonctionné pour moi après avoir essayé tant d'autres suggestions non fonctionnelles. Je vous remercie!
DrB

13

Aucun des éléments ci-dessus ne fonctionne sous OSX.

Procédez comme suit:

perl -pi -w -e 's/SEARCH_FOR/REPLACE_WITH/g;' *.txt

1
comment scape '/' si SEARCH_FOR et REPLACE_WITH sont des chemins?
rraallvv

Utilisez un autre délimiteur. Si vous utilisez des chemins, un signe deux-points ou un tube fonctionnerait. 's | SEARCH | REPLACE | g', par exemple. Notre utilisation des accolades, comme dans 's {SEARCH} {REPLACE}'.
dannysauer

1
dito question, l'essayer non sur Mac - mais cela semble générer une erreur? Par exemple, mon chemin est interprété comme un fichier? -bash: localhost / nohost: aucun fichier ou répertoire de ce type
Timothy T.

cela ne se répète pas dans les dossiers en profondeur. Un seul niveau.
Sergey Romanov

Pour plus d'informations sur cette commande, lisez ceci lifehacker.com/5810026/…
kuzdu

7

Une version qui fonctionne à la fois sur Linux et Mac OS X (en ajoutant le -ecommutateur à sed):

export LC_CTYPE=C LANG=C
find . -name '*.txt' -print0 | xargs -0 sed -i -e 's/this/that/g'

J'ai dû faire l'exportation à partir de cette réponse + la ligne de la réponse acceptée (je ne voulais pas que les fichiers de sauvegarde soient générés)
Lance

2
pour corriger l'erreur de 'séquence d'octets illégale', essayez de définir LOCALE avant d'exécuter la commande: export LC_CTYPE=C && export LANG=C
cjoy

NE JAMAIS EXÉCUTER ceci avec '*' au lieu de '* .filetype' comme je l'ai fait si vous utilisez Git. Ou vous pouvez dire au revoir à tous vos travaux non publiés.
Sergey

1
La version mac de la commande sed nécessite un '' après le -i, donc cette réponse est incorrecte
G Huxley

2

Chaque fois que je tape cette commande, j'ai toujours l'impression de l'arroser ou d'oublier un drapeau. J'ai créé un Gist sur github basé sur la réponse de TaylanUB qui effectue un remplacement de recherche globale à partir du répertoire actuel. Ceci est spécifique à Mac OSX.

https://gist.github.com/nateflink/9056302

C'est bien parce que maintenant je viens d'ouvrir un terminal puis de copier:

curl -s https://gist.github.com/nateflink/9056302/raw/findreplaceosx.sh | bash -s "find-a-url.com" "replace-a-url.com"

Vous pouvez obtenir des erreurs de séquence d'octets étranges, voici donc le code complet:

#!/bin/bash
#By Nate Flink

#Invoke on the terminal like this
#curl -s https://gist.github.com/nateflink/9056302/raw/findreplaceosx.sh | bash -s "find-a-url.com" "replace-a-url.com"

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Usage: ./$0 [find string] [replace string]"
  exit 1
fi

FIND=$1
REPLACE=$2

#needed for byte sequence error in ascii to utf conversion on OSX
export LC_CTYPE=C;
export LANG=C;

#sed -i "" is needed by the osx version of sed (instead of sed -i)
find . -type f -exec sed -i "" "s|${FIND}|${REPLACE}|g" {} +
exit 0

2

C'est ma solution réalisable. sur mac OS X 10.10.4

grep -e 'this' -rl . | xargs sed -i '' 's/this/that/g'

Ceux ci-dessus utilisent find modifiera les fichiers qui ne contiennent pas le texte de recherche (ajoutez une nouvelle ligne à la fin du fichier), ce qui est détaillé.


2

Si vous utilisez un terminal zsh, vous pouvez utiliser la magie des caractères génériques:

sed -i "" "s/search/high-replace/g" *.txt


1

J'ai utilisé ce format - mais ... j'ai trouvé que je devais l'exécuter trois fois ou plus pour qu'il change réellement chaque instance que je trouvais extrêmement étrange. L'exécuter une fois en changerait certains dans chaque fichier mais pas tous. Exécuter exactement la même chaîne deux à quatre fois attraperait toutes les instances.

find . -type f -name '*.txt' -exec sed -i '' s/thistext/newtext/ {} +

Vous deviez exécuter cette commande plusieurs fois b / c votre regex sed a besoin d'un gà la fin, sinon elle ne remplace que la première occurrence de thistextdans une ligne. Donc votre regex devrait êtres/thistext/newtext/g
Les Nightingill

0

https://bitbucket.org/masonicboom/serp est un utilitaire go (c'est-à-dire multiplateforme), testé sur OSX, qui effectue une recherche récursive et remplace du texte dans des fichiers dans un répertoire donné, et confirme chaque remplacement. C'est nouveau, donc peut-être buggy.

L'utilisation ressemble à:

$ ls test
a  d  d2 z
$ cat test/z
hi
$ ./serp --root test --search hi --replace bye --pattern "*"                         
test/z: replace hi with bye? (y/[n]) y
$ cat test/z
bye

0
find . -type f | xargs sed -i '' 's/string1/string2/g'

Reportez-vous ici pour plus d'informations.


-3

La commande sur OSX devrait être exactement la même que sur Unix sous la jolie interface utilisateur.


J'ai pensé autant, mais non. Il met un ./ devant les fichiers lors de l'alimentation de sed, donc sed se plaint d'un «code de commande invalide». Peut-être que je suis juste fatigué ;-)
Jack

@JacobusR, veuillez couper et coller à partir de votre terminal OSX - vous devez entrer quelque chose de légèrement différent.
glenn jackman

3
Vous voudrez peut-être -print0ajouter aux finddrapeaux et -0aux xargsdrapeaux.
jmtd

Vous pouvez également simplement utiliser à la -exec sed -i s/this/that/g {} \+place de -printet xargs
Marian

@JacobusR - cela fonctionne, même s'il y a un chemin d'accès principal './'. Cependant, s'il y a un espace dans le nom de fichier, par exemple "./jacobus \ R.txt", l'utilisation de xargspourrait voir le "./jacobus" délimité par des espaces comme un fichier à passer sed, puis avec "R.txt" , dont aucun n’existe.
Brett Hale

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.