Como extrair os dois primeiros caracteres de uma seqüência de caracteres no script de shell?


123

Por exemplo, dado:

USCAGoleta9311734.5021-120.1287855805

Eu quero extrair apenas:

US

6
Obrigado a todos. Acabei usando 'cut -c1-2', honestamente, eu nem sabia que 'cut' estava lá. Eu gostaria de dizer que tenho bastante experiência na linha de comando - mas aparentemente tenho muito a aprender.
Greg Greg

1
@ Greg, esteja ciente de que o corte é executado como um processo separado - será mais lento que a solução interna do bash que publiquei ao lado na minha resposta. Isso não fará nenhuma diferença, a menos que você esteja processando grandes conjuntos de dados, mas tenha em mente isso.
paxdiablo

Editar Na verdade, acho que essa linha de código provavelmente será executada cerca de 50.000 vezes por relatório. Então, eu posso apenas usar o método Bash interno - que, como você disse, economizará alguns recursos muito necessários.
Greg Greg

Respostas:


180

Provavelmente, o método mais eficiente, se você estiver usando o bashshell (e parece estar, com base nos seus comentários), é usar a variante de sub-string da expansão de parâmetros:

pax> long="USCAGol.blah.blah.blah"
pax> short="${long:0:2}" ; echo "${short}"
US

Isso definirá shortos dois primeiros caracteres de long. Se longfor menor que dois caracteres, shortserá idêntico a ele.

Esse método in-shell geralmente é melhor se você o fizer muito (como 50.000 vezes por relatório, como você mencionou), já que não há sobrecarga na criação do processo. Todas as soluções que usam programas externos sofrerão com essa sobrecarga.

Se você também deseja garantir um comprimento mínimo , pode prendê-lo antes da mão com algo como:

pax> long="A"
pax> tmpstr="${long}.."
pax> short="${tmpstr:0:2}" ; echo "${short}"
A.

Isso garantiria que algo com menos de dois caracteres fosse preenchido à direita com pontos (ou qualquer outra coisa, apenas alterando o caractere usado ao criar tmpstr). Não está claro se você precisa disso, mas pensei em colocá-lo por completo.


Dito isto, existem várias maneiras de fazer isso com programas externos (como se você não tiver bashdisponível), algumas das quais são:

short=$(echo "${long}" | cut -c1-2)
short=$(echo "${long}" | head -c2)
short=$(echo "${long}" | awk '{print substr ($0, 0, 2)}'
short=$(echo "${long}" | sed 's/^\(..\).*/\1/')

Os dois primeiros ( cute head) são idênticos para uma string de linha única - eles basicamente apenas devolvem os dois primeiros caracteres. Eles diferem no que cutfornecerá os dois primeiros caracteres de cada linha e heados dois primeiros caracteres de toda a entrada

O terceiro usa a awkfunção sub-string para extrair os dois primeiros caracteres e o quarto usa sedgrupos de captura (usando ()e \1) para capturar os dois primeiros caracteres e substituir a linha inteira por eles. Ambos são semelhantes a cut- eles entregam os dois primeiros caracteres de cada linha na entrada.

Nada disso importa se você tiver certeza de que sua entrada é uma única linha, todas elas têm um efeito idêntico.


Eu preferiria usar printf '%s'em vez de echono caso de existirem caracteres estranhos na string: stackoverflow.com/a/40423558/895245 Para o POSIX obcecado: head -cnão é POSIX, cut -ce awk substrsão, sed \1não tenho certeza.
Ciro Santilli #

1
@CiroSantilli 中心 改造 中心 996ICU 事件 print usando printf, você nem precisa de um programa adicional. Veja minha resposta .
bschlueter

60

maneira mais fácil é

${string:position:length}

Onde isso extrai a $lengthsubstring de $stringem $position.

Este é um bash embutido; portanto, awk ou sed não é necessário.


Essa é a maneira mais curta, fácil e fácil de obter a substring.
ani627

34

Você chegou várias respostas boas e eu iria com o Bash builtin mim mesmo, mas já que você perguntou sobre sede awke ( quase ) ninguém mais oferecido soluções com base neles, eu oferecer-lhe estes:

echo "USCAGoleta9311734.5021-120.1287855805" | awk '{print substr($0,0,2)}'

e

echo "USCAGoleta9311734.5021-120.1287855805" | sed 's/\(^..\).*/\1/'

O awkprimeiro deve ser bastante óbvio, mas aqui está uma explicação sed:

  • substituto "s /"
  • o grupo "()" de dois dos caracteres ".." começando no início da linha "^" e seguido por qualquer caractere "." repetido zero ou mais vezes "*" (as barras invertidas são necessárias para escapar de alguns dos caracteres especiais)
  • por "/" o conteúdo do primeiro (e somente neste caso) grupo (aqui a barra invertida é uma fuga especial referente a uma subexpressão correspondente)
  • feito "/"

1
Nas strings do awk, comece no índice 1, então você deve usar substr($0,1,2).
Isaac

8

Se você estiver dentro bash, pode dizer:

bash-3.2$ var=abcd
bash-3.2$ echo ${var:0:2}
ab

Isso pode ser exatamente o que você precisa ...


Essa é a resposta mais fácil e simples! funcionou como um encanto
aloha

7

Apenas grep:

echo 'abcdef' | grep -Po "^.."        # ab

Atende às minhas necessidades. Você pode remover a -Popção para torná-la mais curta. Todos os regexs entenderão esse padrão.
datashaman 27/03/19

6

Você pode usar printf:

$ original='USCAGoleta9311734.5021-120.1287855805'
$ printf '%-.2s' "$original"
US

5

colrm - remove colunas de um arquivo

Para deixar os dois primeiros caracteres, remova as colunas começando em 3

cat file | colrm 3

4

Muito tarde, de fato, mas aqui está

sed 's/.//3g'

Ou

awk NF=1 FPAT=..

Ou

perl -pe '$_=unpack a2'

2

Se você deseja usar scripts de shell e não confiar em extensões não-posix (como os chamados bashismos), você pode usar técnicas que não requerem bifurcação de ferramentas externas, como grep, sed, cut, awk etc., que então torne seu script menos eficiente. Talvez a eficiência e a portabilidade do posix não sejam importantes no seu caso de uso. Mas, caso seja (ou apenas um bom hábito), você pode usar o seguinte método de opção de expansão de parâmetro para extrair os dois primeiros caracteres de uma variável de shell:

$ sh -c 'var=abcde; echo "${var%${var#??}}"'
ab

Isso usa a expansão de parâmetro "menor prefixo" para remover os dois primeiros caracteres (esta é a ${var#??}parte) e, em seguida, a expansão de parâmetro "menor sufixo" (a ${var%parte) para remover a cadeia de caracteres com exceção de dois caracteres, exceto o primeiro valor.

Este método foi descrito anteriormente nesta resposta à pergunta "Shell = Verifique se a variável começa com #". Essa resposta também descreve alguns métodos de expansão de parâmetros semelhantes que podem ser usados ​​em um contexto ligeiramente diferente daquele que se aplica à pergunta original aqui.


Melhor resposta, deve estar no topo. sem garfos, sem basismos. funciona mesmo com pequenas conchas, como traço.
exore

1

Se o seu sistema estiver usando um shell diferente (não bash), mas o seu sistema tiver bash, você ainda poderá usar a manipulação de string inerente bashinvocando bashcom uma variável:

strEcho='echo ${str:0:2}' # '${str:2}' if you want to skip the first two characters and keep the rest
bash -c "str=\"$strFull\";$strEcho;"

Isso usa o mesmo método da resposta principal , invocando apenas bashse você ainda não o estiver usando.
palswim

Infelizmente, isso vem com toda a sobrecarga de invocar outro processo, mas às vezes essa sobrecarga não importa tanto quanto simplicidade e familiaridade.
palswim

1

Só por diversão, acrescentarei alguns que, apesar de complicados e inúteis, não foram mencionados:

head -c 2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

echo 'USCAGoleta9311734.5021-120.1287855805' | dd bs=2 count=1 status=none

sed -e 's/^\(.\{2\}\).*/\1/;' <( echo 'USCAGoleta9311734.5021-120.1287855805')

cut -c 1-2 <( echo 'USCAGoleta9311734.5021-120.1287855805')

python -c "print(r'USCAGoleta9311734.5021-120.1287855805'[0:2])"

ruby -e 'puts "USCAGoleta9311734.5021-120.1287855805"[0..1]'


0

se mystring = USCAGoleta9311734.5021-120.1287855805

print substr(mystring,0,2)

imprimiria EUA

onde 0 é a posição inicial e 2 é como meny chars para ler


Diga ... não é o GW-BASIC? Oh, espere, é isso awk. Desculpe, eu não sabia dizer a princípio.
Pausado até novo aviso.

0

É isso que você procura?

my $string = 'USCAGoleta9311734.5021-120.1287855805';

my $first_two_chars = substr $string, 0, 2;

ref: substr


1
como é provável que ele / ela esteja chamando isso a partir do shell, uma forma melhor seriaperl -e 'print substr $ARGV[0], 0, 2' 'USCAGoleta9311734.5021-120.1287855805'
Chas. Owens
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.