web-dev-qa-db-ja.com

Bashスクリプトの非ブランクの長さと数によるSTDINの並べ替え

私はBashスクリプトの学習に取り組んでいますが、この問題に苦労しています。 STDINからの行の束が与えられた場合、最初に行の長さで昇順で並べ替えます。次に、同じ文字数の行がある場合は、その行に含まれる空白以外の文字の数で並べ替えます(これも昇順で)。

私はこれをいくつかの異なる方法で試しましたが、通常、Bashの特異性のいくつかに巻き込まれます。

これが私がこれまでに得たものです:

#!/bin/bash

sorted=()
while IFS='' read -r line; do
    length=${#line}
    if [[ ${sorted[$length]} == "" ]] ; then
        sorted[$length]="$line"
    else
        #non unique length
        #sorted[$length]="${sorted[$length]}\n$line"
        IFS=$'\n' arr=("${sorted[$length]}")
        arr+=("$line")

        spaces=()

        for ((i=0 ; i < ${#arr[@]} ; ++i )) ; do
            spaces[$i]=$(echo "${arr[$i]}" | sed "s: : \n:g" | grep -c " ")
        done

        arr_sorted=()

        for ((i =0 ; i < ${#spaces[@]} ; i++ )) ; do
                for ((j=0 ; j < ${#arr[@]} ; i++ )) ; do

                        this_line_length=$(echo "${arr[$j]}" | sed "s: : \n:g" | grep -c " ")
                        if [[ "$this_line_length" == "${spaces[$i]}" ]] ; then
                            arr_sorted+=("${arr[$j]}")
                            unset arr[$j]
                        fi
                done
        done


    sorted[$length]="${arr_sorted[@]}"


    fi
done

私は先に進んで、これがそれを行うための最良の方法にはほど遠いと思います。 bashの組み込みにあまり依存せずにすべてを実装しようと思っていましたが、今ではかなり無意味に思えます。

3
bash learner

sortcutなどの邪悪な外部の矛盾の使用が許可されている場合:

#! /bin/bash
while IFS= read -r line; do
    squeezed=$( tr -d '[:blank:]' <<<"$line" )
    printf '%d\t%d\t%s\n' ${#line} ${#squeezed} "$line"
done | sort -n -k 1 -k 2 | cut -f 3-

編集:誰もがそれをやっているので、ここにPerlを使った解決策があります:

Perl -e 'print sort { length $a <=> length $b || $a =~ y/ \t//c <=> $b =~ y/ \t//c } <>'
4
Satō Katsura

他と同じ原則を使用します(空白文字の有無にかかわらず、行の長さを取得し、それらを並べ替えてから削除します)が、awkを使用します:

_awk '{NC = length(gensub(/[[:space:]]/, "", "g")); print length, NC, $0}' file |
  sort -nk1,2 |
  sed -r 's/^([0-9]+ ){2}//'
_

gensub(/[[:space:]]/, "", "g")は行内のすべての空白文字を削除し、残りの文字列の長さを取得します

コードブロックまでの質問のテキストを使用して、幅80文字に折りたたむ:

_$ awk '{NC = length(gensub(/[[:space:]]/, "", "g")); print length, NC, $0}' foo | sort -nk1,2 | sed -r 's/^([0-9]+ ){2}//'


 increasing order).
Here's what I've got so far:
f the idiosyncrasies of bash.
iven a bunch of lines from STDIN, sort them first by the length of the line in i
I've tried this a couple of different ways but I usually get caught up in some o
, sort them by the number of nonblank characters contained in the lines (also in
I am working on learning bash scripting but I am struggling with this problem. G
ncreasing order. Then, if there are any lines with the same number of characters
_
3
muru

純粋 bash

sortByLength () 
{ 
    local -a sorted=() sort2
    local line sline sline2 pointer
    while IFS= read -r line; do
        sorted[${#line}]+="$line"
    done
    for pointer in ${!sorted[@]}
    do
        #  ((pointer)) || echo 0: # This will trace empty lines
        sort2=()
        line="${sorted[pointer]}"
        while [ "$line" ]; do
            sline=${line:0:pointer}
            line=${line:pointer}
            sline2=${sline// }
            sort2[${#sline2}]+=${sline}$'\n'
        done
        # echo $pointer:   # This will trace lines length
        printf "%s" "${sort2[@]}"
    done
}

フォークがないので、これははるかに速いかもしれません!

3
F. Hauri

関数:

sortlen() { while read x ; do \
              y=`tr -d '[:blank:]' <<< "$x"` ; echo ${#x} ${#y} "$x" ; \
            done | sort -k 1g,2 -k 2g,3 | cut -d' ' -f3-; }

テスト:

printf "a b c\nabcde\nabcdefg\na\nabcd\n" | sortlen

出力:

a
abcd
a b c
abcde
abcdefg
2
agc

sedソリューションを追加することに抵抗できませんでした:

sed 'h;s/.*/0:0;0123456789+/;G;:count
s/\(.\)\(;.*\1\)\(.\)\(.*\n\)[^[:space:]]/\3\2\3\4x/
s/\(.\)\(:.*\1\)\(.\)\(.*\n\)./\3\2\3\4/;:overflow
s/^+/10/;s/:+/:10/;s/\(.\)+\(.*\1\)\(.\)\(.*\n\)/\30\2\3\4/;t overflow
/\n./b count
G;s/;.*\n/:/' file|sort -t: -n -k 1 -k 2|cut -d: -f 3-

sedスクリプトは、文字と非空白をカウントし、行の先頭に配置します。sortcutは単純です。そして、これはナンセンスだと私に言わないでください。私にとって、それは楽しいです。 (-:

2
Philippos