次の名前のリストがあります。
dog_bone
dog_collar
dragon
cool_dragon
lion
lion_trainer
dog
次のように、別の名前の中に表示される名前を抽出する必要があります。
dragon
lion
dog
マニュアルページを見ましたが、uniq
文字列ではなく行全体を比較しているようです。 bash関数を使用してこれを行う方法はありますか?
答え1
file=/the/file.txt
while IFS= read -r string; do
grep -Fe "$string" < "$file" | grep -qvxFe "$string" &&
printf '%s\n' "$string"
done < "$file"
ファイルの各行は1つ、2つ、read
またはgrep
時には1つのprintf
コマンドを実行するため、それほど効率的ではありません。
ワンコールですべてを行うことができますawk
:
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"
ただし、これはファイル全体がメモリに保存されることを意味します。
答え2
強く打つ
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
答え3
これがPerl方式です。また、ファイルをメモリにロードする必要があります。
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
答え4
bash
バージョンソリューションは次のとおりです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