仅当文件末尾没有行时才如何在其末尾添加行?


10

我想通过添加一行来就地编辑文件,但前提是尚不存在以使我的脚本防弹。

通常我会做类似的事情:

cat >> ~/.bashrc <<EOF
export PATH=~/.composer/vendor/bin:\$PATH
EOF

也可以通过ansible(line+ insertafter=EOF+ regexp)完成此操作,但这是另一回事了。

在vi / ex中,我可以执行以下操作:

ex +'$s@$@\rexport PATH=\~/.composer/vendor/bin:$PATH@' -cwq ~/.bashrc

但是,然后如何在不重复同一行的情况下理想地检查该行是否已经存在(因此什么也不做)?

或者,也许在Ex编辑器中有一些更简单的方法?


你不能外包吗?grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(或者cat,就此而言)?
muru 15/09 / 25,3

我相信这可能类似于此方法,但相反(当字符串不存在时,执行某些操作)。
kenorb

ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"
亚历克斯·克罗尔

1
请注意,这export是一个命令,因此该行的其余部分是一个shell单词,而不是赋值。因此,与使用变量赋值(不使用export)不同,您需要双引号,否则它将在空白处中断。另请参阅如何正确地将路径添加到PATH
通配符

1
我找到了昨天我一直在寻找的实际链接(尽管以上链接也很好)。 局部变量赋值是否需要引号?
通配符

Answers:


6

如果您想防弹,您可能想要比上面复杂的选项更轻便的东西。我会坚持使用POSIX的功能,ex使其变得愚蠢简单:只要删除该行(无论它存在多少次),就将其添加到文件末尾,然后保存并退出:

ex -sc 'g/^export PATH=\~\/\.composer\/vendor\/bin:\$PATH$/d
$a
export PATH=~/.composer/vendor/bin:$PATH
.
x' ~/.bashrc

我锚定了该正则表达式以增强鲁棒性,尽管我认为偶然匹配该正则表达式几乎是不可能的。(锚定表示使用^$因此正则表达式仅在与行匹配时才匹配。)

请注意,+cmdPOSIX不需要语法,而允许多个-c参数的通用功能也不是。


还有许多其他简单的方法可以做到这一点。例如,将行添加到文件的末尾,然后通过外部UNIX过滤器运行文件的最后两行uniq

ex -sc '$a
export PATH=~/.composer/vendor/bin:$PATH
.
$-,$!uniq
x' input

这非常干净和简单,并且完全符合POSIX。它使用Escape功能ex进行外部文本过滤,并使用POSIX指定的shell实用程序uniq

底线ex为就地文件编辑而设计的,它是迄今为止以非交互方式完成此操作的最可移植的方法。vi实际上,它甚至早于原始的名称(名称vi是“可视编辑器”,而基于它的非可视编辑器 ex。)。


为了进一步简化(并将其简化为一行),可使用printf将命令发送到ex

printf %s\\n '$a' 'export PATH=~/.composer/vendor/bin:$PATH' . '$-,$!uniq' x | ex input


2

一个可能的解决方案是创建一个函数来做到这一点:

function! AddLine()

    let l:res = 0
    g/export PATH=\~\/.composer\/vendor\/bin:\\\$PATH/let l:res = l:res+1

    if (l:res == 0)
        normal G
        normal oexport PATH=~/.composer/vendor/bin:\$PATH
    endif

endfunction

首先,我们创建一个局部变量l:res将被用来计算该行出现的次数。

我们使用以下global命令:每次匹配行时l:res都会递增。

最后,如果l:res仍然为0,则表示该行未出现在文件中,因此,由于有了G,我们转到了最后一行,并创建了新行,并o在该行上添加了行。

注意1我用来设置的值的l:res方法有点笨重,可以用@Alex在他的答案中建议的方法替换为:

let l:res = search('export PATH=\~\/.composer\/vendor\/bin:\\\$PATH')

注意2为了使函数更灵活,您可以使用参数:

function! AddLine(line)

    let l:line =  escape(a:line, "/~$" )

    let l:res = 0
    exe "g/" . l:line . "/let l:res = l:res+1"

    if (l:res == 0)
        normal G
        exe "normal o" . a:line
    endif

endfunction
  • 请注意,在字符前面escape()添加a 的技巧\可能会导致搜索出现问题。
  • 另请注意,\在调用函数时,您仍然必须自己转义(我没有设法\自动转义):

    call AddLine("export PATH=~/.composer/vendor/bin:\\$PATH")
    

2

尝试:

ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"

请注意,:appendif endifEx模式下-block不能在内部使用(请参阅VimDoc),因此我必须放在norm A...外面。


1

我通常使用:

perl -i -p0e '$_.="export PATH=~/bin:\$PATH\n" unless m!~/bin:!' ~/.bashrc

1

为了简化并避免出现许多\行,将添加行export PATH=mybin:PATH

主要策略:mybin用text + mybin-path-definition 替换“ non- ”文本(所有文件):

 vim  +'s#\v%^((.|\n)(mybin:)@!)*%$#&\rexport PATH=mybin:PATH#e' -cx bashrc

或带有更多的教学缩进

s#                                    substitute
   \v                                  very magical to redude \

   %^                                  file begin
   ((.|\n)(mybin:)@!)*                 multi-line str without "mybin:"
                                         (any char not followed by "mybin")*
   %$                                  end of file
 #                                    by

如果我们有比赛:

  1. &有全文bashrc
  2. & 不含 /mybin:/

所以:

 #                                    by ...
   &                                   all file
   export PATH=mybin:PATH              and the new path
 #e                                   don't complain if patt not found

-cx                                   save and leave 

更新:最后,我们可以制作一个vim脚本:

$ cat bashrcfix

#!/usr/bin/vim -s
:e ~/fakebashrc
:s#\v%^((.|\n)(mybin:)@!)*%$#&\rexport PATH=mybin:PATH#e
:x

chmod 755并安装它。用法:bashrcfix


注意:在\v模式下,=表示可选


1

这些答案中的大多数似乎很复杂,如何处理好旧的Shell命令:

grep composer/vender ~/.bashrc || cat >> ~/.bashrc <<EOF
export PATH=~/.composer/vendor/bin:\$PATH
EOF

这很容易成为Bash函数:

addBashrcPath() {
  if ! grep "$1" ~/.bashrc >/dev/null 2>&1; then
    printf 'export PATH=%s:$PATH' "$1" >> ~/.bashrc
  fi
}

addBashrcPath "~/.composer/vendor/bin"

编辑:grep的退出代码很重要,在这种情况下,需要反向使用! grepgrep -v在这种情况下将不会按预期退出。谢谢@joeytwiddle的提示。


我认为没有grep -v给您想要的退出代码。但是! grep应该这样做。
joeytwiddle

如果是这样的情况,那么你甚至都不需要的-v只是!
Sukima 2015年

究竟。您可以编辑答案以进行更正。
joeytwiddle

1
1.您应该使用,-F以便不要grep将字符解释为正则表达式,并且2.使用-q而不是将其重定向到/dev/null。请参阅我先前对问题的评论
muru 2015年

好点。很好奇便携性-q-F选择性吗?我认为>/dev/null在不同平台之间更通用(sun vs hp vs Mac OS X vs Ubuntu vs FreeBSD vs…)
Sukima 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.