Jag behöver lite hjälp för att ta reda på hur man använder kommandot sed för att endast visa den första kolumnen och den sista kolumnen i en textfil. Här är vad jag hittills har för kolumn 1:
cat logfile | sed "s/\|/ /"|awk "{print $1}"
Mitt svaga försök att få den sista kolumnen att visas också var:
cat logfile | sed "s/\|/ /"|awk "{print $1}{print $8}"
Detta tar dock den första kolumnen och den sista kolumnen och slår samman dem i en lista. Finns det ett sätt att skriva ut den första och den sista kolumnen tydligt med sed- och awk-kommandon?
Exempelinmatning:
foo|dog|cat|mouse|lion|ox|tiger|bar
Kommentarer
- Ange en del exempelinmatning.
Svar
Nästan där. Lägg bara båda kolumnreferenser bredvid varandra.
cat logfile | sed "s/|/ /" | awk "{print $1, $8}"
Observera också att du inte behöver cat
här .
sed "s/|/ /" logfile | awk "{print $1, $8}"
Observera också att du kan säga awk
att kolumnavgränsarna är |
istället för tomma, så behöver du inte heller sed
.
awk -F "|" "{print $1, $8}" logfile
Enligt förslag av Caleb , om du vill ha en lösning som fortfarande matar ut det sista fältet , även om det inte finns exakt åtta, kan du använda $NF
.
awk -F "|" "{print $1, $NF}" logfile
Om du vill ha utdata för att behålla separatorerna |
, istället för att använda ett mellanslag kan du ange utmatningsfältavgränsarna. Tyvärr är det lite klumpigare än att bara använda flaggan -F
, men här är tre metoder.
-
Du kan tilldela ingången och utmatningsfältavskiljare i
awk
i BEGIN-blocket.awk "BEGIN {FS = OFS = "|"} {print $1, $8}" logfile
-
Du kan tilldela dessa variabler när du ringer till
awk
från kommandoraden via flaggan-v
.awk -v "FS=|" -v "OFS=|" "{print $1, $8}" logfile
-
eller helt enkelt:
awk -F "|" "{print $1 "|" $8}" logfile
Kommentarer
- Bra jobb med att bryta ner hur detta problem kan förenklas. Du kan lägga till en anteckning om hur du använder
|
som en utmatningsavgränsare istället för standardutrymmet för strängsammankoppling. Du kan också förklara att du använder$NF
istället för hårdkodning$8
för att få den sista kolumnen. - efter det hur man uppdaterar filen?
- @pankajprasad Skriv till en ny fil med h
>
skriv sedan över den gamla, eller användsponge
. Det här är verkligen en ny fråga. - @Sparhawk det fungerar, men innehållet raderas raderas. hur ska man hantera det?
- @pankajprasad Du måste ställa en ny fråga. Klicka på den stora blå knappen uppåt som säger ” Ställ fråga ”.
Svar
Du använder awk
ändå:
awk "{ print $1, $NF }" file
Kommentarer
- Skulle ’ t du behöver ange inmatningsfältavgränsaren (eftersom det i det här fallet verkar vara
|
snarare det utrymmet) med-F\|
eller liknande? Tänk också om han ville använda samma avgränsare för utdata? - @Caleb Förmodligen: Jag väntade på att OP skulle bekräfta hur exakt ingången såg ut, snarare än att försöka gissa baserat på de icke-fungerande exemplen …
- Observera att det antas att ingången innehåller minst två fält.
- @St é phaneChazelas OP angav tydligt i koden att det alltid har åtta fält.
- @ michaelb958 Jag tror att ” tydligt ” överdriver fallet, bara lite 🙂
Svar
Byt bara från första till sista |
med en |
(eller mellanslag om du föredrar):
sed "s/|.*|/|/"
Observera att även om det inte finns någon sed
implementering där |
är speciell (så länge som utökad regelbunden uttryck är inte aktiverade via -E
eller i vissa implementeringar), \|
i sig är speciellt i vissa som GNU sed
. Så du bör inte fly |
om du tänker att den ska matcha |
karaktär.
Om du ersätter med mellanslag och om ingången redan kan innehålla rader med endast en |
, måste du behandla det speciellt som |.*|
vann inte match på dessa.Det kan vara:
sed "s/|\(.*|\)\{0,1\}/ /"
(det vill säga att .*|
-delen är valfri) Eller:
sed "s/|.*|/ /;s/|/ /"
eller:
sed "s/\([^|]*\).*|/\1 /"
Om du vill ha första och åttonde fälten oavsett antalet fält i ingången, då är det bara:
cut -d"|" -f1,8
(alla de skulle fungera med alla POSIX-kompatibla verktyg förutsatt att ingången bildar giltig text (i synnerhet sed
kommer vanligtvis inte att fungera om ingången har byte eller sekvenser av byte som inte bildar giltiga tecken i den aktuella platsen som t.ex. printf "unix|St\351phane|Chazelas\n" | sed "s/|.*|/|/"
i ett UTF-8-språk)).
Svar
Om du tycker att du är obekväm och lugnare kan du uppnå samma sak med coreutils:
paste <( cut -d"|" -f1 file) \ <(rev file | cut -d"|" -f1 | rev)
Kommentarer
-
cut
är renare och mer kompakt än awk / sed när du bara är intresserad av den första kolumnen, eller om avgränsningarna är fasta (dvs inte ett varierande antal mellanslag). - Ganska elegant!
Svar
Det verkar som om du försöker få det första och sista textfältet som avgränsas av |
.
Jag antar att din loggfil innehåller texten som nedan,
foo|dog|cat|mouse|lion|ox|tiger|bar bar|dog|cat|mouse|lion|ox|tiger|foo
Och du vill utdata som,
foo bar bar foo
Om ja, då kommer kommandot för dina ”s
Genom GNU sed,
sed -r "s~^([^|]*).*\|(.*)$~\1 \2~" file
Exempel:
$ echo "foo|dog|cat|mouse|lion|ox|tiger|bar" | sed -r "s~^([^|]*).*\|(.*)$~\1 \2~" foo bar
Kommentarer
- Kolumnerna avgränsas inte av ett rör | men de finns i kolumner, jag är intresserad av att använda sed men inte använda kommandot awk som du gjorde i ditt kommando: sed -r ’ s ~ ^ ([^ |] *) . * \ | (. *) $ ~ \ 1 \ 2 ~ ’ fil
- ” Kolumnerna är avgränsas inte av ett rör | men de finns i kolumner ”, menar du att kolumner är åtskilda av mellanslag?
- En provingång och en utdata skulle vara bättre.
Svar
Du borde nog göra det med sed
– jag skulle ändå göra det – men bara för att ingen har skrivit den här ännu:
while IFS=\| read col1 cols do printf %10s%-s\\n "$col1 |" " ${cols##*|}" done <<\INPUT foo|dog|cat|mouse|lion|ox|tiger|bar INPUT
UTGÅNG
foo | bar