Sélectionnez des valeurs uniques ou distinctes dans une liste dans un script shell UNIX
Question
J'ai un script ksh qui renvoie une longue liste de valeurs, séparées par une nouvelle ligne, et je souhaite afficher uniquement les valeurs uniques / distinctes. C'est possible de faire ça?
Par exemple, supposons que ma sortie soit un suffixe de fichier dans un répertoire:
tar gz java gz java tar class class
Je souhaite voir une liste comme celle-ci:
tar gz java class
La solution
Vous voudrez peut-être examiner les applications uniq
et sort
.
./yourscript.ksh | sort | uniq
(Pour info, oui, le tri est nécessaire dans cette ligne de commande, uniq
ne supprime que les lignes dupliquées qui se suivent immédiatement)
EDIT:
Contrairement à ce qui a été posté par Aaron Digulla en relation avec les options de ligne de commande de uniq
:
Étant donné l'entrée suivante:
class jar jar jar bin bin java
uniq
affichera toutes les lignes exactement une fois:
class jar bin java
uniq -d
affichera toutes les lignes qui apparaissent plusieurs fois et les imprimera une fois:
jar bin
uniq -u
affichera toutes les lignes qui apparaissent exactement une fois et les imprimera une fois:
class java
Autres conseils
./script.sh | sort -u
Identique à monoxide's répondre , mais un peu plus concis.
Pour les grands ensembles de données où le tri peut ne pas être souhaitable, vous pouvez également utiliser le script Perl suivant:
./yourscript.ksh | perl -ne 'if (!defined $x{ Pour les grands ensembles de données où le tri peut ne pas être souhaitable, vous pouvez également utiliser le script Perl suivant:
<*>
En gros, cela ne fait que rappeler chaque sortie de ligne afin qu'elle ne la reproduise plus.
Il présente l'avantage par rapport au tri & < | uniq
" solution en ce qu'il n'y a aucun tri requis à l'avant.
}) { print Pour les grands ensembles de données où le tri peut ne pas être souhaitable, vous pouvez également utiliser le script Perl suivant:
<*>
En gros, cela ne fait que rappeler chaque sortie de ligne afin qu'elle ne la reproduise plus.
Il présente l'avantage par rapport au tri & < | uniq
" solution en ce qu'il n'y a aucun tri requis à l'avant.
; $x{ Pour les grands ensembles de données où le tri peut ne pas être souhaitable, vous pouvez également utiliser le script Perl suivant:
<*>
En gros, cela ne fait que rappeler chaque sortie de ligne afin qu'elle ne la reproduise plus.
Il présente l'avantage par rapport au tri & < | uniq
" solution en ce qu'il n'y a aucun tri requis à l'avant.
} = 1; }'
En gros, cela ne fait que rappeler chaque sortie de ligne afin qu'elle ne la reproduise plus.
Il présente l'avantage par rapport au tri & < | uniq
" solution en ce qu'il n'y a aucun tri requis à l'avant.
Avec zsh , vous pouvez faire ceci:
zsh-5.0.0[t]% cat infile
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class
Ou vous pouvez utiliser AWK:
zsh-4.3.9[t]% awk '!_[<*>]++' infile
tar
more than one word
gz
java
class
Transférez-les dans sort
et uniq
. Cela supprime tous les doublons.
uniq -d
ne donne que les doublons, uniq -u
n'en donne que les uniques (bandes en double).
Avec AWK, vous pouvez le faire, je le trouve plus rapidement que le tri
./yourscript.ksh | awk '!a[<*>]++'
Unique, comme demandé, (mais non trié);
utilise moins de ressources système pour moins de ~ 70 éléments (comme testé avec le temps);
écrit pour recevoir les commentaires de stdin,
(ou modifier et inclure dans un autre script):
(Bash)
bag2set () {
# Reduce a_bag to a_set.
local -i i j n=${#a_bag[@]}
for ((i=0; i < n; i++)); do
if [[ -n ${a_bag[i]} ]]; then
a_set[i]=${a_bag[i]}
a_bag[i]=\0'
for ((j=i+1; j < n; j++)); do
[[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=\0'
done
fi
done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
a_bag[i]=$e
i=$i+1
done
bag2set
echo "${a_set[@]}"