Answers:
您不能这样做,因为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
)是非标准的FreeBSD扩展(可以在Unix / Linux之间的工作方式不同),基本上它是一个小号 tream 版 itor,而不是一个文件编辑器。请参阅:防爆模式有实际用途吗?
由于此问题是搜索引擎中排名最高的结果,因此这是一个基于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
在当前shell环境中破坏任何现有变量的值,因此我们应将整个表达式嵌套在括号中以保留先前的值。null
从输出中剥离不可打印的字符。我通过调用dd if=/dev/zero bs=1 count=1 >> file_name
并用十六进制查看来验证了这一点cat file_name | xxd -p
。但是echo $(cat file_name) | xxd -p
被剥夺了。因此,正如Lynch指出的那样,此答案不应用于二进制文件或任何使用不可打印字符的东西。通用的解决方案(稍微慢一些,占用更多的内存,并且仍然剥离不可打印的字符)是:
(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
在当前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
打印一个空字符串。
我尚未在大型文件(可能超过2或4 GB)上进行了测试。
我已从Hart Simha和kos借用了这个答案。
cat
并将其作为第一个参数echo
。当然,不可打印的变量将无法正确输出并破坏数据。不要尝试将文件重定向回自身,这根本不好。
您可以使用process-substitution做到这一点。
虽然bash异步打开所有管道,但是这有点hack,我们必须使用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有时间解析输入文件cat > file_name
写输出试试这个
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>&-
我们还可以定义一个shell函数以使其易于使用:
# 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
另外,请注意,这将保留原始文件的完整副本(直到第三个文件描述符关闭)。如果您使用的是Linux,并且正在处理的文件太大而无法在磁盘上容纳两次,则可以检出此脚本,该脚本将逐个管道将文件传输到指定的命令,同时取消分配已处理的文件块。与往常一样,请阅读使用情况页面中的警告。
我通常使用tee程序执行此操作:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
它自己创建和删除一个临时文件。
tee
不能保证能正常工作。参见askubuntu.com/a/752451/335781。