Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/16.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
Linux 不使用sed或awk从文件中删除特定行_Linux_Bash_Unix_Ed - Fatal编程技术网

Linux 不使用sed或awk从文件中删除特定行

Linux 不使用sed或awk从文件中删除特定行,linux,bash,unix,ed,Linux,Bash,Unix,Ed,我需要使用bash脚本从文件中删除特定的行号 我使用-n选项从grep命令获取行号 我不能使用sed有很多原因,其中最重要的一个原因是它没有安装在这个脚本需要运行的所有系统上,安装它也不是一个选项 awk是不可能的,因为在测试中,在使用不同UNIX/Linux操作系统的RHEL、SunOS、Solaris、Ubuntu等的不同机器上,它有时会在每个机器上给出完全不同的结果。所以,没问题 所讨论的文件只是一个纯文本文件,每行有一条记录,因此除了按编号删除该行之外,不需要做任何花哨的事情 如果可能的

我需要使用bash脚本从文件中删除特定的行号

我使用-n选项从grep命令获取行号

我不能使用sed有很多原因,其中最重要的一个原因是它没有安装在这个脚本需要运行的所有系统上,安装它也不是一个选项

awk是不可能的,因为在测试中,在使用不同UNIX/Linux操作系统的RHEL、SunOS、Solaris、Ubuntu等的不同机器上,它有时会在每个机器上给出完全不同的结果。所以,没问题

所讨论的文件只是一个纯文本文件,每行有一条记录,因此除了按编号删除该行之外,不需要做任何花哨的事情

如果可能的话,我需要避免做一些事情,比如提取文件的内容,不包括我要删除的行,然后覆盖原始文件。

试试看。下面基于文档的示例从test.txt中删除第2行

试试看。下面基于文档的示例从test.txt中删除第2行

如果n是要省略的行:

{
  head -n $(( n-1 )) file
  tail +$(( n+1 )) file
} > newfile
如果n是要省略的行:

{
  head -n $(( n-1 )) file
  tail +$(( n+1 )) file
} > newfile
既然你有grep,最明显的是:

$ grep -v "line to remove" file.txt > /tmp/tmp
$ mv /tmp/tmp file.txt
$
但听起来你不想使用任何临时文件——我假设输入文件很大,而这是一个内存和存储短缺的嵌入式系统。我认为理想情况下,您需要一个就地编辑文件的解决方案。我认为这在dd中是可能的,但还没有弄清楚:

更新-我想出了如何编辑文件的地方与dd。还grep,头和削减是必要的。如果这些不可用,则可以在大部分情况下解决这些问题:

#!/bin/bash

# get the line number to remove
rline=$(grep -n "$1" "$2" | head -n1 | cut -d: -f1)
# number of bytes before the line to be removed
hbytes=$(head -n$((rline-1)) "$2" | wc -c)
# number of bytes to remove
rbytes=$(grep "$1" "$2" | wc -c)
# original file size
fsize=$(cat "$2" | wc -c)
# dd will start reading the file after the line to be removed
ddskip=$((hbytes + rbytes))
# dd will start writing at the beginning of the line to be removed
ddseek=$hbytes
# dd will move this many bytes
ddcount=$((fsize - hbytes - rbytes))
# the expected new file size
newsize=$((fsize - rbytes))
# move the bytes with dd.  strace confirms the file is edited in place
dd bs=1 if="$2" skip=$ddskip seek=$ddseek conv=notrunc count=$ddcount of="$2"
# truncate the remainder bytes of the end of the file
dd bs=1 if="$2" skip=$newsize seek=$newsize count=0 of="$2"
这样运行:

$ cat > file.txt
line 1
line two
line 3
$ ./grepremove "tw" file.txt
7+0 records in
7+0 records out
0+0 records in
0+0 records out
$ cat file.txt
line 1
line 3
$ 

可以说dd是一个非常危险的工具。您很容易无意中覆盖文件或整个磁盘。小心点

既然你有了grep,最明显的是:

$ grep -v "line to remove" file.txt > /tmp/tmp
$ mv /tmp/tmp file.txt
$
但听起来你不想使用任何临时文件——我假设输入文件很大,而这是一个内存和存储短缺的嵌入式系统。我认为理想情况下,您需要一个就地编辑文件的解决方案。我认为这在dd中是可能的,但还没有弄清楚:

更新-我想出了如何编辑文件的地方与dd。还grep,头和削减是必要的。如果这些不可用,则可以在大部分情况下解决这些问题:

#!/bin/bash

# get the line number to remove
rline=$(grep -n "$1" "$2" | head -n1 | cut -d: -f1)
# number of bytes before the line to be removed
hbytes=$(head -n$((rline-1)) "$2" | wc -c)
# number of bytes to remove
rbytes=$(grep "$1" "$2" | wc -c)
# original file size
fsize=$(cat "$2" | wc -c)
# dd will start reading the file after the line to be removed
ddskip=$((hbytes + rbytes))
# dd will start writing at the beginning of the line to be removed
ddseek=$hbytes
# dd will move this many bytes
ddcount=$((fsize - hbytes - rbytes))
# the expected new file size
newsize=$((fsize - rbytes))
# move the bytes with dd.  strace confirms the file is edited in place
dd bs=1 if="$2" skip=$ddskip seek=$ddseek conv=notrunc count=$ddcount of="$2"
# truncate the remainder bytes of the end of the file
dd bs=1 if="$2" skip=$newsize seek=$newsize count=0 of="$2"
这样运行:

$ cat > file.txt
line 1
line two
line 3
$ ./grepremove "tw" file.txt
7+0 records in
7+0 records out
0+0 records in
0+0 records out
$ cat file.txt
line 1
line 3
$ 

可以说dd是一个非常危险的工具。您很容易无意中覆盖文件或整个磁盘。小心点

您可以使用posix shell内置程序,而无需grep,它应该位于任何*nix上

while read LINE || [ "$LINE" ];do
  case "$LINE" in
    *thing_you_are_grepping_for*)continue;;
    *)echo "$LINE";;
  esac
done <infile >outfile

您可以使用posix shell内置程序来完成这项工作,而不必使用grep,posix shell内置程序应该位于任何*nix上

while read LINE || [ "$LINE" ];do
  case "$LINE" in
    *thing_you_are_grepping_for*)continue;;
    *)echo "$LINE";;
  esac
done <infile >outfile

如果您能指出在什么情况下,最明显的Awk脚本会在哪些平台上失败,也许我们可以设计一个解决方案

awk "NR!=$N" infile >outfile
当然,用grep获得$N只是为了把它提供给Awk是非常困难的。这将删除包含首次出现的foo的行:


如果您能指出在什么情况下,最明显的Awk脚本会在哪些平台上失败,也许我们可以设计一个解决方案

awk "NR!=$N" infile >outfile
当然,用grep获得$N只是为了把它提供给Awk是非常困难的。这将删除包含首次出现的foo的行:

鉴于dd被认为对于这种就地行删除来说太危险,我们需要一些其他方法,在这种方法中,我们对文件系统调用具有相当细粒度的控制。我最初的冲动是用c写一些东西,但尽管可能,我认为这有点过分了。相反,值得研究的是通用脚本语言而不是shell脚本语言,因为这些语言通常具有相当低级别的文件API,这些API以相当简单的方式映射到文件系统调用。我猜这可以使用python、perl、Tcl或其他许多可用的脚本语言之一来完成。我对Tcl最熟悉,所以我们来看看:

#!/bin/sh
# \
exec tclsh "$0" "$@"

package require Tclx

set removeline [lindex $argv 0]
set filename [lindex $argv 1]

set infile [open $filename RDONLY]
for {set lineNumber 1} {$lineNumber < $removeline} {incr lineNumber} {
    if {[eof $infile]} {
        close $infile
        puts "EOF at line $lineNumber"
        exit
    }
    gets $infile line
}
set bytecount [tell $infile]
gets $infile rmline

set outfile [open $filename RDWR]
seek $outfile $bytecount start

while {[gets $infile line] >= 0} {
    puts $outfile $line
}

ftruncate -fileid $outfile [tell $outfile]
close $infile
close $outfile
我很想听到一个更通用的bash?最后做部分截断并完成此答案的方法。当然,截断也可以用dd来完成,但我认为我之前的回答已经排除了这一点

对于记录,列出了如何用多种不同的语言执行就地文件截断,以防在您的环境中使用这些语言中的任何一种。

鉴于dd被认为对于就地行删除来说太危险,我们需要一些其他方法,我们可以对文件系统调用进行相当细粒度的控制。我最初的冲动是用c写一些东西,但尽管可能,我认为这有点过分了。相反,值得研究的是通用脚本语言而不是shell脚本语言,因为这些语言通常具有相当低级别的文件API,这些API以相当简单的方式映射到文件系统调用。我猜这可以通过python、perl、Tcl或其他脚本实现 可能提供的ng语言。我对Tcl最熟悉,所以我们来看看:

#!/bin/sh
# \
exec tclsh "$0" "$@"

package require Tclx

set removeline [lindex $argv 0]
set filename [lindex $argv 1]

set infile [open $filename RDONLY]
for {set lineNumber 1} {$lineNumber < $removeline} {incr lineNumber} {
    if {[eof $infile]} {
        close $infile
        puts "EOF at line $lineNumber"
        exit
    }
    gets $infile line
}
set bytecount [tell $infile]
gets $infile rmline

set outfile [open $filename RDWR]
seek $outfile $bytecount start

while {[gets $infile line] >= 0} {
    puts $outfile $line
}

ftruncate -fileid $outfile [tell $outfile]
close $infile
close $outfile
我很想听到一个更通用的bash?最后做部分截断并完成此答案的方法。当然,截断也可以用dd来完成,但我认为我之前的回答已经排除了这一点


记录中列出了如何用多种不同的语言进行就地文件截断,以防在您的环境中使用这些语言。

基于Digital Trampion的Ansare,我发现了一个改进,只需要grep和echo,而不需要tempfile:

echo $(grep -v PATTERN file.txt) > file.txt
根据文件包含的行的类型以及模式是否需要更复杂的语法,您可以使用带双引号的grep命令:

echo "$(grep -v PATTERN file.txt)" > file.txt

从crontab中删除时非常有用基于Digital Trampar的Ansare,我发现一个改进只需要grep和echo,而不需要tempfile:

echo $(grep -v PATTERN file.txt) > file.txt
根据文件包含的行的类型以及模式是否需要更复杂的语法,您可以使用带双引号的grep命令:

echo "$(grep -v PATTERN file.txt)" > file.txt


从crontab中删除时非常有用。ed或red可能会给你想要的:你能在这篇文章中包含你的awk脚本吗。我对你的断言感到非常惊讶,也许太阳4号的旧awk不是傻子。祝你好运。我没有最初使用的awk脚本。这个指令来自那些比我赚更多钱的人……看看脚本部分原始作者的笔记,事实上,我们用作NIS服务器的几个Sun机器出现了问题。它没有说明是什么版本的东西导致了这些问题。ed或red可能会给你你想要的:你能在这篇文章中包含你的awk脚本吗。我对你的断言感到非常惊讶,也许太阳4号的旧awk不是傻子。祝你好运。我没有最初使用的awk脚本。这个指令来自那些比我赚更多钱的人……看看脚本部分原始作者的笔记,事实上,我们用作NIS服务器的几个Sun机器出现了问题。它没有说明是什么版本的东西导致了这些问题。我对Sun ed的经验是,它无法读取非常大的文件而不中断。使用vis-ex模式的工作方式大致相同,并且仅限于ex写入其临时文件的可用空间,该文件可通过以下方式配置:tmp=/path/to/tmpdir或类似方式,请咨询您的vi文档。当然,我们想知道为什么OP不只是使用ed搜索实际模式,而不是使用grep-n获取行号,从grep输出中解析它,用它组成ed输入,并将其传递给ed。当我在strace下运行此操作时,我看到创建了一个临时文件:open/tmp/ed.kNTc8I,O_RDWR | O|u create | O|u EXCL,0600=3。对于我发布的dd解决方案,情况并非如此。@digitaltrampa,临时文件在大小方面是否与原始文件处于相同的范围内?@1_CR-是。至少我用一个1.2MB的文件以交互方式运行了ed strace ed-s ed.txt,并在ed启动后看到一个大小类似的文件出现在/tmp中:-rw---1用户1228800十月2日10:16/tmp/ed.v0fHPp。一旦ed退出,文件就会消失。我对Sun ed的经验是,它无法读取非常大的文件而不中断。使用vis-ex模式的工作方式大致相同,并且仅限于ex写入其临时文件的可用空间,该文件可通过以下方式配置:tmp=/path/to/tmpdir或类似方式,请咨询您的vi文档。当然,我们想知道为什么OP不只是使用ed搜索实际模式,而不是使用grep-n获取行号,从grep输出中解析它,用它组成ed输入,并将其传递给ed。当我在strace下运行此操作时,我看到创建了一个临时文件:open/tmp/ed.kNTc8I,O_RDWR | O|u create | O|u EXCL,0600=3。对于我发布的dd解决方案,情况并非如此。@digitaltrampa,临时文件在大小方面是否与原始文件处于相同的范围内?@1_CR-是。至少我用一个1.2MB的文件以交互方式运行了ed strace ed-s ed.txt,并在ed启动后看到一个大小类似的文件出现在/tmp中:-rw---1用户1228800十月2日10:16/tmp/ed.v0fHPp。一旦ed退出,文件就会消失。甚至:awk'/foo/| | p++'infle>outfile。但这还不到位,fwiw。甚至:啊/foo/| | p++'infle>outfile。但这还没有到位,fwiw。无意冒犯,但本着为正确的工作使用正确的工具的精神,我真的希望OP不要将dd作为解决方案。无意冒犯。我同意dd是一个非常危险的工具,应该非常仔细地考虑它的使用。话虽如此,这是一个非常通用的工具,我认为迄今为止唯一可以用来正确回答OPs问题的工具-即从文件中删除行,没有任何临时文件。创建临时文件的问题在于,在某些系统上,该文件的大小高达1.9 GB,并且该文件所在的目录实际上是从NFS服务器导出的,该服务器将该文件共享给所有需要它的计算机
您需要使用该文件。该NFS服务器还对导出的目录强制执行配额。如果我尝试将它压缩到与临时文件相同的目录中,它很有可能在一些机器上超过配额。如果它首先尝试将其cp到本地计算机,这将意味着大量的网络I/O。是的,我知道我们的设置/拓扑非常复杂。不,我没有设计它…也就是说,我认为dd对于这个例子来说有点太危险了。虽然这是一个很棒的概念!可以说dd是一个非常危险的工具。您很容易无意中覆盖文件或整个磁盘。我的意思是,你可以很容易地用普通的shell重定向来实现。我认为dd在删除磁盘方面的声誉来自于我们对授予它根权限并将其指向磁盘的嗜好。无意冒犯,但本着为正确的工作使用正确的工具的精神,我真的希望OP不要将dd作为解决方案。无意冒犯。我同意dd是一个非常危险的工具,应该非常仔细地考虑它的使用。话虽如此,这是一个非常通用的工具,我认为迄今为止唯一可以用来正确回答OPs问题的工具-即从文件中删除行,没有任何临时文件。创建临时文件的问题是,在某些系统上,该文件的大小高达1.9 GB,并且它所在的目录实际上是从NFS服务器导出的,该服务器将该文件共享给需要使用该文件的所有计算机。该NFS服务器还对导出的目录强制执行配额。如果我尝试将它压缩到与临时文件相同的目录中,它很有可能在一些机器上超过配额。如果它首先尝试将其cp到本地计算机,这将意味着大量的网络I/O。是的,我知道我们的设置/拓扑非常复杂。不,我没有设计它…也就是说,我认为dd对于这个例子来说有点太危险了。虽然这是一个很棒的概念!可以说dd是一个非常危险的工具。您很容易无意中覆盖文件或整个磁盘。我的意思是,你可以很容易地用普通的shell重定向来实现。我认为dd删除磁盘的名声来自于我们对赋予它root权限并将其指向磁盘的嗜好。命令替换将把整个grep输出放入内存(如果可用),然后将整个输出粘贴到echo命令行。对于大于正常命令行长度限制的输入文件,这将中断,该限制不太可能大于几兆字节-不足以用于OP。更不用说echo如何以微妙的方式更改输出-printf将更可靠命令替换将把整个grep输出放入内存(如果可用),然后将整个输出粘贴到echo命令行。对于大于正常命令行长度限制的输入文件,这将中断,该限制不太可能大于几兆字节-不足以用于OP。更不用说echo如何以微妙的方式更改输出-printf将更可靠