How can I use a file in a command and redirect out

2018-12-31 07:24发布

Basically I want to take as input text from a file, remove a line from that file, and send the output back to the same file. Something along these lines if that makes it any clearer.

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name

however, when I do this I end up with a blank file. Any thoughts?

标签: bash redirect io
13条回答
妖精总统
2楼-- · 2018-12-31 07:59

Since this question is the top result in search engines, here's a one-liner from https://serverfault.com/a/547331 that uses a subshell instead of sponge (which often isn't part of a vanilla install like OS X):

echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name

Or the general case:

echo "$(cat file_name)" > file_name

Test from https://askubuntu.com/a/752451:

printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do echo "$(cat file_uniquely_named.txt)" > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt

Should print:

hello
world

Whereas calling cat file_uniquely_named.txt > file_uniquely_named.txt in the current shell:

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

Prints an empty string.

I haven't tested this on large files (probably over 2 or 4 GB).

I have borrowed this answer from Hart Simha and kos.

查看更多
倾城一夜雪
3楼-- · 2018-12-31 08:02

You can't use redirection operator (> or >>) to the same file, because it has a higher precedence and it will create/truncate the file before the command is even invoked. To avoid that, you should use appropriate tools such as tee, sponge, sed -i or any other tool which can write results to the file (e.g. sort file -o file).

Basically redirecting input to the same original file doesn't make sense and you should use appropriate in-place editors for that, for example Ex editor (part of Vim):

ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name

where:

  • '+cmd'/-c - run any Ex/Vim command
  • g/pattern/d - remove lines matching a pattern using global (help :g)
  • -s - silent mode (man ex)
  • -c wq - execute :write and :quit commands

You may use sed to achieve the same (as already shown in other answers), however in-place (-i) is non-standard FreeBSD extension (may work differently between Unix/Linux) and basically it's a stream editor, not a file editor. See: Does Ex mode have any practical use?

查看更多
临风纵饮
4楼-- · 2018-12-31 08:02

One liner alternative - set the content of the file as variable:

VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name
查看更多
余生请多指教
5楼-- · 2018-12-31 08:02

You can use slurp with POSIX Awk:

!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
  q = q ? q RS $0 : $0
}
END {
  print q > ARGV[1]
}

Example

查看更多
孤独总比滥情好
6楼-- · 2018-12-31 08:02

maybe you can do it like this:

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | cat > file_name
查看更多
一个人的天荒地老
7楼-- · 2018-12-31 08:03

Try this

echo -e "AAA\nBBB\nCCC" > testfile

cat testfile
AAA
BBB
CCC

echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC
查看更多
登录 后发表回答