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 Sieex
(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