Como cortar uma string baseada no delimitador que está tendo espaço

1
variable_list="
 any:any:-a -b -c any
 one:one:-c -b -f -m mul
 mul:one:-c -b -f -m mul
"
for f in 'echo $variable_list'
do
  c1='echo $f | cut -d':' -f1';
  c2='echo $f | cut -d':' -f2';
  c3='echo $f | cut -d':' -f3-';
  echo "c1==>$c1 and c2==>$c2 and c3==>$c3";
  #exit 0; ###I made mistake here
done;

Resultado esperado:

c1==>any and c2==>any and c3==>-a -b -c any
c1==>one and c2==>one and c3==>-c -b -f -m mul
c1==>mul and c2==>one and c3==>-c -b -f -m mul

Editar 1:

Percebi que era idiota ao usar o script e, na primeira iteração, usei exit 0 para testá-lo apenas na primeira linha, pois tenho muito disso em real. Estava funcionando como tem que ser.

Posso alcançar a saída mencionada mantendo o variable_list sem modificar o formato / caminho de entrada?

(estou usando o bash)

    
por Spike 12.07.2018 / 16:11

2 respostas

5

Seu problema está nos espaços nos dados. O shell dividirá a string em palavras em todos os espaços e o loop for irá iterar essas palavras.

(Para uma solução que não substitua variable_list por um array, veja o final desta resposta.)

Em vez disso, use uma matriz adequada:

variable_list=(
    "any:any:-a -b -c any"
    "one:one:-c -b -f -m mul"
    "mul:one:-c -b -f -m mul"
)

for var in "${variable_list[@]}"; do
    c1=$( cut -d':' -f1  <<<"$var" )
    c2=$( cut -d':' -f2  <<<"$var" )
    c3=$( cut -d':' -f3- <<<"$var" )
    printf 'c1==>%s and c2==>%s and c3==>%s\n' "$c1" "$c2" "$c3"
done

O uso de uma matriz garante que você possa acessar cada conjunto individual de variáveis como sua própria entrada de matriz, sem depender que elas sejam delimitadas por novas linhas ou por algum outro caractere.

O código também está usando "here-strings" em bash para enviar a string para cut (em vez de echo e um pipe).

Ou com muito mais eficiência,

variable_list=(
    "any:any:-a -b -c any"
    "one:one:-c -b -f -m mul"
    "mul:one:-c -b -f -m mul"
)

for var in "${variable_list[@]}"; do
    IFS=':' read -r c1 c2 c3 <<<"$var"
    printf 'c1==>%s and c2==>%s and c3==>%s\n' "$c1" "$c2" "$c3"
done

A definição de IFS para dois-pontos por read fará com que read divida a entrada em dois pontos (e não em espaços, tabulações e novas linhas).

Observe que toda a cotação acima é significativa. Sem as aspas duplas, o shell executaria a divisão de palavras e a globalização de nomes de arquivos nos valores das variáveis variable_list , var e três c .

Relacionados:

Se tudo que você procura é uma saída específica, então você pode trapacear um pouco:

variable_list=(
    "any:any:-a -b -c any"
    "one:one:-c -b -f -m mul"
    "mul:one:-c -b -f -m mul"
)

( IFS=':'; set -f; printf 'c1==>%s and c2==>%s and c3==>%s\n' ${variable_list[@]} )

Isso executa o printf em uma subshell para que a opção de configuração IFS e -f ( noglob ) não afete o restante do script. Configurar IFS para dois-pontos aqui fará com que o shell expanda a matriz sem aspas variable_list em três conjuntos de três argumentos para printf . printf imprimirá os três primeiros de acordo com sua string de formato e, em seguida, reutilizará esse formato para o próximo conjunto de três argumentos, até que todos os argumentos tenham sido processados.

O set -f impede que a expansão sem aspas de variable_list acione a globalização de nomes de arquivos, caso haja algum caractere de globalização de nomes de arquivos.

Usando uma string delimitada por nova linha:

variable_list="
any:any:-a -b -c any
one:one:-c -b -f -m mul
mul:one:-c -b -f -m mul"

while IFS= read -r var; do
    IFS=':' read -r c1 c2 c3 <<<"$var"
    printf 'c1==>%s and c2==>%s and c3==>%s\n' "$c1" "$c2" "$c3"
done <<<"$variable_list"

Isto lê os dados da string como se viesse de um arquivo.

Relacionados:

por 12.07.2018 / 16:49
0

Você pode usar o awk para obter sua saída:

echo "$variable_list" | awk -F: '
{
  sub("^ ","")
  for(i=1;i<=NF;i++)
    sub(/^/,"c" i "==>",$i)
}1' OFS=" and "
    
por 12.07.2018 / 20:29

Tags