如何从bash的subshel​​l输出中修剪空白而不是换行符?

问题描述

这里已经有数十个,也许是一百个或更多个以前的问题似乎与此“相同”,但是经过大量搜索,我发现什至没有什么可以接近的-尽管我确实学到了很多-所以我决定只是RTFM自己解决这个问题。

问题

我想搜索ps auxwww命令的输出以找到感兴趣的进程,问题是我不能只是简单地使用cut从其中查找所需的确切数据。事实证明,ps试图对输出进行纵列,添加多余的空格或制表符,这些空格或制表符会妨碍用户使用cut来获取正确的数据。

因此,由于我不是bash的高手,所以我进行了搜索...发现的答案都集中在这两个变量上-从我的角度来看,这是一种“备份策略”,它本身并不能解决整个问题-否则它们只会修剪前导或尾随空格或所有“空白”(包括换行符)。 nopE,将无法使用!而且,也不会删除尾随的换行符等等。

所以,重申一下,问题是,如何有效地将空白定义为其他字符之间的单个空格而又不消除换行符呢?

下面,我会给出答案,但是我欢迎其他人给出答案-谁知道,也许有人有更好的答案?!

解决方法

答案:

至少我的回答-请也留下您自己的答案! -就是这样做的:

ps auxwww | grep <program> | tr -s [:blank:] | cut -d ' ' -f <field_of_interest>

这很棒!

很显然,有很多方法可以使它适应其他需求。

,

作为所有管道和grepcut的替代,您可以简单地使用awk。将awk与默认的字段分隔符(FS)设置为在空格上断开的好处在于,它将字段之间的任意数量的空格视为单个分隔符。

因此,使用awk可以消除使用tr -s来“挤压”空白以定义字段的麻烦。此外,awk使用正则表达式可以更好地控制字段匹配,而不必依靠整行的grepcut来定位预定的字段号。 (尽管在某种程度上,您仍然必须告诉awk您感兴趣的ps命令中的哪个字段)

使用bash,您还可以使用进程替换|的输出发送到ps auxwww上的awk,从而消除管道stdin使用重定向,例如awk ... < <(ps auxwww)用于单个整洁的命令行。

要将"program""file_of_interest"放入awk,您有两种选择。您可以使用awk选项初始化-v var=value变量(可以给出多个-v的注解),也可以使用BEGIN规则来初始化变量。唯一的区别是与-v可以为value提供一个shell变量,并且=符号周围不允许有空格,而在BEGIN内则忽略任何空格。 / p>

因此,在您的示例中,有两个示例用于获取firefox进程的虚拟内存大小,您可以使用:

awk -v prog="firefox" -v fnum="5" '
    $11 ~ prog {print $fnum}
' < <(ps auxwww)

(如果您将myprog=firefox作为shell变量,则可以使用-v prog="$myprog"来初始化prog的{​​{1}}变量)

或使用awk规则,您可以执行以下操作:

BEGIN

在上面的每个命令中,它从awk 'BEGIN {prog = "firefox"; fnum = "5"} $11 ~ prog {print $fnum } ' < <(ps auxwww) (字段11)中找到COMMAND字段,并检查它是否包含ps,如果是,则输出字段号。 5每个进程使用的虚拟内存大小。

两者都可以很好地工作,例如

firefox

不要误会我的意思,管道非常好,速度会很慢。对于输出有限的简短命令,差别不会太大,但是当输出较大时,awk -v prog="firefox" -v fnum="5" '$11 ~ prog {print $fnum}' < <(ps auxwww) 将比必须awktr和{{1 }}读取同一记录三遍。

原因是,管道和每一侧的进程都需要由外壳生成单独的进程。因此,尽量减少使用它们,提高脚本执行效率。现在,如果数据和流程一样小,则没有太大区别。但是,如果您要3遍读取3G文件,那就是数量级上的差异。小时相对于分钟或秒。

,

我不得不在 CentosOS Linux 上使用单引号使 tr 像上述那样工作:

ps -o ppid= $$ | tr -d '[:space:]'
,

您可以使用此Perl单线减少管道数量,该单线使用Perl正则表达式而不是单独的grep进程。它将greptrcut合并到一个命令中,并通过一种简单的方式来操纵输出(@F是0索引的字段数组):

示例:


# Start an example process to provide the input for `ps` in the next commands:
/Applications/Emacs.app/Contents/MacOS/Emacs-x86_64-10_14 --geometry 109x65 /tmp/foo &

# Print single space-delimited output of `ps` for all emacs processes:
ps auxwww | perl -lane 'print "@F" if $F[10] =~ /emacs/i'

# Prints:
# bar 72144 0.0 0.5 4610272 82320 s006 SN 11:15AM 0:01.31 /Applications/Emacs.app/Contents/MacOS/Emacs-x86_64-10_14 --geometry 109x65 /tmp/foo

# Print emacs PID and file name opened with emacs:

ps auxwww | perl -lane 'print join "\t",@F[1,-1] if $F[10] =~ /emacs/i'

# Prints:
# 72144   /tmp/foo

Perl单行代码使用以下命令行标志:
-e:告诉Perl在代码中而不是在文件中查找代码。
-n:一次遍历输入一行,默认情况下将其分配给$_
-l:在直接执行代码之前,先剥离输入行分隔符(默认为* NIX上的"\n"),并在打印时附加它。 -a:在空白或$_选项中指定的正则表达式上,将@F拆分为数组-F

另请参见:
perldoc perlrun: how to execute the Perl interpreter: command line switches
perldoc perlre: Perl regular expressions (regexes)