Jak mohu manipulovat s daty založenými na poli z příkazového řádku? Například
- Jak mohu vytisknout pouze řádky, jejichž N-té pole je
foo
? ? - Jak mohu vytisknout pouze řádky, jejichž N-té pole není
foo
? - Jak mohu vytisknout pouze řádky, jejichž N-té pole odpovídá
foo
? - Jak mohu změnit pole N na
foo
?
Existuje standardní přístup nebo sada nástrojů, která usnadňuje manipulaci s daty založenými na poli na systémech *nix?
Přijatá odpověď:
Při práci s poli lze použít dva základní přístupy:i) použít nástroj, který polím rozumí; ii) použijte regulární výraz. První z nich je obvykle robustnější a jednodušší.
Mnoho z běžně dostupných nástrojů na *nix je buď výslovně navrženo tak, aby se vypořádalo s poli, nebo mají šikovné triky, které to usnadní.
1. Použijte nástroj, který rozumí polím
1.1 awk
Klasickým nástrojem je zde awk
. Automaticky rozdělí každý vstupní řádek na pole (oddělovač polí je ve výchozím nastavení prázdný, ale lze jej změnit pomocí -F
flag) a pole jsou pak dostupná pro awk
skript jako $n
kde n
je číslo pole. První pole je $1
, druhý $2
atd.
-
Tisk řádků, jejichž 3. pole je
foo
.awk '$3=="foo"' file
Změna oddělovače na
:
awk -F":" '$3=="foo"' file
Výchozí akce
awk
je tisknout. Výše uvedené příkazy tedy vytisknou všechny řádky, jejichž 3. pole jefoo
. Při použití-F
, můžete nastavit libovolné oddělovače polí a dokonce použít regulární výrazy. -
Jak mohu vytisknout pouze řádky, jejichž 3. pole není
foo
?awk '$3!="foo"' file
-
Jak mohu vytisknout pouze řádky, jejichž 3. pole odpovídá
foo
?Pokud pouze hledáte pole, která odpovídají vzoru (například
foo
odpovídáfoobar
), použijte~
místo==
:awk '$3~/foo/' file
-
Jak mohu vytisknout pouze řádky, jejichž 3. pole neodpovídá
foo
?awk '$3!~/foo/' file
-
Jak mohu změnit 3. pole na
foo
?awk '$3="foo"' file
1.2 Perl
Další možností je perl
jednovrstvé. Stejně jako awk je i Perl plnohodnotný skriptovací jazyk, ale lze jej spustit také jako program příkazového řádku, který jako vstup používá skript. Jeho chování je upraveno přepínači příkazového řádku, z nichž nejdůležitější pro tuto otázku jsou:
-e
:skript, kterýperl
by měl běžet;-n
:čtení vstupního souboru řádek po řádku;-p
:vytiskne každý vstupní řádek po použití skriptu daného-e
;-l
:odstranit koncové nové řádky z každého vstupního řádku a přidat nový řádek do každéhoprint
zavolat;-a
:awk-mode, rozdělit každý vstupní řádek do pole@F
;-F
:oddělovač pole pro-a
.
Důležitý rozdíl oproti awk
je to perl
's -a
přepínač rozdělí soubory do pole. V Perlu začínají pole na 0, ne na 1. To znamená, že 2. pole je ve skutečnosti $F[1]
a ne $F[2]
. S ohledem na toto vše, perl
ekvivalenty výše uvedených jsou:
-
Tisk řádků, jejichž 3. pole je
foo
.perl -ane 'print if $F[2] eq "foo"' file
Změna oddělovače na
:
perl -F":" -ane 'print if $F[2] eq "foo"' file
Na rozdíl od
awk
,perl
nelze použít regulární výrazy jako oddělovače polí. Musí to být konkrétní znak nebo řetězec. -
Jak mohu vytisknout pouze řádky, jejichž 3. pole není
foo
?perl -ane 'print unless $F[2] eq "foo"' file
-
Jak mohu vytisknout pouze řádky, jejichž 3. pole odpovídá
foo
?perl -ane 'print if $F[2]=~/foo/' file
-
Jak mohu vytisknout pouze řádky, jejichž 3. pole neodpovídá
foo
?perl -lane 'print unless $F[2]=~/foo/' file
-
Jak mohu změnit 3. pole na
foo
?Tenhle je v Perlu trochu těžkopádnější. Obvyklý přístup je změnit hodnotu v
@F
pole a poté pole vytiskněte. S jednoduchými soubory oddělenými mezerou je to snadné:perl -lane '$F[2]="foo"; print "@F"' file
S jiným oddělovačem se budete muset
join
pole. V opačném případě bude vytištěno odděleně:perl -F: -lane '$F[2]="foo"; print join ":",@F' file
2. Používejte regulární výrazy
Myšlenka je zde použít regulární výraz (zkráceně „regex“), který definuje pozici cílového řetězce v řádku. Například v souboru, jehož pole jsou oddělena :
, můžeme najít 2. pole tak, že porovnáme vše až do 1. :
(1. pole) a poté hledejte druhé:
^[^:]*:[^:]*:
Tento regulární výraz znamená:
^
:začátek řádku;[^]
:negovaná třída znaků.[^:]
znamená „cokoli kromě:
“;*
:0 nebo více z předchozího vzoru;:
:doslovný:
;
Dohromady to znamená, že první [^:]*
je první pole a druhé je druhé pole. Je zřejmé, že to není příliš praktické, pokud hledáte 14. pole, ale může být užitečné pro jednodušší věci. Jak to tedy implementujeme, abychom manipulovali s našimi daty? Existují různé nástroje, které to umí; v těchto příkladech budu používat sed
ale velmi podobné věci můžete dělat s awk
, perl
nebo python
.
-
Jak mohu vytisknout pouze řádky, jejichž 2. pole je
foo
?sed -n '/^[^:]*:foo:/p' file
-n
potlačí normální výstup a/regex/p
znamená „vytisknout všechny řádky, které odpovídaly regulárnímu výrazu. -
Jak mohu vytisknout pouze řádky, jejichž 2. pole není
foo
?sed '/^[^:]*:foo:/d' file
Logická inverze k výše uvedenému. Zde je
/regex/d
znamená „smazat všechny řádky, kterým odpovídá regulární výraz. -
Jak mohu vytisknout pouze řádky, jejichž 2. pole odpovídá
foo
?sed -n '/^[^:]*:[^:]*foo/p' file
-
Jak mohu vytisknout pouze řádky, jejichž 2. pole neodpovídá
foo
?sed '/^[^:]*:[^:]*foo/d' file
-
Jak mohu změnit 2. pole na
foo
?sed 's/([^:]*:)[^:]*/1foo/' file
Nebo, protože
sed
substituce může přímo řešit výskyt vzorů jeho opakováním s jednoduchým číselným příznakem:sed 's/[^:]*/foo/2' file