这大概是 一个复杂的解决方案.

我正在寻找一个简单的运算符,例如“>>”,但用于前置。

恐怕它不存在。我必须做类似的事情

 mv myfile tmp
 cat myheader tmp > myfile

有更聪明的吗?

有帮助吗?

解决方案

黑客 下面是一个快速的即兴回答,它有效并获得了很多支持。然后,随着这个问题变得越来越受欢迎,时间越来越长,愤怒的人们开始报告说它有点有效,但可能会发生奇怪的事情,或者它根本不起作用,所以它一度被猛烈地否决了。好好玩。

该解决方案利用了系统上文件描述符的精确实现,并且由于不同版本之间的实现差异很大,因此它的成功完全取决于系统,绝对不可移植,并且不应依赖于任何甚至是模糊重要的事情。

现在,排除所有这些因素后,答案是:


为文件创建另一个文件描述符(exec 3<> yourfile)因此写信给(>&3)似乎克服了对同一文件的读/写困境。我可以使用 awk 处理 600K 文件。然而,使用“cat”尝试同样的技巧失败了。

将前缀作为变量传递给 awk (-v TEXT="$text") 克服了文字引号问题,该问题阻止使用 'sed' 执行此技巧。

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3

其他提示

这仍然使用临时文件,但至少它在一行上:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

信用: 重击:在文件中添加文本/行

echo '0a
your text here
.
w' | ed some_file

ed 是标准编辑器! http://www.gnu.org/fun/jokes/ed.msg.html

约翰·梅:您的方法不能保证有效,并且如果您预先添加超过 4096 字节的内容,则可能会失败(至少 gnu awk 会发生这种情况,但我认为其他实现也会有类似的限制)。在这种情况下,它不仅会失败,而且会进入无限循环,读取自己的输出,从而使文件不断增长,直到所有可用空间都被填满。

自己尝试一下:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(警告:一段时间后杀死它,否则它将填满文件系统)

此外,以这种方式编辑文件非常危险,而且是非常糟糕的建议,就好像在编辑文件时发生了某些事情(崩溃、磁盘已满),您几乎肯定会导致文件处于不一致的状态。

没有临时文件是不可能的,但这里有一个单行代码

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

您可以使用其他工具(例如 ed 或 perl)来完成此操作,而无需临时文件。

值得注意的是,它通常是 好主意 使用类似的实用程序安全地生成临时文件 临时表, ,至少如果脚本会以 root 权限执行的话。例如,您可以执行以下操作(再次在 bash 中):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )

如果您在您控制的计算机上需要此功能,请安装包“moreutils”并使用“sponge”。然后你可以这样做:

cat header myfile | sponge myfile

使用 bash Heredoc 可以避免需要 tmp 文件:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

这是有效的,因为在执行带有重定向的 cat 之前,在评估 bash 脚本时会评估 $(cat myfile) 。

假设您要编辑的文件是 my.txt

$cat my.txt    
this is the regular file

您要添加的文件是 header

$ cat header
this is the header

确保头文件中最后有一个空行。
现在你可以在它前面加上

$cat header <(cat my.txt) > my.txt

你最终会得到

$ cat my.txt
this is the header
this is the regular file

据我所知,这只适用于“bash”。

当您开始尝试做 shell 脚本中变得困难的事情时,我强烈建议您考虑使用“适当的”脚本语言(Python/Perl/Ruby/等)重写脚本

至于在文件中添加一行,不可能通过管道来做到这一点,就像当你做类似的事情时 cat blah.txt | grep something > blah.txt, ,它会无意中清空该文件。有一个名为的小实用命令 sponge 你可以安装(你做 cat blah.txt | grep something | sponge blah.txt 它缓冲文件的内容,然后将其写入文件)。它类似于临时文件,但您不必明确执行此操作。但我想说,这是一个比 Perl 等“更糟糕”的要求。

可能有一种方法可以通过 awk 或类似的方法来做到这一点,但如果您必须使用 shell 脚本,我认为临时文件是迄今为止最简单的(/唯一的?)方法。

编辑:这已破了。看 在带有 cat 和 tee 的文件前面添加时出现奇怪的行为

覆盖问题的解决方法是使用 tee:

cat header main | tee main > /dev/null

就像 Daniel Velkov 建议的那样,使用 T 恤。
对我来说,这是一个简单的智能解决方案:

{ echo foo; cat bar; } | tee bar > /dev/null

我用的那一款。这允许您以您喜欢的方式指定顺序、额外字符等:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

附:仅当文件包含带反斜杠的文本时它不起作用,因为它被解释为转义字符

主要是为了乐趣/贝壳高尔夫,但是

ex -c '0r myheader|x' myfile

就可以了,而且没有管道或重定向。当然,vi/ex 并不是真正用于非交互式使用,因此 vi 会短暂闪烁。

为什么不简单地使用 ed 命令(正如 fluffle 已经建议的那样)?

ed 将整个文件读入内存并自动执行就地文件编辑!

所以,如果你的文件不是那么大......

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

另一种解决方法是使用打开的文件句柄,如 Jürgen Hötzel 在 将输出从 sed 's/c/d/' myFile 重定向到 myFile

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

当然,所有这些都可以放在一行中。

cb0 的“无临时文件”解决方案的一个变体,用于在前面添加固定文本:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

同样,这依赖于子 shell 执行 - (..) - 以避免 cat 拒绝使用相同的文件进行输入和输出。

笔记:喜欢这个解决方案。然而,在我的 Mac 中,原始文件丢失了(原以为不应该丢失,但确实丢失了)。这可以通过将您的解决方案编写为以下方式来解决:回声“要预先登记的文本” | cat -file_to_be_modified | cat> tmp_file;mv tmp_file 文件待修改

这是我发现的:

echo -e "header \n$(cat file)" >file
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile

警告:这需要更多的工作来满足OP的需求。

尽管@shixilun 心存疑虑,但应该有一种方法可以使 @shixilun 的 sed 方法发挥作用。将文件读入 sed 替换字符串时(例如,将换行符替换为 ' '。外壳命令 viscat 可以处理不可打印的字符,但不能处理空格,所​​以这不能解决OP的问题:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

由于替代脚本中的原始换行符而失败,需要在其前面添加行继续符 (),并且可能后跟一个 &,以使 shell 和 sed 保持满意,例如 这个答案

sed 非全局搜索替换命令的大小限制为 40K(模式后没有尾随 /g),因此可能会避免匿名者警告的 awk 可怕的缓冲区溢出问题。

sed -i -e "1s/^/new first line\n/" old_file.txt

$(命令) 您可以将命令的输出写入变量。所以我在一行中用三个命令完成了它,并且没有临时文件。

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile

如果你有一个大文件(在我的例子中是几百千字节)并且可以访问 python,这比 cat 管道解决方案:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

解决方案与 printf:

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

你还可以这样做:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

但在这种情况下,你必须确保没有任何 % 任何地方,包括目标文件的内容,因为这可能会被解释并搞砸你的结果。

您可以使用 perl 命令行:

perl -i -0777 -pe 's/^/my_header/' tmp

-i将创建文件的直列替换,-0777将耗尽整个文件,并使 ^仅匹配开始。-pe 将打印所有行

或者如果 my_header 是一个文件:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

其中 /e 将允许在替换中评估代码。

current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

其中“my_file”是要在其前面添加“my_string”的文件。

我喜欢 @绒毛的 编辑 方法 最好的。毕竟,任何工具的命令行开关与脚本编辑器命令本质上都是相同的;没有看到脚本编辑器解决方案“清洁度”有任何减少或诸如此类的情况。

这是我附加的一句话 .git/hooks/prepare-commit-msg 预先添加一个 in-repo .gitmessage 文件提交消息:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

例子 .gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

我正在做 1r 代替 0r, ,因为这会在原始模板的文件顶部留下空的可写行。不要在你的顶部放置一个空行 .gitmessage 那么,你最终会得到两个空行。 -s 抑制 ed 的诊断信息输出。

与经历这件事有关,我 发现 对于 vim 爱好者来说,拥有以下内容也很好:

[core]
        editor = vim -c ':normal gg'

变量,ftw?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list

我认为这是 ed 最干净的变体:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

作为一个函数:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile

实际上,如果您在 BASH 中编写脚本,您只需发出:

cat - yourfile  /tmp/out && mv /tmp/out yourfile

这实际上是您自己在自己的问题中发布的复杂示例。

恕我直言,无论两个文件的大小如何,都没有(并且永远不会有)能够一致且可靠地工作的 shell 解决方案 myheadermyfile. 。原因是,如果您想在不重复到临时文件的情况下执行此操作(并且不让 shell 以静默方式重复到临时文件,例如通过像这样的结构 exec 3<>myfile, 管道至 tee, , ETC。

您正在寻找的“真正”解决方案需要摆弄文件系统,因此它在用户空间中不可用并且依赖于平台:您要求修改正在使用的文件系统指针 myfile 到文件系统指针的当前值 myheader 并在文件系统中替换 EOFmyheader 具有指向当前文件系统地址的链式链接 myfile. 。这不是微不足道的,显然非超级用户无法完成,超级用户也可能无法完成......玩转 inode 等。

不过,您或多或少可以使用循环设备来伪造这一点。参见例如 这个所以线程.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top