15

非root linux用户拥有一个文本文件,该文件位于/等,其中非root linux用户没有创建文件的权限。非root linux用户可以通过vi手动编辑文件,而不会出现问题。通过编程和手动尝试使用替换文件中的文本字符串sed-i,perl-i型,以及其他目录内临时文件方法由于非root linux用户运行文本重新放置脚本时的权限问题而失败。我在Perl Cookbook(后面会提到)中找到了一个解决方案,但它涉及到警告,而且目前看起来有点超出我的能力。有人能建议一个更简单的选择吗?

服务器信息:

  • cat/etc/os释放:Oracle Linux服务器8.9

  • 找到对应内核版本:Linux server01.domain.com 5.4.17-2136.322.6.4.el8uek.x86_64

  • perl语言:(v5.26.3)为x86_64-linux-thread-multi构建

  • 标准偏差:(基于GNU)4.5

  • awk(哇):GNU Awk 4.2.1,API:2.0(GNU MPFR 3.1.6-p2,GNU MP 6.1.2)

  • 不及物动词:VIM-Vi IMproved 8.0(2016年9月12日,编译于2022年8月5日07:42:15)

  • 要编辑的文件位置:/等

  • 上的权限/等:

    drwxr-xr-x 130根12288 6月21日11:50等
  • 要编辑的文本文件:/etc/targetfile(等/目标文件)

    -rw-rw-r--1 justauser组1 1864年6月19日10:52目标文件
  • 文件长度/etc/targetfile(等/目标文件)最多可以有200行,每行大约50个字符。

示例/缩写/etc/targetfile(等/目标文件)内容:

f1112:/dir1/dir2/59.35:N#注释f3332:/dir1/dir2/59.35:N#注释f4442:/dir1/dir2/59.35:N#注释f555:/dir1/dir2/59.35:N#注释f666s2:/dir1/dir2/59.35:N#注释f777s2:/dir1/dir2/59.35:N#注释

目标:在/etc中的targetfile中,以编程方式将/dir1/dir2/59.35更改为/dir1/dir2/59.77,其中运行脚本的非root用户是“justauser”,他没有在/etc.中创建文件的权限。

约束条件:

  • 无法更改/etc上的权限。
  • 无法升级现有的服务器程序/实用程序。
  • 所有程序/命令都必须从bash shell脚本中调用。
  • bash shell脚本必须以“justauser”而不是root身份执行。
  • 强烈建议不要使用sudo。

其他:

以linux用户“justauser”身份登录时,命令行编辑/etc/targetfile(等/目标文件)使用vi很好。命令,如sed-iperl-i型失败,因为“justauser”在/etc中缺少创建临时文件的写入权限。

perl-i-p-e的/f4442:\/dir1\/dir2\/59.35:N/f4442:\/dir1\\/dir2\\/59.77:N/g’/etc/targetfile--无法删除/etc/targetfile:权限被拒绝,正在跳过文件。sed-i的/f4442:\/dir1\/dir2\/59.35:N/f4442:\/dir1\\/dir2\\/59.77:N/'/etc/targetfile--sed:sed:无法打开临时文件/etc/sedO2SLSF:权限被拒绝
粗略的预期用途示例(静态值替换变量):#!/垃圾桶/垃圾桶...函数编辑配置(_E){echo“正在编辑目标文件…”perl-i-p-e的/f4442:\/dir1\/dir2\/59.35:N/f4442:\/dir1\\/dir2\\/59.77:N/g’/etc/targetfile如果[$?-ne 0]然后echo“_editConfig函数出错。正在终止程序”退出代码=1fi(菲涅耳)}...

看到的选项:

Perl Cookbook 7.10在不使用临时文件的情况下就地修改文件:

open(F,“+<$infile”)或die“无法读取$infile:$!”;$out=“”;同时(<F>){s/DATE/localtime/eg;$out.=$;}seek(F,0,0)或die“无法查找$infe:$的开头!”;打印F$out或死亡“无法打印到$infile:$!”;truncate(F,tell(F))或die“无法截断$infe:$!”;close(F)or die“无法关闭$infile:$!”;...“这种方法是为真正有决心的人准备的。它更难写,占用更多内存(可能更多),不保留备份文件,并且可能会混淆其他尝试读取正在更新的文件。因此,在大多数情况下,我们认为这可能不值得。”

最终意见:

是否有一个就地编辑选项不使用比Perl Cookbook示例更简单的目录内临时文件?

对于Perl初学者来说,Perl Cookbook示例有点难理解,它还需要一些修改/转换。。。我想。。。在bash脚本的主体中工作。

新贡献者
乌龟海盗是此网站的新贡献者。请注意要求澄清、评论和回答。查看我们的行为准则.
4
  • “无法升级现有的服务器程序/实用程序。”。。。但是你有吗海绵moreutils公司可能已经安装了吗?
    – 村落
    评论 6月22日2:01
  • 5
    不能写一个测试过的答案(请随便有人把它改成一个答案),但不应该这样vi/etc/filename-c s/xxx/yyy/-c:wq工作? 评论 6月22日6:34
  • 1
    你问题的原因是sed-iperl-i型不要编辑文件;相反,它们用新文件替换文件,用户无法在该目录中创建新文件。 评论 6月24日14:10
  • 不幸的是,没有安装海绵。 评论 2天前

5个答案5

重置为默认值
16

使用预计起飞时间而不是塞德这通常是一个交互式编辑器,但您可以使用here-doc将命令输入到其标准输入中。

ed/etc/filename<<EOF1,$s/旧/新/gw个电动势
2
  • 4
    好的老ed!不错的一个:)(对huuuge文件也很好) 评论 6月22日15:08
  • 我本来打算说“使用ex”(因为vi可以工作),它看起来非常像这样。 评论 6月24日11:55
14

问题在于perl-i型sed-i他们试图在/等、和$用户没有w个仪式许可/等。您可以:

cp/etc/myfile$HOME/myfilesed-我$主页/我的文件cp$HOME/myfile/etc/myfile

这将使备份文件留在$主页。备份文件(旧配置)不属于/等.

5
  • 原因和解决方案基本正确,但不准确:perl-i型(以下无任何扩展-我)将原始文件重命名为某个临时文件,用原始名称将其处理为新文件,然后取消(原始)临时文件的链接。因此没有实际的“备份”文件。 评论 6月21日22:39
  • 但它试图在/等,参见上面的解释。 评论 6月21日22:59
  • 4
    正如我所说,您的回答和推理基本正确(因为在那里创建了新文件,所以需要对/etc进行写权限),唯一的例外是这不是备份文件,而是一个临时文件,随后会被删除。 评论 6月21日23:04
  • 或保存一个步骤sed[…]/etc/myfile>“$HOME”/myfile;cp“$HOME”/myfile/etc/myfile(添加引号)
    – 科斯
    评论 6月22日2:30
  • 4
    反而会建议sed[…]/etc/myfile>“$HOME”/myfile&&cp“$HOME”/myfile/etc/my文件因此,只有在没有错误的情况下,它才会替换原始文件。 评论 6月22日10:45
11

这不是最安全的方法,因为塞德脚本将导致截断/etc/targetfile(等/目标文件).

为了避免无法将临时文件写入/等当然,天真的做法是尝试sed[…]/etc/targetfile>/etc/targetfile,这将截断/etc/targetfile(等/目标文件)之前/etc/targetfile(等/目标文件)有机会被阅读塞德; 解决这个问题的一个方法是使用海绵,它在STDIN完全读取之前不会覆盖目标文件,有效地防止了源文件在完全处理之前被截断,同时允许绕过上的权限所施加的限制/等等.

根据其男人第页:

海绵读取标准输入并将其写入指定的文件。与shell重定向不同,海绵在打开输出文件之前会吸收所有输入。这允许压缩读取和写入同一文件的管道。

sed[…]/etc/targetfile|spava/etc/targetfile
2
  • 2
    如果在[…]部分有输入错误,sed将不输出任何内容,因此您将不会在/etc/targetfile中输出任何内容。。。 评论 6月22日15:07
  • @OlivierDulac公平点,这不是最安全的解决方案。我在答案的开头加了一个大胆的警告,谢谢你指出这一点
    – 科斯
    评论 6月22日15:20
6

我只需要使用awk并将所有内容存储在内存中,然后在末尾将其写出(在awk关闭输入文件后),例如使用任何awk:

哎呀'{sub(“^f4442:/dir1/dir2/59.35:N”,“f4442:/dir1/dir2/59.77:N”);rec=rec$0 ORS}结束{printf“%s”,rec>FILENAME}'文件

通过张贴的示例输入,请注意从顶部起第三行中的更改:

$cat文件f1112:/dir1/dir2/59.35:N#注释f3332:/dir1/dir2/59.35:N#注释f4442:/dir1/dir2/59.35:N#注释f555:/dir1/dir2/59.35:N#注释f666s2:/dir1/dir2/59.35:N#注释f777s2:/dir1/dir2/59.35:N#注释

$awk'(美元){子(“^f4442:/dir1/dir2/59.35:N”,“f4442:/dir1/dir2/59.77:N”);rec=rec$0 ORS}结束{printf“%s”,rec>FILENAME}'文件

$cat文件f1112:/dir1/dir2/59.35:N#注释f3332:/dir1/dir2/59.35:N#注释f4442:/dir1/dir2/59.77:N#注释f555:/dir1/dir2/59.35:N#注释f666s2:/dir1/dir2/59.35:N#注释f777s2:/dir1/dir2/59.35:N#注释

但是,如果文件很大,我建议使用临时文件作为:

tmp=$(mktemp)&&awk'{sub(“^f4442:/dir1/dir2/59.35:N”,“f4442:/dir1/dir2/59.77:N”)}1'文件>“$tmp”&&cat“$tmp”>文件rm-f“$tmp”

这样,您又一次只更改了文件,而不是更换。

附言。就个人而言,我不会使用regexp搜索并替换它,因为它会引入太多关于输入和/或搜索字符串中可能存在的问题,我会使用文字字符串,例如:

awk-v old='f4442:/dir1/dir2/59.35:N'-v new='f4402:/dir1/dir2/59.77:N'索引($0,旧)==1{$0=新子项($0、长度(旧)+1)}{rec=rec$0ORS}结束{printf“%s”,rec>FILENAME}'文件

如果目录路径可能包含反斜杠,则仍需进行调整,但如果需要,这很容易处理,例如请参阅ENVIRON[]公司how-do-i-use-shell-variables-in-an-awk-script.

  • 1
    很好,因为您可以根据行号、内容等进行任意复杂的编辑、删除和插入。对于大型文件,我会使用行数组,而不是一直扩展单个变量记录. 评论 6月22日15:19
  • 1
    @当输入变大时,Paul_Pedant数组也会出现问题,因为当超过之前的分配时,它们被分配成指数级更大的块。我更新了我的答案,建议如果输入文件很大该怎么办。 评论 6月22日17:43
  • 2
    我通常使用普林斯顿cs算法(leipzig1M.txt)中的一个毫在线(125MB)文本文件在8GB笔记本电脑上测试性能和容量,没有问题。但在处理大型数据和大型数据的方法之间切换并不困难。 评论 6月22日22:42
4

我没有在Perl食谱因为它非常罕见,但因为替换字符串的长度相同确切地作为原始字符串,它实际上可以就地更新磁盘文件,只需将查找指针重置回读取该行时的位置即可。

在Ubuntu系统上测试后,以下代码适用于您的文件:

#!/usr/bin/env-perl文件使用严格;使用警告;###my$filename=“$ENV{HOME}/tmp/targetfile”;#用于测试my$filename=“/etc/targetfile”;my$old_string=“f4442:/dir1/dir2/59.35:N”;my$new_string=“f4442:/dir1/dir2/59.77:N”;长度($old_string)==长度($new_string||die“新旧字符串的长度必须相同”;open(my$fh,“+<”,$filename)||die“无法打开$filename进行更新:$!”;$fh->自动刷新(1);我的$filepos=0;同时(<$fh>){下一个,除非是s/\Q$old_string\E/$new_string/g;警告“正在更新位置$filepos处的$filename行”;#如果你有信心,也可以发表评论:)seek($fh,$filepos,0)||die“无法在$filename:$中查找pos$filepos:”;print($fh$_)||die“无法将新行作为pos$filepos:$!写入$filename”;}继续{$filepos=告诉($fh);}close($fh)||die“无法关闭$filename:$!”;

这个故事的寓意是,令人惊讶的是,你真的可以使用相同的手柄二者都阅读如果你很喜欢写作,非常,非常小心,只有在有寻求在stdio文件句柄上更改I/O“方向”(从读到写或从读到写入),如这里所示。这是保持stdio缓冲区有序所必需的,而perl使用自己的I/O缓冲系统,同样的继承POSIX规则也适用。

这种方法特别有吸引力,因为它不再像讨厌的“先将其全部读入内存”方法那样占用与文件大小成比例的内存。这意味着如果你愿意的话,你可以在一个有上亿行的文件上运行它,它所消耗的内存不会比一个小文件多。

然而,考虑到缺乏后援,这是非常勇敢的。

你必须登录来回答这个问题。

不是你想要的答案吗?浏览标记的其他问题.