我想添加一个运算符(例如 ^> )来处理前置而不是附加(>>)。我需要修改 Bash 源还是有更简单的方法(插件等)?
6 回答
First of all, you'd need to modify bash sources and quite heavily. Because, above all, your ^>
would be really hard to implement.
Note that bash redirection operators usually do a very simple writes, and work on a single file (or program in case of pipes) only. Excluding very specific solutions, you usually can't write to a beginning of a file for the very simple reason you'd need to move all remaining contents forward after each write. You could try doing that but it will be hard, very ineffective (since every write will require re-writing the whole file) and very unsafe (since with any error you will end up with random mix of old and new version).
That said, you are indeed probably better off with a function or any other solution which would use a temporary file, like others suggested.
For completeness, my own implementation of that:
prepend() {
local tmp=$(tempfile)
if cat - "${1}" > "${tmp}"; then
mv "${tmp}" "${1}"
else
rm -f "${tmp}"
# some error reporting
fi
}
Note that you unlike @jpa suggested, you should be writing the concatenated data to a temporary file as that operation can fail and if it does, you don't want to lose your original file. Afterwards, you just replace the old file with new one, or delete the temporary file and handle the failure any way you like.
Synopsis the same as with the other solution:
echo test | prepend file.txt
And a bit modified version to retain permissions and play safe with symlinks (if that is necessary) like >>
does:
prepend() {
local tmp=$(tempfile)
if cat - "${1}" > "${tmp}"; then
cat "${tmp}" > "${1}"
rm -f "${tmp}"
else
rm -f "${tmp}"
# some error reporting
fi
}
Just note that this version is actually less safe since if during second cat
something else will write to disk and fill it up, you'll end up with incomplete file.
To be honest, I wouldn't personally use it but handle symlinks and resetting permissions externally, if necessary.
^
是一个糟糕的字符选择,因为它已经用于历史替换。
要向 shell 语法添加新的重定向类型,请从parse.y
. 将它声明为新的%token
以便可以使用,添加它STRING_INT_ALIST other_token_alist[]
以便它可以出现在输出中(例如错误消息),更新redirection
解析器中的规则,并更新词法分析器以在遇到适当的字符时发出此标记。
command.h
包含enum r_instruction
重定向类型,需要对其进行扩展。在处理重定向指令中有一个巨大的switch
语句,实际的重定向是由整个函数执行的。其余的源代码中散布着用于打印、复制和销毁管道的各种功能,这些功能可能也需要更新。make_redirection
make_cmd.c
redir.c
就这样!Bash 并没有那么复杂。
这不讨论如何实现前置重定向,这将是困难的,因为 UNIX 文件 API 仅提供附加和覆盖。附加到文件的唯一方法是完全重写它,这(正如其他答案所提到的)比任何现有的 shell 重定向要复杂得多。
添加运算符可能很难,但也许一个函数就足够了?
function prepend { tmp=`tempfile`; cp $1 $tmp; cat - $tmp > $1; rm $tmp; }
示例使用:
echo foobar | prepend file.txt
将文本“foobar”添加到 file.txt。
大多数 Linux 文件系统不支持前置。事实上,我不知道有谁有一个稳定的用户空间界面。因此,正如其他人已经说过的那样,您只能依赖于覆盖,或者只是初始部分,或者整个文件,这取决于您的需要。
您可以轻松(部分)覆盖 Bash 中的初始文件内容,而无需截断文件:
exec {fd}<>"$filename"
printf 'New initial contents' >$fd
exec {fd}>&-
上面,$fd
是 Bash 自动分配的文件描述符,$filename
是目标文件的名称。Bash 在第一行为目标文件打开一个新的读写文件描述符;这不会截断文件。第二行覆盖文件的初始部分。文件中的位置前进,因此您可以使用多个命令覆盖文件中的连续部分。第三行关闭描述符;由于每个进程只有有限的数量可用,因此您希望在不再需要它们后关闭它们,否则长时间运行的脚本可能会用完。
我认为 bash 的插件架构(通过“启用”内置命令加载共享对象)仅限于提供额外的内置命令。重定向运算符是运行简单命令的语法的一部分,因此我认为您需要修改解析器以识别和处理您的新^>
运算符。
请注意,>
这比您预期的要少:
- 从命令行中删除
>
和以下单词,记住重定向。 - 当命令行被处理并且可以启动命令时,调用
fork(2)
(或clone(2)
)来创建一个新进程。 - 根据命令修改新进程。这包括修改的环境变量 (
SOMEVAR=foo yourcommand
) 之类的内容,但也包括更改的文件描述符。此时,> yourfile
来自 cmdline 的 a 将产生文件open(2)
在 stdout 文件描述符(即 #1)处以只写模式将文件截断为零字节的效果。A>> yourfile
会产生文件在 stdout 中以只写模式和追加模式打开的效果。 - (现在才启动程序,比如
execv(yourprogram, yourargs)
举个简单的例子,重定向可以像这样实现
open(yourfile, O_WRONLY|O_TRUNC);
或者
open(yourfile, O_WRONLY|O_APPEND);
分别。
然后启动的程序将设置正确的环境,并且可以愉快地写入 fd1。从这里开始,不涉及外壳。真正的工作不是由 shell 完成,而是由操作系统完成。由于 Unix 没有前置模式(并且不可能正确地集成该功能),因此您可以尝试的所有内容都以非常糟糕的 hack 告终。
尝试重新考虑您的要求,总有一种更简单的方法。