(「tac」を使用せずに)ファイルから行を逆方向に印刷するにはどうすればよいですか?


26

tacコマンドを使用せずにファイルの行を逆方向に印刷したい。そのようなことをbashで行う他の解決策はありますか?


回答:


33

を使用sedしてエミュレートするtac

sed '1!G;h;$!d' ${inputfile}

3
これは良い解決策ですが、これがどのように機能するかについての説明はさらに良くなります。
チェンレヴィ

10
有名なsedワンライナーです。「36.行の逆順(Unixコマンド "tac"をエミュレート)」を参照してください。それがどのように機能するかの完全な説明については、有名なSed One-Linersで説明されています。
Johnsyweb

6
「これらの2つのワンライナーは、実際に大量のメモリを使用します。これは、印刷前にファイル全体を保持バッファーに逆順に保持するためです。大きなファイルにはこれらのワンライナーを使用しないでください。」
Mr.シカダンス

したがって、他のすべての回答を行ってください(使用しているものを除くsort-一時ファイルを使用する可能性があります)。
ランダム832

1
tacはファイルを逆読みするため、通常のファイルの方が高速であることに注意してください。パイプの場合、他のソリューション(メモリまたは一時ファイルに保持する)と同じようにする必要があるため、それほど高速ではありません。
ステファンシャゼル


6

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file.txt

awk one liners経由


4
短いもの:awk 'a=$0RS a{}END{printf a}'
ninjalj

@ninjalj:短いかもしれませんが、ファイルサイズが大きくなると非常に遅くなります。2分30秒待ってからgaveめました。but your first perl reverse <> `それは(私にとって)ページ上で最高/最速の回答で、このawk回答の10倍の速さです(すべてのawk anseresは時間的にほぼ同じです)
Peter.O

1
それともawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
ステファンChazelas

5

あなたがbashでそれをするように頼んだように、ここにawk、sedまたはperlを使用せず、bash関数だけを使用するソリューションがあります:

reverse ()
{
    local line
    if IFS= read -r line
    then
        reverse
        printf '%s\n' "$line"
    fi
}

の出力

echo 'a
b
c
d
' | reverse

d
c
b
a

予想通り。

ただし、行はメモリに格納され、関数のインスタンスが再帰的に呼び出されるたびに1行保存されることに注意してください。大きなファイルには注意してください。


1
他の最も遅い回答と比較しても、ファイルサイズが大きくなるとすぐに非実用的に遅くなり、あなたが示唆したように、メモリをかなり簡単に吹き飛ばします:* bash再帰関数...しかし、それは興味深いアイデアです。セグメンテーション違反*
-Peter.O

4

あなたはそれをパイプすることができます:

awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

awk各行の先頭には、行番号の後にスペースが続きます。sort逆の順序、数値形式の最初のフィールド(行番号)でソートすることによって行の順序を逆にします。そして、sed行番号を取り除きます。

次の例では、これを実際に実行しています。

pax$ echo 'a
b
c
d
e
f
g
h
i
j
k
l' | awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

以下を出力します:

l
k
j
i
h
g
f
e
d
c
b
a

OK。ありがとう!これを試してみます。そしてコメントをありがとう!

5
cat -nのように振る舞うawk '{print NR" "$0}'
チェンレヴィ

2
私はそれがシュワルツ変換と呼ばれるか、装飾-並べ替え-非装飾
-ninjalj

この一般的なアプローチは、タスクが大きすぎてメモリ内で合理的に実行できない場合に、ソートがディスク上のファイルを使用して処理するという点で優れています。ただし、パイプではなくステップ間で一時ファイルを使用した場合は、メモリがより穏やかになる可能性があります。
mc0e

1

perlの場合:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'

2
または短いperl -e 'print reverse<>'
-ninjalj

2
(実際には、短い方がありますが、それはです本当に醜い、証人そのawfulness: perl -pe '$\=$_.$\}{'
ninjalj

@Frederik Deweerdt:高速ですが、最初の行を失います... @ ninjalj:reverse<)高速です:良い!しかし、「本当にい」ものは、行数が増えるにつれて非常に遅くなります。!!
....-Peter.O

@ Peter.Oそこに-nは余計なものがありました、ありがとう。
フレデリクデューエルト

これの良い点は、ファイルの内容が必ずしもメモリに読み込まれるとは限らないことです(可能性のあるチャンク単位の例外を除くsort)。
クサラナナンダ

0

BASHのみのソリューション

ファイルをbash配列に読み込み(1行=配列の1要素)、逆の順序で配列を出力します。

i=0 

while read line[$i] ; do
    i=$(($i+1))
done < FILE


for (( i=${#line[@]}-1 ; i>=0 ; i-- )) ; do
    echo ${line[$i]}
done

インデントされた行で試してみてください... philfrのバージョンは少し優れていますが、それでもveeeery slooooowなので、テキスト処理に関しては、を使用しないでくださいwhile..read
-don_crissti

IFS=''およびread -rを使用して、すべての種類のエスケープと後続のIFS削除がそれを妨害しないようにします。mapfile ARRAY_NAMEただし、bash ビルトインは配列を読み取るためのより良いソリューションだと思います。
Arthur2e5

0

Bash、mapfilefiximanへのコメントで言及されており、実際にはおそらくより良いバージョンです:

# last [LINES=50]
_last_flush(){ BUF=("${BUF[@]:$(($1-LINES)):$1}"); } # flush the lines, can be slow.
last(){
  local LINES="${1:-10}" BUF
  ((LINES)) || return 2
  mapfile -C _last_flush -c $(( (LINES<5000) ? 5000 : LINES+5 )) BUF
  BUF=("${BUF[@]}") # Make sure the array subscripts make sence, can be slow.
  ((LINES="${#BUF[@]}" > LINES ? LINES : "${#BUF[@]}"))
  for ((i="${#BUF[@]}"; i>"${#BUF[@]}"-LINES; i--)); do echo -n "${BUF[i]}"; done
}

そのパフォーマンスは基本的にsedソリューションに匹敵し、要求された行の数が減少するにつれて高速になります。



0
  • nlで行に番号を付けます
  • 番号で逆順に並べ替えます
  • sedで数字を削除します

ここに示すように:

echo 'e
> f
> a
> c
> ' | nl | sort -nr | sed -r 's/^ *[0-9]+\t//'

結果:

c
a
f
e

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.