2016-06-08 7 views
0
declare -a array1=(1 2 3 4 5 6 7 8 9 10 11 12) 
declare -a array2=(1 2 3 5 6 7 9 10 11 12) 

In bash, wie kann ich eine dritte Reihe der Werte erhalten, die in array2 in array1 aber fehlend vorhanden sind? In dem obigen Beispiel wird der erwartete Ausgang ist (4 8)Finden Sie heraus, welche Werte von `array1` fehlen in` array2`

+0

@andlrc. Vielen Dank. Ich verstehe nicht ganz, was eine Befehlsersetzung ist (wird aber die Frage untersuchen). Ich habe die erste geändert, um ein Array korrekt zu definieren. –

+0

@ Remi.b Irgendwelche Gründe, warum Sie das tun? Dies ist nicht wirklich, was Shell-Skripting ist. Natürlich ist es möglich, aber haben Sie ein bestimmtes Ziel vor Augen? – andlrc

Antwort

1

Mit mapfile, comm, sort und Prozess Substitution:

array1=(1 2 3 4 5 6 7 8 9 10 11 12) 
array2=(1 2 3 5 6 7 9 10 11 12) 
mapfile -t arr < <(comm -23 --nocheck-order \ 
         <(printf "%s\n" "${array1[@]}" | sort -n) \ 
         <(printf "%s\n" "${array2[@]}" | sort -n)) 

Ergebnis:

$ declare -p arr 
declare -a arr='([0]="4" [1]="8")' 

Erläuterung, von innen nach außen:

  • Pr int das Array ein Element pro Zeile und sort:

    $ printf "%s\n" "${array1[@]}" | sort -n 
    1 
    2 
    3 
    4 
    5 
    6 
    7 
    8 
    9 
    10 
    11 
    12 
    

    und das gleiche für array2.

  • diese Rohre in der Prozess Substitutionen Wickel und sie als Argument für comm:

    comm -23 --nocheck-order \ 
        <(printf "%s\n" "${array1[@]}" | sort -n) \ 
        <(printf "%s\n" "${array2[@]}" | sort -n) 
    

    Die -23 das Ergebnis zu Werten eindeutig die ersten Anordnung reduziert; --nocheck-order unterdrückt eine Warnung, dass die Eingabe nicht lexikographisch sortiert ist. Der Ausgang dieser ist

    4 
    8 
    
  • jede Zeile in einem Array-Element lesen mit mapfile (-t entfernt die Zeilenumbrüche):

    mapfile -t arr < <(comm -23 --nocheck-order \ 
            <(printf "%s\n" "${array1[@]}" | sort -n) \ 
            <(printf "%s\n" "${array2[@]}" | sort -n)) 
    

    Nun arr die beiden Werte, wie oben gezeigt enthält.

Der sort Schritt ist nicht unbedingt erforderlich, aber macht die Lösung Arbeit für nicht sortiert und Arrays.

+1

Sie könnten 'printf% s \ n" "$ {array1 [@]}" 'anstelle des IFS + echo-Konstrukts verwenden. In diesem Beispiel ist die Verwendung von '" $ {array1 [@]} "' anstelle von '" $ {array1 [*]} "wichtig für die' printf'-Variante; Mit '*' erhalten Sie einen einzelnen String, der als Argument an 'printf' übergeben wird. –

+0

@ JonathanLeffler Das ist in der Tat eleganter. '$ {array1 [*]}" 'anstelle von' "$ {array1 [@]}" 'ist übrigens auch in meiner Variante wichtig, da sonst 'IFS' nicht verwendet wird, um die Array-Elemente zu trennen - nur ein Leerzeichen. –

1

Nach onliner den Trick:

diff -y <(printf '%s\n' "${array2[@]}") <(printf '%s\n' "${array1[@]}") | grep -Po '[\|\<\>][\t]\K[0-9]+$' 

Die printf verwendet Elemente in separaten Zeilen zu drucken. Jetzt gibt die diff -y die Ausgabe:

1   1  
2   2 
3   3 
5   | 4 
6   5 
7   6 
9   7 
10  | 8 
11   9 
12   10 
      11 
      12 

jetzt alles, was Sie haben die Nummern nach den | (oder manchmal < oder >) zu filtern. Ich habe dafür grep verwendet, aber sed kann auch verwendet werden. Wenn Ihr Array nicht sortiert ist, fügen Sie einfach so zu jedem printf wie folgt hinzu:

Verwandte Themen