Hier is mijn commando (opzettelijk breken):
grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i "" "/FOO/{s/FOO/BAR/g; w /dev/stdout }"
Aan de hoge kant -level: grep
voor FOO in de blah
directory; pijp alleen de bestandsnaam in (vanwege -l
) naar sed
; sed
voert een inline vervanging uit (-i ""
) en drukt af alleen de gewijzigde term in /dev/stdout
.
Als ik de -l
zou weglaten en pipe, ik krijg dit terug van grep
:
/Users/gjtorikian/blah/baz.cs:1:FOO /Users/gjtorikian/blah/bar.js:1:FOO
Wat ik wil is sed
om de inline vervanging uit te voeren, en laat me dan het bestand en de term zien die vervangen zijn; bijvoorbeeld:
/Users/gjtorikian/blah/baz.cs:1:BAR /Users/gjtorikian/blah/bar.js:1:BAR
Is zoiets mogelijk? Als het ertoe doet, geef ik er de voorkeur aan om het alleen te houden met grep
/ sed
. Moet ik een tweede grep
na het ?
Reacties
- Ik zie ' geen manier om haal de huidige bestandsnaam op in sed, dus je hebt waarschijnlijk een tweede grep nodig.
- Ook heb je ' de , dat ' de standaard is.
- In OS X is
''
niet de standaard .
Answer
Een mogelijkheid zou zijn om de uitvoer van grep
naar een afzonderlijk 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/"
Je zou sed
het regelnummer kunnen laten afdrukken (met het =
commando) wanneer het een overeenkomst vindt en verder doen postprocessing.
Het zou waarschijnlijk duidelijker zijn om awk te gebruiken.
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
Antwoord
Zoals ik “mn omdat ik geen Engels als moedertaal heb, heb ik het waarschijnlijk niet begrepen.
Om een map te “grepen” heb je “-r” nodig. Bij gebruik van “-l” wordt alleen de bestandsnaam afgedrukt en het grijpen stopt na de eerste keer dat het voorkomt.
# 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
Antwoord
Ik denk dat je” het probleem te ingewikkeld maakt door het in één keer te proberen. Je kunt eenvoudig een
grep -H -n /Users/gjtorikian/blah | sed "s/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/"
doen om de lijst met bestanden met regelnummers en vervangingen ophalen (dit zou moeten werken, zolang uw bestandsnamen geen dubbele punten bevatten, maar dat is sowieso een slecht idee in Mac OS …). Daarna kunt u een
sed -i "" "s/FOO/BAR/g" /Users/gjtorikian/blah
Hier is geen grep en xarg nodig (je kunt wel een “find … | xarg” doen als je veel bestanden hebt). Als je je zorgen maakt over de duplicaties, kun je de twee regels in een script of functie plaatsen en daar variabele vervangingen uitvoeren.
Opmerkingen
Antwoord
Vervang FOO recursief door BAR in de huidige map (stille modus)
grep -rl "FOO" | xargs sed -i "s/FOO/BAR/"
Antwoord
One liner
grep -n FOO * -R|awk -F\: "{ print $1 }"|sort|uniq|while read file; do sed -i "s/FOO/BAR/g" $file; done
sed -i ...
verandert de bestandstijden zelfs als de stream ongewijzigd is.-i
schendt de filosofie van Unix ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Als je dat wilt vermijden, gebruik danex
(bijv.ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah
).