Bash 如何从命令的输出中获取特定元素,然后以表格格式显示?

Bash 如何从命令的输出中获取特定元素,然后以表格格式显示?,bash,macos,awk,terminal,Bash,Macos,Awk,Terminal,我倾向于使用macos命令diskutil info-所有这些都是经常使用的,它有比我通常想要的更多的信息,并且它以一种列表类型的格式显示它,并且连接了19个硬盘驱动器,这意味着大量的滚动。我试图从diskutil info-all的输出中只提取四位信息,然后将其格式化为一个包含四列的表 我正在使用grep获取我想要的确切信息: diskutil info-all | grep-e设备标识符:-e设备节点:-e卷名称:-e卷UUID: 结果是这样的: Device Identifier:

我倾向于使用macos命令diskutil info-所有这些都是经常使用的,它有比我通常想要的更多的信息,并且它以一种列表类型的格式显示它,并且连接了19个硬盘驱动器,这意味着大量的滚动。我试图从diskutil info-all的输出中只提取四位信息,然后将其格式化为一个包含四列的表

我正在使用grep获取我想要的确切信息:

diskutil info-all | grep-e设备标识符:-e设备节点:-e卷名称:-e卷UUID:

结果是这样的:

   Device Identifier:        disk0
   Device Node:              /dev/disk0
   Volume Name:              Not applicable (no file system)
   Device Identifier:        disk1
   Device Node:              /dev/disk1
   Volume Name:              Not applicable (no file system)
   Device Identifier:        disk1s1
   Device Node:              /dev/disk1s1
   Volume Name:              EFI
   Device Identifier:        disk1s2
   Device Node:              /dev/disk1s2
   Volume Name:              Old-Timemachine-Lion
   Volume UUID:              D04D1888-ABD9-3480-9692-60ECA458C372
   Device Identifier:        disk1s3
   Device Node:              /dev/disk1s3
   Volume Name:              Backup-Lion
   Volume UUID:              8C737824-F790-324B-AC4C-6F398D6CE947
   Device Identifier:        disk1s4
   Device Node:              /dev/disk1s4
   Volume Name:              Old-Timemachine-Garnet
   Volume UUID:              DECBEC2C-ADFD-397E-88B6-B9CC962E00E4
我想去掉第一列,然后将第二列分成四列,每个参数对应一列。上面的输出片段如下所示:

   disk0      /dev/disk0        Not applicable (no file system)
   disk1      /dev/disk1        Not applicable (no file system)
   disk1s1    /dev/disk1s1      EFI
   disk1s2    /dev/disk1s2      Old-Timemachine-Lion                D04D1888-ABD9-3480-9692-60ECA458C372
   disk1s3    /dev/disk1s3      Backup-Lion                         8C737824-F790-324B-AC4C-6F398D6CE947
   disk1s4    /dev/disk1s4      Old-Timemachine-Garnet              ECBEC2C-ADFD-397E-88B6-B9CC962E00E4
我发现我可以使用cut来摆脱第一列,并认为我应该能够使用printf和命令替换来格式化为列。比如:

printf "%14s    %14s    %14s    %14s\n" "$(diskutil info -all | grep -e "Device Identifier:" -e "Device Node:" -e "Volume Name:" -e "Volume UUID:" | cut -c 30-70)"
它可以去掉第一列,但不格式化,就像printf不工作一样。输出如下所示:

disk0
/dev/disk0
Not applicable (no file system)
disk1
/dev/disk1
Not applicable (no file system)
disk1s1
/dev/disk1s1
EFI
disk1s2
/dev/disk1s2
Old-Timemachine-Lion
D04D1888-ABD9-3480-9692-60ECA458C372
disk1s3
/dev/disk1s3
Backup-Lion
8C737824-F790-324B-AC4C-6F398D6CE947
disk1s4
/dev/disk1s4
Old-Timemachine-Garnet
DECBEC2C-ADFD-397E-88B6-B9CC962E00E4
我不擅长shell脚本,所以我怀疑我的方法是最好的,但我在这里遗漏了什么和/或做错了什么?我想学习。

您可以在diskutil info-all | grep-e设备标识符:-e设备节点:-e卷名称:-e卷UUID后使用以下awk命令:

在哪里

BEGIN{FS=:;}在以下位置定义awk的字段分隔符: /设备标识符/{print}在每次到达新设备标识符时打印一个下线 {printf$2}将水平转换垂直磁盘属性 END{printf\n}打印最后一个EOL字符 sed-e的s/\{3,\}/@/g;|column-s'@-t通过管道将其传输到sed和column以正确地重塑其形状,您将获得以下输出: 您可以在diskutil info-all | grep-e设备标识符之后使用以下awk命令:-e设备节点:-e卷名称:-e卷UUID:

在哪里

BEGIN{FS=:;}在以下位置定义awk的字段分隔符: /设备标识符/{print}在每次到达新设备标识符时打印一个下线 {printf$2}将水平转换垂直磁盘属性 END{printf\n}打印最后一个EOL字符 sed-e的s/\{3,\}/@/g;|column-s'@-t通过管道将其传输到sed和column以正确地重塑其形状,您将获得以下输出: 以下awk可能会在同样的情况下帮助您

awk '
/Device Identifier/{
  if(val){
    print val};
  val=$NF
}
/Device Node:/{
  val=val OFS $NF
}
/Volume Name:/{
  sub(/Volume Name:* +/,"");
  val=val OFS $0
}
/Volume UUID:/{
  sub(/Volume UUID:* +/,"");
  val=val OFS $0
}
END{
  if(val){
    print val}
}
'   Input_file
输出如下

disk0 /dev/disk0    Not applicable (no file system)
disk1 /dev/disk1    Not applicable (no file system)
disk1s1 /dev/disk1s1    EFI
disk1s2 /dev/disk1s2    Old-Timemachine-Lion    D04D1888-ABD9-3480-9692-60ECA458C372
disk1s3 /dev/disk1s3    Backup-Lion    8C737824-F790-324B-AC4C-6F398D6CE947
disk1s4 /dev/disk1s4    Old-Timemachine-Garnet    DECBEC2C-ADFD-397E-88B6-B9CC962E00E4
以下awk可能会在同样的情况下帮助您

awk '
/Device Identifier/{
  if(val){
    print val};
  val=$NF
}
/Device Node:/{
  val=val OFS $NF
}
/Volume Name:/{
  sub(/Volume Name:* +/,"");
  val=val OFS $0
}
/Volume UUID:/{
  sub(/Volume UUID:* +/,"");
  val=val OFS $0
}
END{
  if(val){
    print val}
}
'   Input_file
输出如下

disk0 /dev/disk0    Not applicable (no file system)
disk1 /dev/disk1    Not applicable (no file system)
disk1s1 /dev/disk1s1    EFI
disk1s2 /dev/disk1s2    Old-Timemachine-Lion    D04D1888-ABD9-3480-9692-60ECA458C372
disk1s3 /dev/disk1s3    Backup-Lion    8C737824-F790-324B-AC4C-6F398D6CE947
disk1s4 /dev/disk1s4    Old-Timemachine-Garnet    DECBEC2C-ADFD-397E-88B6-B9CC962E00E4

我的建议是使用awk。这正是它的用途。这是一个时髦的工具,但一旦你学会了它,它对所有形式的翻译/制表任务都很有用

下面是您的问题的一个快速尝试:

#!/bin/awk

/Device Identifier:/ { device=$3 }
/Device Node:/ { node=$3 }
/Volume Name:/ { split($0,parts,/:/); part2=parts[2]; sub(/^[ \t]*/,"",part2); name=part2 }
/Volume UUID:/ { uuid=$3 }
/^\*+/ { printf("%-10s %-16s %-40s %s\n",device,node,name,uuid) }
将其放入文件中,然后运行diskutil info-all | awk-f~/Desktop/that_file.awk


这只是冰山一角。awk可以做决定,做数学,等等。这是一个穷人的perl,但我发现当我使用它时,我的头不会爆炸

我的建议是使用awk。这正是它的用途。这是一个时髦的工具,但一旦你学会了它,它对所有形式的翻译/制表任务都很有用

下面是您的问题的一个快速尝试:

#!/bin/awk

/Device Identifier:/ { device=$3 }
/Device Node:/ { node=$3 }
/Volume Name:/ { split($0,parts,/:/); part2=parts[2]; sub(/^[ \t]*/,"",part2); name=part2 }
/Volume UUID:/ { uuid=$3 }
/^\*+/ { printf("%-10s %-16s %-40s %s\n",device,node,name,uuid) }
将其放入文件中,然后运行diskutil info-all | awk-f~/Desktop/that_file.awk

这只是冰山一角。awk可以做决定,做数学,等等。这是一个穷人的perl,但我发现当我使用它时,我的头不会爆炸

在将输入传递给awk之前,我使用sed做了一些预处理。此外,我还将每个字段中的空格替换为下划线,这对于在列-t中提供更一致的结果不是问题

在将输入传递给awk之前,我使用sed做了一些预处理。此外,我还将每个字段中的空格替换为下划线,这对于在列-t中提供更一致的结果不是问题


在diskutils结果中,每行开始前是否有空格?或者它是在SO?中格式化时引入的东西?@sjsam-diskutil在每行的开头加上三个空格,因此awk中的sub/^*/将提供足够的修剪。@sjsam,ghoti是正确的。diskutil在每行的开头插入了三个空格。我并不特别关心他们。也许有一个或两个或三个空间将结果从边缘移开会更漂亮?在diskutils结果中,每行开始之前是否有空间?或者它是在SO?中格式化时引入的东西?@sjsam-diskutil在每行的开头加上三个空格,因此awk中的sub/^*/将提供足够的修剪。@sjsam,ghoti是正确的。diskutil在每行的开头插入了三个空格。我并不特别关心他们。也许有一个或两个或三个空间将结果从边缘移开更漂亮?你注意到这里介绍的标签了吗?例如,不适用

乐?谢谢你的选择!是时候休息一下,吃午饭了;-我已经编辑了这篇文章来纠正这一点,谢谢!似乎每个人都建议使用awk,但没有人提出我所做的与printf不兼容的原因。我有点不高兴。但是,尽管这些解决方案都是awk的变体,但您是唯一一个解释您添加到我最初的命令尝试中的代码的作用的人。我非常感激这一点,因为在没有解释的情况下,代码解决了任务,但没有教会我如何很好地工作,因为我还不知道sed或awk。非常感谢。printf解决方案不起作用的原因是printf在读取其输入方面很幼稚。输入的每个字按顺序与每个格式说明符匹配。Printf不知道不适用,例如,没有文件系统意味着只转到一个%14s格式说明符,不适用。。。被拆分,每个单词依次转到每个%14s。我试图用带有IFS=\t的制表符分隔的输出来欺骗它,并像不适用的无文件系统一样引用它,但这不起作用。你注意到这里介绍的标签了吗?例如不适用?谢谢您的选择!是时候休息一下,吃午饭了;-我已经编辑了这篇文章来纠正这一点,谢谢!似乎每个人都建议使用awk,但没有人提出我所做的与printf不兼容的原因。我有点不高兴。但是,尽管这些解决方案都是awk的变体,但您是唯一一个解释您添加到我最初的命令尝试中的代码的作用的人。我非常感激这一点,因为在没有解释的情况下,代码解决了任务,但没有教会我如何很好地工作,因为我还不知道sed或awk。非常感谢。printf解决方案不起作用的原因是printf在读取其输入方面很幼稚。输入的每个字按顺序与每个格式说明符匹配。Printf不知道不适用,例如,没有文件系统意味着只转到一个%14s格式说明符,不适用。。。被拆分,每个单词依次转到每个%14s。我试图用带有IFS=\t的制表符分隔的输出来欺骗它,并像不适用的无文件系统一样引用它,但这不起作用。ihtoh和@ravindersingh13解析卷名的能力比我强得多。感谢您的帮助。你和@Ravindersingh13都是在awk中完成的。我真的需要学习awk,因为解决方案似乎更简单?详细说明您所做的工作将非常有帮助。很难用500个字符来描述awk,但这里是这样的:awk程序是一系列模式/代码对。左侧的每个图案在每个输入行中再次匹配。如果行与模式匹配,则执行右侧的语句。它使用类似于shell的语言,包含变量、函数、条件、循环等。我的程序非常典型。每个模式都在输出中寻找某些内容。匹配该模式的行执行右侧的代码,该代码只需提取它要查找的值并将其存储在变量中$0是整个输入行,$1是第一个空格分隔的字段,$2是第二个字段,依此类推。方便的是,disktuil在每条记录后吐出一行'*****',因此我使用该模式转储最后一组值。远非完美,但简单且可能有用。我在谷歌上搜索并找到了一本我将在本周末阅读的[awk教程]。为了快速解决这个问题,我在逃离一段时间后将@Allan提供的东西变成了一个别名。我正在考虑将我在所有这些问题的有用答案中看到的内容转换成一个带有表头的bash函数,可能还有一个可选的arugument,以进一步减少输出,只获取其中包含UUID的行,并在执行过程中学习一点awk.:-非常感谢您抽出时间回来详细介绍您的代码。非常感谢。哦,@ravindersingh13解析卷名的能力比我强多了。谢谢你在这方面的帮助。你和@Ravindersingh13都是在awk中完成的。我真的需要学习awk,因为解决方案似乎更简单?详细说明您所做的工作将非常有帮助。很难用500个字符来描述awk,但这里是这样的:awk程序是一系列模式/代码对。左侧的每个图案在每个输入行中再次匹配。如果行与模式匹配,则执行右侧的语句。它使用类似于shell的语言,包含变量、函数、条件、循环等。我的程序非常典型。每个模式都在输出中寻找某些内容。匹配该模式的行执行右侧的代码,该代码只需提取它要查找的值并将其存储在变量中$0是整个输入行,$1是第一个空格分隔的字段,$2是第二个字段,依此类推。方便的是,disktuil在每条记录后面吐出一行'*****',s
o我使用该模式转储最后一组值。远非完美,但简单且可能有用。我在谷歌上搜索并找到了一本我将在本周末阅读的[awk教程]。为了快速解决这个问题,我在逃离一段时间后将@Allan提供的东西变成了一个别名。我正在考虑将我在所有这些问题的有用答案中看到的内容转换成一个带有表头的bash函数,可能还有一个可选的arugument,以进一步减少输出,只获取其中包含UUID的行,并在执行过程中学习一点awk.:-非常感谢您抽出时间回来详细介绍您的代码。非常感谢。谢谢你在这方面的帮助。我不知道sed或awk,所以我没有真正了解代码中发生了什么,这很不幸。我确实需要学习这些明显强大的工具。添加下划线并没有真正困扰我,但同时我不明白它们为什么有用。@CKnight column-t tab根据空格deimiter分隔字段。想想如果一个域中有空格会发生什么。这就是下划线起作用的地方。很好,你正计划在你的剧目中加入sed和awk。它们确实是很棒的工具-再次感谢你。是的,这个周末计划学习一些awk:我还没有在谷歌上搜索到关于sed的教程,但它已经在我的清单上了。谢谢你的帮助。我不知道sed或awk,所以我没有真正了解代码中发生了什么,这很不幸。我确实需要学习这些明显强大的工具。添加下划线并没有真正困扰我,但同时我不明白它们为什么有用。@CKnight column-t tab根据空格deimiter分隔字段。想想如果一个域中有空格会发生什么。这就是下划线起作用的地方。很好,你正计划在你的剧目中加入sed和awk。它们确实是很棒的工具-再次感谢你。是的,这个周末计划学习一些awk:我还没有在谷歌上搜索到关于sed的教程,但它已经在我的清单上了。谢谢你在这方面的帮助。你和@James都是用awk做的。我真的需要学习awk。关于你所做的事情的细节确实会有帮助,因为所有人使用的awk在这一点上对我来说都很模糊。@CKnight,很高兴它帮助了你。别担心,一旦你开始学习,一切都会好起来的。我们在这里每天都在学习,干杯:嗨,我在@James在一篇评论中说,左边是模式,右边是做某事的awk代码的上下文中再次审视你的代码。我猜测右括号里的就是右括号。但是在你的代码底部你说输入文件。这到底是什么?这是diskutil的管道输入结果吗?感谢您的帮助。你和@James都是用awk做的。我真的需要学习awk。关于你所做的事情的细节确实会有帮助,因为所有人使用的awk在这一点上对我来说都很模糊。@CKnight,很高兴它帮助了你。别担心,一旦你开始学习,一切都会好起来的。我们在这里每天都在学习,干杯:嗨,我在@James在一篇评论中说,左边是模式,右边是做某事的awk代码的上下文中再次审视你的代码。我猜测右括号里的就是右括号。但是在你的代码底部你说输入文件。这到底是什么?这是来自diskutil的管道输入结果吗?