Hier ist mein Befehl (absichtlich abbrechen):

grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i "" "/FOO/{s/FOO/BAR/g; w /dev/stdout }" 

Hoch -level: grep für FOO im Verzeichnis blah; Pipe nur im Dateinamen (wegen -l) bis sed; sed führt eine Inline-Ersetzung durch (-i "") und druckt nur der geänderte Begriff in /dev/stdout.

Wenn ich den -l weglassen würde und Pipe, ich bekomme dies zurück von grep:

/Users/gjtorikian/blah/baz.cs:1:FOO /Users/gjtorikian/blah/bar.js:1:FOO 

Ich möchte sed, um das Inline-Ersetzen durchzuführen, und zeigen Sie mir dann die ersetzte Datei und den ersetzten Begriff. Beispiel:

/Users/gjtorikian/blah/baz.cs:1:BAR /Users/gjtorikian/blah/bar.js:1:BAR 

Ist so etwas möglich? Wenn es darauf ankommt, würde ich es vorziehen, es nur mit grep / sed zu behalten. Muss ich eine zweite grep nach dem ?

Kommentare

  • Ich sehe ' keinen Weg zu Rufen Sie den aktuellen Dateinamen in sed ab, sodass Sie wahrscheinlich einen zweiten grep benötigen.
  • Außerdem benötigen Sie ' nicht die , dass ' die Standardeinstellung ist.
  • Unter OS X ist '' nicht die Standardeinstellung .

Antwort

Eine Möglichkeit wäre, die Ausgabe von grep zu einem separaten sed Filter.

 grep -l FOO "/Users/gjtorikian/blah/"* | { tee /dev/fd/3 | xargs sed -i -e "/FOO/{" -e "s/FOO/BAR/g" -e "w /dev/stdout" } 3>&1 | sed "s/:FOO$/:BAR/"  

Sie können sed veranlassen, die Zeilennummer (mit dem Befehl =) zu drucken, wenn eine Übereinstimmung gefunden wird, und weitere Schritte ausführen Nachbearbeitung.

Es wäre wahrscheinlich klarer, awk zu verwenden.

 for x in "/Users/gjtorikian/blah/"*; do awk " sub(/FOO/, "BAR") {found=1; print FILENAME ":" NR ":" "BAR" >"/dev/stderr"} 1 {print} END {exit(!found)} " "$x" >"$x.tmp" && mv "$x.tmp" "$x" done  

Antwort

Wie ich „mn Ich habe es wahrscheinlich nicht verstanden.

Um ein Verzeichnis zu „grep“, benötigen Sie „-r“. Die Verwendung von „-l“ gibt nur den Dateinamen aus und hört nach dem ersten Auftreten auf zu greifen.

# pattern=/home ; grep -l "$pattern" /etc/[a-z]* 2>/dev/null | while read line ; do echo "$line:$pattern" ; done /etc/adduser.conf:/home /etc/fstab:/home /etc/fstab~:/home /etc/libuser.conf:/home /etc/mpd.conf:/home /etc/mpd.conf~:/home /etc/mtab:/home /etc/netscsid.conf:/home /etc/passwd:/home /etc/passwd-:/home /etc/sudoers:/home /etc/sudoers.tmp~:/home 

Antwort

Ich denke, Sie machen das Problem zu kompliziert, indem Sie versuchen, es auf einmal zu tun. Sie können einfach ein

grep -H -n /Users/gjtorikian/blah | sed "s/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/" 

ausführen Holen Sie sich die Liste der Dateien mit Zeilennummern und Ersetzungen (dies sollte funktionieren, solange Ihre Dateinamen keine Doppelpunkte enthalten, aber das ist unter Mac OS sowieso eine schlechte Idee …). Anschließend können Sie ein

ausgeben

sed -i "" "s/FOO/BAR/g" /Users/gjtorikian/blah 

Hier wird kein grep und xarg benötigt (Sie können jedoch „find … | xarg“ ausführen, wenn Sie viele Dateien haben). Wenn Sie besorgt sind Über die Duplikate können Sie die beiden Zeilen in ein Skript oder eine Funktion einfügen und dort Variablensubstitutionen durchführen.

Kommentare

  • sed -i ... ändert die Dateizeitzeiten, auch wenn der Stream unverändert bleibt.
  • @jww: Sie ' haben Recht, dass ' s weil ist ein Stream-Editor und kein Datei-Editor. Daher verstößt die Option -i gegen die Unix-Philosophie ( mywiki.wooledge) .org / BashFAQ / 021 # Using_nonstandard_tools ). Wenn Sie dies vermeiden möchten, verwenden Sie ex (z. B. ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah).

Antwort

Ersetzen Sie FOO rekursiv durch BAR im aktuellen Verzeichnis (leiser Modus)

grep -rl "FOO" | xargs sed -i "s/FOO/BAR/" 

Antwort

Ein Liner

grep -n FOO * -R|awk -F\: "{ print $1 }"|sort|uniq|while read file; do sed -i "s/FOO/BAR/g" $file; done 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.