英文:
How to print a varriable to one column
问题
可以有人帮助我吗?
我正在尝试打印一个命令的输出,用于检查SuSE产品的注册情况。
命令输出(/usr/bin/SUSEConnect --status-text):
已安装的产品:
桌面应用程序模块
(sle-module-desktop-applications/15.2/x86_64)
已注册
服务器应用程序模块
(sle-module-server-applications/15.2/x86_64)
已注册
基础系统模块
(sle-module-basesystem/15.2/x86_64)
已注册
遗留模块
(sle-module-legacy/15.2/x86_64)
未注册
我只过滤了它
sudo /usr/bin/SUSEConnect --status-text | sed -r '/^\s*$/d' | tr -d '[:blank:]' | grep -Ev "^-|InstalledProducts"
桌面应用程序模块
(sle-module-desktop-applications/15.2/x86_64)
已注册
服务器应用程序模块
(sle-module-server-applications/15.2/x86_64)
已注册
基础系统模块
(sle-module-basesystem/15.2/x86_64)
已注册
遗留模块
(sle-module-legacy/15.2/x86_64)
未注册
但我想删除星号(*)并打印如下:
已安装的产品: 桌面应用程序模块 已注册
服务器应用程序模块 已注册
基础系统模块 已注册
遗留模块 未注册
谢谢。
英文:
Can anybody help me please?
I am trying to print output of a command output for a bash script to check the SuSE product registration
command output (/usr/bin/SUSEConnect --status-text)
Installed Products:
------------------------------------------
Desktop Applications Module
(sle-module-desktop-applications/15.2/x86_64)
Registered
------------------------------------------
Server Applications Module
(sle-module-server-applications/15.2/x86_64)
Registered
------------------------------------------
Basesystem Module
(sle-module-basesystem/15.2/x86_64)
Registered
------------------------------------------
Legacy Module
(sle-module-legacy/15.2/x86_64)
Not Registered
------------------------------------------
I just filtered it
sudo /usr/bin/SUSEConnect --status-text|sed -r '/^\s*$/d'|tr -d '[:blank:]'|grep -Ev "^-|InstalledProducts"
DesktopApplicationsModule
(sle-module-desktop-applications/15.2/x86_64)
Registered
ServerApplicationsModule
(sle-module-server-applications/15.2/x86_64)
Registered
BasesystemModule
(sle-module-basesystem/15.2/x86_64)
Registered
LegacyModule
(sle-module-legacy/15.2/x86_64)
NotRegistered
But I want to remove the lines (*) and print like
Installed Products : DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
Thanks,
答案1
得分: 2
使用任何 POSIX 的 awk
加上 column
:
$ cat tst.awk
BEGIN { OFS="\t" }
!NF { next }
{ gsub(/^[[:space:]]+|[[:space:]]+$/,"") }
NR==1 { vals[1,1] = $0; next }
/^---$/ { ++numRows; colNr=1; next }
{
vals[numRows,++colNr] = $0
numCols = colNr
}
END {
numRows -= (colNr == 1)
for ( rowNr=1; rowNr<=numRows; rowNr++) {
for ( colNr=1; colNr<=numCols; colNr++ ) {
printf "%s%s", vals[rowNr,colNr], (colNr<numCols ? OFS : ORS)
}
}
}
$ awk -f tst.awk file | column -s $'\t' -t
Installed Products: Desktop Applications Module (sle-module-desktop-applications/15.2/x86_64) Registered
Server Applications Module (sle-module-server-applications/15.2/x86_64) Registered
Basesystem Module (sle-module-basesystem/15.2/x86_64) Registered
Legacy Module (sle-module-legacy/15.2/x86_64) Not Registered
如果你喜欢的话,你可以选择只打印一些列而不是全部,例如:
for ( rowNr=1; rowNr<=numRows; rowNr++) {
print vals[rowNr,1], vals[rowNr,2], vals[rowNr,4]
}
英文:
Using any POSIX awk
plus column
:
$ cat tst.awk
BEGIN { OFS="\t" }
!NF { next }
{ gsub(/^[[:space:]]+|[[:space:]]+$/,"") }
NR==1 { vals[1,1] = $0; next }
/^-+$/ { ++numRows; colNr=1; next }
{
vals[numRows,++colNr] = $0
numCols = colNr
}
END {
numRows -= (colNr == 1)
for ( rowNr=1; rowNr<=numRows; rowNr++) {
for ( colNr=1; colNr<=numCols; colNr++ ) {
printf "%s%s", vals[rowNr,colNr], (colNr<numCols ? OFS : ORS)
}
}
}
<p>
$ awk -f tst.awk file | column -s $'\t' -t
Installed Products: Desktop Applications Module (sle-module-desktop-applications/15.2/x86_64) Registered
Server Applications Module (sle-module-server-applications/15.2/x86_64) Registered
Basesystem Module (sle-module-basesystem/15.2/x86_64) Registered
Legacy Module (sle-module-legacy/15.2/x86_64) Not Registered
You can choose to only print some columns instead of all of them if you prefer, e.g.:
for ( rowNr=1; rowNr<=numRows; rowNr++) {
print vals[rowNr,1], vals[rowNr,2], vals[rowNr,4]
}
答案2
得分: 1
以下是代码段的翻译部分:
应该适用于任何 POSIX `awk`(已测试过 GNU `awk` 和 macOS 附带的 BSD `awk`):
```bash
$ awk -v RS='' -v h='Installed Products:' '
{gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,"")}
NR%3==2 {p=$0} NR%3==0 {printf("%-25s%-35s%s\n", h, p, $0); h=""}' file
Installed Products: Desktop Applications Module Registered
Server Applications Module Registered
Basesystem Module Registered
Legacy Module Not Registered
调整输出字段宽度(25,35)以适应实际数据。
解释:将 RS
设置为空字符串时,记录分隔符为一个或多个空行。所有记录都经过预处理(gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,"")
)以保留只有第一行,去除前导和尾随空格的内容。对于第2、5、8...条记录(应用程序名称),我们将记录存储在变量 p
中。对于第3、6、9...条记录(注册状态),我们打印当前应用程序的输出:
- 存储在变量
h
中的标题,左对齐占用25个字符, - 存储在变量
p
中的应用程序名称,左对齐占用35个字符, - 注册状态,即当前记录(
$0
)。
在第一次 printf
后,我们清除标题(h=""
)。
如果您希望自动计算输出字段宽度,我们必须等到输入结束后才能打印,因为我们需要知道最长的应用程序名称:
$ awk -v RS='' -v h='Installed Products:' '
{gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,"")}
NR%3==2 {p[++n]=$0; mp=length>mp?length:mp} NR%3==0 {q[n]=$0}
END {mh=length(h)
for(i=1;i<=n;i++)
printf("%-*s%-*s%s\n", mh+1, i==1?h:"", mp+1, p[i], q[i])}' file
Installed Products: Desktop Applications Module Registered
Server Applications Module Registered
Basesystem Module Registered
Legacy Module Not Registered
注意:正如其他答案建议的那样,我们也可以使用 column
实用程序进行最终格式化,但是我们需要为 column
的输入选择一个字段分隔符,该分隔符在您的主要输入中找不到。除非您知道这样的分隔符并且确保它在您的主要输入中绝对找不到,否则只使用 awk
来完成整个操作可能更安全。
英文:
The following should work with any POSIX awk
(tested with GNU awk
and the BSD awk
that comes with macOS):
$ awk -v RS='' -v h='Installed Products:' '
{gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,"")}
NR%3==2 {p=$0} NR%3==0 {printf("%-25s%-35s%s\n", h, p, $0); h=""}' file
Installed Products: Desktop Applications Module Registered
Server Applications Module Registered
Basesystem Module Registered
Legacy Module Not Registered
Adapt the output field widths (25, 35) to your actual data.
Explanations: with RS
set to the null string the record separator is one or more blank lines. All records are preprocessed (gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,"")
) to keep only the first line with leading and trailing spaces removed. For records 2, 5, 8... (the application name) we store the record in variable p
. For records 3, 6, 9... (the registration status) we print the output for the current application:
- the header stored in variable
h
, left-aligned on 25 characters, - the application name stored in variable
p
, left-aligned on 35 characters, - the registration status, that is, the current record (
$0
).
After the first printf
we clear the header (h=""
).
If you prefer to automatically compute the output field widths we must wait until the end of the input before we can print because we need to know the longest application name:
$ awk -v RS='' -v h='Installed Products:' '
{gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,"")}
NR%3==2 {p[++n]=$0; mp=length>mp?length:mp} NR%3==0 {q[n]=$0}
END {mh=length(h)
for(i=1;i<=n;i++)
printf("%-*s%-*s%s\n", mh+1, i==1?h:"", mp+1, p[i], q[i])}' file
Installed Products: Desktop Applications Module Registered
Server Applications Module Registered
Basesystem Module Registered
Legacy Module Not Registered
Note: as other answers suggest we could also use the column
utility for the final formatting but we would then need to chose a field separator for the input of column
that cannot be found in your primary input. Unless you know such a separator and are 100% sure it cannot be found in your primary input, doing the whole stuff with just awk
is maybe a bit safer.
答案3
得分: 0
我已经将您的命令输出复制到一个名为 'f' 的文件中以进行一些测试,您不需要使用 tr
和 grep
,只需使用 sed
就可以删除所有不必要的内容:
$ sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ //g' f
DesktopApplicationsModule
Registered
ServerApplicationsModule
Registered
BasesystemModule
Registered
LegacyModule
NotRegistered
现在让我们使用 printf
来漂亮地打印这些:
$ printf '%-30s%s\n' $(sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ //g' f)
DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
看起来很酷),为了使它符合您的要求,让我们这样做:
# 将所有数据添加到数组中
arr=( $(sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ //g' f) )
# 打印初始文本和数组中的前两个项目
printf '已安装产品:%-30s%s\n' "${arr[@]:0:2}"
# 打印其余部分,循环遍历数组的其余部分
for ((i=2; i<${#arr[@]}; i+=2)); do
# 在此处添加空值以确保对齐
printf '%21s%-30s%s\n' '' "${arr[@]:i:2}"
done
结果:
已安装产品:DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
更新,这是 sed
规则 /^\s*$|^-|\(.*\)|^Ins/d;s/ //g
的解释。有两个通过 ;
分隔的 sed
命令。第一个 /.../d
- 删除命令,第二个 s///g
- 替代命令。让我们仔细看看它们:
-
/^\s*$|^-|\(.*\)|^Ins/d
此命令由多个正则表达式模式组成,用于匹配不需要的字符串,所有匹配的字符串都将被删除:^\s*$
- 将匹配只包含空格字符的字符串^-
- 将匹配以 '-' 开头的字符串\(.*\)
- 将匹配带有括号的字符串^Ins
- 将匹配以 'Ins' 开头的字符串
-
s/ //g
将所有空格替换为无(移除空格),我添加了这个因为 OP 在描述中提供的期望输出中没有空格。
但根据 OP 的评论,空格必须保留,让我们这样做。看起来我们只需要删除第二个 s/ //g
命令?不,这会破坏输出:
已安装产品:Desktop Applications
Module Registered
Server Applications
Module Registered
Basesystem Module
Registered Legacy
Module Not
Registered
相反,我们需要将其更改(替换)为某种模式,例如 _SPS_
,以在数组中创建正确数量的项,然后将其改回为空格。脚本如下:
# 将所有数据添加到数组中
arr=( $(sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ /_SPS_/g' f) )
arr=( "${arr[@]//_SPS_/ }" ) # 在整个数组中将 _SPS_ 更改回空格
# 打印初始文本和数组中的前两个项目
printf '已安装产品:%-30s%s\n' "${arr[@]:0:2}"
# 打印其余部分,循环遍历数组的其余部分
for ((i=2;i<${#arr[@]}; i+=2)); do
# 在此处添加空值以确保对齐
printf '%21s%-30s%s\n' '' "${arr[@]:i:2}"
done
请注意,现在我将 ${arr[@]:i:2}
包装在 ""
中是必不可少的,因为现在我们有带有空格的项。以下是输出:
已安装产品: Desktop Applications Module Registered
Server Applications Module Registered
Basesystem Module Registered
Legacy Module Not Registered
英文:
I've copied your command output to a file 'f' to make some tests, you don't need tr
and grep
, sed
alone can remove all unnecessary stuff:
$ sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ //g' f
DesktopApplicationsModule
Registered
ServerApplicationsModule
Registered
BasesystemModule
Registered
LegacyModule
NotRegistered
Now to pretty print this lets use printf
:
$ printf '%-30s%s\n' $(sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ //g' f)
DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
Looks cool) and to make it the way you wanted lets do this:
# add all data to an array
arr=( $(sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ //g' f) )
# print initial text and first 2 items from array
printf 'Installed Products : %-30s%s\n' ${arr[@]:0:2}
# print the rest, loop over the rest of array
for ((i=2; i<${#arr[@]}; i+=2)); {
# empty valued added here⤵ to propper alighnment
printf '%21s%-30s%s\n' '' ${arr[@]:i:2}
}
Result:
Installed Products : DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
Update, here is the sed rule /^\s*$|^-|\(.*\)|^Ins/d;s/ //g
explanation. There are 2 sed commands separated via ';'. First /.../d
- delete command and second s///g
- substitute command. Lets take a closer look at them:
/^\s*$|^-|\(.*\)|^Ins/d
this command is consists of several regex patterns to match unwanted strings, all matched strings would be removed:
^\s*$
- will match strings of 'space' characters^-
- will match strings starting from '-'\(.*\)
- will match strings with '()'^Ins
- will match strings starting with 'Ins'
s/ //g
substitute all spaces with nothing(remove spaces) I've
added this coz OP has the desired output in description without spaces.
But according to the OP's comment spaces must be preserved, lets do this. Looks like we only need to remove 2-nd sed command s/ //g
? Nope this will ruin the output:
Installed Products : Desktop Applications
Module Registered
Server Applications
Module Registered
Basesystem Module
Registered Legacy
Module Not
Registered
Instead we need to change(substitute) it to some pattern for example _SPS_
to create popper amount of items in array and then change it back to spaces. The script will be like this:
# add all data to an array
arr=( $(sed -r '/^\s*$|^-|\(.*\)|^Ins/d;s/ /_SPS_/g' f) )
arr=( "${arr[@]//_SPS_/' '}" ) # change _SPS_ back to space in whole array
# print initial text and first 2 item from array
printf 'Installed Products : %-30s%s\n' "${arr[@]:0:2}"
# print the rest, loop over the rest of array
for ((i=2;i<${#arr[@]}; i+=2)); {
# empty valued added here to propper alighnment
printf '%21s%-30s%s\n' '' "${arr[@]:i:2}"
}
Notice that now I'm wrapping ${arr[@]:i:2}
in "
this is essential coz now we've got items with spaces. And here is the output:
Installed Products : Desktop Applications Module Registered
Server Applications Module Registered
Basesystem Module Registered
Legacy Module Not Registered
答案4
得分: 0
简化你的管道,使用更多的逻辑在你的 sed
中。你不需要单独调用 tr
和 grep
。
sudo /usr/bin/SUSEConnect --status-text |
sed -n '/Installed Products:/h; /Module$/H;
/Registered/{ H; g; s/ //g; s/\n/\t/g; p; z; x; }' |
column --table --separator $'\t'
或者,将其折叠成更密集的单行:
sudo /usr/bin/SUSEConnect --status-text|sed -n '/Installed Products:/h; /Module$/H; /Registered/{H;g;s/ //g;s/\n/\t/g;p;z;x;}'|column -ts $'\t'
要查看它的运行情况,我只是手动从你的示例中生成了输出:
$: echo "
Installed Products:
------------------------------------------
Desktop Applications Module
(sle-module-desktop-applications/15.2/x86_64)
Registered
------------------------------------------
Server Applications Module
(sle-module-server-applications/15.2/x86_64)
Registered
------------------------------------------
Basesystem Module
(sle-module-basesystem/15.2/x86_64)
Registered
------------------------------------------
Legacy Module
(sle-module-legacy/15.2/x86_64)
Not Registered
" | sed -n '/Installed Products:/h; /Module$/H;
/Registered/{ H; g; s/ //g; s/\n/\t/g; p; z; x; }' |
column --table --separator $'\t'
InstalledProducts: DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
稍微分解一下 -
-n
不打印除非我说的。
/Installed Products:/h
将此行放入 h
old 缓冲区(擦除任何先前的内容)
/Module$/H
添加此行到 H
old 缓冲区
/Registered/{
在此行上打开一组命令:
H
添加到 H
old 缓冲区
g
用 H
old 缓冲区内容替换模式缓冲区
s/ //g
移除所有空格
s/\n/\t/g
将换行符替换为制表符
p
打印编辑后的模式缓冲区
z
清空模式缓冲区 - GNU 特有
x
交换模式和保持缓冲区(清空保持缓冲区)
}
关闭代码块
如果不使用 GNU sed
,请将 z
替换为 s/.//g
,并且可能需要将所有这些命令放在不同的行上,而不是只用分号分隔。
如果你想包含名称,添加 -E
并将 /Module$/H
更改为 /Module$|[(]/H
-
sudo /usr/bin/SUSEConnect --status-text | sed -En '
/Installed Products:/h; /Module$|[(]/H;
/Registered/{ H; g; s/ //g; s/\n/\t/g; p; s/.//g; x; }
' | column --table --separator $'\t'
InstalledProducts: DesktopApplicationsModule (sle-module-desktop-applications/15.2/x86_64) Registered
ServerApplicationsModule (sle-module-server-applications/15.2/x86_64) Registered
BasesystemModule (sle-module-basesystem/15.2/x86_64) Registered
LegacyModule (sle-module-legacy/15.2/x86_64) NotRegistered
正如 Renaud 指出的那样,使用 column
需要使用不在你的输入中的字段分隔符。在尝试时,我成功地使用了回车 (\r
) 和响铃字符 (\a
)。但这可能因系统而异。
英文:
Shorten your pipeline with a little more logic in your sed
.
You don't need separate calls to tr
and grep
.
sudo /usr/bin/SUSEConnect --status-text |
sed -n '/Installed Products:/h; /Module$/H;
/Registered/{ H; g; s/ //g; s/\n/\t/g; p; z; x; }' |
column --table --separator $'\t'
or, collapsed to a denser single line,
sudo /usr/bin/SUSEConnect --status-text|sed -n '/Installed Products:/h; /Module$/H; /Registered/{H;g;s/ //g;s/\n/\t/g;p;z;x;}'|column -ts $'\t'
To see it in action I just manually generated the output from your example:
$: echo "
Installed Products:
------------------------------------------
Desktop Applications Module
(sle-module-desktop-applications/15.2/x86_64)
Registered
------------------------------------------
Server Applications Module
(sle-module-server-applications/15.2/x86_64)
Registered
------------------------------------------
Basesystem Module
(sle-module-basesystem/15.2/x86_64)
Registered
------------------------------------------
Legacy Module
(sle-module-legacy/15.2/x86_64)
Not Registered
" | sed -n '/Installed Products:/h; /Module$/H;
/Registered/{ H; g; s/ //g; s/\n/\t/g; p; z; x; }' |
column --table --separator $'\t'
InstalledProducts: DesktopApplicationsModule Registered
ServerApplicationsModule Registered
BasesystemModule Registered
LegacyModule NotRegistered
Broken out a little -
-n
don't print unless I say so.
/Installed Products:/h
put this line in the h
old buffer (wipes any previous content)
/Module$/H
add this line to the H
old buffer
/Registered/{
open a block of commands on this line:
H
add to the H
old buffer
g
replace the pattern buffer with the hold buffer contents
s/ //g
remove all spaces
s/\n/\t/g
replace newlines with tabs
p
p
rint the edited pattern buffer
z
z
ap (empty) the pattern buffer - GNU specific
x
e
xchange the pattern and hold buffers (empties the hold)
}' close the code block
If not using GNU sed
, replace the z
with s/.//g
, and you might need all these commands on different lines instead of just separated with semicolons.
If you wanted to include the names, add -E
and change /Module$/H
to /Module$|[(]/H
-
sudo /usr/bin/SUSEConnect --status-text | sed -En '
/Installed Products:/h; /Module$|[(]/H;
/Registered/{ H; g; s/ //g; s/\n/\t/g; p; s/.//g; x; }
' | column --table --separator $'\t'
InstalledProducts: DesktopApplicationsModule (sle-module-desktop-applications/15.2/x86_64) Registered
ServerApplicationsModule (sle-module-server-applications/15.2/x86_64) Registered
BasesystemModule (sle-module-basesystem/15.2/x86_64) Registered
LegacyModule (sle-module-legacy/15.2/x86_64) NotRegistered
As Renaud pointed out, using column
does require you use a field separator not in your input. While playing with this I made it work with a carriage return (\r
) and a bell character (\a
). YMMV.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论