Bash 如何在命令中使用文件并将输出重定向到同一文件而不截断它?
基本上,我希望从文件中获取输入文本,从该文件中删除一行,然后将输出发送回同一文件。如果这能让事情变得更清楚的话Bash 如何在命令中使用文件并将输出重定向到同一文件而不截断它?,bash,redirect,io,Bash,Redirect,Io,基本上,我希望从文件中获取输入文本,从该文件中删除一行,然后将输出发送回同一文件。如果这能让事情变得更清楚的话 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name 然而,当我这样做时,我得到的是一个空白文件。 有什么想法吗?改用sed: sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name 您不能这样做,因为bash首先处理重定向,然后执行命令。所以当grep查看文件名时,它已
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name
然而,当我这样做时,我得到的是一个空白文件。
有什么想法吗?改用sed:
sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
您不能这样做,因为bash首先处理重定向,然后执行命令。所以当grep查看文件名时,它已经是空的了。不过,您可以使用临时文件
#!/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 -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name
还有
ed
(作为sed-i
的替代方案):
一个线性替代方案-将文件内容设置为变量:
VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name
您可以将slurp与POSIX Awk一起使用:
!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
q = q ? q RS $0 : $0
}
END {
print q > ARGV[1]
}
不能对同一文件使用重定向运算符(
或>
),因为它具有更高的优先级,并且它将在调用命令之前创建/截断文件。为了避免这种情况,您应该使用适当的工具,如tee
、spoone
、sed-i
或任何其他可以将结果写入文件的工具(例如sort file-o file
)
基本上,将输入重定向到同一原始文件是没有意义的,您应该为此使用适当的就地编辑器,例如Ex editor(Vim的一部分):
其中:
/'+cmd'
-运行任何Ex/Vim命令-c
-使用(g/pattern/d
)删除与模式匹配的行帮助:g
-静音模式(-s
)man ex
-执行-c wq
和:write
命令:quit
您可以使用
sed
来实现相同的功能(如其他答案中所示),但是in-place(-i
)是非标准的FreeBSD扩展(在Unix/Linux之间的工作方式可能不同),基本上它是一个s流ed编辑器,而不是一个文件编辑器。请参阅:试试这个简单的
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
您的文件这次不会为空:),您的输出也会打印到终端。您可以使用
不过这有点像黑客,因为bash异步打开所有管道,我们必须使用sleep
soymmv解决这个问题
在您的示例中:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
创建一个临时文件,接收grep的输出>(sleep 1&&cat>文件名)
延迟一秒钟,让grep有时间解析输入文件sleep 1
- 最后,
写入输出cat>file\u name
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
它自己创建和删除临时文件。试试这个
echo -e "AAA\nBBB\nCCC" > testfile
cat testfile
AAA
BBB
CCC
echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC
因为这个问题是搜索引擎中最重要的结果,这里有一个基于的单行程序,它使用子shell而不是
海绵(通常不是像OS X那样的普通安装的一部分):
一般情况是:
echo "$(cat file_name)" > file_name
编辑,上述解决方案有一些警告:
(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)
- 应使用
printf“%s”
而不是echo
,以便包含-n
的文件不会导致不希望的行为
- 命令替换会删除尾随的换行符(),因此我们应该在输出中附加一个后缀字符,如
x
,并通过类似${v%x}
的方式将其从外部删除
- 使用临时变量
$v
会在当前shell环境中跺跺任何现有变量$v
的值,因此我们应该将整个表达式嵌套在括号中以保留以前的值
- bash等shell的另一个bug/特性是命令替换会从输出中删除不可打印的字符,如
null
。我通过调用ddif=/dev/zero bs=1 count=1>>file_name
并使用cat file_name | xxd-p查看它来验证这一点。但是echo$(cat文件名)|xxd-p
被剥离。因此,这个答案应该不用于二进制文件或任何使用不可打印字符的文件,例如
一般解决方案(albiet稍微慢一点,内存更密集,仍然剥离不可打印的字符)是:
(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)
测试地点:
鉴于在当前shell中调用cat file\u uniquely\u named.txt>file\u uniquely\u 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
printf“hello\nworld\n”>文件\u unique named.txt&&for((i=0;i file\u unique named.txt;done;cat file\u unique named.txt;rm file\u unique named.txt
打印一个空字符串
我还没有在大文件(可能超过2或4 GB)上测试过这一点
我从和中借用了这个答案。以下内容将完成与海绵
相同的事情,而不需要更多的工具:
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
siphon()
{
local tmp file rc=0
[ "$#" -ge 2 ] || { echo "Usage: siphon filename [command...]" >&2; return 1; }
file="$1"; shift
tmp=$(mktemp -- "$file.XXXXXX") || return
"$@" <"$file" >"$tmp" || rc=$?
mv -- "$tmp" "$file" || rc=$(( rc | $? ))
return "$rc"
}
#将文件导入命令,并将该命令的输出导入管道
#返回到同一文件中,确保该文件未被截断。
#参数:
#$1:档案。
#$2:命令。(以$3…为参数)
#看https://stackoverflow.com/a/55655338/773113
虹吸管()
{
本地tmp文件rc=0
[“$#”-ge 2]|{echo”用法:虹吸文件名[命令…]“>&2;返回1;}
file=“$1”移位
tmp=$(mktemp--“$file.XXXXXX”)||返回
“$@”“$tmp”| | rc=$?
mv--“$tmp”“$file”| | rc=$((rc |$?))
返回“$rc”
}
这是非常可能的,您只需确保
hello
world
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
shuf --output=file --random-source=/dev/zero
# 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
siphon()
{
local tmp file rc=0
[ "$#" -ge 2 ] || { echo "Usage: siphon filename [command...]" >&2; return 1; }
file="$1"; shift
tmp=$(mktemp -- "$file.XXXXXX") || return
"$@" <"$file" >"$tmp" || rc=$?
mv -- "$tmp" "$file" || rc=$(( rc | $? ))
return "$rc"
}
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
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