grep "^$1"
mais ou menos funciona, mas como faço para escapar "$1"
então o grep não interpreta nenhum caractere especialmente?
Ou existe uma maneira melhor?
Editar: Eu não quero pesquisar por "^$1"
, mas por uma string fixa inserida dinamicamente que deve apenas ser correspondido se estiver no início de uma linha. Isso é o que eu quis dizer com $1
.
Comentários
Resposta
Não consigo “pensar em uma maneira de fazer isso usando grep
; ^
em si é parte de um expressão regular, portanto, usá-la requer que expressões regulares sejam interpretadas. É trivial usar correspondência de substring em awk
, perl
ou qualquer outra coisa:
awk -v search="$1" "substr($0, 1, length(search)) == search { print }"
Para lidar com strings de pesquisa contendo \
, você pode usar o mesmo truque de Resposta de 123 “:
search="$1" awk "substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }"
Comentários
- Isso não ‘ funcionará para strings como como
\/
- @ 123 na verdade, eu ‘ adicionei uma variante para lidar com isso.
- Ainda falhará para strings complicadas como
\\\/\/\/\\\\/
que é visto como\\///\\/
no programa. Pelo que eu sei, não há como escapar adequadamente das barras invertidas no awk, a menos que você saiba quantas serão usadas de antemão. - @ 123 obrigado, I ‘ adaptei seu truque de examinar o ambiente para evitar o processamento de escape.
- Ainda gosto mais dessa solução. Eficiente (awk + nenhum tempo perdido olhando ao redor), a inicialização rápida (awk + nenhum processo adicional necessário para configurar o estado) usa ferramentas padrão e é bastante conciso. Todas as outras respostas carecem de pelo menos algumas delas. (Eficiência é um ponto forte aqui, já que grep é conhecido por sua velocidade incomparável.)
Resposta
Se você apenas precisa verificar se uma correspondência foi encontrada ou não, corte todas as linhas de entrada para o comprimento do prefixo desejado ($1
) e use grep de padrão fixo:
if cut -c 1-"${#1}" | grep -qF "$1"; then echo "found" else echo "not found" fi
Também é fácil obter a contagem das linhas correspondentes:
cut -c 1-"${#1}" | grep -cF "$1"
Ou os números das linhas de todas as linhas correspondentes (os números das linhas começam em 1):
cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1
Você pode alimentar os números das linhas para head
e tail
para obter o texto completo das linhas correspondentes, mas nesse ponto é mais fácil obter apenas uma linguagem de script moderna como Python ou Ruby.
(Os exemplos acima assumem Posix grep e cut. Eles assumem que o arquivo a ser pesquisado vem da entrada padrão, mas pode ser facilmente adaptado para receber um nome de arquivo.)
Editar: Você também deve garantir que o padrão ( $1
) não é uma string de comprimento zero. Caso contrário, cut
falhará dizendo values may not include zero
. Além disso, se estiver usando Bash, use set -o pipefail
para detectar saídas de erro por cut
.
Resposta
Uma maneira de usar perl que respeitará as barras invertidas
v="$1" perl -ne "print if index($_, $ENV{"v"} )==0" file
Isso define a variável de ambiente v para o comando, então imprime se o índice da variável é 0, ou seja, o início da linha.
Você também pode fazer o mesmo no awk
v="$1" awk "index($0, ENVIRON["v"])==1" file
Resposta
Esta é uma opção totalmente bash, não que eu recomende o bash para processamento de texto, mas funciona.
#!/usr/bin/env bash # searches for $1 at the beginning of the line of its input len=${#1} while IFS= read -r line do [[ "${line:0:len}" = "$1" ]] && printf "%s\n" "$line" done
O script calcula o comprimento len
do parâmetro inserido $ 1 e usa a expansão do parâmetro em cada linha para ver se os primeiros len
caracteres correspondem a $ 1. Se sim, imprime a linha.
Resposta
Se seu $1
for ASCII puro e seu grep
tem a opção -P
(para habilitar PCRE), você pode fazer isso:
#!/bin/bash line_start="$1" line_start_raw=$(printf "%s" "$line_start" | od -v -t x1 -An) line_start_hex=$(printf "\\x%s" $line_start_raw) grep -P "^$line_start_hex"
A ideia aqui é que grep -P
permite expressões regulares com \xXX
para especificar caracteres literais, onde XX
é o valor ASCII hexadecimal desse caractere. O caractere er é correspondido literalmente, mesmo que seja um caractere regex especial.
od
é usado para converter o início da linha esperado em uma lista de valores hexadecimais, que são então encadeados, cada um prefixado com \x
por printf. ^
é então anexado a esta string para construir o regex necessário.
Se o seu $1
for unicode, então isso se torna um pouco mais difícil, porque não há uma correspondência 1: 1 de caracteres para bytes hexadecimais conforme a saída de od
.
Resposta
Se seu grep tiver a opção -P, o que significa PCRE , você pode fazer isso:
grep -P "^\Q$1\E"
Consulte esta pergunta , e consulte o documento PCRE para obter detalhes, se desejar.
Resposta
Como um filtro:
perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern
Execute em um ou mais arquivos:
perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern file..
A seção “Quoting metacaracteres” da documentação perlre explica:
Citando metacaracteres
Metacaracteres com barra invertida em P erl são alfanuméricos, como
\b
,\w
,\n
. Ao contrário de algumas outras linguagens de expressão regular, não há símbolos de barra invertida que não sejam alfanuméricos. Portanto, qualquer coisa que se pareça com\\
,\(
,\)
,\[
,\]
,\{
ou\}
é sempre interpretado como um personagem literal, não um metacaractere. Isso já foi usado em um idioma comum para desativar ou citar os significados especiais dos metacaracteres de expressão regular em uma string que você deseja usar como padrão. Basta citar todos os caracteres que não sejam de “palavras”:$pattern =~ s/(\W)/\\$1/g;
(Se
use locale
estiver definido, isso depende de o local atual.) Hoje é mais comum usar a funçãoquotemeta
ou a\Q
sequência de escape de metaquitação para desativar os significados especiais de todos os metacaracteres como este:/$unquoted\Q$quoted\E$unquoted/
Cuidado se você colocar barras invertidas literais (aquelas que não estão dentro de variáveis interpoladas) entre
\Q
e\E
, a interpolação de barra invertida com aspas duplas pode levar a resultados confusos. Se você precisar usar barras invertidas literais em\Q...\E
, consulte “Detalhes sangrentos de análise de construções citadas” em perlop .
quotemeta
e\Q
são totalmente descritos em quotemeta .
Resposta
Se houver um personagem que você usa “t use, você pode usar isso para marcar o início da linha. Por exemplo, $"\a"
(ASCII 007). É feio, mas vai funcionar:
{ echo "this is a line to match"; echo "but this is not"; } >file.txt stuffing=$"\a" # Guaranteed never to appear in your source text required="this" # What we want to match that beginning of a line match=$(sed "s/^/$stuffing/" file.txt | grep -F "$stuffing$required" | sed "s/^$stuffing//") if [[ -n "$match" ]] then echo "Yay. We have a match: $match" fi
Se você não precisa das linhas correspondentes, pode descartar a sed
final e usar grep -qF
. Mas é muito mais fácil com awk
(ou perl
) …
Resposta
Quando você deseja olhar em um arquivo sem um loop, você pode usar:
Corte o arquivo com o comprimento da pesquisa string
Procure strings fixas e retorne os números das linhas
grep -Fn "$1" <(cut -c1-${#1} < file)
Use os números das linhas para algo como sed -n "3p;11p" file
sed -n "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed "s/:.*/p;/" | tr -d "\n")" file
Quando quiser excluir essas linhas, use
sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed "s/:.*/d;/" | tr -d "\n")" file
grep '^$1'
? Ou ‘ não quis dizer que deseja evitar que$1
seja expandido pelo shell?grep
também, mas você ‘ primeiro terá que escapar qualquer caractere especial em sua string, por exemploprintf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile