Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 如何在命令中使用文件并将输出重定向到同一文件而不截断它?_Bash_Redirect_Io - Fatal编程技术网

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'
    /
    -c
    -运行任何Ex/Vim命令
  • g/pattern/d
    -使用(
    帮助:g
    )删除与模式匹配的行
  • -s
    -静音模式(
    man ex
  • -c wq
    -执行
    :write
    :quit
    命令

您可以使用
sed
来实现相同的功能(如其他答案中所示),但是in-place(
-i
)是非标准的FreeBSD扩展(在Unix/Linux之间的工作方式可能不同),基本上它是一个sed编辑器,而不是一个文件编辑器。请参阅:

试试这个简单的

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)
  • >(sleep 1&&cat>文件名)
    创建一个临时文件,接收grep的输出
  • sleep 1
    延迟一秒钟,让grep有时间解析输入文件
  • 最后,
    cat>file\u name
    写入输出

我通常使用tee程序来执行此操作:

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