无需 sed 或 awk 即可轻松从输出中获取特定列

无需 sed 或 awk 即可轻松从输出中获取特定列

sed有没有比使用and更快地获取几列值的方法awk

例如,如果我有输出ls -hal /并且我只想获取文件和目录名称和大小,我该如何容易地迅速地这样做,而不必花几分钟调整我的命令。

total 16078
drwxr-xr-x    33 root  wheel   1.2K Aug 13 16:57 .
drwxr-xr-x    33 root  wheel   1.2K Aug 13 16:57 ..
-rw-rw-r--     1 root  admin    15K Aug 14 00:41 .DS_Store
d--x--x--x     8 root  wheel   272B Jun 20 16:40 .DocumentRevisions-V100
drwxr-xr-x+    3 root  wheel   102B Mar 27 12:26 .MobileBackups
drwx------     5 root  wheel   170B Jun 20 15:56 .Spotlight-V100
d-wx-wx-wt     2 root  wheel    68B Mar 27 12:26 .Trashes
drwxrwxrwx     4 root  wheel   136B Mar 30 20:00 .bzvol
srwxrwxrwx     1 root  wheel     0B Aug 13 16:57 .dbfseventsd
----------     1 root  admin     0B Aug 16  2012 .file
drwx------  1275 root  wheel    42K Aug 14 00:05 .fseventsd
drwxr-xr-x@    2 root  wheel    68B Jun 20  2012 .vol
drwxrwxr-x+  289 root  admin   9.6K Aug 13 10:29 Applications
drwxrwxr-x     7 root  admin   238B Mar  5 20:47 Developer
drwxr-xr-x+   69 root  wheel   2.3K Aug 12 21:36 Library
drwxr-xr-x@    2 root  wheel    68B Aug 16  2012 Network
drwxr-xr-x+    4 root  wheel   136B Mar 27 12:17 System
drwxr-xr-x     6 root  admin   204B Mar 27 12:22 Users
drwxrwxrwt@    6 root  admin   204B Aug 13 23:57 Volumes
drwxr-xr-x@   39 root  wheel   1.3K Jun 20 15:54 bin
drwxrwxr-t@    2 root  admin    68B Aug 16  2012 cores
dr-xr-xr-x     3 root  wheel   4.8K Jul  6 13:08 dev
lrwxr-xr-x@    1 root  wheel    11B Mar 27 12:09 etc -> private/etc
dr-xr-xr-x     2 root  wheel     1B Aug 12 21:41 home
-rw-r--r--@    1 root  wheel   7.8M May  1 20:57 mach_kernel
dr-xr-xr-x     2 root  wheel     1B Aug 12 21:41 net
drwxr-xr-x@    6 root  wheel   204B Mar 27 12:22 private
drwxr-xr-x@   68 root  wheel   2.3K Jun 20 15:54 sbin
lrwxr-xr-x@    1 root  wheel    11B Mar 27 12:09 tmp -> private/tmp
drwxr-xr-x@   13 root  wheel   442B Mar 29 23:32 usr
lrwxr-xr-x@    1 root  wheel    11B Mar 27 12:09 var -> private/var

我意识到有无数的选择ls,我可能会做到对于这个特定的例子那样,但这是一个普遍的问题,我想要一个通用的解决方案来轻松快速地获取特定的列。

cut不会削减它,因为它不需要正则表达式,而且我几乎从未遇到过用单个空格分隔列的情况。如果它能工作的话那就完美了:

ls -hal / | cut -d'\s' -f5,9

awk并且sed比我想要的更通用,基本上是整个语言本身。我没有反对他们,只是除非我最近和他们一起做了很多事情,否则需要相当大的心理转变才能开始用他们的方式思考并写出一些有用的东西。我通常正在思考我试图解决的其他问题,突然必须解决一个sed/awk问题分散了我的注意力。

有没有灵活的捷径来实现我想要的?

答案1

我不知道为什么

ls -hal / | awk '{print $5, $9}'

在你看来,这对你的思维过程的破坏性比

ls -hal / | cut -d'\s' -f5,9

如果它有效的话,就会如此。你真的需要把它写下来吗?只需几行即可自动awk添加。 {}(对我来说,最难的问题是记住哪个字段号对应于哪条数据,但也许您没有这个问题。)

你不必使用全部awk 的功能;为了简单地输出特定列,您需要了解很少的 awk。

令人恼火的问题是,如果您想要输出符号链接以及文件名,或者您的文件名中可能包含空格。 (或者,更糟糕的是,换行符)。通过假设的正则表达式感知剪切,这不是问题(换行符除外);你只需替换-f5,9-f5,9-.但是,“从字段 9 到末尾”没有 awk 语法,您必须记住如何编写 for 循环。

这是一个小 shell 脚本,它将cut-style-f选项转换为 awk 程序,然后运行 ​​awk 程序。它需要更好的错误检查,但它似乎有效。 (额外的好处:-d通过将选项传递给 awk 程序来处理该选项。)

#!/bin/bash
prog=\{
while getopts f:d: opt; do
  case $opt in
    f) IFS=, read -ra fields <<<"$OPTARG"
       for field in "${fields[@]}"; do
         case $field in
           *-*) low=${field%-*}; high=${field#*-}
                if [[ -z $low  ]]; then low=1; fi
                if [[ -z $high ]]; then high=NF; fi
                ;;
            "") ;;
             *) low=$field; high=$field ;;
         esac
         if [[ $low == $high ]]; then
           prog+='printf "%s ", $'$low';'
         else
           prog+='for (i='$low';i<='$high';++i) printf "%s ", $i;'
         fi
       done
       prog+='printf "\n"}'
       ;;
    d) sep="-F$OPTARG";;
    *) exit 1;;
  esac
done
if [[ -n $sep ]]; then
  awk "$sep" "$prog"
else
  awk "$prog"
fi

快速测试:

$ ls -hal / | ./cut.sh -f5,9-
7.0K bin 
5.0K boot 
4.2K dev 
9.0K etc 
1.0K home 
8.0K host 
33 initrd.img -> /boot/initrd.img-3.2.0-51-generic 
33 initrd.img.old -> /boot/initrd.img-3.2.0-49-generic 
...

答案2

我相信没有比 sed 或 awk 更简单的解决方案了。但你可以编写自己的函数。

这是列表功能(复制粘贴到您的终端):

function list() { ls -hal $1 | awk '{printf "%-10s%-30s\n", $5, $9}'; }

然后使用列表函数:

list /

list /etc

答案3

您不能只谈论“列”而不解释列是什么!

在 unix 文本处理中很常见的是使用空格作为列(字段)分隔符和(自然)换行符作为行或记录分隔符。然后awk是一个优秀的工具,也非常可读:

# for words (columns) 5 and 9:
ls -lah | awk '{print $5 " " $9}'
# or this, for the fifth and the last word:
ls -lah | awk '{print $5 " " $NF}'

如果列按字符排序,也许cut -c会更好。

ls -lah | cut -c 31-33,46-

您可以awk通过该选项告诉使用其他字段分隔符-F。如果您不将-c(或-b) 与 一起使用cut,请使用-f来指定要输出的列。

诀窍在于对输入的了解

一般来说,使用文本处理工具解析 、 和类似工具的输出并不总是一个好主意,至少如果您希望可移植/兼容的话lsdf不是。ps在这些情况下,请尝试强制以 POSIX 定义的格式输出。有时,这可以通过将某个选项(-P也许)传递给生成输出的命令来实现。有时通过设置环境变量(例如)POSIXLY_CORRECT或调用特定的二进制文件(例如/usr/xpg4/bin/ls.

答案4

我很惊讶没有人写过这个,但如果您唯一的反对意见cut是它不会将重复的空格作为单个分隔符处理,那么您只需挤压重复的空格怎么样?这就是 的用途之一tr

ls -l | tr -s ' ' | cut -d ' ' -f5,9

鉴于ls -l您的问题中显示的输出,结果将是:

1.2K .
1.2K ..
15K .DS_Store
272B .DocumentRevisions-V100
102B .MobileBackups
170B .Spotlight-V100
68B .Trashes
136B .bzvol
0B .dbfseventsd
0B .file
42K .fseventsd
68B .vol
9.6K Applications
238B Developer
2.3K Library
68B Network
136B System
204B Users
204B Volumes
1.3K bin
68B cores
4.8K dev
11B etc
1B home
7.8M mach_kernel
1B net
204B private
2.3K sbin
11B tmp
442B usr
11B var

相关内容