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

  • sed -i ... verandert de bestandstijden zelfs als de stream ongewijzigd is.
  • @jww: je ' hebt gelijk, dat ' s omdat is een stream-editor en geen bestandseditor, dus de optie -i schendt de filosofie van Unix ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Als je dat wilt vermijden, gebruik dan ex (bijv. ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah).

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 

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *