回答:
bashは最初にリダイレクトを処理してからコマンドを実行するため、これを行うことはできません。そのため、grepがfile_nameを調べるときには、すでに空になっています。ただし、一時ファイルを使用できます。
#!/bin/sh
tmpfile=$(mktemp)
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile}
cat ${tmpfile} > file_name
rm -f ${tmpfile}
そのように、を使用mktemp
してtmpfileを作成することを検討してください。ただし、それはPOSIXではないことに注意してください。
>
する前にリダイレクトによってファイルが開かれ、切り捨てられますgrep
。
sponge
コマンドを使用した回答を受け入れる必要があります。
この種のタスクにはスポンジを使用します。moreutilsの一部。
このコマンドを試してください:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name
brew install moreutils
。
sudo apt-get install moreutils
、Debianベースのシステム。
代わりにsedを使用してください:
sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
-i
は、GNUのみの拡張機能です。
-i ''
拡張子は厳密に必須ではありませんが、そう-i
オプションは必要ありませんいくつかの引数を。
同じファイルにリダイレクト演算子(>
または>>
)を使用することはできません。これは、優先順位が高く、コマンドが呼び出される前にファイルが作成または切り捨てられるためです。それを避けるには、次のような適切なツールを使用する必要がありtee
、sponge
、sed -i
またはファイル(例えばに結果を書き込むことができ、他のツールをsort file -o file
)。
基本的に同じ元のファイルに入力をリダイレクトするのは意味がなく、適切なインプレースエディター、たとえばExエディター(Vimの一部)を使用する必要があります。
ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name
どこ:
'+cmd'
/ -c
-Ex / Vimコマンドを実行するg/pattern/d
- グローバル(help :g
)を使用してパターンに一致する行を削除する-s
-マナーモード(man ex
)-c wq
-実行:write
と:quit
コマンドあなたは使用することができますsed
が、(既に他の回答のように)同じことを達成するためにインプレース(-i
(UNIX / Linuxの間で異なる動作をする場合があります)非標準のFreeBSD拡張がある)と基本的には、A社のS tream 編 itor、ないファイルエディタ。参照:Exモードには実用的な用途がありますか?
この質問は検索エンジンの上位の結果であるため、代わりにサブシェルを使用するhttps://serverfault.com/a/547331に基づくワンライナーは次のとおりですsponge
(多くの場合、OS Xのような基本的なインストールの一部ではありません)。 :
echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name
一般的なケースは次のとおりです。
echo "$(cat file_name)" > file_name
編集、上記のソリューションにはいくつかの警告があります:
printf '%s' <string>
をecho <string>
含むファイルが-n
望ましくない動作を引き起こさないように、代わりに使用する必要があります。x
、出力へと経由して外部にそれを削除する一時変数のパラメータ展開のように${v%x}
。$v
既存の変数の値が踏みにじられる$v
ため、式全体を括弧でネストして、以前の値を保持する必要があります。null
が出力からのように印刷できない文字を取り除くことです。これを呼び出しdd if=/dev/zero bs=1 count=1 >> file_name
、16進数でを表示して確認しましたcat file_name | xxd -p
。しかしecho $(cat file_name) | xxd -p
、取り除かれます。だから、この答えはすべきではないとして、印刷不能文字を使用してバイナリファイルか何かで使用することがリンチを指摘しました。一般的な解決策(albietは少し遅く、メモリを大量に消費し、印刷できない文字を取り除きます)は次のとおりです。
(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)
https://askubuntu.com/a/752451からテストします。
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do (v=$(cat file_uniquely_named.txt; printf x); printf '%s' ${v%x} > file_uniquely_named.txt); done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
印刷する必要があります:
hello
world
一方cat file_uniquely_named.txt > file_uniquely_named.txt
、現在のシェルで呼び出す:
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do cat file_uniquely_named.txt > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
空の文字列を出力します。
私はこれを大きなファイル(おそらく2 GBまたは4 GB以上)でテストしていません。
cat
して最初の引数としてに配置しecho
ます。もちろん、印刷不可能な変数は正しく出力されず、データが破損します。ファイルをそれ自体にリダイレクトしようとしないでください。
これは、process-substitutionを使用して行うことができます。
bashはすべてのパイプを非同期で開きsleep
、YMMV を使用して回避する必要があるため、少しハックです。
あなたの例では:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
>(sleep 1 && cat > file_name)
grepから出力を受け取る一時ファイルを作成しますsleep 1
入力ファイルを解析するためのgrep時間を与えるための1秒の遅延cat > file_name
に出力を書き込みますPOSIX Awkでslurpを使用できます。
!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
q = q ? q RS $0 : $0
}
END {
print q > ARGV[1]
}
これを試して
echo -e "AAA\nBBB\nCCC" > testfile
cat testfile
AAA
BBB
CCC
echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC
以下sponge
は、必要とせずに、同じことを実行しますmoreutils
。
shuf --output=file --random-source=/dev/zero
この--random-source=/dev/zero
部分はshuf
、シャッフルをまったく行わずにその処理を行うようにトリックするため、入力を変更せずにバッファーに入れます。
ただし、パフォーマンス上の理由から、一時ファイルを使用するのが最善です。だから、これは私があなたのために一般化した方法でそれをする私が書いた関数です:
# Pipes a file into a command, and pipes the output of that command
# back into the same file, ensuring that the file is not truncated.
# Parameters:
# $1: the file.
# $2: the command. (With $3... being its arguments.)
# See https://stackoverflow.com/a/55655338/773113
function siphon
{
local tmp=$(mktemp)
local file="$1"
shift
$* < "$file" > "$tmp"
mv "$tmp" "$file"
}
これは非常に可能です。出力を書き込むときに、別のファイルに書き込むことを確認する必要があります。これを行うには、ファイル記述子を開いた後、ファイルに書き込む前にファイルを削除します。
exec 3<file ; rm file; COMMAND <&3 >file ; exec 3>&-
または行ごとに、よりよく理解するには:
exec 3<file # open a file descriptor reading 'file'
rm file # remove file (but fd3 will still point to the removed file)
COMMAND <&3 >file # run command, with the removed file as input
exec 3>&- # close the file descriptor
COMMANDが正常に実行されない場合、ファイルの内容が失われるため、これは依然として危険なことです。これは、COMMANDがゼロ以外の終了コードを返した場合にファイルを復元することで軽減できます。
exec 3<file ; rm file; COMMAND <&3 >file || cat <&3 >file ; exec 3>&-
使いやすいシェル関数を定義することもできます:
# Usage: replace FILE COMMAND
replace() { exec 3<$1 ; rm $1; ${@:2} <&3 >$1 || cat <&3 >$1 ; exec 3>&- }
例:
$ echo aaa > test
$ replace test tr a b
$ cat test
bbb
また、これにより、元のファイルの完全なコピーが保持されます(3番目のファイル記述子が閉じられるまで)。Linuxを使用していて、処理中のファイルが大きすぎてディスクに2度収まらない場合は、このスクリプトをチェックして、既に処理されたファイルの割り当てを解除しながら、ファイルを指定されたコマンドにブロックごとにパイプすることができます。ブロック。いつものように、使用法ページの警告を読んでください。
私は通常、これを行うためにT字プログラムを使用します。
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
それ自体が一時ファイルを作成して削除します。
tee
動作する保証はありません。askubuntu.com/a/752451/335781を参照してください。