GNU/Linux >> Linux の 問題 >  >> Linux

Bashの配列から一意の値を取得するにはどうすればよいですか?

これはすでに回答済みですが、検索結果の上位に表示され、誰かの役に立つかもしれません.

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
~>

配列要素に空白またはその他のシェル特殊文字が含まれている場合 (そして、それらが含まれていないことを確認できますか?)、まずそれらをキャプチャするために (常にこれを行う必要があります)、配列を二重引用符で表現します!例えば"${a[@]}" . Bash は文字通りこれを「個別の argument 内の各配列要素」と解釈します。 ".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 は、配列に戻る各値のエスケープを取り除くために必要です。


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 の 2 番目の値に到達します 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 が必要な理由 単語分割の前に配列値が決定されるということです。これは、コマンド置換の出力が 単一の単語 と見なされることを意味します key=value ペアのセットではありません。

これはサブシェルを使用しますが、bash ビルトインのみを使用して配列値を処理します。 eval の使用を評価してください 批判的な目で。 chepner、glenn jackman、greycat があなたのコードに間違いを見つけられないという確信が 100% ない場合は、代わりに for ループを使用してください。


少しハックですが、これで十分です:

echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

並べ替えられた一意の結果を配列に保存するには、配列の割り当てを行います:

sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

シェルがヒアストリングをサポートしている場合 (bash 必要があります)、echo を割くことができます 次のように変更して処理します:

tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '

2021 年 8 月 28 日時点のメモ:

ShellCheck wiki 2207 によると、read -a 分割を避けるためにパイプを使用する必要があります。したがって、bash ではコマンドは次のようになります。

IFS=" " read -r -a ids <<< "$(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')"

または

IFS=" " read -r -a ids <<< "$(tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' ')"

入力:

ids=(aa ab aa ac aa ad)

出力:

aa ab ac ad

説明:

  • "${ids[@]}" - echo の一部として使用されるかどうかにかかわらず、シェル配列を操作するための構文 またはヒアストリング。 @ part は「配列内のすべての要素」を意味します
  • tr ' ' '\n' - すべてのスペースを改行に変換します。配列は、スペースで区切られた単一行の要素としてシェルに表示されるためです。また、並べ替えは入力が別の行にあることを想定しているためです。
  • sort -u - 一意の要素のみを並べ替えて保持
  • tr '\n' ' ' - 前に追加した改行をスペースに戻します。
  • $(...) - コマンド置換
  • 余談:tr ' ' '\n' <<< "${ids[@]}" より効率的な方法です:echo "${ids[@]}" | tr ' ' '\n'

Linux
  1. IP (Linux) からホスト名を取得するには?

  2. bash でディスク上で利用可能なバイト数のみを取得するにはどうすればよいですか?

  3. WSL (Ubuntu) Bash から Windows 実行可能ファイルを実行するにはどうすればよいですか?

  1. bashで文字列から最後の数字を取得するにはどうすればよいですか?

  2. grep 出力から一意の結果を除外するにはどうすればよいですか?

  3. bash履歴から重複を削除するにはどうすればよいですか?

  1. bashからネットマスクを取得するには?

  2. bashで配列値をシフトする方法

  3. .py ファイルからバイナリを取得するにはどうすればよいですか