web-dev-qa-db-fra.com

sed beginner: changer toutes les occurrences d'un dossier

J'ai besoin de rechercher et de remplacer une expression régulière sur tous les fichiers d'un dossier (et de ses sous-dossiers). Quelle serait la commande Linux shell pour faire cela?

Par exemple, je veux exécuter cela sur tous les fichiers et remplacer l'ancien fichier par le nouveau texte remplacé.

sed 's/old text/new text/g' 
76
nickf

Il n'y a aucun moyen de le faire en utilisant uniquement sed. Vous devrez utiliser au moins l'utilitaire find ensemble:

find . -type f -exec sed -i.bak "s/foo/bar/g" {} \;

Cette commande va créer un .bak fichier pour chaque fichier modifié.

Notes:

  • Le -i l'argument de la commande sed est une extension GNU, donc, si vous exécutez cette commande avec le sed du BSD, vous devrez rediriger la sortie vers un nouveau fichier puis renommez-le.
  • L'utilitaire find n'implémente pas le -exec argument dans les anciennes boîtes UNIX, vous devrez donc utiliser un | xargs au lieu.
115
osantana

Je préfère utiliser find | xargs cmd plus de find -exec parce que c'est plus facile à retenir.

Cet exemple remplace globalement "foo" par "bar" dans les fichiers .txt au niveau ou en dessous de votre répertoire actuel:

find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g"

Le -print0 et -0 les options peuvent être omises si vos noms de fichiers ne contiennent pas de caractères géniaux tels que des espaces.

36
Dennis

Pour la portabilité, je ne me fie pas aux fonctionnalités de sed qui sont spécifiques à Linux ou BSD. À la place, j'utilise le script overwrite du livre de Kernighan et Pike sur l'environnement de programmation Unix.

La commande est alors

find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';'

Et le script overwrite (que j'utilise partout) est

#!/bin/sh
# overwrite:  copy standard input to output after EOF
# (final version)

# set -x

case $# in
0|1)        echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2
esac

file=$1; shift
new=/tmp/$$.new; old=/tmp/$$.old
trap 'rm -f $new; exit 1' 1 2 15    # clean up files

if "$@" >$new               # collect input
then
    cp $file $old   # save original file
    trap 'trap "" 1 2 15; cp $old $file     # ignore signals
          rm -f $new $old; exit 1' 1 2 15   # during restore
    cp $new $file
else
    echo "overwrite: $1 failed, $file unchanged" 1>&2
    exit 1
fi
rm -f $new $old

L'idée est qu'il écrase un fichier uniquement si une commande réussit. Utile dans find et aussi là où vous ne souhaitez pas utiliser

sed 's/old/new/g' file > file  # THIS CODE DOES NOT WORK

car le shell tronque le fichier avant que sed puisse le lire.

6
Norman Ramsey

Puis-je suggérer (après avoir sauvegardé vos fichiers):

find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';'
1
paxdiablo