Obtenha uma linha específica do arquivo de texto usando apenas o script de shell

100

Estou tentando obter uma linha específica de um arquivo de texto.

Até agora, online eu só vi coisas como sed, (eu só posso usar sh -não bash ou sed ou algo parecido). Preciso fazer isso usando apenas um script de shell básico.

cat file | while read line
    do
       #do something
    done

Eu sei como iterar pelas linhas, conforme mostrado acima, mas e se eu só precisar obter o conteúdo de uma linha específica

GangstaGraham
fonte
você sabe o número da linha?
Mehul Rathod
1
Então você começa a contar.
Ignacio Vazquez-Abrams
sim, o número da linha é 5 @MehulRathod
GangstaGraham
3
Por que está cattudo bem, mas sednão está? Isso não faz sentido.
William Pursell
5
Porque ninguém pode dizer não cat. Aw ... fofo cat!

Respostas:

204

sed:

sed '5!d' file

awk:

awk 'NR==5' file
Kent
fonte
E com o comando sh, não posso usar sed, awk. Eu deveria deixar isso mais claro na pergunta.
GangstaGraham
@GangstaGraham você disse que sabe como iterar através das linhas, que tal adicionar um contador? se o contador atingir seu número de linha alvo, pegue a linha e quebre o loop. ajuda?
Kent,
4
@KanagaveluSugumar leu a página de informações do sed. 5!dsignifica excluir todas as linhas, exceto 5. shell var é possível, você precisa de aspas duplas.
Kent,
13
Eu sugeriria adicionar outra variante: sed -n 5pIsso parece mais lógico de lembrar para iniciantes, porque -nsignifica "sem saída por padrão" e psignifica "imprimir", e não há menção potencialmente confusa de exclusão (quando as pessoas falam de arquivos, excluir linhas tende a significa algo diferente).
Josip Rodin,
1
@JosipRodin você tem razão, -n '5p'trabalha para esse problema também. A diferença aqui é que 5!dvocê pode adicionar -ipara gravar a alteração de volta no arquivo. no entanto, com -n 5pvocê sed -n '5p' f > f2&& mv f2 fnovamente, para esta questão, concordo com sua opinião.
Kent,
21

Assumindo que lineé uma variável que contém o número da linha necessária, se você pode usar heade tail, é bastante simples:

head -n $line file | tail -1

Caso contrário, isso deve funcionar:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done
micromoses
fonte
Essa -eqcomparação é para inteiros, portanto, ela quer um número de linha, não o conteúdo da linha ( $line). Isso deve ser corrigido definindo, por exemplo, want=5antes do loop e, em seguida, usando a -eqcomparação ativada $want. [mudou de uma edição rejeitada]
Josip Rodin
1
@JosipRodin Fiz uma sugestão de edição independente baseada em seu comentário, pois concordo com ela. Espero que desta vez não seja rejeitado.
Victor Zamanian
15

Você poderia usar sed -n 5p file.

Você também pode obter um intervalo, por exemplo sed -n 5,10p file,.

Nomas Prime
fonte
11

Melhor método de desempenho

sed '5q;d' file

Porque sedpara de ler qualquer linha após a 5ª

Atualizar experimento do Sr. Roger Dueck

Instalei wcanadian-insane (6,6 MB) e comparei sed -n 1p / usr / share / dict / words e sed '1q; d' / usr / share / dict / words usando o comando time; o primeiro levou 0,043s, o segundo apenas 0,002s, então usar 'q' é definitivamente uma melhoria de desempenho!

fé em seu
fonte
1
Isso também é comumente escrito:sed -n 5q
William Pursell,
3
Eu gosto dessa solução porque sedpara de ler qualquer linha após a 5ª.
Anthony Geoghegan
1
Instalei wcanadian-insane (6,6 MB) e comparei sed -n 1p /usr/share/dict/wordse sed '1q;d' /usr/share/dict/wordsusando o timecomando; o primeiro levou 0,043s, o segundo apenas 0,002s, então usar 'q' é definitivamente uma melhoria de desempenho!
Roger Dueck
5

Se, por exemplo, você deseja obter as linhas 10 a 20 de um arquivo, pode usar cada um destes dois métodos:

head -n 20 york.txt | tail -11

ou

sed -n '10,20p' york.txt 

p no comando acima significa impressão.

Aqui está o que você verá: insira a descrição da imagem aqui

Mona Jalal
fonte
2

A maneira padrão de fazer esse tipo de coisa é usar ferramentas externas. Não permitir o uso de ferramentas externas ao escrever um script de shell é um absurdo. No entanto, se você realmente não deseja usar ferramentas externas, pode imprimir a linha 5 com:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Observe que isso imprimirá a linha lógica 5. Ou seja, se input-filecontiver continuações de linha, elas serão contadas como uma única linha. Você pode alterar esse comportamento adicionando -rao comando de leitura. (Que provavelmente é o comportamento desejado.)

William Pursell
fonte
1
$((++i))parece ser um bashismo; se o OP está restrito ao uso de ferramentas externas, eu não presumiria que eles teriam acesso a mais do que um plano/bin/sh
Josip Rodin
@JosipRodin Não, é um recurso POSIX (mas o suporte para ++incrementos é especificamente marcado como opcional).
tripleee
@tripleee não funciona com o painel moderno como / bin / sh, então eu não confiaria nele.
Josip Rodin,
Mas uma solução alternativa simples, como $((i+=1))funciona no Dash, também.
triplo de
$(($i+1))é a solução alternativa simples que eu estava pensando.
Josip Rodin
1

Em paralelo com a resposta de William Pursell , aqui está uma construção simples que deve funcionar até mesmo no shell Bourne v7 original (e, portanto, também em locais onde o Bash não está disponível).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Observe também a otimização para breaksair do loop quando obtivemos a linha que procurávamos.

triplo
fonte
0

Eu particularmente não gostei de nenhuma das respostas.

Aqui está como eu fiz.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"
cpp_guy_who_does_gfx
fonte
-1

Fácil com perl! Se você deseja obter as linhas 1, 3 e 5 de um arquivo, diga / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
Dagelf
fonte
seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'mas o smartmatch é experimental e seu uso desencorajado
Sorin
Nenhuma das outras soluções é tão concisa ou permite tanta flexibilidade. (Por que parece que tudo o que economiza tempo e torna as coisas mais fáceis é "desencorajado" por "pessoas inteligentes", devemos todos ficar olhando para as telas o dia todo?)
dagelf
-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"
Oder
fonte
3
você poderia pelo menos descrever um pouco o porquê deste trabalho para tornar mais claro para a pessoa que fez a pergunta?
ted
Assim, o primeiro grep seleciona todas as linhas adicionando números de linha em seus inícios. Então o segundo grep seleciona uma linha específica combinando o número da linha no início. E, finalmente, o número da linha é cortado a partir do início da linha em eco.
Oder
Isso é complexo e ineficiente em comparação com o sed -n 5pque, claro, ainda pode ser otimizado para algo comosed -n '5!d;p;q'
tripleee