このようなCSVファイルがあります
AS2345、ASDF1232、Mr。Plain Example、110 Binary ave。、アトランティス、RI、12345、(999)123-5555、1.56 AS2345、ASDF1232、Mrs。Plain Example、1121110 Ternary st 。 110 Binary ave ..、Atlantis、RI、12345、(999)123-5555,1.56 AS2345、ASDF1232、Mr. Plain Example、110 Binary ave。、Liberty City、RI、12345、(999)123 -5555,1.56 AS2345、ASDF1232、Mr. Plain Example、110 Ternary ave。、Some City、RI、12345、(999)123-5555,1.56
スペースを含む行の長さでソートする必要があります。次のコマンドにはスペースが含まれていませんが、それを修正する方法はありますか?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
または、同じ長さの行の元の(おそらく意図しない)サブソートを行うには:
cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-
どちらの場合も、最終的なカットのためにawkから離れることで、指定された問題を解決しました。
質問では、一致する長さの行に対してさらにソートする必要があるかどうかは指定されませんでした。これは望ましくないと想定し、-s
(--stable
)を使用して、このような行が相互にソートされないようにし、入力で発生する相対的な順序でそれらを保持することを提案しました。
(これらのタイのソートをより詳細に制御したい場合は、ソートの--key
オプションを参照してください。)
以下の違いに注意することは興味深いです:
echo "hello awk world" | awk '{print}'
echo "hello awk world" | awk '{$1="hello"; print}'
彼らはそれぞれ得ます
hello awk world
hello awk world
(gawk's)マニュアルの関連セクション は、1つのフィールドを変更するとawkが(セパレーターなどに基づいて)$ 0全体を再構築することを脇に置いて言及しているだけです。クレイジーな振る舞いではないと思います。これがあります:
「最後に、フィールドとOFSの現在の値を使用して、awkにレコード全体を再構築させると便利な場合があります。これを行うには、一見無害な割り当てを使用します。」
$1 = $1 # force record to be reconstituted
print $0 # or whatever else with $0
「これにより、awkは強制的にレコードを再構築します。」
aa A line with MORE spaces
bb The very longest line in the file
ccb
9 dd equal len. Orig pos = 1
500 dd equal len. Orig pos = 2
ccz
cca
ee A line with some spaces
1 dd equal len. Orig pos = 3
ff
5 dd equal len. Orig pos = 4
g
neillbからのAWKソリューション は、本当にawk
を使用したい場合に最適であり、面倒な理由を説明しますが、必要なのは仕事を迅速に行い、あなたが何をするかに気をつけて、一つの解決策は、Perlのsort()
関数をカスタムcaparisonルーチンとともに使用して、入力行を反復することです。ライナーは次のとおりです。
Perl -e 'print sort { length($a) <=> length($b) } <>'
これを必要な場所でパイプラインに配置し、(cat
またはシェルリダイレクトから)STDINを受け取るか、別の引数としてファイル名をPerlに渡してファイルを開かせます。
私の場合、最初に最も長い行が必要だったので、比較で$a
と$b
を交換しました。
代わりに次のコマンドを試してください。
awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-
以下は、この質問に対するその他の回答からのソリューションにわたるベンチマークの結果です。
Perl
ソリューション 11.2秒かかりましたPerl
solution 11.6秒かかりましたawk
ソリューション #1は20秒かかりましたawk
ソリューション #2は23秒かかりましたawk
ソリューション 24秒かかりましたawk
ソリューション 25秒かかりましたbash
ソリューション は、awk
ソリューションよりも400倍長くかかります(100000行の切り捨てられたテストケースを使用)。それはうまく機能し、永遠にかかります。Perl
オプションまた、別のPerlソリューションを追加しました。
Perl -ne 'Push @a, $_; END{ print sort { length $a <=> length $b } @a }' file
純粋なバッシュ:
declare -a sorted
while read line; do
if [ -z "${sorted[${#line}]}" ] ; then # does line length already exist?
sorted[${#line}]="$line" # element for new length
else
sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length
fi
done < data.csv
for key in ${!sorted[*]}; do # iterate over existing indices
echo -e "${sorted[$key]}" # echo lines with equal length
done
length()
関数にはスペースが含まれます。私はあなたのパイプラインをほんの少し調整します( UUOC の回避を含む)。
awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'
sed
コマンドは、awk
コマンドによって追加された数字とコロンを直接削除します。または、awk
からフォーマットを維持します:
awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'
1)純粋なawkソリューション。行の長さが1024を超えることはできないと仮定しましょう
猫のファイル名| awk 'BEGIN {min = 1024; s = "";} {l = length($ 0); if(l <min){min = l; s = $ 0;}} END {print s} '
2)すべての行にたった1つの単語があると仮定した1つのライナーbashソリューションですが、すべての行に同じ数の単語がある場合は修正できます。
LINES = $(cat filename); $ LINESのk do printf "$ k";エコー$ k | wc -L;完了| sort -k2 | head -n 1 |カット-d "" -f1
ファイルに数値で始まる行が含まれている場合、これらのソリューションは機能しないことがわかりました。これらのソリューションは、カウントされたすべての行とともに数値的にソートされるためです。解決策は、sort
に-g
(数値ソート)の代わりに-n
(一般数値ソート)フラグを付けることです。
awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-
以下は、長さで行をソートするマルチバイト互換の方法です。以下が必要です。
wc -m
が利用可能です(macOSにはあります)。LC_ALL=UTF-8
を設定するなどして、マルチバイト文字をサポートしています。これは、.bash_profileで設定するか、単に次のコマンドの前に追加することで設定できます。testfile
には、ロケールに一致する文字エンコード(UTF-8など)があります。完全なコマンドは次のとおりです。
cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-
パートごとの説明:
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
←awk変数l
の各行のコピーを作成し、'
ごとに二重エスケープするため、行をシェルコマンドとして安全にエコーできます(\047
は8進数の単一引用符です)表記法)。cmd=sprintf("echo \047%s\047 | wc -m", l);
←これは実行するコマンドで、エスケープされた行をwc -m
にエコーします。cmd | getline c;
←コマンドを実行し、返される文字カウント値をawk変数c
にコピーします。close(cmd);
←シェルコマンドへのパイプを閉じて、1つのプロセスで開いているファイルの数がシステム制限に達しないようにします。sub(/ */, "", c);
←wc
によって返される文字カウント値から空白を削除します。{ print c, $0 }
←行の文字カウント値、スペース、および元の行を出力します。| sort -ns
←行を(先頭に追加された文字カウント値で)数値的にソートし(-n
)、安定したソート順を維持します(-s
)。| cut -d" " -f2-
←付加された文字カウント値を削除します。各行に対してサブコマンドを実行する必要があるため、低速です(高速のMacbook Proでは1秒あたり160行のみ)。
または、gawk
(バージョン3.1.5以降、gawkはマルチバイト対応)だけでこれを行うだけで、大幅に高速になります。すべてのエスケープと二重引用符を使用してawkからシェルコマンドに安全に行を渡すのは非常に困難ですが、これは追加のソフトウェアのインストールを必要としない唯一の方法です(gawkはデフォルトでは利用できませんマックOS)。