UNIXソートでヘッダー行を無視する方法はありますか?


102

UNIX(私の場合はCygwin)の並べ替えユーティリティを使用して並べ替えようとしている固定幅フィールドファイルがあります。

問題は、ファイルの上部に2行のヘッダーがあり、ファイルの下部に並べ替えられていることです(各ヘッダー行はコロンで始まるため)。

「最初の2行を並べ替えずに渡す」、またはコロン行を先頭に並べ替える順序を指定する方法をsortに指示する方法はありますか-残りの行は常に6桁の数字で始まります(実際にはIソートしている)それが役立つ場合。

例:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
500123TSTMY_RADAR00
222334NOTALINEOUT01
477821USASHUTTLES21
325611LVEANOTHERS00

ソートする必要があります:

:0:12345
:1:6:2:3:8:4:2
010005TSTDOG_FOOD01
222334NOTALINEOUT01
325611LVEANOTHERS00
477821USASHUTTLES21
500123TSTMY_RADAR00

記録のために:これまでに使用しているコマンドラインは "sort -t \\ -k1.1,1.6 <file>"です[データにはスペースを含めることができますが、バックスラッシュを含めることはできません]
Rob Gilliam

回答:


124
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

括弧はサブシェルを作成し、stdoutをラップして、単一のコマンドから来たかのようにパイプまたはリダイレクトできるようにします。


ありがとう。私はこの回答を最も完全で簡潔に見えるので受け入れます(そして、それが何をしているのか理解しています!)-"head -n 2"である必要があります:-)
Rob Gilliam

1
ありがとう、「頭」の部分を修正しました。
BobS 2013年

4
パイプインされたデータでこのバージョンを機能させる方法はありますか?で試しましたtee >(head -n $header_size) | tail -n +$header_size | sortが、ヘッドがtail|sortパイプの後ろを走っているように見えるため、ヘッダーが最後に印刷されてしまいます。これは確定的ですか、それとも競合状態ですか?
Damien Pollet 2014年

catstdinを一時ファイルにリダイレクトするために使用する場所をつなぎ合わせて、その新しいファイルに対して上記のコマンドを実行することもできますが、次のようにawkベースのソリューションのいずれかを使用する方がよいほど醜くなり始めています他の応答。
BobS 2014年

@DamienPollet:Dave回答を参照してください。
ジョナサンレフラー、2015

63

を使用してもかまわない場合はawkawkの組み込みパイプ機能を利用できます

例えば。

extract_data | awk 'NR<3{print $0;next}{print $0| "sort -r"}' 

これは最初の2行を逐語的に出力し、残りをにパイプしsortます。

これには、パイプ入力の一部を選択的にソートできるという非常に具体的な利点があることに注意してください。提案されている他のすべての方法は、複数回読み取ることができるプレーンファイルのみをソートします。これは何でも機能します。


2
とてもいいです、そしてそれはファイルだけでなく、任意のパイプで動作します!
ラポ2014年

4
美しいawkが私を驚かせることは決してありません。また、必要はありません$0print十分です。
nachocab 2015年

1
@SamWatkins freeseekの 答えはそれほど醜くない。
フェス。

ソートするために-rオプションは何をしていますか?これは逆ソートになるはずですか?
gvrocha

32

パイプされたデータで機能するバージョンは次のとおりです。

(read -r; printf "%s\n" "$REPLY"; sort)

ヘッダーに複数の行がある場合:

(for i in $(seq $HEADER_ROWS); do read -r; printf "%s\n" "$REPLY"; done; sort)

この解決策はここからです


9
いいね。シングルヘッダーの場合はextract_data | (read h; echo "$h"; sort) 、覚えるのに十分短い長さを使用 します。あなたの例はより多くのエッジケースをカバーしています。:)これが最良の答えです。パイプで動作します。いやいや
フェス。

1
わかりました、私はこれをたどりました、そして、bashはこの仕事をするために特別な長さに行くようです。一般に、これをCまたは他の言語でコーディングした場合、stdioは最初のヘッダー行だけを読み取るわけではないため、機能しません。シーク可能なファイルで実行すると、bashはより大きなチャンク(テストでは128バイト)を読み取り、最初の行の終わりからlseekします。パイプで実行すると、bashは行の終わりを通過するまで一度に1文字ずつ読み取ります。
Sam Watkins、

いいね!あなただけのヘッダを食べたいなら、それは覚えさらに簡単です:extract_data | (read; sort)
ジェイソン・スアレス

これはほぼ完璧ですが、先頭と末尾のスペースを維持するために、 "read"ではなく "IFS = read"を使用する必要があります。
Stanislav German-Evtushenko 2017年

6
これは私の意見では受け入れられる答えになるはずです。パイプで連結されたデータでも機能するという点で、シンプルで簡潔で柔軟性があります。
ポールI

12

単純なケースでsedは、仕事をエレガントに行うことができます:

    your_script | (sed -u 1q; sort)

または同等に、

    cat your_data | (sed -u 1q; sort)

キーは1q- にあり、最初の行(ヘッダー)を出力して終了します(残りの入力はに残しますsort)。

与えられた例で2qは、トリックを行います。

-uスイッチ(バッファなし)がそれらのために必要とされsedますが通過することを(とりわけ、GNUの)それ以外の場合は、チャンク単位で入力を読んでいました、それによってデータを消費sのsort代わりに。


1
こんにちは、@ Andrea; Stack Overflowへようこそ。少なくともWindowsのGit Bashでテストしているときは(6年前に別のジョブを使用していたCygwinから移動しました)、あなたの答えが機能しないと思います。sedコマンドは、stdinからすべてのデータをプルします。ソートするために渡すデータはありません。コマンドをcat your_data |に変更してみてください。(sed 1q; wc -l)私が何を意味するかを確認します。
Rob Gilliam

1
これは、次のように、sedコマンドに2回目に入力を渡した場合に機能します。cat sortMe.csv | (sed 1q sortMe.csv; sort -t、-k3 -rn)> Sorted.csv
Harry Cramer


4
head -2 <your_file> && nawk 'NR>2' <your_file> | sort

例:

> cat temp
10
8
1
2
3
4
5
> head -2 temp && nawk 'NR>2' temp | sort -r
10
8
5
4
3
2
1

3

2行のコードしか必要ありません...

head -1 test.txt > a.tmp; 
tail -n+2 test.txt | sort -n >> a.tmp;

数値データの場合、-nが必要です。アルファソートの場合、-nは必要ありません。

ファイルの例:
$ cat test.txt

ヘッダ
8
5
100
1
-1

結果:
$ cat a.tmp

ヘッダー
-1
1
5
8
100


1
これは基本的に受け入れられた答えと同じ答えではありませんか?(BobSのアプローチを除いて、結果をstdoutに配置し、必要に応じて、ファイルに書き込む前に他のフィルターを介して結果を送信できるようにします)
Rob Gilliam

1

引数が並べ替えとまったく同じであるbash関数を次に示します。サポートファイルとパイプ。

function skip_header_sort() {
    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then
        local file=${@: -1}
        set -- "${@:1:$(($#-1))}"
    fi
    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file
}

使い方。この行は、少なくとも1つの引数があるかどうか、最後の引数がファイルかどうかをチェックします。

    if [[ $# -gt 0 ]] && [[ -f ${@: -1} ]]; then

これにより、ファイルが個別の引数に保存されます。最後の議論を消そうとしているので。

        local file=${@: -1}

ここでは、最後の引数を削除します。ソート引数として渡したくないので。

        set -- "${@:1:$(($#-1))}"

最後に、awkでソートする引数(ファイルの場合は最後の引数を差し引いたもの)を渡してawkの部分を実行します。これはDaveによって最初に提案され、並べ替えの引数を取るように変更されました。$fileパイピングしている場合は空になるため、無視されるという事実に依存しています。

    awk -vsargs="$*" 'NR<2{print; next}{print | "sort "sargs}' $file

カンマ区切りファイルでの使用例。

$ cat /tmp/test
A,B,C
0,1,2
1,2,0
2,0,1

# SORT NUMERICALLY SECOND COLUMN
$ skip_header_sort -t, -nk2 /tmp/test
A,B,C
2,0,1
0,1,2
1,2,0

# SORT REVERSE NUMERICALLY THIRD COLUMN
$ cat /tmp/test | skip_header_sort -t, -nrk3
A,B,C
0,1,2
2,0,1
1,2,0

0

Pythonの場合:

import sys
HEADER_ROWS=2

for _ in range(HEADER_ROWS):
    sys.stdout.write(next(sys.stdin))
for row in sorted(sys.stdin):
    sys.stdout.write(row)

システムにPythonがインストールされていると仮定します(私のものではありません)
Rob Gilliam

0

他の回答から導き出されたbashシェル関数を次に示します。ファイルとパイプの両方を処理します。最初の引数は、ファイル名またはstdinの「-」です。残りの引数はソートに渡されます。いくつかの例:

$ hsort myfile.txt
$ head -n 100 myfile.txt | hsort -
$ hsort myfile.txt -k 2,2 | head -n 20 | hsort - -r

シェル関数:

hsort ()
{
   if [ "$1" == "-h" ]; then
       echo "Sort a file or standard input, treating the first line as a header.";
       echo "The first argument is the file or '-' for standard input. Additional";
       echo "arguments to sort follow the first argument, including other files.";
       echo "File syntax : $ hsort file [sort-options] [file...]";
       echo "STDIN syntax: $ hsort - [sort-options] [file...]";
       return 0;
   elif [ -f "$1" ]; then
       local file=$1;
       shift;
       (head -n 1 $file && tail -n +2 $file | sort $*);
   elif [ "$1" == "-" ]; then
       shift;
       (read -r; printf "%s\n" "$REPLY"; sort $*);
   else
       >&2 echo "Error. File not found: $1";
       >&2 echo "Use either 'hsort <file> [sort-options]' or 'hsort - [sort-options]'";
       return 1 ;
   fi
}

0

これはIan Sherbinの回答と同じですが、私の実装は:-

cut -d'|' -f3,4,7 $arg1 | uniq > filetmp.tc
head -1 filetmp.tc > file.tc;
tail -n+2 filetmp.tc | sort -t"|" -k2,2 >> file.tc;

-4
cat file_name.txt | sed 1d | sort 

これはあなたが望むことをします。


1)これはヘッダー行を削除して残りを並べ替えるだけで、ヘッダー行の下のすべてを並べ替えることはせず、ヘッダーをそのまま残します。2)ヘッダーが実際に2行である場合、最初の行のみを削除します(質問を読んでください)。3)「sed 1d <file_name.txt」または「sed 1d file_name.txt」でも同じ効果があるのに、「cat file_name.txt | sed 1d」を使用するのはなぜですか?
Rob Gilliam
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.