如何将变量打印到一列中

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

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=&quot;\t&quot; }
!NF { next }
{ gsub(/^[[:space:]]+|[[:space:]]+$/,&quot;&quot;) }
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&lt;=numRows; rowNr++) {
        for ( colNr=1; colNr&lt;=numCols; colNr++ ) {
            printf &quot;%s%s&quot;, vals[rowNr,colNr], (colNr&lt;numCols ? OFS : ORS)
        }
    }
}

<p>

$ awk -f tst.awk file | column -s $&#39;\t&#39; -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&lt;=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=&#39;&#39; -v h=&#39;Installed Products:&#39; &#39;
  {gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,&quot;&quot;)}
  NR%3==2 {p=$0} NR%3==0 {printf(&quot;%-25s%-35s%s\n&quot;, h, p, $0); h=&quot;&quot;}&#39; 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.*)?$/,&quot;&quot;)) 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=&quot;&quot;).

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=&#39;&#39; -v h=&#39;Installed Products:&#39; &#39;
  {gsub(/^[[:space:]]*|[[:space:]]*(\n.*)?$/,&quot;&quot;)}
  NR%3==2 {p[++n]=$0; mp=length&gt;mp?length:mp} NR%3==0 {q[n]=$0}
  END {mh=length(h)
       for(i=1;i&lt;=n;i++)
         printf(&quot;%-*s%-*s%s\n&quot;, mh+1, i==1?h:&quot;&quot;, mp+1, p[i], q[i])}&#39; 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' 的文件中以进行一些测试,您不需要使用 trgrep,只需使用 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 - 替代命令。让我们仔细看看它们:

  1. /^\s*$|^-|\(.*\)|^Ins/d 此命令由多个正则表达式模式组成,用于匹配不需要的字符串,所有匹配的字符串都将被删除:

    • ^\s*$ - 将匹配只包含空格字符的字符串
    • ^- - 将匹配以 '-' 开头的字符串
    • \(.*\) - 将匹配带有括号的字符串
    • ^Ins - 将匹配以 'Ins' 开头的字符串
  2. 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 &#39;/^\s*$|^-|\(.*\)|^Ins/d;s/ //g&#39; f
DesktopApplicationsModule
Registered
ServerApplicationsModule
Registered
BasesystemModule
Registered
LegacyModule
NotRegistered

Now to pretty print this lets use printf:

$ printf &#39;%-30s%s\n&#39; $(sed -r &#39;/^\s*$|^-|\(.*\)|^Ins/d;s/ //g&#39; 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 &#39;/^\s*$|^-|\(.*\)|^Ins/d;s/ //g&#39; f) )

# print initial text and first 2 items from array
printf &#39;Installed Products : %-30s%s\n&#39; ${arr[@]:0:2}

# print the rest, loop over the rest of array
for ((i=2; i&lt;${#arr[@]}; i+=2)); {
	# empty valued added here⤵ to propper alighnment
	printf &#39;%21s%-30s%s\n&#39;   &#39;&#39;   ${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:

  1. /^\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'
  1. 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 &#39;/^\s*$|^-|\(.*\)|^Ins/d;s/ /_SPS_/g&#39; f) )
arr=( &quot;${arr[@]//_SPS_/&#39; &#39;}&quot; ) # change _SPS_ back to space in whole array

# print initial text and first 2 item from array
printf &#39;Installed Products : %-30s%s\n&#39; &quot;${arr[@]:0:2}&quot;

# print the rest, loop over the rest of array
for ((i=2;i&lt;${#arr[@]}; i+=2)); {
	# empty valued added here to propper alighnment
	printf &#39;%21s%-30s%s\n&#39; &#39;&#39; &quot;${arr[@]:i:2}&quot;
}

Notice that now I'm wrapping ${arr[@]:i:2} in &quot; 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 中。你不需要单独调用 trgrep

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 将此行放入 hold 缓冲区(擦除任何先前的内容)
/Module$/H 添加此行到 Hold 缓冲区
/Registered/{ 在此行上打开一组命令:
H 添加到 Hold 缓冲区
gHold 缓冲区内容替换模式缓冲区
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 &#39;/Installed Products:/h; /Module$/H; 
          /Registered/{ H; g; s/ //g; s/\n/\t/g; p; z; x; }&#39; |
  column --table --separator $&#39;\t&#39; 

or, collapsed to a denser single line,

sudo /usr/bin/SUSEConnect --status-text|sed -n &#39;/Installed Products:/h; /Module$/H; /Registered/{H;g;s/ //g;s/\n/\t/g;p;z;x;}&#39;|column -ts $&#39;\t&#39; 

To see it in action I just manually generated the output from your example:

$: echo &quot;
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

&quot; | sed -n &#39;/Installed Products:/h; /Module$/H; 
            /Registered/{ H; g; s/ //g; s/\n/\t/g; p; z; x; }&#39; |
    column --table --separator $&#39;\t&#39; 
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 hold buffer (wipes any previous content)
/Module$/H add this line to the Hold buffer
/Registered/{ open a block of commands on this line:
H add to the Hold 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 print the edited pattern buffer
z zap (empty) the pattern buffer - GNU specific
x exchange 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 &#39;
  /Installed Products:/h; /Module$|[(]/H;
  /Registered/{ H; g; s/ //g; s/\n/\t/g; p; s/.//g; x; }
&#39; | column --table --separator $&#39;\t&#39;
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.

huangapple
  • 本文由 发表于 2023年7月13日 18:51:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76678549.html
匿名

发表评论

匿名网友

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

确定