如何使用sed、awk或perl替换/etc/skel/.bashrc或.bashrc文件中的PS1变量?

huangapple go评论52阅读模式
英文:

How to replace PS1 variable in /etc/skel/.bashrc or .bashrc file using sed, awk, or perl?

问题

I will only translate the non-code part of your text:

感谢Charles Duffy在评论中提供了宝贵的反馈。原始问题出现在项目1的背景下。但根据反馈,似乎项目2是更好的选项来管理系统管理任务。我可能会在完成对perl/etc/profile.d的研究后,开另一个问题,或者在此回答中发布一些信息。

  1. 原始问题:如何使用sedawkperl在文件中搜索和替换文字字符串,例如.bashrc文件中的PS1=<string>
  2. 更好的问题:如何使用存储在/etc/profile.d中的脚本来启用所需的提示行为,这些脚本在用户登录时自动执行?

目标是在Debian PC和DietPi(ARM)Linux系统上自动化自定义的bash提示。自定义提示在命令和终端输出之间插入一个空白行,如下所示,但颜色不同:

root@dpiBox /root
#

user@dpiBox /home/user
$

其中,root@dpiBox是红色的,user@dpiBox是绿色的,当前工作目录始终以蓝色显示。

当创建新的user时,会将/etc/skel/.bashrc的副本放置在用户的家目录中,命名为/home/user/.bashrc。root用户已经有一个副本,命名为/root/.bashrc

我的原始计划是在添加新用户之前通过脚本编辑/etc/skel/.bashrc,并手动编辑/root/.bashrc。但是当root或任何用户登录时,这些脚本会自动在/etc/profile.d下正确执行。Charles Duffy似乎建议使用这个功能来更好地管理系统管理挑战!

默认/etc/skel/.bashrc/root/.bashrc/home/user/.bashrc中的相关行:

# /root/bashtest(模拟默认的.bashrc)
# /etc/skel/.bashrc
# /root/.bashrc
# /home/user/.bashrc

#force_color_prompt=yes

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[3[01;32m\]\u@\h\[3[00m\]:\[3[01;34m\]\w\[3[00m\]$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$ '
fi

步骤1 - 取消注释以打开彩色提示的行:

force_color_prompt=yes

步骤2 - 用新的PS1(在此处显示)替换旧的PS1:

PS1='${debian_chroot:+($debian_chroot)}\n\[3[01;32m\]\u@\h\[3[00m\]\[3[01;34m\] $(pwd -P) \[3[00m\]\n$ '

[01;32m\]代码是/home/user/.bashrc的绿色。将此代码更改为[01;31m\],这是/root/.bashrc的红色。

步骤1 - sed:好结果

/usr/local/bin/bashsed(用于编辑bashtest/.bashrc的开发脚本)

#!/bin/bash
# 取消注释以强制启用彩色提示
off="#force_color_prompt=yes"
on="force_color_prompt=yes"
sed -i "s|$off|$on|" /root/bashtest

sed代码可以使用双引号或单引号来定义onoff。在sed命令中必须使用双引号。

步骤1 - perl:好结果

/usr/local/bin/bashperl(用于编辑bashtest/.bashrc的开发脚本)

#!/bin/bash
# 取消注释以强制启用彩色提示
perl -i.bak -pe 's/#force_color_prompt=yes/force_color_prompt=yes/' /root/bashtest

i.bak开关将/root/bashtest备份为/root/bashtest.bak,然后在原地编辑该文件。此命令也可以在命令行上使用,而无需使用i.bak开关。

步骤2 - sed:sed -i "s|$old|$new|" /root/bashtest的结果不好

/usr/local/bin/bashsed(用于编辑bashtest/.bashrc的开发脚本)

#!/bin/bash
# 用新的PS1替换旧的PS1
# 旧的                                    \[3[01;32m\]\u@\h\[3[00m\]:\[3[01;34m\]\w\[3[00m\]\
# PS1='${debian_chroot:+($debian_chroot)}\[3[01;32m\]\u@\h\[3[00m\]:\[3[01;34m\]\w\[3[00m\]$ '

old='\\[\3\[01;32m\\]\\u@\\h\\[\3\[00m\\]:\\[\3\[01;34m\\]\\w\\[\3\[00m\\]\\'

# 新的                                    \n\[3[01;32m\]\u@\h\[3[00m\]\[3[01;34m\] $(pwd -P) \[3[00m\]\n\
# PS1='${debian_chroot:+($debian_chroot)}\n\[3[01;32m\]\u@\h\[3[00m\]\[3[01;34m\] $(pwd -P) \[3[00m\]\n$ '

new='\\n\\[\3\[

<details>
<summary>英文:</summary>

**Edit - 06/08/23 - Original Question Kept Substantially the Same**

Thanks to Charles Duffy for providing valuable feedback in comments. The original question occurs in the context of item 1. But based on the feedback it appears that item 2 is the better option for managing system administration tasks. I may open another question or perhaps post some information in my answer here when I finish my research into ```perl``` and ```/etc/profile.d```.

1. Original Question: How to search and replace literal strings in a file, such as ```PS1=&lt;string&gt;``` in a ```.bashrc``` file, using ```sed```, ```awk```, or ```perl```?
2. Better question: How to enable desired prompt behavior using script(s) stored in ```/etc/profile.d``` that automatically execute whenever a user logs in?

Goal is to automate custom bash prompt on Debian PC and DietPi (ARM) Linux systems. The custom prompt inserts a blank line, to separate commands from terminal output, and looks like this but with different colors:

root@dpiBox /root

user@dpiBox /home/user
$

where root@dpiBox is red; user@dpiBox is green; and the present working directory is always shown in blue.

When a new ```user``` is created then a copy of ```/etc/skel/.bashrc``` is placed in the user&#39;s home directory as ```/home/user/.bashrc```. The root user already has a copy made as ```/root/.bashrc```.

My original plan was to edit ```/etc/skel/.bashrc``` via script before adding new users and to manually edit the ```/root/.bashrc```. But when root or any user log in this automatically executes scripts properly installed under ```/etc/profile.d```. Charles Duffy seems to be advising the use of this feature for better management of system administration challenges!

Relevant lines in default ```/etc/skel/.bashrc``` and ```/root/.bashrc``` and ```/home/user/.bashrc```

/root/bashtest (simulates default .bashrc)

/etc/skel/.bashrc

/root/.bashrc

/home/user/.bashrc

#force_color_prompt=yes

if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}[\033[01;32m]\u@\h[\033[00m]:[\033[01;34m]\w[\033[00m]$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$ '
fi


Step 1 - Uncomment line to turn on color prompt:

force_color_prompt=yes


Step 2 - Replace old PS1 (above) with new PS1 (shown here) to customize bash prompt:

PS1='${debian_chroot:+($debian_chroot)}\n[\033[01;32m]\u@\h[\033[00m][\033[01;34m] $(pwd -P) [\033[00m]\n$ '

The code ```[01;32m\]``` is green for ```/home/user/.bashrc```. Change this code to ```[01;31m\]``` which is red for ```/root/.bashrc```.

**Step 1 - sed:** good result

/usr/local/bin/bashsed (development script to edit bashtest/.bashrc)

#!/bin/bash

uncomment to force color prompt on

off="#force_color_prompt=yes"
on="force_color_prompt=yes"
sed -i "s|$off|$on|" /root/bashtest

```sed``` code works with double quotes or single quotes used to define ```on``` and ```off```. The double quotes must be used in the sed command.

**Step 1 - perl:** good result 

/usr/local/bin/bashperl (development script to edit bashtest/.bashrc)

#!/bin/bash

uncomment to force color prompt on

perl -i.bak -pe 's/#force_color_prompt=yes/force_color_prompt=yes/' /root/bashtest

The ```i.bak``` switch backs up ```/root/bashtest``` as ```/root/bashtest.bak``` then edits the file in place. This command can be used on the command line without using the ```i.bak``` switch.

**Step 2 - sed:** Bad result for ```sed -i &quot;s|$old|$new|&quot; /root/bashtest```

/usr/local/bin/bashsed (development script to edit bashtest/.bashrc)

#!/bin/bash

replace old PS1 with new PS1

old [\033[01;32m]\u@\h[\033[00m]:[\033[01;34m]\w[\033[00m]\

PS1='${debian_chroot:+($debian_chroot)}[\033[01;32m]\u@\h[\033[00m]:[\033[01;34m]\w[\033[00m]$ '

old='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\'

new \n[\033[01;32m]\u@\h[\033[00m][\033[01;34m] $(pwd -P) [\033[00m]\n\

PS1='${debian_chroot:+($debian_chroot)}\n[\033[01;32m]\u@\h[\033[00m][\033[01;34m] $(pwd -P) [\033[00m]\n$ '

new='\n\[\033[01;32m\]\u@\h\[\033[00m\]\[\033[01;34m\] $(pwd -P) \[\033[00m\]\n\'

sed -i "s|$old|$new|" /root/bashtest

Step 2 the code runs without error, but produces **no results**, when double quotes are used to define ```old``` and ```new```. The code throws an error ```sed: -e expression #1, char 149: unterminated `s&#39; command``` when single quotes are used to define ```old``` and ```new```.

**Edit - 06/08/23 - Comments**

Posted my solution to **Step 2** using ```perl``` call in a ```bash``` script.

Based on feedback from Charles Duffy the ```perl``` solution for **Step 2** can be modified to use literal strings stored as bash environment variables. Relevant information is provided near the top of the page under this link:

https://mywiki.wooledge.org/BashFAQ/021

According to Charles Duffy scripts should run from ```/etc/profile.d``` when a user logs in to better manage sysadmin tasks. I need to do more research to understand this feedback. Here are two links that might be helpful:

https://serverfault.com/questions/434321/when-are-scripts-inside-etc-profile-d-executed

https://unix.stackexchange.com/questions/64258/what-do-the-scripts-in-etc-profile-d-do

**Links in Original Question**

I have searched extensively, tried dozens of experiments in the context below, and I cannot make sense of these references:

https://linuxhint.com/replace_string_in_file_bash/

https://stackoverflow.com/questions/55540596/why-would-sed-have-trouble-seeing-my-ps1-prompt

https://stackoverflow.com/questions/54599967/replacement-of-ps1-variable-in-bashrc-using-sed-or-perl

https://stackoverflow.com/questions/18376870/regarding-sed-expression-evaluation-in-shell

So I need to have my &quot;sed&quot; examined! I would not mind using awk if it does the job, but then I need to see the simple command structure, before inserting the literal strings. Otherwise I always mess up the syntax.

</details>


# 答案1
**得分**: 1

使用```perl```而不是```sed```或```awk```解决的。主要依赖以下参考文档:

https://www.perl.com/pub/2004/08/09/commandline.html/

```old```在PS1中的文本:

[\033[01;32m]\u@\h[\033[00m]:[\033[01;34m]\w[\033[00m]\


```perl```匹配的转义后的```old```文本:

\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\


```new```在PS1中的文本:

\n[\033[01;32m]\u@\h[\033[00m][\033[01;34m] $(pwd -P) [\033[00m]\n\


```perl```替换操作的转义后的```new```文本:

\n\[\033[01;32m\]\u@\h\[\033[00m\]\[\033[01;34m\] $(pwd -P) \[\033[00m\]\n\


用于调试字面字符串的转义序列的命令行模式:

```perl &#39;s/old/DummyString/&#39; &lt; ps1old &gt; ps1out; cat ps1out

这种方法通过在占位符old和占位符new表示的字面字符串上从左到右测试转义序列模式,并将结果写入文件ps1out来调试。当转义序列失败时,文件ps1old或文件ps1new保持不变;文件ps1out恢复为相应输入文件的副本;perl提供有用的调试信息。当转义序列成功时,文件ps1out显示了“DummyString”作为oldnew的部分替代,从左到右逐步调试转义模式以系统地隔离语法错误。

在调试占位符oldnew的字面字符串后,将工作结果传输到带有perl调用的bash脚本中,如下所示。

一个改进可能是将更改写入新文件,然后将其移动到目标.bashrc并保留.bashrc.bak作为备份。

如果此脚本运行多次,它将用编辑后的版本覆盖备份副本!

Bash脚本调用perl

#!/bin/bash

# 命令行测试将结果写入ps1out而不更改ps1old
# perl -pe &#39;s/#force_color_prompt=yes/force_color_prompt=yes/&#39; &lt; ps1old &gt; ps1out; cat ps1out

# 命令行测试将结果写入ps1out而不更改ps1old
# perl -pe &#39;s/\\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]:\\\[\3\[01;34m\\]\\w\\\[\3\[00m\\]\\/\\n\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]\\\[\3\[01;34m\\] $\(pwd -P\) \\[\3\[00m\\]\\n\\/&#39; &lt; ps1old &gt; ps1old; cat ps1out

# 如果脚本运行两次或更多次,它将覆盖以前的.bashrc.bak!
# 使用-i.bak开关备份目标.bashrc,以在原地取消注释文件!
perl -i.bak -pe &#39;s/#force_color_prompt=yes/force_color_prompt=yes/&#39; .bashrc

# 在测试PS1转义模式后,将旧的PS1替换为新的PS1
perl -i -pe &#39;s/\\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]:\\\[\3\[01;34m\\]\\w\\\[\3\[00m\\]\\/\\n\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]\\\[\3\[01;34m\\] $\(pwd -P\) \\[\3\[00m\\]\\n\\/&#39; .bashrc
英文:

Solved using perl instead of sed or awk. Mostly relied on the following reference:

https://www.perl.com/pub/2004/08/09/commandline.html/

old literal string in PS1:

\[3[01;32m\]\u@\h\[3[00m\]:\[3[01;34m\]\w\[3[00m\]\

old string escaped for perl match:

\\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]:\\\[\3\[01;34m\\]\\w\\\[\3\[00m\\]\\

new literal string in PS1

\n\[3[01;32m\]\u@\h\[3[00m\]\[3[01;34m\] $(pwd -P) \[3[00m\]\n\

new string escaped for perl replace operation:

\\n\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]\\\[\3\[01;34m\\] $\(pwd -P\) \\[\3\[00m\\]\\n\\

Used pattern on command line to debug escape sequences for literal strings:

perl &#39;s/old/DummyString/&#39; &lt; ps1old &gt; ps1out; cat ps1out
# ~/ps1old

#force_color_prompt=yes

if [ &quot;$color_prompt&quot; = yes ]; then
    PS1=&#39;${debian_chroot:+($debian_chroot)}\[3[01;32m\]\u@\h\[3[00m\]:\[3[01;34m\]\w\[3[00m\]$ &#39;
else
    PS1=&#39;${debian_chroot:+($debian_chroot)}\u@\h:\w$ &#39;
fi
perl &#39;s/new/DummyString/&#39; &lt; ps1new &gt; ps1out; cat ps1out
# ~/ps1new

#force_color_prompt=yes

if [ &quot;$color_prompt&quot; = yes ]; then
    PS1=&#39;${debian_chroot:+($debian_chroot)}\n\[3[01;32m\]\u@\h\[3[00m\]\[3[01;34m\] $(pwd -P) \[3[00m\]\n$ &#39;
else
    PS1=&#39;${debian_chroot:+($debian_chroot)}\u@\h:\w$ &#39;
fi
perl &#39;s/old/new/&#39; &lt; ps1old &gt; ps1out; cat ps1out

Command line solution with test input file ps1old writes result to ps1out

perl -pe &#39;s/\\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]:\\\[\3\[01;34m\\]\\w\\\[\3\[00m\\]\\/\\n\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]\\\[\3\[01;34m\\] $\(pwd -P\) \\[\3\[00m\\]\\n\\/&#39; &lt; ps1old &gt; ps1old; cat ps1out

This method writes results to file ps1out while testing escape sequence patterns from left to right on literal strings represented by placeholder old and placeholder new.

When the escape sequence fails file ps1old or file ps1new is unchanged; file ps1out reverts to a copy of the respective input file; and perl provides valuable debug information.

When the escape sequence succeeds file ps1out shows the dummy string DummyString as replacement for portions of old or new working from left to right to debug escape pattern. This can be done one escape character at a time to isolate syntax errors systematically.

After debugging literal strings for both placeholders old and new then transfer working results to bash script with perl calls as shown below.

An improvement might be to write changes to a new file then move that to target .bashrc and keep .bashrc.bak for backup.

If this script is run more than once it overwrites the backup copy with the edited version!

Bash script calls perl

#!/bin/bash

# Command line test writes result to ps1out without changing ps1old
# perl -pe &#39;s/#force_color_prompt=yes/force_color_prompt=yes/&#39; &lt; ps1old &gt; ps1out; cat ps1out

# Command line test writes result to ps1out without changing ps1old
# perl -pe &#39;s/\\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]:\\\[\3\[01;34m\\]\\w\\\[\3\[00m\\]\\/\\n\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]\\\[\3\[01;34m\\] $\(pwd -P\) \\[\3\[00m\\]\\n\\/&#39; &lt; ps1old &gt; ps1old; cat ps1out

# If script is run two or more times it overwrites previous .bashrc.bak!
# Backup target .bashrc using -i.bak switch to uncomment file in place!
perl -i.bak -pe &#39;s/#force_color_prompt=yes/force_color_prompt=yes/&#39; .bashrc

# Replace old PS1 with new PS1 after testing PS1 escape pattern on command line
perl -i -pe &#39;s/\\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]:\\\[\3\[01;34m\\]\\w\\\[\3\[00m\\]\\/\\n\\[\3\[01;32m\\]\\u@\\h\\\[\3\[00m\\]\\\[\3\[01;34m\\] $\(pwd -P\) \\[\3\[00m\\]\\n\\/&#39; .bashrc

huangapple
  • 本文由 发表于 2023年6月8日 00:22:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76425351.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定