Remove palavras duplicadas de duas variáveis

0

Desejo remover todas as ocorrências de palavras que aparecem em VAR1 e que também aparecem em VAR2 .

Por exemplo, para:

VAR1=a2,a3,a4,a5,a6,a1
VAR2=a1,a2,a6,a4,a7,a8

Eu quero que o resultado seja:

VAR1=a3,a5
VAR2=a7,a8
    
por Nir 18.01.2018 / 09:54

6 respostas

0

Solução estendida tr + sort + paste + comm :

VAR1="a2,a3,a4,a5,a6,a1"
VAR2="a1,a2,a6,a4,a7,a8"
temp1="$VAR1"
temp2="$VAR2"

VAR1=$(comm -23 <(tr ',' '\n' <<<"$temp1" | sort) <(tr ',' '\n' <<<"$temp2" | sort) | paste -d',' -s)
VAR2=$(comm -13 <(tr ',' '\n' <<<"$temp1" | sort) <(tr ',' '\n' <<<"$temp2" | sort) | paste -d',' -s)

Resultados:

$ echo "$VAR1"
a3,a5

$ echo "$VAR2"
a7,a8
    
por 18.01.2018 / 10:14
1

Em zsh , você usaria seus operadores de disjunção de matriz:

$ VAR1=a2,a3,a4,a5,a6,a1
$ VAR2=a1,a2,a6,a4,a7,a8
$ array1=(${(s:,:)VAR1})
$ array2=(${(s:,:)VAR2})
$ echo ${(j:,:)array1:|array2}
a3,a5
$ echo ${(j:,:)array2:|array1}
a7,a8
    
por 18.01.2018 / 23:11
0

No script abaixo, atribuímos tmp1 = $ VAR1 e tmp2 = $ VAR2 como variável de temperamento.

Abaixo do script awk one liner é testado e funcionou bem

tmp1=$VAR1;tmp2=$VAR2

 VAR1=$(awk 'NR==FNR {a[$1];next}!($1 in a) {print $1}' <(echo $tmp2|perl -pne "s/,/\n/g") <(echo $tmp1|perl -pne "s/,/\n/g")|perl -pne "s/\n/,/g"| sed "s/,$//g")



VAR2=$(awk 'NR==FNR {a[$1];next}!($1 in a) {print $1}' <(echo $tmp1|perl -pne "s/,/\n/g") <(echo $tmp2|perl -pne "s/,/\n/g")|perl -pne "s/\n/,/g"| sed "s/,$//g")

saída

echo $VAR1

a3, a5

echo $VAR2

a7, a8

    
por 18.01.2018 / 16:11
0

Usando bash associative arrays para manter os resultados, faça um loop em cada um deles, remova os que estão em comum e junte os arrays às variáveis originais:

VAR1=a2,a3,a4,a5,a6,a1
VAR2=a1,a2,a6,a4,a7,a8

OIFS=$IFS
IFS=,
declare -A v1 v2
set -f
for v in $VAR1
do
  v1["$v"]=1
done

for v in $VAR2
do
  v2["$v"]=1
done

for v in "${!v1[@]}"
do
  #if it's there, remove it from v1 too
  if [[ ${v2[$v]} -eq 1 ]]
  then
    unset v1["$v"]
    unset v2["$v"]
  fi
done
for v in "${!v2[@]}"
do
  #if it's there, remove it from v2 too
  if [[ ${v1[$v]} -eq 1 ]]
  then
    unset v1["$v"]
    unset v2["$v"]
  fi
done


VAR1=$(set -- "${!v1[@]}"; IFS=,; echo "$*")
VAR2=$(set -- "${!v2[@]}"; IFS=,; echo "$*")
unset v1 v2
IFS=$OIFS
    
por 18.01.2018 / 17:01
0

Abordagem semelhante à resposta de Jeff, mas usando perl

source <(
    perl -se '
        @v1 = split /,/, $v1;
        @v2 = split /,/, $v2;
        @h1{@v1} = (1) x @v1; delete @h1{@v2};
        @h2{@v2} = (1) x @v2; delete @h2{@v1};
        printf qq{VAR1only="%s"\n}, join ",", keys %h1;
        printf qq{VAR2only="%s"\n}, join ",", keys %h2;
    ' -- -v1="$VAR1" -v2="$VAR2"
)

echo "$VAR1only -- $VAR2only"
a3,a5 -- a7,a8
    
por 18.01.2018 / 21:05
0

Uma variação da resposta da Praveen Kumar BS :

tmp1="$VAR1"
tmp2="$VAR2"
awk_prog='BEGIN {FS=","; ORS=","}
        NR==1 {for (i=1; i<=NF; i++) a[$i]}
        NR==2 {for (i=1; i<=NF; i++) if (!($i in a)) print $i}'
VAR1=$(printf '%s\n' "$tmp2" "$tmp1" | awk "$awk_prog" | sed 's/,$//')
VAR2=$(printf '%s\n' "$tmp1" "$tmp2" | awk "$awk_prog" | sed 's/,$//')
  • Como estamos fazendo exatamente a mesma coisa duas vezes, salve o programa awk em uma variável, em vez de digitá-lo duas vezes.
  • Defina o separador f ield s (separador de campos de entrada ) para vírgula ( , ), então o awk vai separar as strings de entrada separadas por vírgula apropriadamente, e não precisamos usar outro programa para alterar vírgulas para novas linhas.
  • Defina o o utput r ecord s eparator (ORS) como vírgula, então quando dizemos print something , a saída é something, em vez de something(newline) , e não precisamos usar outro programa para alterar as novas linhas para vírgulas.
  • A primeira linha ( NR==1 ) de entrada para awk é a string que contém as palavras que queremos remover (da outra string). Usando um truque que deve ser um pouco conhecido agora, e é descrito com mais detalhes em outro lugar neste site, "Lembrar" as palavras desta string criando um elemento na matriz associativa a com a palavra como um índice.
  • A segunda linha ( NR==2 ) de entrada para awk é a string que contém as palavras que queremos manter (a menos que eles também apareçam na outra string). Para cada palavra nesta linha, imprima-a a menos que seja sinalizado no array a como tendo sido visto na primeira corda.
  • Essa noção da primeira string e da segunda string aparecendo como a primeira linha e a segunda linha, respectivamente, irá quebrar se qualquer uma das strings contiver nova (s) linha (s). Se esta é uma possibilidade, mude a inicialização para

    tmp1="$(tr '\n' ',' <<< "$VAR1")"
    tmp2="$(tr '\n' ',' <<< "$VAR2")"
    

    Observe que a sintaxe "string aqui" ( <<< ) não é compatível com POSIX e pode não funcionar em shells além do bash.

  • VAR1 deve ser definido para as palavras que não estão em tmp2 , mas estão em tmp1 , e vice-versa.
  • Cada "linha" (registro) de saída de awk termina com uma vírgula em vez de uma nova linha, Portanto, use sed para remover uma vírgula (se houver) do final. (Poderíamos ter feito isso inteiramente em awk , mas desta forma é mais fácil.)
por 18.01.2018 / 21:42