web-dev-qa-db-ja.com

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

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

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

並べ替えに「最初の2行を並べ替えずに渡す」、またはコロン行を先頭に並べ替える順序を指定する方法があります-残りの行は常に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
86
Rob Gilliam
(head -n 2 <file> && tail -n +3 <file> | sort) > newfile

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

105
BobS

awkの使用を気にしない場合は、awkの組み込みパイプ機能を利用できます。

例えば。

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

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

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

51
Dave

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

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

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

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

この解決策は here

28
freeseek

tail -n +3 <file> | sort ...を使用できます(tailはファイルの内容を3行目から出力します)。

6
Anton Kovalenko
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
4
Vijay

コードは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

3
Ian Sherbin

単純な場合、sedは仕事をエレガントに行うことができます。

    your_script | (sed -u 1q; sort)

または同等に、

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

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

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

-uスイッチ(バッファなし)は、そうでなければ入力をチャンクで読み取るseds(特にGNU)に必要です。そのため、代わりにsortを通過したいデータを消費します。

1
Andrea

そのため、引数がソートとまったく同じである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
1
flu

これは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;
0
Bik

以下は、他の回答から派生したbashシェル関数です。ファイルとパイプの両方を処理します。最初の引数はファイル名、または標準入力の場合は「-」です。残りの引数はソートに渡されます。いくつかの例:

$ 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
JonDeg

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)
0
crusaderky