Bash - atribui a matriz à variável como string

2

Eu tenho este código, ele imprime o resultado correto, mas não consigo descobrir como obter o eco da última linha em uma variável.

# hostname is 'tech-news-blog-324344' . Setting it into array host_name_array
IFS='-' read -r -a host_name_array <<< "$(hostname)" 
#removing the last part of string after last "-"
unset 'host_name_array[${#host_name_array[@]}-1]'
( IFS=$'-'; echo "${host_name_array[*]}" )  
#result is 'tech-news-blog'

Como poderia obter o valor da última linha em uma variável? Eu tentei o seguinte:

( IFS=$'-'; URL="${host_name_array[*]}" )

Mas eu recebo o resultado "blog de notícias de tecnologia" com espaços entre partes do array em vez de '-'.

    
por BenB 08.01.2017 / 06:49

4 respostas

2

Quando IFS='-' read -r -a host_name_array <<< "$(hostname)" é executado, a matriz é (tech news blog 324344) .

Depois que o elemento final for removido com unset 'host_name_array[${#host_name_array[@]}-1]' , a matriz será (tech news blog) .

Portanto, para que isso aconteça com tech-news-blog , alguma substituição terá que ser feita, pois echo "${host_name_array[*]}" produzirá tech news blog :

com tr: echo "${host_name_array[*]}" | tr ' ' '-'

sed: echo "${host_name_array[*]}" | sed 's/ /-/g'

    
por 08.01.2017 / 07:00
3

Você pode fazer isso enviando os elementos separados por - e, em seguida, remover o último - por expansão de parâmetro:

$ var=$(printf '%s-' "${host_name_array[@]}")

$ echo "$var"
foo-bar-spam-egg-

$ var="${var%-}"

$ echo "$var"
foo-bar-spam-egg

Além disso, você precisa de ${host_name_array[@]} em vez de ${host_name_array[*]} para evitar a saída dos elementos como um todo separados pelo primeiro caractere de IFS .

O que você está tentando fazer pode ser alcançado com uma simples expansão de parâmetros:

${var%-*}

Exemplo:

$ var=$(hostname)

$ echo "${var%-*}"
    
por 08.01.2017 / 07:08
2

(...) introduz um subshell. Então $URL não seria definido depois disso (ainda tem o valor que tinha antes da subcamada). Você quer:

IFS=-
read -r -a host_name_array <<< "$(hostname)" 
unset 'host_name_array[${#host_name_array[@]}-1]'
URL="${host_name_array[*]}"

"${host_name_array[*]}" une os elementos nas matrizes no primeiro caractere de $IFS , da mesma forma que "$*" faz no padrão sh .

Se o motivo pelo qual você está usando um subshell é porque não deseja modificar o $IFS globalmente, é possível fazer isso em uma função em que você atribui $IFS um escopo local:

f() {
  local IFS=-
  ...
}
f

Ou use a substituição de comando que também cria um subshell, mas permite passar dados ao shell pai:

URL=$(IFS=-; printf '%s\n' "${host_name_array[*]}")

Aqui, eu usaria a expansão sh padrão para remover esse componente final:

URL=$(uname -n) # uname -n is the standard equivalent of hostname
URL=${URL%-*}

Tem várias vantagens em relação ao acima:

  • funciona mesmo que o texto contenha caracteres de nova linha (muito improvável para nomes de host aqui);
  • se o texto não contiver - , ele não removerá nada;
  • é mais eficiente. read <<< $(hostname) significa executar hostname , ler sua saída por meio de um canal, armazená-lo em um arquivo temporário e ter read lendo a primeira linha dele;
  • não atrapalha o namespace da variável com essas variáveis temporárias
  • é mais curto;
  • é portátil. Não é necessário instalar bash para executar esse código. O sh do sistema será suficiente.

Em qualquer caso, lembre-se de citar suas variáveis ao usá-las em contextos de lista:

printf '%s\n' "$URL"

Ao fazer:

echo $URL

Você está invocando o operador split + glob. Assim, se $IFS ainda contiver - , esse $URL seria dividido em - e echo produziria essas palavras separadas por espaços.

    
por 08.01.2017 / 09:46
0

resposta específica: whipe garfos !

Como isso já usa bashisms, sed , tr ou qualquer outra ferramenta externa são inúteis:

IFS='-' read -r -a host_name_array < <(hostname)
#removing the last part of string after last "-"
unset host_name_array[${#host_name_array[@]}-1]

shorted=${host_name_array[*]}
echo ${shorted// /-}

ou mais simples:

Isso é muito mais curto, rápido e simples:

read myvar < <(hostname)
echo ${myvar%-*}

E se você realmente precisar de host_name_array por outros motivos:

read shorted < <(hostname)
host_name_array=(${shorted//-/ })
shorted=${shorted%-*}

Então você tem uma matriz e um nome de host em curto:

declare -p host_name_array shorted

retornará algo como:

declare -a host_name_array='([0]="tech" [1]="news" [2]="blog" [3]="324344")'
declare -- shorted="tech-news-blog"
    
por 08.01.2017 / 09:20