bash-ファイルの各行をペアにします


10

この質問は、これこの質問に強く関連しています。複数の行を含むファイルがあり、各行はファイルへのパスです。次に、各行を(それ自体ではなく)それぞれ異なる行とペアにします。また、私の目的でA Bはペアはペアと等しいB Aので、これらの組み合わせの1つだけを作成する必要があります。

files.dat 短縮表記でこのように読みます。各文字はファイルパスです(絶対または相対)

a
b
c
d
e

次に、私の結果は次のようになります。

a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

できればbashでこれを解決したいと思います。他の質問とは異なり、私のファイルリストはかなり小さい(約200行)ため、ループとRAM容量を使用しても問題はありません。


それは適切なbashである必要がありますか、それともbashコマンドラインを介して利用できる何かである必要がありますか?他のユーティリティは、テキストを処理するために配置されています。
ジェフシャラー

@JeffSchaller bashコマンドラインからアクセスできるもの。私は少し不明瞭で申し訳ありませんでした
Enno

これはコードゴルフになりつつあります:P
Richard de Wit

3
一般的なルールとして、重要なことをする必要がある限り、BASHよりもお気に入りのスクリプト言語を使用してください。(たとえば、特殊文字やスペースに対して)脆弱性が低くなり、必要に応じて(3つが必要な場合、またはそれらの一部をフィルターで除外して)展開するのがはるかに容易になります。PythonまたはPerlはほとんどすべてのLinuxボックスにインストールする必要があるため、(Busyboxなどの組み込みシステムで作業している場合を除いて)それらは良い選択です。
Davidmh

回答:


7

次のコマンドを使用します。

awk '{ name[$1]++ }
    END { PROCINFO["sorted_in"] = "@ind_str_asc"
        for (v1 in name) for (v2 in name) if (v1 < v2) print v1, v2 }
        ' files.dat

PROCINFOgawk拡張機能の場合があります。あなたawkがそれをサポートしていない場合は、PROCINFO["sorted_in"] = "@ind_str_asc"行を省略して出力をパイプしてsortください(出力をソートしたい場合)。

(これは、入力をソートする必要がありませ。)


8
$ join -j 2 -o 1.1,2.1 file file | awk '!seen[$1,$2]++ && !seen[$2,$1]++'
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

これは、入力ファイルのどの行にも空白が含まれていないことを前提としています。また、ファイルがソートされていることも前提としています。

このjoinコマンドは、ファイル内の行の完全な外積を作成します。これは、存在しないフィールドでファイルをそれ自体と結合することによって行われます。非標準-j 2-1 2 -2 2-j2GNUを使用しない限り、)置き換えられる可能性がありますjoin

awkコマンドは、この結果を読み取り、唯一まだ見られていないペアで結果を出力します。


「ファイルがソートされている」とはどういう意味ですか?どの基準で並べ替えましたか?
Enno

@Ennoソートされた方法でsort -bソートされます。 joinソートされた入力ファイルが必要です。
クサラナンダ

8

pythonソリューション。入力ファイルはitertools.combinations、標準ライブラリから供給されます。標準ライブラリは、フォーマットされ、標準出力に出力される2長のタプルを生成します。

python3 -c 'from itertools import combinations
with open("file") as f:
    lines = (line.rstrip() for line in f)
    lines = ("{} {}".format(x, y) for x, y in combinations(lines, 2))
    print(*lines, sep="\n")
'

6

rubyインストールした場合:

$ ruby -0777 -F'\n' -lane '$F.combination(2) { |c| puts c.join(" ")}' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
  • -0777 ファイル全体を丸呑みにする(OPでファイルサイズが小さいと述べられているので問題ないはずです)
  • -F'\n'改行に基づいて分割するため、各行は$F配列の要素になります
  • $F.combination(2)2一度に組み合わせ要素を生成する
  • { |c| puts c.join(" ")} 必要に応じて印刷
  • 入力ファイルに重複が含まれる可能性がある場合は、 $F.uniq.combination(2)


一度に3つの要素:

$ ruby -0777 -F'\n' -lane '$F.combination(3) { |c| puts c.join(" ")}' ip.txt
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e


ありperl(ジェネリックではない)

$ perl -0777 -F'\n' -lane 'for $i (0..$#F) {
                             for $j ($i+1..$#F) { 
                               print "$F[$i] $F[$j]\n" } }' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e


awk

$ awk '{ a[NR]=$0 }
       END{ for(i=1;i<=NR;i++)
              for(j=i+1;j<=NR;j++)
                print a[i], a[j] }' ip.txt 
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

5

これは純粋なシェルの1つです。

test $# -gt 1 || exit
a=$1
shift
for f in "$@"
do
  echo $a $f
done
exec /bin/sh $0 "$@"

例:

~ (137) $ sh test.sh $(cat file.dat)
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
~ (138) $ 

1
あなたのようなもので、より良いオフにしているので、コマンド置換ストリップは、改行を末尾<file.dat xargs test.shよりtest.sh $(cat file.dat)
iruvar

1

使用Perlすると、次のように実行できます。

$ perl -lne '
     push @A, $_}{
     while ( @A ) {
        my $e = shift @A;
        print "$e $_" for @A;
     }
' input.txt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.