Eu tenho um arquivo neste formato:
[#] OWNER_NAME NAME SIZE
[6] Robottinosino Software 200
[42] Robottinosino Ideas worth zero 188
[12] Robottinosino Ideas worth zero or more 111
[13] I am Batman Hardware 180
[25] Robottinosino Profile Pictures 170
e eu gostaria de poder fazer o seguinte usando ferramentas de linha de comando:
my_command "Ideas worth zero"
e obtenha este resultado:
42
e não correr o risco de obter este resultado:
12
Pensei em usar grep para identificar a linha, awk para obter o primeiro campo, mas não tenho certeza de como corresponder de maneira confiável e eficiente em todo o campo 'NAME', sem contar em qual coluna o texto 'OWNER_NAME' e 'SIZE' apareça no cabeçalho e obtenha tudo o que está no meio com alguns cortes de espaços em branco.
Observe que 'OWNER_NAME' pode ter mais de uma palavra: por exemplo, 'OWNER_NAME' = "Eu sou o Batman".
Alguma idéia com implementação concomitante?
O que tenho que seguir aqui é apenas a velha família de cat, head, tail, awk, sed, grep, cut, etc.
Responder1
OK, se o comprimento das colunas não for conhecido, eu mudaria para uma linguagem mais poderosa que o bash:
#!/usr/bin/perl
use warnings;
use strict;
my $string = shift;
open my $FH, '<', '1.txt' or die $!;
my $first_line = <$FH>;
my ($before, $name) = $first_line =~ /(.* )(NAME *)/;
my $column = length $before;
$string .= ' ' x (length($name) - length $string); # adjust the length of $string
while (<$FH>) {
if ($column == index $_, $string, $column) {
/^\[([0-9]+)\]/ and print "$1\n";
}
}
Responder2
Se as larguras dos campos forem constantes - ou seja, o formato do arquivo que você mostrou com as larguras dos campos que você possui estão no máximo - você pode usar GNU awk ( gawk(1)
) e definir a FIELDWIDTHS
variável para usar análise de largura fixa:
gawk -v searchstr="Ideas worth zero" -- '
BEGIN { FIELDWIDTHS="6 15 27 5" } # assuming the final field width is 5
# Pre-process data
{
gsub(/[^[:digit:]]/, "", $1) # strip out non-numbers
for (i = 2; i <= NF; i++)
gsub(/[[:space:]]*$/, "", $i) # strip trailing whitespace
}
# match here
$3 == searchstr { print $1 }
' file.txt
Você pode agrupar isso em um script de shell ou em uma função e parametrizar searchstr
( -v searchstr="$1"
).
No entanto, se os campos tiverem largura variável - ou seja, se os dados mudarem, a largura dos campos poderá mudar - você precisará ser um pouco mais inteligente e determinar dinamicamente as larguras dos campos inspecionando a primeira linha. Dado que um campo é chamado OWNER_NAME
, usando um sublinhado, estou assumindo que os espaços não estão presentes nos nomes dos campos, então posso assumir que os espaços em branco separam os nomes dos campos.
Com isso definido, você pode substituir a BEGIN...
linha por este código:
NR == 1 {
for (i = 2; i <= NF; i++)
FIELDWIDTHS=FIELDWIDTHS index($0" ", " "$i" ")-index($0" ", " "$(i-1)" ") " "
FIELDWIDTHS=FIELDWIDTHS "5" # assuming 5 is the width of the last field
next
}
Isso examinará os campos na primeira linha e calculará as larguras dos campos calculando a diferença entre as posições dos campos subsequentes do segundo ao último campo. Presumi que a largura do último campo fosse 5, mas acho que você pode simplesmente colocar um número grande lá e funcionará com o que sobrou.
Precisamos procurar um espaço antes e depois do nome para garantir que não encontraremos NAME
dentro OWNER_NAME
(ou se havia um campo chamado OWNER
) e, em vez disso, corresponder o campo inteiro (também precisamos anexar um espaço para $0
garantir que possamos corresponder a um espaço no final, mesmo que não houvesse nenhum lá).
Você poderia ser mais sofisticado para poder consultar por nome de campo em vez de corresponder apenas on $3
, mas deixarei isso para você.
Responder3
Provavelmente o mais simples é filtrar as linhas primeiro por 'Ideias que valem zero e depois descartar as linhas'... ou mais':
grep 'Ideas worth zero' | grep -v 'Ideas worth zero or more'
E para obter o número desse canal, insira:
cut -d' ' -f1 | tr -d ']['
Que corta o primeiro campo (delimitado por um espaço) e removendo os colchetes.
O melhor seria se você pudesse alterar ligeiramente o formato do arquivo de forma que ele venha com delimitadores de campo adequados.
Responder4
Isso pode ajudá-lo:
function my_command () {
sed -n $(cut -b22-48 1.txt |
grep -n "$1"' *$' |
cut -f1 -d: )p 1.txt \
| cut -d' ' -f1 | tr -d ']['
}
Ele corta apenas a coluna relevante da entrada, procura o número da linha onde a string aparece, depois pega essa linha e mantém apenas o número da primeira coluna dela.