aa ab aa ac aa ad
などを含む配列があります。次に、この配列からすべての一意の要素を選択します。考えてみれば、これは他の質問で述べたように、sort | uniq
またはで簡単になりますsort -u
が、配列では何も変更されていません...コードは次のとおりです。
echo `echo "${ids[@]}" | sort | uniq`
私は何が間違っているのですか?
回答:
少しハッキーですが、これでうまくいくはずです:
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
並べ替えられた一意の結果を配列に保存するには、配列の割り当てを実行します。
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
シェルがherestrings(bash
should)をサポートしている場合は、次のようにecho
変更することでプロセスを節約できます。
tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '
入力:
ids=(aa ab aa ac aa ad)
出力:
aa ab ac ad
説明:
"${ids[@]}"
-シェル配列の一部として使用されているecho
か、herestringとして使用されているかに関係なく、シェル配列を操作するための構文。この@
部分は「配列内のすべての要素」を意味しますtr ' ' '\n'
-すべてのスペースを改行に変換します。配列はシェルによって、スペースで区切られた1行の要素として認識されるためです。また、sortは入力が別々の行にあることを想定しているためです。sort -u
-一意の要素のみを並べ替えて保持しますtr '\n' ' '
-以前に追加した改行をスペースに戻します。$(...)
-コマンド置換tr ' ' '\n' <<< "${ids[@]}"
が、より効率的な方法です。echo "${ids[@]}" | tr ' ' '\n'
printf
そのように使うことができることにさえ気づいていませんでした(フォーマット文字列より多くの引数を与えてください)
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
。追加の括弧なしで、それは文字列としてそれを与えていました。
... | uniq | ...
代わりにを使用してください... | sort -u | ...
。
uniq
のみを削除します。この回答の例では、は元のと同じになります。順序を維持するには、を試してください。stackoverflow.com/questions/1444406/…も参照してください。sorted_unique_ids
ids
... | awk '!seen[$0]++'
Bashバージョン4以降を実行している場合(Linuxの最新バージョンの場合)、元の配列の各値を含む新しい連想配列を作成することで、bashで一意の配列値を取得できます。このようなもの:
$ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad
これが機能するのは、任意の配列(連想配列または従来型、任意の言語)では、各キーは1回しか表示できないためです。場合for
ループの第二の値に到達するaa
にa[2]
は、上書きb[aa]
のために元々設定されましたa[0]
。
ネイティブbashでの作業はsort
、パイプややなどの外部ツールを使用するよりも高速ですuniq
が、データセットが大きい場合は、awkやpythonなどのより強力な言語を使用するとパフォーマンスが向上する可能性があります。
自信がある場合は、の機能をfor
使用printf
して複数の引数の形式をリサイクルすることでループを回避できますが、これにはが必要なようですeval
。(問題がなければ、今すぐ読むのをやめてください。)
$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )
このソリューションが必要eval
とする理由は、単語分割の前に配列値が決定されるためです。つまり、コマンド置換の出力は、キーと値のペアのセットではなく、単一の単語と見なされます。
これはサブシェルを使用しますが、配列値の処理にはbashビルトインのみを使用します。必ずeval
批判的な目で使用を評価してください。chepner、glenn jackman、またはgreycatがコードに問題がないことを100%確信していない場合は、代わりにforループを使用してください。
これはすでに回答済みだと思いますが、検索結果でかなり高く表示され、誰かを助けるかもしれません。
printf "%s\n" "${IDS[@]}" | sort -u
例:
~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>
ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(`printf "%s\n" "${ids[@]}" |sort -u`)
、それで私IFS=$'\n'
は@gniourf_gniourfによって提案されたものを追加しました
IFS=$'\n'; ids2=(...)
変数割り当ての前の一時的な割り当ては不可能だからです。代わりに、次の構造を使用してくださいIFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)"
。
配列要素に空白またはその他のシェル特殊文字がある場合(そして、それらがないことを確認できますか?)、まずそれらをキャプチャするために(そして常にこれを行う必要があります)、配列を二重引用符で囲みます!例:"${a[@]}"
。Bashは、これを文字通り「個別の引数の各配列要素」として解釈します。bash内では、これは常に常に機能します。
次に、並べ替えられた(そして一意の)配列を取得するには、並べ替えが理解できる形式に変換し、bash配列要素に戻すことができるようにする必要があります。これは私が思いついた最高のものです:
eval a=($(printf "%q\n" "${a[@]}" | sort -u))
残念ながら、これは空の配列の特殊なケースでは失敗し、空の配列を1つの空の要素の配列に変えます(printfには0の引数がありましたが、それでも1つの空の引数があるかのように出力します-説明を参照してください)。したがって、ifまたは何かでそれをキャッチする必要があります。
説明:printfの%q形式は、bashがevalのようなもので回復できるような方法で、出力された引数を「エスケープ」します。各要素は独自の行でシェルエスケープされて印刷されるため、要素間の区切り文字は改行のみであり、配列の割り当てでは各行が要素として扱われ、エスケープされた値がリテラルテキストに解析されます。
例えば
> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''
evalは、配列に戻る各値からエスケープを取り除くために必要です。
uniq
代わりにを使用してくださいsort -u
。
uniq
ソートされていないリストでは正しく機能しないため、常にと組み合わせて使用する必要があることに注意してくださいsort
。
'sort'を使用して、forループの出力を順序付けることができます。
for i in ${ids[@]}; do echo $i; done | sort
「-u」を使用して重複を排除します。
for i in ${ids[@]}; do echo $i; done | sort -u
最後に、配列を一意の要素で上書きすることができます。
ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
ids=( `for i in ${ids[@]}; do echo $i; done | uniq` )
一意の値で構成される新しい配列を作成するには、配列が空でないことを確認してから、次のいずれかを実行します。
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')
警告:のようなことをしようとしないでくださいNewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) )
。それはスペースで壊れます。
sort -u
れますuniq
。
uniq
は、隣接する重複行のみをマージするため、と同じではありませんawk '!x[$0]++'
。
猫番号.txt
1 2 3 4 4 3 2 5 6
行を列に出力します:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}'
1
2
3
4
4
3
2
5
6
重複するレコードを見つけます。
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'
4
3
2
重複するレコードを置き換える:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'
1
2
3
4
5
6
Uniqレコードのみを検索します。
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}
1
5
6
bash内部のみを使用するソリューションが必要な場合は、値を連想配列のキーとして設定してから、キーを抽出できます。
declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do
uniqs["${f}"]=""
done
for thing in "${!uniqs[@]}"; do
echo "${thing}"
done
これは出力します
bar
foo
bar none
埋め込まれた空白を処理するための別のオプションは、でnull区切り、printf
で区別しsort
、ループを使用して配列にパックすることです。
input=(a b c "$(printf "d\ne")" b c "$(printf "d\ne")")
output=()
while read -rd $'' element
do
output+=("$element")
done < <(printf "%s\0" "${input[@]}" | sort -uz)
この終わりに、input
及びoutput
所望の値を含む(但し、順序は重要ではありません)。
$ printf "%q\n" "${input[@]}"
a
b
c
$'d\ne'
b
c
$'d\ne'
$ printf "%q\n" "${output[@]}"
a
b
c
$'d\ne'
これを試して、ファイルの最初の列の一意の値を取得します
awk -F, '{a[$1];}END{for (i in a)print i;}'
uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"