Comando para obter a enésima linha de STDOUT

210

Existe algum comando bash que permita obter a enésima linha do STDOUT?

Ou seja, algo que levaria isso

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

e fazer algo como

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

Sei que isso seria uma prática ruim ao escrever scripts que deveriam ser reutilizados, mas, ao trabalhar com o shell dia a dia, seria útil para mim filtrar meu STDOUT dessa maneira.

Também sei que seria um comando semi-trivial para escrever (buffer STDOUT, retorne uma linha específica), mas quero saber se existe algum comando shell padrão para fazer isso que estaria disponível sem que eu colocasse um script no lugar.

Alan Storm
fonte
5
Eu só votou isso para o uso da palavramagic-command
Sevenearths

Respostas:

332

Usando sed, apenas pela variedade:

ls -l | sed -n 2p

O uso dessa alternativa, que parece mais eficiente, pois para de ler a entrada quando a linha necessária é impressa, pode gerar um SIGPIPE no processo de alimentação, o que, por sua vez, pode gerar uma mensagem de erro indesejada:

ls -l | sed -n -e '2{p;q}'

Eu já vi com bastante frequência que geralmente uso o primeiro (que é mais fácil de digitar), embora lsnão seja um comando que reclama quando obtém o SIGPIPE.

Para várias linhas:

ls -l | sed -n 2,4p

Para vários intervalos de linhas:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'
Jonathan Leffler
fonte
O que significa p? como 2p 3p? No sentido que entendo, é para 2/3 da linha, mas qual é a teoria / entendimento por trás disso?
Leo Ufimtsev
4
@LeoUfimtsev: pé a seddeclaração que imprime as linhas identificadas pelo intervalo de linhas que a precede. Assim, 2,4psignifica que as linhas de impressão 2, 3, 4.
Jonathan Leffler
3
E nsignifica NÃO imprimir todas as outras linhas
Timo
85
ls -l | head -2 | tail -1
multidão
fonte
13
+2, mas eu sugiro head -n 2 | tail -n 1- caras e coroas modernas tendem a emitir avisos de outra forma.
Michael Krelin - hacker 15/09/09
2
Usar dois processos para filtrar os dados é um pouco exagerado (mas, dado o poder das máquinas hoje em dia, elas provavelmente aguentariam). A generalização para lidar com um intervalo não é totalmente trivial (para o intervalo N..M, você usa head -n M | tail -n M-N+1), e a generalização para lidar com vários intervalos não é possível.
23611 Jonathan Leffler
3
cabeça enfiada na cauda? horrível. Várias maneiras melhores de fazer isso.
CAMH
16
Grande coisa sobre linha de comando * nix: um milhão e uma maneiras de fazer tudo e todo mundo tem um favorito e um ódios todas as outras maneiras ... :-)
Beggs
1
Como imprimir um intervalo de linhas de outra forma: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(pode adicionar mais condições mais fáceis, vários intervalos, etc ...)
user10607
41

Alternativa para o bom caminho da cabeça / cauda:

ls -al | awk 'NR==2'

ou

ls -al | sed -n '2p'
ChristopheD
fonte
1
meu awk é melhor, mas sed é legal.
Michael Krelin - hacker 15/09/09
Não há necessidade de um "se" - veja a resposta do hacker (exceto a parte sobre encontrar todos os arquivos no sistema). ;-)
Pausado até novo aviso.
o que '2p'significa?
Página
1
@shredding resposta curta imprimir segunda linha; long -> Quando usado, somente as linhas que correspondem ao padrão recebem o comando após o endereço. Resumidamente, quando usado com o sinalizador / p, as linhas correspondentes são impressas duas vezes: arquivo sed '/ PATTERN / p'. E, claro PADRÃO é qualquer expressão regular mais
Medhat
e se 2for uma variável? Cada exemplo usa aspas simples, mas com um var você precisa de aspas duplas. Poderíamos "programar" o awk para trabalhar também com aspas simples ao usar vars? Exemplo line =1; ls | awk 'NR==$line'. Eu sei que bashusa aspas duplas com vars.
Timo
21

Do sed1line :

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

Do awk1line :

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files
Mark Edgar
fonte
9

Por uma questão de completude ;-)

código mais curto

find / | awk NR==3

vida mais curta

find / | awk 'NR==3 {print $0; exit}'
Michael Krelin - hacker
fonte
Você não está brincando com integridade!
Pausado até novo aviso.
Eu não sou ;-) Na verdade, eu não sei por que todo mundo está usando ls- a pergunta original era sobre alguém STDOUT, então eu pensei que era melhor tê-lo maior.
Michael Krelin - hacker 15/09/09
Pelo menos com o GNU awk, a ação padrão é { print $0 }, portanto, `awk 'NR == 3' é uma maneira mais curta de escrever o mesmo.
ephemient 15/09/09
Você provavelmente deve adicionar "; exit" a essa ação. Não adianta processar o restante das linhas quando você não fará nada com elas.
CAMH
@camh: Veja a resposta de Jonathan Leffer sobre isso: "pode ​​gerar um SIGPIPE no processo de alimentação, o que por sua vez pode gerar uma mensagem de erro indesejada".
ephemient 16/09/09
7

Experimente esta sedversão:

ls -l | sed '2 ! d'

Ele diz "exclua todas as linhas que não são a segunda".

Pausado até novo aviso.
fonte
6

Você pode usar o awk:

ls -l | awk 'NR==2'

Atualizar

O código acima não conseguirá o que queremos devido a um erro de um por um: a primeira linha do comando ls -l é a linha total . Para isso, o seguinte código revisado funcionará:

ls -l | awk 'NR==3'
Hai Vu
fonte
4

Perl está facilmente disponível para você?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Substitua, obviamente, o número que desejar para 7.

comida de gato
fonte
Este é o meu favorito, pois parece ser o mais fácil de generalizar para o que eu era pessoalmente depois do que é a cada enésimo, perl -n -e 'if ($.% 7 == 0) {print; saída (0); } '
mat kelcey 8/01/12
@matkelcey também você pode fazer $ awk 'NR % 7' filenamepara imprimir todas as 7ª linhas do arquivo.
precisa saber é o seguinte
3

Outro pôster sugerido

ls -l | head -2 | tail -1

mas se você enfiar a cabeça na cauda, ​​parece que tudo até a linha N é processado duas vezes.

Tubulação de cauda na cabeça

ls -l | tail -n +2 | head -n1

seria mais eficiente?

ninguém
fonte
0

Sim, a maneira mais eficiente (como já apontado por Jonathan Leffler) é usar o sed com print & quit:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

fonte
O uso de pipefail e PIPESTATUS é muito interessante e isso adiciona muito a esta página.
Mike S
0

Hmm

sed não funcionou no meu caso. Eu proponho:

para linhas "ímpares" 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

para linhas "pares" 2,4,6,8 ls | awk '0 == (NR)% 2'

stan
fonte
-1

Para mais perfeição ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Jogue fora as linhas até chegar à segunda e depois imprima a primeira linha. Então, imprime a terceira linha.

Se é apenas a segunda linha ..

ls -l | (read; head -n1)

Coloque quantas leituras forem necessárias.

Shizzmo
fonte
-1

Adicionei isso no meu .bash_profile

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

E é obras

gdf <file name>
Joel AZEMAR
fonte
Estou intrigado - o que isso tem a ver com a pergunta?
Jonathan Leffler