cat ./text | awk ' { if ( length > x ) { x = length; y = $0 } }END{ print y }'
UPD : resumindo todos os conselhos nos comentários
awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text
Estou procurando o método mais simples para imprimir a linha mais longa em um arquivo. Eu fiz algumas pesquisas e surpreendentemente não consegui encontrar uma resposta. Costumo imprimir o comprimento da linha mais longa em um arquivo, mas não sei como imprimir realmente a linha mais longa. Alguém pode fornecer uma solução para imprimir a linha mais longa em um arquivo? Desde já, obrigado.
sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Primeiro, lê o arquivo dentro da substituição do comando e produz o comprimento da linha mais longa, (anteriormente, expand
converte as tabulações em espaços, para superar a semântica de wc -L
- cada tab na linha adicionará < em> 8 em vez de 1 para o comprimento da linha). Esse comprimento é então usado em uma expressão sed
que significa "encontre uma linha com esse número de caracteres, imprima e feche". Então, isso pode ser o ideal, já que a linha mais longa está próxima do topo do arquivo, heheh (obrigado pelos comentários impressionantes e construtivos).
Outra, eu pensei antes do sed (em bash):
#!/bin/bash
while read -r line; do
(( ${#line} > max )) && max=${#line} && longest="$line"
done
echo "$longest"
Aqui está uma solução Perl:
perl -e 'while(<>){
$l=length;
$l>$m && do {$c=$_; $m=$l}
} print $c' file.txt
Ou, se você quiser imprimir todas as linhas mais longas
perl -e 'while(<>){
$l=length;
push @{$k{$l}},$_;
$m=$l if $l>$m;
} print @{$k{$m}}' file.txt
Como não tinha nada melhor para fazer, fiz alguns benchmarks em um arquivo de texto de 625M. Surpreendentemente, minha solução Perl foi consistentemente mais rápida que as outras. Concedido, a diferença com a solução awk
aceita é pequena, mas está lá. Obviamente, as soluções que imprimem várias linhas são mais lentas, então classifico por tipo, e mais rápido a mais lento.
Imprima apenas uma das linhas mais longas:
$ time perl -e 'while(<>){
$l=length;
$l>$m && do {$c=$_; $m=$l}
} print $c' file.txt
real 0m3.837s
user 0m3.724s
sys 0m0.096s
$ time awk 'length > max_length { max_length = length; longest_line = $0 }
END { print longest_line }' file.txt
real 0m5.835s
user 0m5.604s
sys 0m0.204s
$ time sed -rn "/.{$(<file.txt expand -t1 |wc -L)}/{p;q}" file.txt
real 2m37.348s
user 2m39.990s
sys 0m1.868s
Imprimir todas as linhas mais longas:
$ time perl -e 'while(<>){
$l=length;
push @{$k{$l}},$_;
$m=$l if $l>$m;
} print @{$k{$m}}' file.txt
real 0m9.263s
user 0m8.417s
sys 0m0.760s
$ time awk 'length >x { delete y; x=length }
length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file.txt
real 0m10.220s
user 0m9.925s
sys 0m0.252s
## This is Chris Down's bash solution
$ time ./a.sh < file.txt
Max line length: 254
Lines matched with that length: 2
real 8m36.975s
user 8m17.495s
sys 0m17.153s
grep -Em1 "^.{$(wc -L <file.txt)}\$" file.txt
O comando é excepcionalmente difícil de ler sem prática porque mistura sintaxe shell e regexp.
Para explicação, vou usar o pseudocódigo simplificado primeiro. As linhas que começam com ##
não são executadas no shell.
Este código simplificado usa o nome de arquivo F e deixa de fora citando e partes de regexps para legibilidade.
O comando tem duas partes, uma invocação grep
- e uma wc
:
## grep "^.{$( wc -L F )}$" F
O wc
é usado em uma expansão de processo, $( ... )
, portanto é executado antes de grep
. Calcula o comprimento da linha mais longa. A sintaxe de expansão do shell é misturada com a sintaxe do padrão de expressão regular de maneira confusa, então eu decomponho a expansão do processo:
## wc -L F
e 42
e ## grep "^.{42}$" F
Aqui, a expansão do processo foi substituída pelo valor que retornaria, criando a linha de comando grep
que é usada. Agora podemos ler a expressão regular com mais facilidade: ela corresponde exatamente do início ( ^
) ao final ( $
) da linha. A expressão entre eles corresponde a qualquer caractere, exceto newline, repetido por 42 vezes. Combinado, isto é, linhas que consistem em exatamente 42 caracteres.
Agora, voltando aos comandos reais do shell: O grep
option -E
( --extended-regexp
) permite não escapar do {}
para legibilidade. A opção -m 1
( --max-count=1
) faz com que pare depois que a primeira linha é encontrada. O <
no comando wc
grava o arquivo em seu stdin, para evitar que wc
imprima o nome do arquivo junto com o tamanho.
Para tornar os exemplos mais legíveis com o nome do arquivo ocorrendo duas vezes, usarei uma variável f
para o nome do arquivo; Cada $f
no exemplo pode ser substituído pelo nome do arquivo.
f="file.txt"
Mostre a primeira linha mais longa - a primeira linha com a maior linha:
grep -E -m1 "^.{$(wc -L <"$f")}\$" "$f"
Mostre todas as linhas mais longas - todas as linhas que são tão longas quanto a linha mais longa:
grep -E "^.{$(wc -L <"$f")}\$" "$f"
Mostre a última linha mais longa - a última linha que é longa como a linha mais longa:
tac "$f" | grep -E -m1 "^.{$(wc -L <"$f")}\$"
Mostre a única linha mais longa - a linha mais longa que todas as outras linhas ou falhe:
[ $(grep -E "^.{$(wc -L <"$f")}\$" "$f" | wc -l) = 1 ] && grep -E "^.{$(wc -L <"$f")}\$" "$f"
(O último comando é ainda mais ineficiente que os outros, pois repete o comando grep completo. Obviamente deve ser decomposto para que a saída de wc
e as linhas gravadas por grep
sejam salvas em variáveis. < br>
Note que todas as linhas mais longas podem ser todas as linhas. Para salvar em uma variável, apenas as duas primeiras linhas precisam ser mantidas.
O seguinte exemplo seria, e deveria ter sido, um comentário para a resposta dmitry.malikov , mas por causa do uso inútil de espaço visível para comentários , Eu escolhi para apresentá-lo aqui, onde pelo menos será visto ...
Esta é uma variação simples do dmitry's
método awk single-pass.
Imprime todas as linhas "iguais". (Nota: delete array
é uma extensão do gawk).
awk 'length >x { delete y; x=length }
length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file
cat filename | awk '{ print length }' | sort -n | tail -1
Em puro bash:
#!/bin/bash
_max_length=0
while IFS= read -r _line; do
_length="${#_line}"
if (( _length > _max_length )); then
_max_length=${_length}
_max_line=( "${_line}" )
elif (( _length == _max_length )); then
_max_line+=( "${_line}" )
fi
done
printf 'Max line length: %d\n' "${_max_length}"
printf 'Lines matched with that length: %d\n' "${#_max_line[@]}"
(( ${#_max_line[@]} )) && printf '%s\n' '----------------' "${_max_line[@]}"
Eu desenvolvi um pequeno script de shell para isso. Ele exibe comprimento, linha # e linha por tamanho que excede um tamanho específico, como 80 caracteres:
#!/bin/sh
# Author: Surinder
if test $# -lt 2
then
echo "usage: $0 length file1 file2 ..."
echo "usage: $0 80 hello.c"
exit 1
fi
length=$1
shift
LONGLINE=/tmp/longest-line-$$.awk
cat << EOF > $LONGLINE
BEGIN {
}
/.*/ {
current_length=length(\#!/bin/sh
# Author: Surinder
if test $# -lt 2
then
echo "usage: $0 length file1 file2 ..."
echo "usage: $0 80 hello.c"
exit 1
fi
length=$1
shift
LONGLINE=/tmp/longest-line-$$.awk
cat << EOF > $LONGLINE
BEGIN {
}
/.*/ {
current_length=length(\%pre%);
if (current_length >= expected_length) {
printf("%d at line # %d %s\n", current_length, NR, \%pre%);
}
}
END {
}
EOF
for file in $*
do
echo "$file"
cat $file | awk -v expected_length=$length -f $LONGLINE |sort -nr
done
rm $LONGLINE
);
if (current_length >= expected_length) {
printf("%d at line # %d %s\n", current_length, NR, \%pre%);
}
}
END {
}
EOF
for file in $*
do
echo "$file"
cat $file | awk -v expected_length=$length -f $LONGLINE |sort -nr
done
rm $LONGLINE
Você pode usar wc
:
wc -L fileName