【问题标题】:How to use functions in bash? [duplicate]如何在 bash 中使用函数? [复制]
【发布时间】:2017-09-10 17:46:17
【问题描述】:

我有一个文件,其中包含针对文件名的文件哈希。 例如,

fb7e0a4408e46fd5573ffb9e73aec021a9dcf426235c0ccfc37d2f5e09a68a23 /path/to/some/file
237e0a4408e46fe3573f239e73aec021a9dcf426235c023fc37d2f5e09a68a12 /path/to/another/file
... and so on...

我需要将哈希转换为base64 编码格式。

所以我使用了 bash 函数和 awk 的组合。

这是我写的,

#!/bin/sh
base64Encode() {
     $1 | openssl base64 -A
}
awk ' { t = base64Encode $1; print t } ' file.txt

但它似乎不起作用。我正在使用hashdeep 生成哈希列表文件,而hashdeep 不支持base64 编码输出。这就是我使用openssl 的原因。

任何有关这方面的帮助或提示都会很棒!

编辑:

给定的答案有效,但我似乎遇到了其他问题。

通常cat filename | openssl dgst -sha256 | openssl base64 -Afilename 文件提供base64 编码输出,这是绝对正确的, 和来自hashdeep 的输出匹配来自cat filename | openssl dgst -sha256 的输出。 因此,我想将上述步骤获得的输出通过管道传输到openssl base64 -A 以获取base64 输出。但是,我仍然得到与实际结果不同的值。

虽然这可能适合单独的问题,但我仍然希望对此提供任何支持。

【问题讨论】:

  • 你期待什么输出?不需要文件名吗?
  • @123 我会使用文件名,但我无法使base64 编码工作。这只是一个演示代码。
  • awk 不是外壳。不能从 awk 脚本调用 shell 函数,就像不能从 awk 脚本调用 C 函数一样,反之亦然。在这种情况下,与从 shell 调用你的 shell 函数相比,awk 脚本对你完全没有任何作用 - 有什么理由不这样做吗?
  • 这可行,但我正在做的仍然有错误。通常cat filename | openssl dgst -sha256 | openssl base64 -Afilename 文件提供一个绝对正确的base64 编码输出,并且hashdeep 的输出与cat filename | openssl dgst -sha256 的输出相匹配。因此,我想将上述步骤获得的输出通过管道传输到openssl base64 -A 以获取base64 输出。但是,我仍然得到与实际结果不同的值。
  • Stack Overflow 是一个编程和开发问题的网站。这个问题似乎离题了,因为它与编程或开发无关。请参阅帮助中心的What topics can I ask about here。也许Super UserUnix & Linux Stack Exchange 会是一个更好的提问地方。另见Where do I post questions about Dev Ops?

标签: linux bash unix awk openssl


【解决方案1】:

仅限 awk:

$ awk '{ c="echo " $1 "|openssl base64 -A"
         c | getline r
         print r }' file
ZmI3ZTBhNDQwOGU0NmZkNTU3M2ZmYjllNzNhZWMwMjFhOWRjZjQyNjIzNWMwY2NmYzM3ZDJmNWUwOWE2OGEyMwo=
MjM3ZTBhNDQwOGU0NmZlMzU3M2YyMzllNzNhZWMwMjFhOWRjZjQyNjIzNWMwMjNmYzM3ZDJmNWUwOWE2OGExMgo=

对于紧凑的单线版本,请参阅下面@123 的评论。

...和@EdMorton 的超紧(阅读:超证明)版本。

【讨论】:

  • 可以awk '{ "echo " $1 "|openssl base64 -A" | getline}1' file
  • @123 当然可以,但是我的眼睛看起来太难描述了,我不得不把它分解。
  • @123 否,因为那样你就不能用close(c) 简单地关闭管道。 James - 请记住测试 getline 是否成功并关闭管道 (if ( (c | getline line) > 0 ) r = line; else r = "N/A"; close(c)) 但当然 awk 在这种情况下对您没有任何帮助,因此您不会在如此简单的应用程序中真正使用它,您只需使用 shell脚本。
  • @EdMorton 为什么需要简单地关闭管道?
  • 一般来说,你必须关闭管道,这样当它的输入流关闭时,它右边的命令进程可以终止,所以你不能得到“打开的管道太多”失败,所以您有 2 个选择:a) c="echo " $1 "|openssl base64 -A"; c | getline r; close(c) 或 b) "echo " $1 "|openssl base64 -A" | getline r; close("echo " $1 "|openssl base64 -A")。显然a 更可取,因此如果您在错误的位置获得空格字符或命令字符串和关闭字符串之间存在其他差异,您不会冒险不关闭管道。
【解决方案2】:

因为你特别问函数怎么用,所以我把问题分成几个小函数。在所有(更大的)bash 程序中,这是一个很好的做法。

基本规则是:函数的行为与任何其他命令一样:

  • 你可以重定向他们的输入/输出
  • 您可以使用参数调用它们
  • 等等。

最好的函数就像常见的 unix 可执行文件,例如从标准输入读取并打印到标准输出。这也允许您在管道中使用它们。

所以,现在重写:

# function for create base64 - reads from stdin, writes to stdout
base64Encode() {
        openssl base64 -A
}

# function for dealing with your file
# e.g. reads lines "hash path" and prints "base64 path"
convert_hashes() {
        while read -r hash path; do
                b64=$(base64Encode <<< "$hash")
                echo "$b64 $path"
        done
}

#the "main" program
convert_hashes < your_file.txt

输出

ZmI3ZTBhNDQwOGU0NmZkNTU3M2ZmYjllNzNhZWMwMjFhOWRjZjQyNjIzNWMwY2NmYzM3ZDJmNWUwOWE2OGEyMwo= /path/to/some/file
MjM3ZTBhNDQwOGU0NmZlMzU3M2YyMzllNzNhZWMwMjFhOWRjZjQyNjIzNWMwMjNmYzM3ZDJmNWUwOWE2OGExMgo= /path/to/another/file

是的,我知道,我只想要没有附加路径的 base64。当然,您可以修改上述convert_hashes 并从输出中删除路径,例如而不是echo "$b64 $path",您可以使用echo "$b64",输出将只是b64字符串-但是您在函数中丢失了信息-哪个字符串属于哪个路径-恕我直言,不是最佳做法。

因此,您可以保留函数原样,并使用其他工具来获取第一列 - 并且仅在需要时 - 例如在“主”程序中。这样你就为以后更通用的方式设计了一个函数。

convert_hashes < your_file.txt | cut -d ' ' -f1

输出

ZmI3ZTBhNDQwOGU0NmZkNTU3M2ZmYjllNzNhZWMwMjFhOWRjZjQyNjIzNWMwY2NmYzM3ZDJmNWUwOWE2OGEyMwo=
MjM3ZTBhNDQwOGU0NmZlMzU3M2YyMzllNzNhZWMwMjFhOWRjZjQyNjIzNWMwMjNmYzM3ZDJmNWUwOWE2OGExMgo=

现在想象一下,你扩展了脚本,不想使用文件,但输入来自另一个程序:让我们用下面的 get_data 函数来模拟这个(当然,在真实的应用程序中它会做其他事情,而不仅仅是cat

get_data() {
cat <<EOF
fb7e0a4408e46fd5573ffb9e73aec021a9dcf426235c0ccfc37d2f5e09a68a23 /path/to/some/file
237e0a4408e46fe3573f239e73aec021a9dcf426235c023fc37d2f5e09a68a12 /path/to/another/file
EOF
}

现在您可以将以上所有内容用作:

get_data | convert_hashes

输出将与上面相同。

当然,你也可以对输出做一些事情,比如说

get_data | convert_hashes | grep another/file | cut -d ' ' -f1
MjM3ZTBhNDQwOGU0NmZlMzU3M2YyMzllNzNhZWMwMjFhOWRjZjQyNjIzNWMwMjNmYzM3ZDJmNWUwOWE2OGExMgo=

当然,如果你有这样的“模块化”结构,你可以很容易地更换任何部件,而无需触摸其他部件,比如说用base64命令替换openssl

base64Encode() {
        base64
}

一切都会继续工作,没有任何其他变化。当然,在真正的应用程序中(可能)拥有只调用一个程序的函数是毫无意义的——但我特别这样做是因为你询问了这些函数。

否则,上面可以简单地完成:

while read -r hash path; do
    openssl base64 -A <<<"$hash"
    echo
    #or echo $(openssl base64 -A <<<"$hash")
    #or printf "%s\n" $(openssl base64 -A <<<"$hash")
done < your_file.txt

甚至

cut -d ' ' -f1 base  | xargs -I% -n1 bash -c 'echo $(openssl base64 -A <<<"%")'

您需要echoprint,因为openssl 默认情况下不打印换行符。输出:

ZmI3ZTBhNDQwOGU0NmZkNTU3M2ZmYjllNzNhZWMwMjFhOWRjZjQyNjIzNWMwY2NmYzM3ZDJmNWUwOWE2OGEyMwo=
MjM3ZTBhNDQwOGU0NmZlMzU3M2YyMzllNzNhZWMwMjFhOWRjZjQyNjIzNWMwMjNmYzM3ZDJmNWUwOWE2OGExMgo=

Ps:老实说,我不明白为什么你需要 base64 编码一些已经编码的哈希 - 但 YMMV。 :)

【讨论】:

  • 感谢您提供如此丰富的答案!
  • @Ozil 没问题,享受 - 只是仍然不明白你想要实现什么......
猜你喜欢
  • 2020-06-08
  • 2018-06-19
  • 2020-11-19
  • 2013-10-25
  • 2017-05-20
  • 2018-11-25
  • 1970-01-01
  • 2018-02-03
  • 2013-02-07
相关资源
最近更新 更多