
行または列から一意に並べ替える方法を教えてください。次のような情報があります。
Special c1,c2,c5,c7,c1,c2
Special2 C6
Special
(との間にあるのは TAB 文字ですc1...
)。
次のような出力が欲しいです:
Special c1,c2,c5,c7
Special2 C6
どうすればこれを実現できるでしょうか?
答え1
使用この答え、
perl -MList::MoreUtils=uniq -laF'\t' -ne '
$F[1] = join(",", uniq(sort(split(",", $F[1])))); print join("\t", @F)'
これは外部パッケージに依存しますリスト::MoreUtils外部依存関係をインストールしたくない場合は、uniq
関数を再実装するとPerlのほんの数行(ただし、macOS では基本システムの一部としてインストールされているようです。)
答え2
perl -F'\t|,' -lane 'my %h; print shift @F, "\t", join ",", sort grep !$h{$_}++, @F' dataf
説明
-F'\t|,'
@F
=> は各レコード フィールドを個TAB
または個の文字の配列に分割しますcomma
。-l
RS
とnewline
も設定されORS
ますnewline
。-a
FS
は、選択された基準に基づいて各レコードを単語に自動分割します-F
。-n
入力時に暗黙的なレコード読み取りループを設定し、AND
要求された場合にのみ印刷します。-e
上記で選択した内容Perl
に基づいて入力の各レコードに対して実行されるコードです。RS
-l
- 最初の要素は によって指定され
shift
、残りの要素はuniquified
によって指定されます。ハッシュ のキーとして格納され%h
、レコードが読み込まれるたびに再生成されます。一意の要素はソートされ、カンマで結合されて出力されます。
答え3
OpenBSD awk
、GNU awk
、およびでテスト済みmawk
:
awk -F ',| +' '{ for (i = 2; i <= NF; ++i) { print $1, $i } }' data.in |
sort -u |
awk '{ f[$1] = (f[$1] ? f[$1] "," : "") $2 } END { for (k in f) { print k, f[k] } }'
1つ目はawk
与えられたデータを次のように拡張します。
Special c1
Special c2
Special c5
Special c7
Special c1
Special c2
Special2 C6
フィールド区切り文字としてコンマと複数のスペースの両方を使用し、入力の各レコード (行) に対して、最初のフィールドを出力し、その後に他の各フィールドを順番に別々の行に出力します。これは、区切り文字として適切に解釈される場所以外に、行にスペースやコンマがないことを前提としています。
sort
真ん中のものはそれを分類します
Special2 C6
Special c1
Special c2
Special c5
Special c7
完全な行をソートキーとして使用してソートを実行し、重複する行を破棄します。
最後にawk
データを結合して
Special c1,c2,c5,c7
Special2 C6
これは、最初のフィールドを連想配列のキーとして使用し、対応するデータをコンマで区切って連結したものを 2 番目のフィールドの値として保存することで行われます。最後に、収集されたすべてのデータが印刷されます。
答え4
もう一つの方法は一行で:
while read line; do echo "$line" | awk '{print $1}' | tr '\n' ' '; echo "$line" | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ',' | sed -e 's/.$//g'; echo; done < file_to_sort
各行の最初の列 ( echo $line | awk '{print $1}' | tr '\n' ' ';
) を取得し、2 番目の列の値を ',' で区切って並べ替え、単一の列に変換してから適用しsort
、元の書式で単一の行に戻します ( echo $line | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ','
)。
@tripleee の提案に従って行分割を実行します。
while IFS=$'\t' read first second; do printf "%s\t%s\n" "$first" "$(tr ',' '\n' <<<"$second" | sort | tr '\n' ',' | sed -e 's/.$//g';)"; done < file_to_sort