英文:
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
的研究后,开另一个问题,或者在此回答中发布一些信息。
- 原始问题:如何使用
sed
、awk
或perl
在文件中搜索和替换文字字符串,例如.bashrc
文件中的PS1=<string>
? - 更好的问题:如何使用存储在
/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
代码可以使用双引号或单引号来定义on
和off
。在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=<string>``` 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'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 "s|$old|$new|" /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' 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 "sed" 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 's/old/DummyString/' < ps1old > ps1out; cat ps1out
这种方法通过在占位符old
和占位符new
表示的字面字符串上从左到右测试转义序列模式,并将结果写入文件ps1out
来调试。当转义序列失败时,文件ps1old
或文件ps1new
保持不变;文件ps1out
恢复为相应输入文件的副本;perl
提供有用的调试信息。当转义序列成功时,文件ps1out
显示了“DummyString”作为old
或new
的部分替代,从左到右逐步调试转义模式以系统地隔离语法错误。
在调试占位符old
和new
的字面字符串后,将工作结果传输到带有perl
调用的bash
脚本中,如下所示。
一个改进可能是将更改写入新文件,然后将其移动到目标.bashrc
并保留.bashrc.bak
作为备份。
如果此脚本运行多次,它将用编辑后的版本覆盖备份副本!
Bash脚本调用perl
#!/bin/bash
# 命令行测试将结果写入ps1out而不更改ps1old
# perl -pe 's/#force_color_prompt=yes/force_color_prompt=yes/' < ps1old > ps1out; cat ps1out
# 命令行测试将结果写入ps1out而不更改ps1old
# perl -pe '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\\/' < ps1old > ps1old; cat ps1out
# 如果脚本运行两次或更多次,它将覆盖以前的.bashrc.bak!
# 使用-i.bak开关备份目标.bashrc,以在原地取消注释文件!
perl -i.bak -pe 's/#force_color_prompt=yes/force_color_prompt=yes/' .bashrc
# 在测试PS1转义模式后,将旧的PS1替换为新的PS1
perl -i -pe '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\\/' .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 's/old/DummyString/' < ps1old > ps1out; cat ps1out
# ~/ps1old
#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
perl 's/new/DummyString/' < ps1new > ps1out; cat ps1out
# ~/ps1new
#force_color_prompt=yes
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\n\[3[01;32m\]\u@\h\[3[00m\]\[3[01;34m\] $(pwd -P) \[3[00m\]\n$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$ '
fi
perl 's/old/new/' < ps1old > ps1out; cat ps1out
Command line solution with test input file ps1old
writes result to ps1out
perl -pe '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\\/' < ps1old > 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 's/#force_color_prompt=yes/force_color_prompt=yes/' < ps1old > ps1out; cat ps1out
# Command line test writes result to ps1out without changing ps1old
# perl -pe '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\\/' < ps1old > 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 's/#force_color_prompt=yes/force_color_prompt=yes/' .bashrc
# Replace old PS1 with new PS1 after testing PS1 escape pattern on command line
perl -i -pe '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\\/' .bashrc
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论