在Bash中存储文件处理阶段

詹姆斯·泰勒(James Taylor)

我正在写一个相当长的命令,该命令可以转换文件中的文本:

grep -o '^[^#]*' file.txt | grep ':' | cut -d ':' -f1 | uniq | gcut -d '/' -f1,3 --output-delimiter=$'\t'

我想将其转换为shell脚本。尽管此脚本可以作为一系列管道正常运行,但我很难将其分解并一次进行一次转换。

我最初尝试在每个阶段设置变量,例如:

CONTENT=$(grep -o '^[^#]*' $1)
SEGMENTS=$($CONTENT | grep ':')

但不断得到:

命令太长:


我也将其分解为子shell(我认为这就是它们的名字):

CONTENT=(grep -o '^[^#]*' $1)

而且我看到这echo $CONTENT将打印命令,而不是文本,因此我想我可以:

SEGMENTS=($CONTENT | grep ':')

解析“ |”附近的错误


我也尝试过:

CONTENT=$(grep -o '^[^#]*' $1)
SEGMENTS=(cat <($CONTENT) | grep ':')

但这似乎也不起作用。

如何以更易读的形式分解很长的文本转换字符串?非常感谢你的帮助!

查尔斯·达菲

字面上的答案可能看起来像这样:

#!/bin/bash
#      ^^^^- needed for herestrings (the <<< syntax)

content=$(grep -o '^[^#]*' <file.txt)
segments=$(grep ':'        <<<"$content")
fields=$(cut -d ':' -f1    <<<"$segments")
uniq_fields=$(uniq         <<<"$fields")
result=$(gcut -d '/' -f1,3 --output-delimiter=$'\t')

没有bash,这些阶段可能看起来像:

segments=$(printf '%s\n' "$content" | grep ':')

但是,请不要这样做:它效率极低,使用的内存比原始代码多得多,并且无法并行运行(因此,如果输入文件的大小很大,则运行时间会更长)。


如果您的目标是允许检查,请考虑以下内容:

grep -o '^[^#]*' file.txt | tee without_comments.txt \
  | grep ':'              | tee colons_only.txt \
  | cut -d ':' -f1        | tee fields_only.txt \
  | uniq                  | tee fields_uniq.txt \
  | gcut -d '/' -f1,3 --output-delimiter=$'\t'

...这将为您提供每个阶段的单独输出。或者,如果您想要代码,则无需在开发和生产模式之间进行更改,请考虑使用一个函数:

set -o pipefail # prevent presence of a pipeline from changing exit status

logging() {
  filename=$1; shift
  if [ -n "$logdir" ]; then
    "[email protected]" | tee -- "$logdir/$filename"
  else
    "[email protected]"
  fi
}

logging     without_comments.txt grep -o '^[^#]*' file.txt \
  | logging colons_only.txt      grep ':' \
  | logging fields_only.txt      cut -d ':' -f1 \
  | logging fields_uniq.txt      uniq \
  | gcut -d '/' -f1,3 --output-delimiter $'\t'

...仅当变量logdir为非空时才记录日志


考虑到手头的工作,我建议改用awk。以下将远远更有效率,而且也被提出,它是更具可读性以及参数:

awk '
  BEGIN { IFS=":"; OFS="\t"; }    # split input on :s, combine output with tabs
  /#/ { gsub(/#.*/, "") }         # remove comments
  /:/ { seen[$1]++ }              # put field 1 of each line with a : into a map
  END {
    for (i in seen) {
      split($1, pieces, "/")      # split each map key on "/"s
      print pieces[0], pieces[2]  # and put the 1st and 3rd in output
    }
  }  
'

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

TOP 榜单

热门标签

归档