Listen Sie Zeichenfolgen auf, die Teilzeichenfolgen anderer Zeichenfolgen in der Liste sind

Listen Sie Zeichenfolgen auf, die Teilzeichenfolgen anderer Zeichenfolgen in der Liste sind

Ich habe eine Liste mit Namen wie diese:

dog_bone
dog_collar
dragon
cool_dragon
lion
lion_trainer
dog

Ich muss Namen extrahieren, die in anderen Namen vorkommen, wie folgt:

dragon
lion
dog

uniqIch habe die Manpage durchgesehen , aber es scheint, als würden ganze Zeilen verglichen, keine Zeichenfolgen. Gibt es eine Möglichkeit, dies mit einer Bash-Funktion zu tun?

Antwort1

file=/the/file.txt
while IFS= read -r string; do
  grep -Fe "$string" < "$file" | grep -qvxFe "$string" &&
    printf '%s\n' "$string"
done < "$file"

Dadurch werden ein read, zwei grepund manchmal ein printfBefehl pro Dateizeile ausgeführt, was also nicht sehr effizient ist.

Sie können das Ganze mit einem einzigen awkAufruf erledigen:

awk '{l[NR]=$0}
     END {
       for (i=1; i<=NR; i++)
         for (j=1; j<=NR; j++)
           if (j!=i && index(l[j], l[i])) {
             print l[i]
             break
           }
     }' < "$file"

das bedeutet allerdings, dass die gesamte Datei im Speicher abgelegt wird.

Antwort2

Schlag

names=(
  dog_bone
  dog_collar
  dragon
  cool_dragon
  lion
  lion_trainer
  dog
)

declare -A contained                 # an associative array
for (( i=0; i < ${#names[@]}; i++ )); do 
    for (( j=0; j < ${#names[@]}; j++ )); do 
        if (( i != j )) && [[ ${names[i]} == *"${names[j]}"* ]]; then
            contained["${names[j]}"]=1
        fi 
    done
done
printf "%s\n" "${!contained[@]}"    # print the array keys
dog
dragon
lion

Antwort3

Hier ist ein Perl-Ansatz. Auch hier muss die Datei in den Speicher geladen werden:

perl -le '@f=<>; foreach $l1 (@f){ 
                    chomp($l1); 
                    foreach $l2 (@f){ 
                        chomp($l2); 
                        next if $l1 eq $l2; 
                        $k{$l1}++ if $l2=~/$l1/;
                    }
                } print join "\n", keys %k' file

Antwort4

Hier ist eine bashVersionslösung 4.x:

#!/bin/bash

declare -A output
readarray input < '/path/to/file'

for i in "${input[@]}"; do
  for j in "${input[@]}"; do
    [[ $j = "$i" ]] && continue
    if [ -z "${i##*"$j"*}" ]; then
      if [[ ! ${output[$j]} ]]; then
        printf "%s\n" "$j"
        output[$j]=1
      fi
    fi
  done
done

verwandte Informationen