【问题标题】:Local vs Supplied Shell Script Arguments本地与提供的 Shell 脚本参数
【发布时间】:2021-12-31 06:25:23
【问题描述】:

编写一个接收 3 个参数的 shell 脚本,但在脚本中,其中一个命令需要在应用分隔符后检查第一个值

#!/bin/bash
awk -F'\t' '$1 ~ /$1/&&/$2/ {print $1FS$3}' $3

这个命令被调用:

bash search.sh 5 AM filename.txt

并且应该如下执行:

awk -F'\t' '$1 ~ /5/&&/AM/ {print $1FS$3}' filename.txt

该命令在 shell 脚本之外正常运行,在 shell 脚本中使用它时现在什么也不返回。 文件名.txt:

03:00:00 AM John-James Hayward  Evalyn Howell   Chyna Mercado
04:00:00 AM Chyna Mercado   Cleveland Hanna Katey Bean
05:00:00 AM Katey Bean  Billy Jones Evalyn Howell
06:00:00 AM Evalyn Howell   Saima Mcdermott Cleveland Hanna
07:00:00 AM Cleveland Hanna Abigale Rich    Billy Jones

预期输出:

05:00:00 AM Billy Jones

【问题讨论】:

  • AM 后面有标签吗?如果没有,时间和名称将是一个字段。
  • 您的时间戳可以是除整点以外的任何时间,例如你能在你的输入中有03:05:20吗?您是否尝试打印恰好在该时间或以第一个数字开头的小时内出现的行?
  • 在您的输入中如何表示午夜 - 00:00:00 AM 或其他?
  • AM 后面有个标签。理论上,时间戳可以包含的不仅仅是小时,出于本次分配的目的,它们不会。是的,午夜表示为 12:00:00 AM

标签: bash shell awk


【解决方案1】:

当您单引号时,您的参数不会被 shell 扩展。您可以使用 awk 变量(如@JohnKugelman 建议的那样 ) 更清楚地分离 shell 和 awk 代码:

#!/bin/bash
awk -F'\t' -v p="$1" -v p2="$2" '($0 ~ p && $0 ~ p2) {print $1FS$3}' "$3"

我在这里使用通用变量名称(pp2)来强调您没有锚定您的正则表达式,因此它们确实在孔线上而不是小时和上午/下午匹配符合预期。

【讨论】:

  • 最好使用-v 将变量传递给awk 以避免特殊字符的麻烦。
  • @EdMorton 先生,你是个逗比 :-)
  • 所以 -v p="$1" 在命令中创建一个临时变量,使用 p 引用并表示 $1 提供给 shell 脚本?这意味着如果我要在代码中有另一行需要引用它,就必须再次包含它?另外, $0 ~ p 正在检查 p 的整个分隔部分,对吗?
  • 是的,它创建了一个名为 p 的 awk 变量,其值为 "$1",在这种情况下,shell 将其扩展为第一个命令行参数。您可以根据需要在 awk 脚本中多次引用 p。是的,awk 变量$0 是整行,$0 ~ p 对照正则表达式p 检查它。您使用 -F 设置字段分隔符,因此您可以使用 $1、$2 等(不清楚测试数据中的空格和制表符是什么,或者您是否真的想与特定字段匹配;请参阅其他答案那个)。
  • 顺便说一句,您也可以完全跳过 shell,只需使用 she bang #!/usr/bin/awk -f 编写一个 awk 脚本,但这可能是 gnu awk 特定的。见stackoverflow.com/questions/1418245/…
【解决方案2】:

不要在 awk 脚本中嵌入 shell 变量。

这是一个带有一些解释性 cmets 的解决方案:

#!/bin/bash

[[ $# -lt 2 ]] && exit 1       ## two args required, plus files or piped/redirected input

hour="$(printf '%02d' "$1")"   ## add a leading zero if neccesary
pm=${2^^}                      ## capitalise

shift 2

time="^$hour:.* $pm\$"         ## match the whole time field

awk -F '\t' -v time="$time" \
'$1 ~ time {print $1,$3}' "$@" ## if it matches, print fields 1 and 3 (date, second name)

用法是bash search.bash HOUR PM [FILE] ...,或者./search HOUR PM [FILE] ...,如果你让它可执行的话。例如./search 5 am file.txt./search 05 AM file.txt

我假设每个字段都由制表符分隔。

【讨论】:

  • 如果每个字段都由制表符分隔,那么time="^$hour:.* $pm\$" 在前两个字段之间使用空白而不是制表符将永远无法匹配任何输入行。 .* 也有点脆弱,以防 AMPM 可能出现在输入行中的单词中间(请记住,存在的某些名称可能不符合您对以什么顺序使用哪些字符的期望!)。
【解决方案3】:

这可能是你想要做的(未经测试,使用任何 awk):

#!/usr/bin/env bash

awk -v hour="$1" -v ampm="$2" '
    BEGIN {
        FS = OFS = "\t"
        time = sprintf("%02d:00:00", hour)
    }
    ($1 == time) && ($2 == ampm) {
        print $1, $3
    }
' "${3:--}"

请注意,即使您的输入文件包含10:00:00 AM 并且使用的参数是1 AM,上述方法也可以工作。其他一些解决方案将失败,因为它们使用的是部分正则表达式比较,因此 arg 1 将匹配 10:00:0011:00:0012:00:00 输入中的 1s。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多