私は次の形式のファイルを持っています
列1列2 str1 1 str2 2 str3 3
列を再配置したい。以下のコマンドを試してみました
カット-f2,1file.txt
このコマンドは列を並べ替えません。なぜそれが機能しないのか考えていますか?
cut(1)
マニュアルページの場合:
-b、-c、または-fのいずれか1つだけを使用します。各LISTは、1つの範囲、またはコンマで区切られた多数の範囲で構成されます。選択された入力は、読み取られるのと同じ順序で書き込まれ、1回だけ書き込まれます。
最初にフィールド1に到達するため、フィールド2が印刷されます。
awk
代わりに使用してください:
awk '{ print $2 " " $1}' file.txt
組み合わせcut
てpaste
:
paste <(cut -f2 file.txt) <(cut -f1 file.txt)
コメント経由:次の手順を実行することで、バシズムを回避し、カットの1つのインスタンスを削除することができます。
paste file.txt file.txt | cut -f2,3
シェルだけを使用して、
while read -r col1 col2
do
echo $col2 $col1
done <"file"
そのためにPerlを使用できます。
perl -ane 'print "$F[1] $F[0]\n"' < file.txt
perlを実行する利点は、(Perlを知っている場合)列を再配置するよりもFではるかに多くの計算を実行できることです。
使用join
:
join -t $'\t' -o 1.2,1.1 file.txt file.txt
ノート:
-t $'\t'
GNU では、失敗することなくjoin
より直感的になります(coreutils v8.28以前?)。おそらく、次のような回避策が必要になるバグです。参照:unix結合区切り文字char。-t '\t'
$
$
join
作業中のファイルは1つだけですが、2つのファイル名が必要です。同じ名前を2回使用するとjoin
、目的のアクションを実行するようになります。
リソースが少ないシステムjoin
の場合、他の回答で使用されているツールの一部よりもフットプリントが小さくなります。
wc -c $(realpath `which cut join sed awk perl`) | head -n -1
43224 /usr/bin/cut
47320 /usr/bin/join
109840 /bin/sed
658072 /usr/bin/gawk
2093624 /usr/bin/perl
非常によく似た作業をしているだけで、私は専門家ではありませんが、使用したコマンドを共有したいと思いました。複数列のcsvがあり、そのうち4列しか必要なかったので、それらを並べ替える必要がありました。
私のファイルはパイプ'|'でした 区切られていますが、それは交換できます。
LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv
確かにそれは本当にラフで準備ができていますが、それに合わせて微調整することができます!
列を複製してから実行することを提案する回答への追加と同じようにcut
。複製paste
などの場合、ファイルに対してのみ機能し、ストリームに対しては機能しません。その場合は、sed
代わりに使用してください。
cat file.txt | sed s/'.*'/'&\t&'/ | cut -f2,3
これはファイルとストリームの両方で機能します。これは、を使用してファイルから読み取るのではなく、列を再配置する前にcat
何か面白いことをした場合に興味深いものです。
比較すると、以下は機能しません。
cat file.txt | paste - - | cut -f2,3
ここで、double stdinプレースホルダーpaste
はstdinを複製しませんが、次の行を読み取ります。
sedを使用する
sedを基本的な正規表現のネストされた部分式とともに使用して、列の内容をキャプチャして並べ替えます。このアプローチは、この場合のように、列を並べ替えるカットの数が限られている場合に最適です。
\(
基本的な考え方は、検索パターンの興味深い部分をとで囲むことです。これは、検索パターン内の部分式の連続した位置を表す置換パターンで\)
再生できます。\#
#
例えば:
$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"
収量:
bar foo
部分式の外側のテキストはスキャンされますが、置換文字列で再生するために保持されません。
質問では固定幅の列については説明しませんでしたが、これは提起された解決策の価値のある尺度であるため、ここで説明します。簡単にするために、ファイルがスペースで区切られていると仮定しますが、ソリューションは他の区切り文字に拡張できます。
崩壊するスペース
最も単純な使用法を説明するために、複数のスペースを1つのスペースに折りたたむことができ、2番目の列の値がEOLで終了している(スペースが埋め込まれていない)と仮定します。
ファイル:
bash-3.2$ cat f
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 nl
0000040 s t r 2 sp sp sp sp sp sp sp 2 nl s t r
0000060 3 sp sp sp sp sp sp sp 3 nl
0000072
変身:
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000 C o l u m n 2 sp C o l u m n 1 nl
0000020 1 sp s t r 1 nl 2 sp s t r 2 nl 3 sp
0000040 s t r 3 nl
0000045
列幅の保持
次に、列の幅を変えながら、一定幅の列を持つファイルにメソッドを拡張してみましょう。
ファイル:
bash-3.2$ cat f2
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f2
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 sp
0000040 sp sp sp sp sp nl s t r 2 sp sp sp sp sp sp
0000060 sp 2 sp sp sp sp sp sp nl s t r 3 sp sp sp
0000100 sp sp sp sp 3 sp sp sp sp sp sp nl
0000114
変身:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r 2 sp sp sp sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
最後に、質問の例には長さが等しくない文字列は含まれていませんが、このsed式はこのケースをサポートしています。
ファイル:
bash-3.2$ cat f3
Column1 Column2
str1 1
string2 2
str3 3
変身:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1
1 str1
2 string2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r i n g 2 sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
シェルでの列の並べ替えの他の方法との比較
驚くべきことに、ファイル操作ツールの場合、awkはフィールドからレコードの終わりまでのカットには適していません。sedでは、これは正規表現を使用して実行できます。たとえば\(xxx.*$\)
、xxx
は列に一致する式です。
シェルスクリプト内に実装する場合、サブシェルの貼り付けと切り取りの使用には注意が必要です。コマンドラインから機能するコードは、シェルスクリプト内に持ち込まれた場合、解析に失敗します。少なくともこれは私の経験でした(それが私をこのアプローチに駆り立てました)。
@Metからの回答を拡張し、Perlも使用し
ます。入力と出力がTABで区切られている場合:
perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file
入力と出力が空白で区切られている場合:
perl -lane 'print join " ", @F[1, 0]' in_file
ここで
-e
は、Perlに、個別のスクリプトファイルではなく、インラインでコードを検索するように指示
-n
し、一度に1行ずつ入力を読み取り、その行を読み取った後に(* NIX上の)
-l
入力レコードセパレーターを削除し、出力を追加します。レコード区切り文字(* NIX上)をそれぞれに、空白の入力行を配列に分割し、空白の代わりにTABの入力行を配列に分割します。\n
chomp
\n
print
-a
@F
-F'\t'
-a
@F
@F[1, 0]
は、配列の2番目と1番目の要素で構成される配列@F
です。Perlの配列はゼロインデックスであり、のフィールドcut
は1インデックスであることに注意してください。したがって、のフィールド@F[0, 1]
はのフィールドと同じcut -f1,2
です。
このような表記により、上記に投稿された他のいくつかの回答よりも柔軟な入力操作が可能になることに注意してください(単純なタスクには問題ありません)。例えば:
# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file
# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file