短い質問:
sedがファイルを変更しないのはなぜですか?確認する方法はありますか?
長い質問:
以前私のファイルで作業していたsedコマンドを実行してみました。私はこれを学んだここ9月にまた来てください。四半期ごとに、私は多くのスペースと1つでなければなりませんが、2つに分割された列を含む4つの巨大なファイルを受け取ります。次のコマンドを実行してスペースを見て、列41と42をマージしました。
sudo sed -i -e 's/ \{1,\}"/"/g' -e 's/" \{1,\}/"/g' -e 's/","//41' original_file.txt
昨日初めて何も起こりませんでした。約3秒ほど待ってから何も起こりませんが、通常20〜30分かかります。ファイルを確認しましたが、スペースはまだ残っています。私のシステムで利用可能なファイルサイズはまだ3倍で、RAM(512GB RAM)で利用可能なファイルサイズは2倍です。ラムは重要ではなく、ただそこに入れようとしています。
以下を使用して別のファイルに書き込もうとしました。
sudo sed -e 's/ \{1,\}"/"/g' -e 's/" \{1,\}/"/g' -e 's/","//41' original_file.txt > formatted_file.txt
これは生成されますが、formatted_file.txt
完全に空になります。
誰かが私が何を間違っているのか、問題を特定する方法を教えてもらえますか?
編集する:
サンプル入力は以下にあります。スタックオーバーフロー300以上の熱があるという点を除いてください。
ベストアンサー1
入力ファイルを含むコメントで見つかりましたビッグエンディアン方式 UTF-16従来の一般的な7ビットASCIIまたは8ビット拡張ASCIIの代わりにフォーマットを使用します。 UTF-16は文字ごとに2バイト形式であり、通常のASCIIをエンコードするために使用される場合は、「ASCII」文字0x00
(NULバイト、Aバイト-バイトペア(ビッグエンディアン、反対側のリトルエンディアン)^@
)cat -A
less
回避策は、ファイルを通常のASCIIに変換することです。たとえば、標準または類似のユーティリティを使用してCR-LF(dos / windows行の終わり)をLF(unix行の終わり)に変換する代わりに、次のようにしてfromdos
テキストを残りの部分で利用可能な形式に変換する必要があります。スクリプトsed
:
sed -e '1 s/^\xff\xfe|^\xfe\xff//; s/\x00//g; s/\x0d$//'
このsed
スクリプトは次のとおりです。
0xfffe
最初の行の先頭からバイト順のマークを削除または削除します。0xfeff
- 発生位置に関係なく、すべての入力行からすべてのNUL文字を削除します。
0x0d
行末のキャリッジリターン文字()を削除します。
注:これはASCII文字のみを含むUTF-16エンコーディングテキストでのみ機能します。他の種類の文字(英語以外のテキストなど)を含むUTF-16テキストファイルを完全に破損させます。
最後に、perl
純粋なASCII、UTF-8、UTF-16などを含むさまざまな一般形式のテキストの優れたサポートがあります。すべての形式を処理し、すべての形式間変換用のライブラリモジュールがあります。単純なスクリプトをに変換するのはsed
非常に簡単なperl
ので、スクリプトのPerlバージョンは単純かもしれません(テストされていませんが動作するかもしれません)。
#!/usr/bin/perl
use strict;
use feature 'unicode_strings';
while(<>) {
s/^\xff\xfe|^\xfe\xff// if ($. == 1); # strip Byte Order marker from 1st line
s/\x0d$//; # strip CR from each end-of-line
s/ *"/"/g; # get rid of all spaces immediately before " characters
s/" */"/g; # get rid of all spaces immediately after " characters
# A very primitive split(). Should use a real CSV parser here, like the
# Text::CSV module which properly copes with embedded quotes and commas etc
# in string fields. This would also allow proper processing of each field to
# remove any extra whitespace characters rather than the quick-and-dirty hack of
# global regexp substitutions above.
my @fields = split /,/;
# perl arrays start from zero. This appends the "fake" field 42 onto field 41,
# and then deletes field 42.
$fields[40] .= $fields[41];
delete $fields[41];
print join(',',@fields), "\n";
}
以前の答えには、まだ(IMO)の有用な情報が含まれています。
awk
。より作業に優れたツールがありますsed
。
たとえば、GNU(またはなどのPCREを理解するawk
他のツール)を使用すると、次のようになります。awk
\s
\S
awk '{$0=gensub(/\s*(\S+)/,"\\1",42)}1' original > fixed
42列の直前のすべてのスペースを削除して、41列と42列をマージします。
PCREでない場合は、次のものを代わりに使用しawk
てください。[[:space:]]
\s
[^[:space:]]
\S
awk '{$0=gensub(/[[:space:]]*(\[^[:space:]]+)/,"\\1",42)}1' original > fixed
また、入力ファイルの正確な特性に応じて、この操作の場合。perl
より良いかもしれませんawk
。たとえば、CSVファイルを解析し、CSVレコードの個々のフィールドを処理するモジュールがあります。
ところで、私の考えにはこのsed
スクリプトがひどいと思います。これは、コマンド区切り文字として単一のsedスクリプトの代わりに複数の引数を使用しているためです-e
。;
使用したい場合は、sed
少なくとも効果的かつ効率的に使用してください。あなたのsed
スクリプトは次のように書くのが最善です。
sed -e 's/ \{1,\}"/"/g; s/" \{1,\}/"/g; s/","//41' original > fixed
でも:
sed -e 's/ \{1,\}"/"/g
s/" \{1,\}/"/g
s/","//41' original > fixed
それでもエラーを修正する必要がありますが、少なくともデバッグできる読みやすさがあるため、問題をより簡単に見つけることができます。
また、あなたが思うように、「所定の位置」の編集ではない場合もあります-i
。--in-place
一時ファイルを作成し、その場所に移動する方法で動作します。これにより、ハードリンクを含むinodeを変更せずに維持する必要があるすべてのエントリが中断されます。
cat temp.txt > original.txt; rm temp.txt
変更された出力を一時ファイル(temp.txtなど)に書き込んでから、同じinodeを維持しながら元のファイルを変更されたバージョンで上書きすることをお勧めします。