Toon posts:

Find and Replace op Unix

Pagina: 1
Acties:

Verwijderd

Topicstarter
Hi,

Ik ben aan het zoeken naar een methode om een regel text te vervangen in een file, zonder deze te hoeven openen met bijvoorbeeld "pico".

Het gaat om 400 files in één directory, waarvan de tweede text regel vervangen moet worden. Overal staat hetzelfde op de tweede regel, en overal moet eenzelfde stukje text daarvoor in de plaats komen.

Wie weet het antwoord?

Tx in @vance!

Verwijderd

info sed
perldoc perl
man grep
info gawk

Ziezo, dat zijn alle tools die je nodig hebt. :)

Verwijderd

Topicstarter
Thanx. :)
Dat wordt wordt dus wel even een uitzoek werk.

Kun je iets meer vertellen?

info sed = om find & replace (gemaakt met gawk) te laten uitvoeren?
perldoc perl
man grep = om de files waarbij ik dat wil te definieren?
info gawk = om te definieren wat het script moet doen?

:?

Verwijderd

aan alleen sed (Stream EDitor) heb je in principe al genoeg...

  • phreggle
  • Registratie: Juni 2002
  • Niet online
Zoiets wordt het dan (ongetest) (een beetje getest en het lijkt te werken :) ):
code:
1
for i in *; do cat $i | sed '2,1 s/source/dest/' > $i; done

Waarin de tekst 'source' en 'dest' de desbetreffende strings zijn (die altijd hetzelfde waren).

Succes ermee en vergeet niet eerst een backup te maken :)

Verwijderd

Jij zoekt naar Regular Expressions. Een manier om kort, maar krachtig naar een bepaalde string te zoeken, en iets met die string te doen, wanneer ie gevonden is.

Als die programma'tjes kunnen eigenlijk door elkaar gebruikt worden (TMTOWTDI: There's More Than One Way To Do It). Je neemt er gewoon die uit waarvan je de documentatie het beste snapt.

Verwijderd

ok, scriptje, origineel bedoel om wat nameserverzooi te doen, maar uitstekend door jou te gebruiken :

script zelf :
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
&LoadImportData;


foreach $ll (@domeindata) {
  $rewrite = 0;
  @filedata = 0;
  $file = $ll;
  $file =~ s/ //g;
  open(DATA, "./$file");
  flock(DATA, 2);
  $teller = 0;
  while($linein = <DATA>) {
    chomp $linein;
    if($linein =~ /BRONREGEL/) {
    $linein =~ s/BRONREGEL/DOELREGEL/g;
    $rewrite = 1;
    }
    $filedata[$teller] = $linein;
    $teller++;
  }
  flock(DATA, 8);
  close(DATA);

  if($rewrite eq 1) {
    open(DATA, "+<./$file");
    flock(DATA, 2);
    truncate(DATA, 0);
    seek(DATA, 0, 0);
    foreach $fline (@filedata) {
    print DATA "$fline\n";
    }
    flock(DATA, 8);
    close(DATA);
  }


}
exit;



sub LoadImportData {
 open(DATA, "./import");
 flock(DATA, 2);
 $teller = 0;
 while($linein = <DATA>) {
      chomp $linein;
      $domeindata[$teller] = $linein;
      $teller++;
 }
 flock(DATA, 8);
 close(DATA);
}

doe eerst
code:
1
ls > import

en dan
code:
1
perl filename.pl

Verwijderd

Ik zou het met sed doen. Stel dat alle files op .txt eindigen:
code:
1
2
3
4
for i in *.txt
do
    sed '2 s/.*/nieuwe tekst/' $i > $i.new
done

Dit vervangt gewoon de hele 2e regel (ongeacht de inhoud) door de tekst "nieuwe tekst".

na inspectie van de *.txt.new bestanden (om te zien of ze goed zijn) kun je ze dan hernoemen naar de oude extensie (waarbij de oude files verloren gaan):
code:
1
2
3
4
for i in *.txt
do
    mv -f $i.new $i
done

tiepvouten voorbehouden, succes!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 14-08 09:26

deadinspace

The what goes where now?

Op zaterdag 22 juni 2002 15:41 schreef phreggle het volgende:
code:
1
for i in *; do cat $i | sed '2,1 s/source/dest/' > $i; done
Daar zou ik mee oppassen; sed kan niet altijd even goed omgaan met files die hij moet lezen en dan tegelijkertijd overschreven worden (slechte ervaringen mee). wbsofts manier is dan verstandiger.

  • phreggle
  • Registratie: Juni 2002
  • Niet online
Op zaterdag 22 juni 2002 20:36 schreef deadinspace het volgende:

[..]

Daar zou ik mee oppassen; sed kan niet altijd even goed omgaan met files die hij moet lezen en dan tegelijkertijd overschreven worden (slechte ervaringen mee). wbsofts manier is dan verstandiger.
Ik ken het probleem idd, maar ik dacht dat sed het alleen lastig had met een dubbele redirect (in de zin van: sed 's/bla/boe/' < file > file). Bij mijn linux installatie gaat het met cat ... wel goed trouwens. :)

Maar je hebt wel gelijk dat extra zekerheid nooit verkeerd is. (extra backups ook niet :D )

Verwijderd

Op zaterdag 22 juni 2002 20:41 schreef phreggle het volgende:

[..]

Ik ken het probleem idd, maar ik dacht dat sed het alleen lastig had met een dubbele redirect (in de zin van: sed 's/bla/boe/' < file > file). Bij mijn linux installatie gaat het met cat ... wel goed trouwens. :)

Maar je hebt wel gelijk dat extra zekerheid nooit verkeerd is. (extra backups ook niet :D )
de constructie sed < file > file zal altijd misgaan, en dat ligt niet aan sed, maar aan de shell, die opent namelijk de files, en geeft de open filehandles door aan sed (of welk ander programma ook). Sed leest in dat geval gewoon van stdin en schrijft naar stdout.

De shell opent dus "file" om te lezen, en vervolgens "file" om te schrijven. Bij de laatste actie wordt de file ge'truncate', afgekapt dus, daardoor zal sed ook niets meer lezen.

Dus volgens mij is werken via een tempfile altijd het beste. Wat ook kan is zeg maar andersom:
code:
1
2
3
4
5
6
SED_EXPRESSIE='2 s/.*/nieuwe tekst/'
for i in *.txt
do
    cp "$i" "$i.bak" &&
    sed "$SED_EXPRESSIE"  "$i.bak" > "$i"
done

dan worden de tekstfiles altijd gebackupt. Ook zal de sed expressie niet worden uitgevoerd als het maken van de backup (.bak) niet lukte.

Ik heb ook nog even dubbele aanhalingstekens toegevoegd, zodat eventuele spaties in de filenamen netjes worden getolereerd :-)

Verwijderd

trouwens....
Op zaterdag 22 juni 2002 20:41 schreef phreggle het volgende:
Bij mijn linux installatie gaat het met cat ... wel goed trouwens. :)
Dat het met cat wel lukt is misschien geluk met een wat kleinere file: cat buffert eerst een deel (ik weet niet precies hoeveel) en dat krijgt sed al binnen blijkbaar, voordat de file getruncate wordt.

Ik weet trouwens niet precies wat er gebeurt als een file geopend wordt om te lezen en daarna getruncate door een 'open for write'. Kan er dan nog wel gelezen worden op andere plaatsen dan het begin van de file?? (FileSystem guru's .... waar zijn jullie :) )

(Ik weet in elk geval dat je in Unix/Linux rustig met rm een file kan wissen terwijl die nog geopend is; zolang een file geopend is wordt hij niet echt gewist, zogauw hij echter gesloten wordt is hij ook ineens echt verdwenen.)

  • phreggle
  • Registratie: Juni 2002
  • Niet online
Op zaterdag 22 juni 2002 21:37 schreef wbsoft het volgende:
trouwens....
[..]

Dat het met cat wel lukt is misschien geluk met een wat kleinere file: cat buffert eerst een deel (ik weet niet precies hoeveel) en dat krijgt sed al binnen blijkbaar, voordat de file getruncate wordt.

Ik weet trouwens niet precies wat er gebeurt als een file geopend wordt om te lezen en daarna getruncate door een 'open for write'. Kan er dan nog wel gelezen worden op andere plaatsen dan het begin van de file?? (FileSystem guru's .... waar zijn jullie :) )

(Ik weet in elk geval dat je in Unix/Linux rustig met rm een file kan wissen terwijl die nog geopend is; zolang een file geopend is wordt hij niet echt gewist, zogauw hij echter gesloten wordt is hij ook ineens echt verdwenen.)
Inderdaad, je hebt helemaal gelijk :D
Eerlijk gezegd heb ik hier nog nooit zo bij stil gestaan. Net ff snel een testje gedaan:
code:
1
2
3
4
5
6
7
8
9
phreggle@lappie test $ cp /usr/share/dict/words .
phreggle@lappie test $ ls -l words 
-rw-r--r--    1 phreggle users     2486824 Jun 22 21:40 words
phreggle@lappie test $ wc -l words 
 234937 words
phreggle@lappie test $ cat words | sed '2,1 s/.*/bla/' > words 
phreggle@lappie test $ wc -l words 
    838 words
phreggle@lappie test $

Oeps :o

Nu hebben we wel experimenteel vastgelegd dat die inlees-buffer rond de 838 regels ligt :+

Verwijderd

mja.. dat kun je ook gewoon met emacs doen :P

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 14-08 09:26

deadinspace

The what goes where now?

Op zaterdag 22 juni 2002 21:37 schreef wbsoft het volgende:
Ik weet trouwens niet precies wat er gebeurt als een file geopend wordt om te lezen en daarna getruncate door een 'open for write'. Kan er dan nog wel gelezen worden op andere plaatsen dan het begin van de file?? (FileSystem guru's .... waar zijn jullie :) )
Hangt er vanaf hoe de file geopend wordt voor schrijven. Als hij O_CREAT geopend wordt kun je gewoon nog overal lezen in de file, en als hij O_CREAT | O_TRUNC geopend wordt, dan lezen alle read()s 0 bytes (totdat er data in is geschreven).
En de stdout redirection van bash opent hem O_CREAT | O_TRUNC.
Op zaterdag 22 juni 2002 21:48 schreef phreggle het volgende:
code:
1
2
phreggle@lappie test $ wc -l words 
    838 words

Nu hebben we wel experimenteel vastgelegd dat die inlees-buffer rond de 838 regels ligt :+
code:
1
2
3
4
5
6
7
[marcelm@nothing marcelm]$ cp -L /usr/share/dict/words .
[marcelm@nothing marcelm]$ cat words | sed '2,1 s/.*/bla/' > words
[marcelm@nothing marcelm]$ wc -l words 
    851 words
[marcelm@nothing marcelm]$ ls -l words 
-rw-r--r--    1 marcelm  marcelm    8190 Jun 22 22:42 words
[marcelm@nothing marcelm]$

Een buffer van 8 KB lijkt mij waarschijnlijker ;)

  • phreggle
  • Registratie: Juni 2002
  • Niet online
Op zaterdag 22 juni 2002 23:07 schreef deadinspace het volgende:

[..]

Een buffer van 8 KB lijkt mij waarschijnlijker ;)
Daarom stond er een :+ smiley achter, want een regel zegt natuurlijk niks :)

Verwijderd

Topicstarter
Ontzettend bedankt iedereen. Onderstaand was voor mij de DE oplossing! :)
Op zaterdag 22 juni 2002 18:56 schreef wbsoft het volgende:
Ik zou het met sed doen. Stel dat alle files op .txt eindigen:
code:
1
2
3
4
for i in *.txt
do
    sed '2 s/.*/nieuwe tekst/' $i > $i.new
done

Dit vervangt gewoon de hele 2e regel (ongeacht de inhoud) door de tekst "nieuwe tekst".

na inspectie van de *.txt.new bestanden (om te zien of ze goed zijn) kun je ze dan hernoemen naar de oude extensie (waarbij de oude files verloren gaan):
code:
1
2
3
4
for i in *.txt
do
    mv -f $i.new $i
done

tiepvouten voorbehouden, succes!

Verwijderd

replace is ook een prima alternatief voor sed, en kfilereplace is een grafisch homesite-achtig search&replace programma voor kde.
Pagina: 1