bash - Encontre diferença entre duas variáveis

1

Eu tenho duas variáveis: var1="1, 2, 3, 4" e var2="3, 4, 5, 6" .
Eu gostaria de obter um novo, var3 , contendo as diferenças entre $var1 e $var2 .
O resultado esperado deve ser var3=1, 2, 5, 6 .

Eu tentei diff , mas a saída não é o que eu queria:

diff <(echo "$var1") <(echo "$var2")
1c1
< 1, 2, 3, 4
---
> 3, 4, 5, 6

Qual outra solução me permite ter var3 sem criar nenhum arquivo?

    
por Lejour 23.11.2017 / 15:06

3 respostas

2

Existem muitas maneiras ...

  • Você pode usar sort , tr unique e paste e $() para executá-los e "transformar a saída em uma variável"

    #!/bin/bash
    var1="1, 2, 3, 4"; var2="3, 4, 5, 6"
    var3=$(echo " ${var1}, ${var2}" | tr ',' '\n' | sort | uniq -u | paste -sd,)
    echo $var3
    
    1, 2, 5, 6
    

    Para cada um dos comandos anteriores, você pode ler mais com, por exemplo, man sort

  • Você pode transformar a variável nos arrays do bash e trabalhar neles (siga o seguinte apenas como uma dica, porque há um número incontável de maneiras de perceber isso ...)

    #!/bin/bash#!/bin/bash
    var1="1, 2, 3, 4"; var2="3, 4, 5, 6"
    
    # here you transform the variable in array
    IFS=',' read -ra ADDR <<< "$var1"    
    IFS=',' read -ra ADDR2 <<< "$var2"
    
    # then for each element in the 1st array you search if in the 2nd too
    SEP=""; var3=""
    for i in "${ADDR[@]}"; do
      Found=0
      for j in "${ADDR2[@]}"; do
          [[ "$i" -eq "$j" ]] && Found=1
      done 
      [[ $Found == 0 ]]  && { var3="$var3$SEP$i" ; SEP=", "; }
    done
    
    # then for each element in the 2nd array you search if in the 1st too
    for j in "${ADDR2[@]}"; do
      Found=0
      for i in "${ADDR[@]}"; do
          [[ "$i" -eq "$j" ]] && Found=1
      done 
      [[ $Found == 0 ]]  && { var3="$var3$SEP$j" ; SEP=", "; }
    done
    
    echo $var3
    
  • usando awk (ou, para ser preciso, gawk )

    #!/bin/bash
    var1="1, 2, 3, 4"; var2="3, 4, 5, 6"
    var3=$(echo "$var1, $var2" | \
           awk -F ',' '{for (i=1;i<=NF;i++) {A[$i]++;} } 
                    END{ SEP=""; 
                         for (i in A) {if (A[i]==1){
                            printf ("%s%s", SEP,i); SEP=", "} 
                         }
                       }'
          )
    echo $var3
    

Nota: a segunda e a terceira saída não são ordenadas ...

Notas atualizadas : ... e há um espaço antes de $var1 e $var2 porque no seu formato estranho ( :-) ) há espaços após a vírgula ( , ) então você precisa de cuidados especiais para todos os comandos que tomam apenas um caractere como separador ... isso corrige o problema se houver um , 1 na segunda string ... o que você não é capaz encontre com man <command> você pode tentar encontrar com man bash ou com help command ...

Ad nauseam :

  • diff style, no espírito da sua tentativa ... talvez você possa procurar um formato de saída mais aconchegante ( man diff )

    diff --ignore-all-space   \ 
         <(echo "$var1" | tr ',' '\n' ) <(echo "$var2" | tr ',' '\n')\
         | grep -v "^---" | grep -v "^[0-9c0-9]" | tr -d '<||>|| |'  \
         | paste -sd,
    
por 23.11.2017 / 16:31
0

Não tenho tempo para uma explicação completa, mas:

var1="1, 2, 3, 4"; var2="3, 4, 5, 6"
comm -3 <(grep -oP '\d+' <<<"$var1" | sort) <(grep -oP '\d+' <<<"$var2" | sort) |
  tr -d '\t' |
  paste -sd,
1,2,5,6
    
por 23.11.2017 / 16:02
0

Outra opção:

#!/usr/bin/bash

var1="1, 2, 3, 4"
var2="3, 4, 5, 6"
out=""

for num in 'echo $var1,$var2 | tr -d " "| tr "," "\n " | sort | uniq | tr "\n" " "'
do
        if ('grep -v $num <<< "$var1" >/dev/null 2>&1' || 'grep -v $num <<< "$var2" >/dev/null 2>&1')
        then
                out="$out,$num"
        fi
done

echo $out | sed -e 's/,//'

E corra

$ ./test.sh 
1,2,5,6
    
por 23.11.2017 / 16:26