extrair parte de uma string usando bash / cut / split

121

Eu tenho uma string como esta:

/var/cpanel/users/joebloggs:DNS9=domain.com

Preciso extrair o nome de usuário ( joebloggs) desta string e armazená-lo em uma variável.

O formato da string será sempre o mesmo com exceção de joebloggse, domain.comportanto, estou pensando que a string pode ser dividida duas vezes usando cut?

A primeira divisão seria dividida por :e armazenaríamos a primeira parte em uma variável para passar para a segunda função de divisão.

A segunda divisão seria dividida /e armazenada a última palavra ( joebloggs) em uma variável

Eu sei como fazer isso em php usando matrizes e divisões, mas estou um pouco perdido no bash.

Craig Edmonds
fonte

Respostas:

333

Para extrair joebloggsdesta string em bash usando a expansão de parâmetro sem nenhum processo extra ...

MYVAR="/var/cpanel/users/joebloggs:DNS9=domain.com" 

NAME=${MYVAR%:*}  # retain the part before the colon
NAME=${NAME##*/}  # retain the part after the last slash
echo $NAME

Não depende de joebloggsestar em uma determinada profundidade no caminho.


Resumo

Uma visão geral de alguns modos de expansão de parâmetro, para referência ...

${MYVAR#pattern}     # delete shortest match of pattern from the beginning
${MYVAR##pattern}    # delete longest match of pattern from the beginning
${MYVAR%pattern}     # delete shortest match of pattern from the end
${MYVAR%%pattern}    # delete longest match of pattern from the end

Portanto, #significa combinar desde o início (pense em uma linha de comentário) e %significa partir do final. Uma instância significa o mais curto e duas instâncias significa o mais longo.

Você pode obter substrings com base na posição usando números:

${MYVAR:3}   # Remove the first three chars (leaving 4..end)
${MYVAR::3}  # Return the first three characters
${MYVAR:3:5} # The next five characters after removing the first 3 (chars 4-9)

Você também pode substituir strings ou padrões específicos usando:

${MYVAR/search/replace}

O patternestá no mesmo formato da correspondência de nome de arquivo, então *(qualquer caractere) é comum, geralmente seguido por um símbolo específico como /ou.

Exemplos:

Dada uma variável como

MYVAR="users/joebloggs/domain.com" 

Remova o caminho deixando o nome do arquivo (todos os caracteres até uma barra):

echo ${MYVAR##*/}
domain.com

Remova o nome do arquivo, deixando o caminho (exclua a correspondência mais curta após a última /):

echo ${MYVAR%/*}
users/joebloggs

Obtenha apenas a extensão do arquivo (remova tudo antes do último período):

echo ${MYVAR##*.}
com

NOTA: Para fazer duas operações, você não pode combiná-las, mas deve atribuir a uma variável intermediária. Portanto, para obter o nome do arquivo sem caminho ou extensão:

NAME=${MYVAR##*/}      # remove part before last slash
echo ${NAME%.*}        # from the new var remove the part after the last period
domain
Beroe
fonte
Não tenho certeza se este é um argumento a favor ou contra o uso criativo de grep, mas tente com VAR = / aqui / é / a / caminho: com / a / dois pontos / dentro: DNS9 = domínio.com
rici
2
Doce! E isso é feito dentro do shell de execução, portanto, muito mais rápido do que aqueles que usam outros comandos.
stolsvik
3
@Fadi Você deve alternar o caractere curinga para vir antes dos dois pontos e usar em #vez de %. Se você quiser apenas a parte após o último dois pontos, use ${MYVAR##*:}para obter a parte após os primeiros dois pontos, use${MYVAR#*:}
beroe
4
Amigo, você não sabe quantas vezes voltei a esta resposta. Obrigado!
Joel B
1
Ótima resposta! Pergunta: Se meu padrão fosse uma variável, eu o digitaria assim ${RET##*$CHOP}ou assim ${RET##*CHOP}(ou de outra forma)? EDITAR: parece ser o primeiro,${RET##*$CHOP}
Ctrl S
43

Defina uma função como esta:

getUserName() {
    echo $1 | cut -d : -f 1 | xargs basename
}

E passe a string como parâmetro:

userName=$(getUserName "/var/cpanel/users/joebloggs:DNS9=domain.com")
echo $userName
Stefano Sanfilippo
fonte
1
Essa resposta me ajudou a alcançar o que vim fazer aqui. Não há respostas aceitas e esta recebe meu voto pela simplicidade.
Harperville
1
A única correção que tive que fazer no comando acima foi remover o ':', assim echo $1 | cut -d -f 1 | xargs. 1 para ans simples e arrumado.
Bhushan
20

E quanto ao sed? Isso funcionará em um único comando:

sed 's#.*/\([^:]*\).*#\1#' <<<$string
  • O #estão sendo usados ​​para divisores regex em vez de, /uma vez que a string está /nele.
  • .*/ agarra a corda até a última barra invertida.
  • \( .. \)marca um grupo de captura. Isto é\([^:]*\) .
    • O [^:]diz qualquer caractere _ exceto dois pontos e *significa zero ou mais.
  • .* significa o resto da linha.
  • \1significa substituir o que foi encontrado no primeiro (e único) grupo de captura. Este é o nome.

Aqui está a análise combinando a string com a expressão regular:

        /var/cpanel/users/           joebloggs  :DNS9=domain.com joebloggs
sed 's#.*/                          \([^:]*\)   .*              #\1       #'
David W.
fonte
Dissecção super agradável!
kyb
11

Usando um único sed

echo "/var/cpanel/users/joebloggs:DNS9=domain.com" | sed 's/.*\/\(.*\):.*/\1/'
Yann Moisan
fonte
10

Usando um único Awk:

... | awk -F '[/:]' '{print $5}'

Ou seja, usando como separador de campo /ou :, o nome de usuário está sempre no campo 5.

Para armazená-lo em uma variável:

username=$(... | awk -F '[/:]' '{print $5}')

Uma implementação mais flexível com sedisso não exige que o nome de usuário seja o campo 5:

... | sed -e s/:.*// -e s?.*/??

Ou seja, exclua tudo de :e além e, em seguida, exclua tudo até o último /. sedprovavelmente também é mais rápido do que awk, então esta alternativa é definitivamente melhor.

janos
fonte