Hoe kan ik de eerste regel van een tekstbestand verwijderen met bash/sed-script?

Ik moet herhaaldelijk de eerste regel uit een enorm tekstbestand verwijderen met een bash-script.

Op dit moment gebruik ik sed -i -e "1d" $FILE– maar het verwijderen duurt ongeveer een minuut.

Is er een efficiëntere manier om dit te bereiken?


Antwoord 1, autoriteit 100%

Probeer tail:

tail -n +2 "$FILE"

-n x: druk gewoon de laatste xregels af. tail -n 5zou je de laatste 5 regels van de invoer geven. Het +teken keert het argument om en laat tailalles afdrukken behalve de eerste x-1regels. tail -n +1zou het hele bestand afdrukken, tail -n +2alles behalve de eerste regel, enz.

GNU tailis veel sneller dan sed. tailis ook beschikbaar op BSD en de vlag -n +2is consistent in beide tools. Controleer de FreeBSDof OS Xman-pagina’s voor meer.

De BSD-versie kan echter veel langzamer zijn dan sed. Ik vraag me af hoe ze dat voor elkaar hebben gekregen; tailzou gewoon een bestand regel voor regel moeten lezen, terwijl sedbehoorlijk complexe bewerkingen uitvoert waarbij een script wordt geïnterpreteerd, reguliere expressies worden toegepast en dergelijke.

Opmerking: u kunt in de verleiding komen om

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

maar dit geeft je een leeg bestand. De reden is dat de omleiding (>) plaatsvindt voordat tailwordt aangeroepen door de shell:

  1. Shell kapt bestand $FILE
  2. af

  3. Shell maakt een nieuw proces voor tail
  4. Shell leidt stdout van het tail-proces om naar $FILE
  5. tailleest van de nu lege $FILE

Als je de eerste regel in het bestand wilt verwijderen, gebruik dan:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

De &&zorgt ervoor dat het bestand niet wordt overschreven als er een probleem is.


Antwoord 2, autoriteit 21%

U kunt -i gebruiken om het bestand bij te werken zonder ‘>’ exploitant. De volgende opdracht verwijdert de eerste regel uit het bestand en slaat deze op in het bestand (gebruikt een tijdelijk bestandachter de schermen) .

sed -i '1d' filename

Antwoord 3, autoriteit 6%

Voor degenen die SunOS gebruiken dat niet-GNU is, zal de volgende code helpen:

sed '1d' test.dat > tmp.dat 

Antwoord 4

Je kunt dit eenvoudig doen met:

cat filename | sed 1d > filename_without_first_line

op de opdrachtregel; of om de eerste regel van een bestand permanent te verwijderen, gebruik de in-place modus van sed met de -ivlag:

sed -i 1d <filename>

Antwoord 5

Nee, dat is ongeveer net zo efficiënt als je kunt krijgen. Je zou een C-programma kunnen schrijven dat het werk een beetje sneller zou kunnen doen (minder opstarttijd en verwerkingsargumenten), maar het zal waarschijnlijk naar dezelfde snelheid neigen als sed als bestanden groot worden (en ik neem aan dat ze groot zijn als het een minuut duurt ).

Maar uw vraag heeft hetzelfde probleem als zoveel andere, omdat het de oplossing veronderstelt. Als je ons in detail zou vertellen watje probeert te doen in plaats van hoe, kunnen we misschien een betere optie voorstellen.

Als dit bijvoorbeeld een bestand A is dat door een ander programma B wordt verwerkt, zou een oplossing zijn om de eerste regel niet te verwijderen, maar programma B aan te passen om het anders te verwerken.

Stel dat al uw programma’s aan dit bestand A worden toegevoegd en dat programma B momenteel de eerste regel leest en verwerkt voordat deze wordt verwijderd.

Je zou programma B opnieuw kunnen ontwerpen, zodat het niet probeert de eerste regel te verwijderen, maar een blijvende (waarschijnlijk op bestanden gebaseerde) offset in het bestand A handhaaft, zodat het de volgende keer dat het wordt uitgevoerd, ernaar kan streven offset, verwerk de lijn daar en werk de offset bij.

Vervolgens, op een rustige tijd (middernacht?), zou het een speciale verwerking van bestand A kunnen doen om alle momenteel verwerkte regels te verwijderen en de offset terug op 0 te zetten.

Het zal zeker sneller zijn voor een programma om een ​​bestand te openen en te zoeken in plaats van te openen en te herschrijven. Deze discussie gaat er natuurlijk van uit dat je controle hebt over programma B. Ik weet niet of dat het geval is, maar er kunnen andere mogelijke oplossingen zijn als je meer informatie geeft.


Antwoord 6

De spongeutilvermijdt de noodzaak om met een tijdelijk bestand te jongleren:

tail -n +2 "$FILE" | sponge "$FILE"

Antwoord 7

Als je het bestaande bestand wilt wijzigen, kun je altijd de originele edgebruiken in plaats van de streaming-opvolger sed:

ed "$FILE" <<<$'1d\nwq\n'

De opdracht edwas de originele UNIX-teksteditor, voordat er zelfs maar schermvullende terminals waren, laat staan ​​grafische werkstations. De ex-editor, het best bekend als wat u gebruikt wanneer u typt achter de dubbele punt-prompt in vi, is een uitgebreideversie van ed, zoveel van dezelfde commando’s werken. Hoewel edbedoeld is om interactief te worden gebruikt, kan het ook in batchmodus worden gebruikt door er een reeks opdrachten naar toe te sturen, wat deze oplossing doet.

De reeks <<<$'1d\nwq\n'maakt gebruik van de moderne shells-ondersteuning voor here-strings (<<<) en ANSI-aanhalingstekens ($'') om invoer te geven aan de opdracht eddie uit twee regels bestaat: 1d, die dregel 1verwijdert, en dan wq, die whet bestand terugstuurt naar schijf en dan qpast bij de bewerkingssessie.


Antwoord 8

Zoals Pax al zei, zul je waarschijnlijk niet sneller worden dan dit. De reden is dat er bijna geen bestandssystemen zijn die het afkappen vanaf het begin van het bestand ondersteunen, dus dit wordt een O(n)-bewerking waarbij nde grootte is van het bestand. Wat u echter veelsneller kunt doen, is de eerste regel overschrijven met hetzelfde aantal bytes (misschien met spaties of een opmerking) wat voor u zou kunnen werken, afhankelijk van wat u precies probeert te doen (wat is dat trouwens?).


Antwoord 9

Je kuntde aanwezige bestanden bewerken: gebruik gewoon de -i-vlag van perl, als volgt:

perl -ni -e 'print unless $. == 1' filename.txt

Hierdoor verdwijnt de eerste regel, zoals u vraagt. Perl zal het hele bestand moeten lezen en kopiëren, maar het zorgt ervoor dat de uitvoer wordt opgeslagen onder de naam van het originele bestand.


Antwoord 10

zou de regels moeten tonen behalve de eerste regel :

cat textfile.txt | tail -n +2

Antwoord 11

Kan vim hiervoor gebruiken:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

Dit zou sneller moeten zijn, omdat vim niet het hele bestand leest tijdens het proces.


Antwoord 12

Hoe zit het met het gebruik van csplit?

man csplit
csplit -k file 1 '{1}'

Antwoord 13

Deze oneliner is voldoende:

echo "$(tail -n +2 "$FILE")" > "$FILE"

Het werkt, aangezien tailwordt uitgevoerd vóór echoen dan wordt het bestand ontgrendeld, dus geen tijdelijk bestand nodig.


Antwoord 14

Omdat het klinkt alsof ik het verwijderen niet kan versnellen, denk ik dat een goede aanpak is om het bestand als volgt in batches te verwerken:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

Het nadeel hiervan is dat als het programma halverwege wordt afgebroken (of als er een slechte sql in zit – waardoor het “proces”-gedeelte doodgaat of vastloopt), er regels zullen zijn die worden overgeslagen, of tweemaal verwerkt.

(bestand1 bevat regels sql-code)


Antwoord 15

Als u wilt herstellen na een fout, kunt u gewoon een bestand opbouwen met wat u tot nu toe hebt gedaan.

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

Antwoord 16

Op basis van 3 andere antwoorden heb ik deze syntaxis bedacht die perfect werkt in mijn Mac OSx bash-shell:

line=$(head -n1 list.txt && echo "$(tail -n +2 list.txt)" > list.txt)

Testcase:

~> printf "Line #%2d\n" {1..3} > list.txt
~> cat list.txt
Line # 1
Line # 2
Line # 3
~> line=$(head -n1 list.txt && echo "$(tail -n +2 list.txt)" > list.txt)
~> echo $line
Line # 1
~> cat list.txt
Line # 2
Line # 3

Antwoord 17

Zou het lukken om staart op N-1 regels te gebruiken en dat naar een bestand te leiden, gevolgd door het verwijderen van het oude bestand en het hernoemen van het nieuwe bestand naar de oude naam?

Als ik dit programmatisch zou doen, zou ik het bestand doorlezen en de bestandsoffset onthouden, na het lezen van elke regel, zodat ik terug kon gaan naar die positie om het bestand met één regel minder erin te lezen.

>

Other episodes