パイプラインでの複数のtrプロセスを回避するために、trコマンドをチェーン化できますか?


11

私はたくさんのtxtファイルを持っています。それらを小文字で、アルファベットと1行につき1ワードだけ出力したいのですが、次のようなtrパイプラインでいくつかのコマンドを使用して実行できます。

tr -d '[:punct:]' <doyle_sherlock_holmes.txt | tr '[:upper:]' '[:lower:]' | tr ' ' '\n'

これを1回のスキャンで実行することは可能ですか?私はこれを行うには、Cプログラムを書くことができますが、私は感じて使用してそれを行う方法がありますようにtrsedawkまたはperl


どのOSを使用していますか?GNUツールにアクセスできますか?
terdon

回答:


9

複数の翻訳を組み合わせることができます(ロケール依存セットの重複を含む複雑なケースを除く)が、削除と翻訳を組み合わせることができません。

<doyle_sherlock_holmes.txt tr -d '[:punct:]' | tr '[:upper:] ' '[:lower:]\n'

への2 tr回の呼び出しは、より複雑なツールへの1回の呼び出しよりも高速になる可能性がありますが、これは、入力サイズ、さまざまな文字の比率tr、オペレーティングシステムの実装、競合するツールの数、数に大きく依存します。コアなど


再結合するかどうかわからないtr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas

1
@Costas句読点を改行に変換します。この特定のアプリケーションでは問題ないかもしれませんが、出力は元のアプリケーションと同じではありません。
Gilles「SO-邪悪なことをやめよう」

@Costas-改行はここでアクセプト可能かもしれませんが、大文字の文字を絞るのは難しいと思います。例:printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'gets a\na\na'、およびの変換... '[:lower:]\n'は必ずしも何もしない場合があり'[:punct:]'ます-一部trのsはset1を2に一致するように切り捨て、一部のは暗黙のを行います[\n*]。そこの範囲を使用する方が良いです。
mikeserv 2015

4

ここにいくつかのアプローチがあります:

  • GNU greptr:すべての単語を見つけて小文字にする

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grepおよびperl:上記と同じですが、perlは小文字への変換を処理します

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl:すべてのアルファベット文字を検索し、小文字で出力します(@steeldriverに感謝):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed:アルファベットまたはスペース以外のすべての文字を削除し、すべてのアルファベット文字を小文字バージョンに置き換え、すべてのスペースを改行で置き換えます。これは、すべての空白がスペースではなく、タブではないと想定していることに注意してください。

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file

2
のようなものperl -lne 'print lc for /[[:alpha:]]+/g'も動作しますか?それとも貧弱なスタイルですか?(私はperlの
初心者で

@steeldriverはい、いいですね!Perlを学んでいるなら、きっとそのモットーに出会ったことでしょう。TMTOWTDI :)ありがとう、それを追加します。
terdon

3
新しいバージョン(> 4.2.1)の場合sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas

@Costasああ、今sedできる\w?涼しい!
terdon

@terdon-それはしばらくの間行われていますが、Costasが言及しなかったため、上記のコメントで最も興味深いのは、GNU sed-zero delimitスイッチ\0NULです。改行ではなくsを循環します。あなたが何かをするときはかなりクールですtar -c . | tr -s \\0 | sed -z ...が、少し遅いです。
mikeserv 2015

4

はい。あなたはそのワット/を行うことができますtrASCIIのロケールで(GNUのために、あるtrとにかく、その唯一の範囲の一種)。POSIXクラスを使用するか、8進数で各文字のバイト値を参照できます。また、変換を複数の範囲に分割することもできます。

LC_ALL=C tr '[:upper:]\0-\101\133-140\173-\377' '[:lower:][\n*]' <input

上記のコマンドは、すべての大文字を小文字に変換し、小文字を完全に無視し、他のすべての文字を改行に変換します。もちろん、それから大量の空白行が発生します。tr -sリピートは切り替えqueezeは、そのような場合に有用かもしれないが、あなたが一緒にそれを使用する場合[:upper:][:lower:]変換後、あなたはよくとして大文字を絞る羽目になる。そのように、それはまだ2番目のフィルターを必要とします...

LC... tr ... | tr -s \\n

...または...

LC... tr ... | grep .

...そのため、実行するよりもはるかに不便になります...

LC_ALL=C tr -sc '[:alpha:]' \\n <input | tr '[:upper:]' '[:lower:]'

... -cアルファベット順のシーケンスを1つの改行に詰め込んで、パイプの反対側で上部から下部への変換を行います。

それは、その性質の範囲が役に立たないと言っているのではありません。次のようなもの:

tr '\0-\377' '[1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][0*]' </dev/random

...値のスペクトラム拡散で入力バイトをすべての桁に変換するため、非常に便利です。無駄にしたくありません。

変換を行う別の方法は、関与し得ますdd

tr '\0-\377' '[A*64][B*64][C*64][D*64]' </dev/urandom |
dd bs=32 cbs=8 conv=unblock,lcase count=1

dadbbdbd
ddaaddab
ddbadbaa
bdbdcadd

は変換と変換のdd両方unblocklcase同時に実行できるため、多くの作業をそれに渡すことさえ可能かもしれません。ただし、これは、1ワードあたりのバイト数を正確に予測できる場合にのみ、または少なくともunblock各ブロックの末尾で末尾のスペースを使用するため、少なくとも各ワードにスペースを事前に予測可能なバイトカウントまで埋め込むことができる場合にのみ役立ちます。


+2参加のボーナスポイントdd:)
tlehman

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